* Merged v4

* Completed some todos
* Fixed PermChecker
* Updated packages
* Upped version to alpha2
* Cleanup
This commit is contained in:
Kwoth
2024-03-25 13:41:44 +00:00
44 changed files with 519 additions and 370 deletions

View File

@@ -2,6 +2,23 @@
Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
## [4.3.20] - 20.01.2024
### Fixed
- Fixed `.config searches followedStreams.maxCount` not working
## [4.3.19] - 20.01.2024
### Added
- Added `followedStreams.maxCount` to `searches.yml` which lets bot owners change the default of 10 per server
### Changed
- Improvements to GPT ChatterBot (thx alexandra)
- Add a personality prompt to tweak the way chatgpt bot behaves
- Added Chat history support to chatgpt ChatterBot
- Chatgpt token usage now correctly calculated
- More chatgpt configs in `games.yml`
## [4.3.18] - 26.12.2023 ## [4.3.18] - 26.12.2023
### Added ### Added
@@ -23,7 +40,6 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
### Removed ### Removed
- `.revimg` and `.revav` as google removed reverse image search - `.revimg` and `.revav` as google removed reverse image search
-
## [4.3.17] - 06.09.2023 ## [4.3.17] - 06.09.2023

View File

@@ -1,13 +1,39 @@
using OneOf; using Nadeko.Bot.Db.Models;
using OneOf;
using OneOf.Types; using OneOf.Types;
namespace Nadeko.Bot.Common; namespace Nadeko.Bot.Common;
public interface IPermissionChecker public interface IPermissionChecker
{ {
Task<OneOf<Success, Error<LocStr>>> CheckAsync(IGuild guild, Task<PermCheckResult> CheckPermsAsync(IGuild guild,
IMessageChannel channel, IMessageChannel channel,
IUser author, IUser author,
string module, string module,
string? cmd); string? cmd);
} }
[GenerateOneOf]
public partial class PermCheckResult
: OneOfBase<PermAllowed, PermCooldown, PermGlobalBlock, PermDisallowed>
{
public bool IsAllowed
=> IsT0;
public bool IsCooldown
=> IsT1;
public bool IsGlobalBlock
=> IsT2;
public bool IsDisallowed
=> IsT3;
}
public readonly record struct PermAllowed;
public readonly record struct PermCooldown;
public readonly record struct PermGlobalBlock;
public readonly record struct PermDisallowed(int PermIndex, string PermText, bool IsVerbose);

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
@@ -13,10 +13,11 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Nadeko.Bot.Db\Nadeko.Bot.Db.csproj" /> <ProjectReference Include="..\Nadeko.Bot.Db\Nadeko.Bot.Db.csproj" />
<ProjectReference Include="..\Nadeko.Common\Nadeko.Common.csproj" /> <ProjectReference Include="..\Nadeko.Common\Nadeko.Common.csproj" />
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" /> <PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="OneOf.SourceGenerator" Version="3.0.263" />
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta17" /> <PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta17" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" /> <PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14" /> <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0009" /> <PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0009" />
<PackageReference Include="CommandLineParser" Version="2.9.1" /> <PackageReference Include="CommandLineParser" Version="2.9.1" />
@@ -25,7 +26,7 @@
<Publish>True</Publish> <Publish>True</Publish>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<ProjectReference Include="..\Nadeko.Medusa\Nadeko.Medusa.csproj" /> <ProjectReference Include="..\Nadeko.Medusa\Nadeko.Medusa.csproj" />

View File

@@ -60,7 +60,6 @@ public class CommandHandler : INService, IReadyExecutor, ICommandHandler
public async Task OnReadyAsync() public async Task OnReadyAsync()
{ {
Log.Information("Command handler runnning on ready");
// clear users on short cooldown every GLOBAL_COMMANDS_COOLDOWN miliseconds // clear users on short cooldown every GLOBAL_COMMANDS_COOLDOWN miliseconds
using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(GLOBAL_COMMANDS_COOLDOWN)); using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(GLOBAL_COMMANDS_COOLDOWN));
while (await timer.WaitForNextTickAsync()) while (await timer.WaitForNextTickAsync())

View File

@@ -7,7 +7,7 @@ namespace NadekoBot.Services;
public sealed class StatsService : IStatsService, IReadyExecutor, INService public sealed class StatsService : IStatsService, IReadyExecutor, INService
{ {
public const string BOT_VERSION = "5.0.0-alpha1"; public const string BOT_VERSION = "5.0.0-alpha2";
public string Author public string Author
=> "Kwoth#2452"; => "Kwoth#2452";

View File

@@ -23,7 +23,7 @@ public static class Rgba32Extensions
{ {
using var frame = imgArray[i].Frames.CloneFrame(frameNumber % imgArray[i].Frames.Count); using var frame = imgArray[i].Frames.CloneFrame(frameNumber % imgArray[i].Frames.Count);
var offset = xOffset; var offset = xOffset;
imgFrame.Mutate(x => x.DrawImage(frame, new(offset, 0), new GraphicsOptions())); imgFrame.Mutate(x => x.DrawImage(frame, new Point(offset, 0), new GraphicsOptions()));
xOffset += imgArray[i].Bounds().Width; xOffset += imgArray[i].Bounds().Width;
} }
} }

View File

@@ -28,5 +28,5 @@ public class DiscordUser : DbEntity
=> UserId.GetHashCode(); => UserId.GetHashCode();
public override string ToString() public override string ToString()
=> Username + "#" + Discriminator; => Discriminator == "0000" ? Username : Username + "#" + Discriminator;
} }

View File

@@ -2,7 +2,6 @@
namespace Nadeko.Bot.Db.Models; namespace Nadeko.Bot.Db.Models;
// todo db required, nullable?
public class AntiRaidSetting : DbEntity public class AntiRaidSetting : DbEntity
{ {
public int GuildConfigId { get; set; } public int GuildConfigId { get; set; }

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<RootNamespace>NadekoBot.Db</RootNamespace> <RootNamespace>NadekoBot.Db</RootNamespace>
@@ -9,19 +9,19 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="linq2db.EntityFrameworkCore" Version="7.3.0" /> <PackageReference Include="linq2db.EntityFrameworkCore" Version="8.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.4"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.3">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.3" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
<PackageReference Include="EFCore.NamingConventions" Version="7.0.2" /> <PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net" Version="3.203.0" /> <PackageReference Include="Discord.Net" Version="3.203.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<RootNamespace>NadekoBot.Modules.Expresssions</RootNamespace> <RootNamespace>NadekoBot.Modules.Expresssions</RootNamespace>
@@ -15,7 +15,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net" Version="3.203.0" /> <PackageReference Include="Discord.Net" Version="3.203.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -20,20 +20,20 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
private const string PREPEND_EXPORT = private const string PREPEND_EXPORT =
""" """
# Keys are triggers, Each key has a LIST of expressions in the following format: # Keys are triggers, Each key has a LIST of expressions in the following format:
# - res: Response string # - res: Response string
# id: Alphanumeric id used for commands related to the expression. (Note, when using .exprsimport, a new id will be generated.) # id: Alphanumeric id used for commands related to the expression. (Note, when using .exprsimport, a new id will be generated.)
# react: # react:
# - <List # - <List
# - of # - of
# - reactions> # - reactions>
# at: Whether expression allows targets (see .h .exprat) # at: Whether expression allows targets (see .h .exprat)
# ca: Whether expression expects trigger anywhere (see .h .exprca) # ca: Whether expression expects trigger anywhere (see .h .exprca)
# dm: Whether expression DMs the response (see .h .exprdm) # dm: Whether expression DMs the response (see .h .exprdm)
# ad: Whether expression automatically deletes triggering message (see .h .exprad) # ad: Whether expression automatically deletes triggering message (see .h .exprad)
""";
""";
private static readonly ISerializer _exportSerializer = new SerializerBuilder() private static readonly ISerializer _exportSerializer = new SerializerBuilder()
.WithEventEmitter(args .WithEventEmitter(args
@@ -63,7 +63,9 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
private ConcurrentDictionary<ulong, NadekoExpression[]> newguildExpressions = new(); private ConcurrentDictionary<ulong, NadekoExpression[]> newguildExpressions = new();
private readonly DbService _db; private readonly DbService _db;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
// private readonly PermissionService _perms; // private readonly PermissionService _perms;
// private readonly GlobalPermissionService _gperm; // private readonly GlobalPermissionService _gperm;
// private readonly CmdCdService _cmdCds; // private readonly CmdCdService _cmdCds;
@@ -238,46 +240,40 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
if (expr is null || expr.Response == "-") if (expr is null || expr.Response == "-")
return false; return false;
var result = await _permChecker.CheckAsync(
guild,
msg.Channel,
msg.Author,
"ACTUALEXPRESSIONS",
expr.Trigger
);
if (!result.IsT0)
return false;
// todo print error etc
try try
{ {
// if (guild is SocketGuild sg) if (guild is SocketGuild sg)
// { {
// var pc = _perms.GetCacheFor(guild.Id); var result = await _permChecker.CheckPermsAsync(
// if (!pc.Permissions.CheckPermissions(msg, expr.Trigger, "ACTUALEXPRESSIONS", out var index)) guild,
// { msg.Channel,
// if (pc.Verbose) msg.Author,
// { "ACTUALEXPRESSIONS",
// var permissionMessage = _strings.GetText(strs.perm_prevent(index + 1, expr.Trigger
// Format.Bold(pc.Permissions[index].GetCommand(_cmd.GetPrefix(guild), sg))), );
// sg.Id);
// if (!result.IsAllowed)
// try {
// { if (result.TryPickT3(out var disallowed, out _))
// await msg.Channel.SendErrorAsync(_eb, permissionMessage); {
// } var permissionMessage = _strings.GetText(strs.perm_prevent(disallowed.PermIndex + 1,
// catch Format.Bold(disallowed.PermText)),
// { sg.Id);
// }
// try
// Log.Information("{PermissionMessage}", permissionMessage); {
// } await msg.Channel.SendErrorAsync(_eb, permissionMessage);
// }
// return true; catch
// } {
// } }
Log.Information("{PermissionMessage}", permissionMessage);
}
return true;
}
}
var sentMsg = await expr.Send(msg, _client, false); var sentMsg = await expr.Send(msg, _client, false);
@@ -556,7 +552,8 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
foreach (var entry in data) foreach (var entry in data)
{ {
var trigger = entry.Key; var trigger = entry.Key;
await uow.Set<NadekoExpression>().AddRangeAsync(entry.Value.Where(expr => !string.IsNullOrWhiteSpace(expr.Res)) await uow.Set<NadekoExpression>().AddRangeAsync(entry.Value
.Where(expr => !string.IsNullOrWhiteSpace(expr.Res))
.Select(expr => new NadekoExpression .Select(expr => new NadekoExpression
{ {
GuildId = guildId, GuildId = guildId,

View File

@@ -76,8 +76,12 @@ public class ChatterBotService : IExecOnMessage
case ChatBotImplementation.Gpt3: case ChatBotImplementation.Gpt3:
if (!string.IsNullOrWhiteSpace(_creds.Gpt3ApiKey)) if (!string.IsNullOrWhiteSpace(_creds.Gpt3ApiKey))
return new OfficialGpt3Session(_creds.Gpt3ApiKey, return new OfficialGpt3Session(_creds.Gpt3ApiKey,
_gcs.Data.ChatGpt.Model, _gcs.Data.ChatGpt.ModelName,
_gcs.Data.ChatGpt.ChatHistory,
_gcs.Data.ChatGpt.MaxTokens, _gcs.Data.ChatGpt.MaxTokens,
_gcs.Data.ChatGpt.MinTokens,
_gcs.Data.ChatGpt.PersonalityPrompt,
_client.CurrentUser.Username,
_httpFactory); _httpFactory);
Log.Information("Gpt3 will not work as the api key is missing."); Log.Information("Gpt3 will not work as the api key is missing.");
@@ -125,17 +129,14 @@ public class ChatterBotService : IExecOnMessage
if (message is null || cbs is null) if (message is null || cbs is null)
return false; return false;
var res = await _perms.CheckAsync(sg, var res = await _perms.CheckPermsAsync(sg,
usrMsg.Channel, usrMsg.Channel,
usrMsg.Author, usrMsg.Author,
"games", "games",
CleverBotResponseStr.CLEVERBOT_RESPONSE); CleverBotResponseStr.CLEVERBOT_RESPONSE);
// todo this needs checking, this might block all messages in a channel if cleverbot is enabled but blocked if (!res.IsAllowed)
// need to check what kind of block it is return false;
// might be the case for other classes using permission checker
if (!res.IsT0)
return true;
var channel = (ITextChannel)usrMsg.Channel; var channel = (ITextChannel)usrMsg.Channel;
var conf = _ps.GetConfig(); var conf = _ps.GetConfig();
@@ -183,7 +184,7 @@ public class ChatterBotService : IExecOnMessage
} }
_ = channel.TriggerTypingAsync(); _ = channel.TriggerTypingAsync();
var response = await cbs.Think(message); var response = await cbs.Think(message, usrMsg.Author.ToString());
await channel.SendConfirmAsync(_eb, await channel.SendConfirmAsync(_eb,
title: null, title: null,
response.SanitizeMentions(true) response.SanitizeMentions(true)

View File

@@ -11,7 +11,13 @@ public class Gpt3Response
public class Choice public class Choice
{ {
public string Text { get; set; } [JsonPropertyName("message")]
public Message Message { get; init; }
}
public class Message {
[JsonPropertyName("content")]
public string Content { get; init; }
} }
public class Gpt3ApiRequest public class Gpt3ApiRequest
@@ -19,12 +25,22 @@ public class Gpt3ApiRequest
[JsonPropertyName("model")] [JsonPropertyName("model")]
public string Model { get; init; } public string Model { get; init; }
[JsonPropertyName("prompt")] [JsonPropertyName("messages")]
public string Prompt { get; init; } public List<GPTMessage> Messages { get; init; }
[JsonPropertyName("temperature")] [JsonPropertyName("temperature")]
public int Temperature { get; init; } public int Temperature { get; init; }
[JsonPropertyName("max_tokens")] [JsonPropertyName("max_tokens")]
public int MaxTokens { get; init; } public int MaxTokens { get; init; }
}
public class GPTMessage
{
[JsonPropertyName("role")]
public string Role {get; init;}
[JsonPropertyName("content")]
public string Content {get; init;}
[JsonPropertyName("name")]
public string Name {get; init;}
} }

View File

@@ -3,5 +3,5 @@ namespace NadekoBot.Modules.Games.Common.ChatterBot;
public interface IChatterBotSession public interface IChatterBotSession
{ {
Task<string> Think(string input); Task<string> Think(string input, string username);
} }

View File

@@ -18,7 +18,7 @@ public class OfficialCleverbotSession : IChatterBotSession
_httpFactory = factory; _httpFactory = factory;
} }
public async Task<string> Think(string input) public async Task<string> Think(string input, string username)
{ {
using var http = _httpFactory.CreateClient(); using var http = _httpFactory.CreateClient();
var dataString = await http.GetStringAsync(string.Format(QueryString, input, cs ?? "")); var dataString = await http.GetStringAsync(string.Format(QueryString, input, cs ?? ""));

View File

@@ -1,63 +1,99 @@
#nullable disable #nullable disable
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Net.Http.Json; using System.Net.Http.Json;
using SharpToken;
namespace NadekoBot.Modules.Games.Common.ChatterBot; namespace NadekoBot.Modules.Games.Common.ChatterBot;
public class OfficialGpt3Session : IChatterBotSession public class OfficialGpt3Session : IChatterBotSession
{ {
private string Uri private string Uri
=> $"https://api.openai.com/v1/completions"; => $"https://api.openai.com/v1/chat/completions";
private readonly string _apiKey; private readonly string _apiKey;
private readonly string _model; private readonly string _model;
private readonly int _maxHistory;
private readonly int _maxTokens; private readonly int _maxTokens;
private readonly int _minTokens;
private readonly string _nadekoUsername;
private readonly GptEncoding _encoding;
private List<GPTMessage> messages = new();
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
public OfficialGpt3Session( public OfficialGpt3Session(
string apiKey, string apiKey,
Gpt3Model model, ChatGptModel model,
int chatHistory,
int maxTokens, int maxTokens,
int minTokens,
string personality,
string nadekoUsername,
IHttpClientFactory factory) IHttpClientFactory factory)
{ {
_apiKey = apiKey; _apiKey = apiKey;
_httpFactory = factory; _httpFactory = factory;
switch (model) switch (model)
{ {
case Gpt3Model.Ada001: case ChatGptModel.Gpt35Turbo:
_model = "text-ada-001"; _model = "gpt-3.5-turbo";
break; break;
case Gpt3Model.Babbage001: case ChatGptModel.Gpt4:
_model = "text-babbage-001"; _model = "gpt-4";
break; break;
case Gpt3Model.Curie001: case ChatGptModel.Gpt432k:
_model = "text-curie-001"; _model = "gpt-4-32k";
break;
case Gpt3Model.Davinci003:
_model = "text-davinci-003";
break; break;
} }
_maxHistory = chatHistory;
_maxTokens = maxTokens; _maxTokens = maxTokens;
_minTokens = minTokens;
_nadekoUsername = nadekoUsername;
_encoding = GptEncoding.GetEncodingForModel(_model);
messages.Add(new GPTMessage(){Role = "user", Content = personality, Name = _nadekoUsername});
} }
public async Task<string> Think(string input) public async Task<string> Think(string input, string username)
{ {
messages.Add(new GPTMessage(){Role = "user", Content = input, Name = username});
while(messages.Count > _maxHistory + 2){
messages.RemoveAt(1);
}
int tokensUsed = 0;
foreach(GPTMessage message in messages){
tokensUsed += _encoding.Encode(message.Content).Count;
}
tokensUsed *= 2; //Unsure why this is the case, but the token count chatgpt reports back is double what I calculate.
//check if we have the minimum number of tokens available to use. Remove messages until we have enough, otherwise exit out and inform the user why.
while(_maxTokens - tokensUsed <= _minTokens){
if(messages.Count > 2){
int tokens = _encoding.Encode(messages[1].Content).Count * 2;
tokensUsed -= tokens;
messages.RemoveAt(1);
}
else{
return "Token count exceeded, please increase the number of tokens in the bot config and restart.";
}
}
using var http = _httpFactory.CreateClient(); using var http = _httpFactory.CreateClient();
http.DefaultRequestHeaders.Authorization = new("Bearer", _apiKey); http.DefaultRequestHeaders.Authorization = new("Bearer", _apiKey);
var data = await http.PostAsJsonAsync(Uri, new Gpt3ApiRequest() var data = await http.PostAsJsonAsync(Uri, new Gpt3ApiRequest()
{ {
Model = _model, Model = _model,
Prompt = input, Messages = messages,
MaxTokens = _maxTokens, MaxTokens = _maxTokens - tokensUsed,
Temperature = 1, Temperature = 1,
}); });
var dataString = await data.Content.ReadAsStringAsync(); var dataString = await data.Content.ReadAsStringAsync();
try try
{ {
var response = JsonConvert.DeserializeObject<Gpt3Response>(dataString); var response = JsonConvert.DeserializeObject<Gpt3Response>(dataString);
string message = response?.Choices[0]?.Message?.Content;
return response?.Choices[0]?.Text; //Can't rely on the return to except, now that we need to add it to the messages list.
_ = message ?? throw new ArgumentNullException(nameof(message));
messages.Add(new GPTMessage(){Role = "assistant", Content = message, Name = _nadekoUsername});
return message;
} }
catch catch
{ {

View File

@@ -8,7 +8,7 @@ namespace NadekoBot.Modules.Games.Common;
public sealed partial class GamesConfig : ICloneable<GamesConfig> public sealed partial class GamesConfig : ICloneable<GamesConfig>
{ {
[Comment("DO NOT CHANGE")] [Comment("DO NOT CHANGE")]
public int Version { get; set; } = 2; public int Version { get; set; } = 3;
[Comment("Hangman related settings (.hangman command)")] [Comment("Hangman related settings (.hangman command)")]
public HangmanConfig Hangman { get; set; } = new() public HangmanConfig Hangman { get; set; } = new()
@@ -108,14 +108,22 @@ public sealed partial class GamesConfig : ICloneable<GamesConfig>
public sealed partial class ChatGptConfig public sealed partial class ChatGptConfig
{ {
[Comment(@"Which GPT-3 Model should bot use. [Comment(@"Which GPT-3 Model should bot use.
'ada001' - cheapest and fastest gpt35turbo - cheapest
'babbage001' - 2nd option gpt4 - 30x more expensive, higher quality
'curie001' - 3rd option gp432k - same model as above, but with a 32k token limit")]
'davinci003' - Most expensive, slowest")] public ChatGptModel ModelName { get; set; } = ChatGptModel.Gpt35Turbo;
public Gpt3Model Model { get; set; } = Gpt3Model.Ada001;
[Comment(@"How should the chat bot behave, what's its personality? (Usage of this counts towards the max tokens)")]
public string PersonalityPrompt { get; set; } = "You are a chat bot willing to have a conversation with anyone about anything.";
[Comment(@"The maximum number of messages in a conversation that can be remembered. (This will increase the number of tokens used)")]
public int ChatHistory { get; set; } = 5;
[Comment(@"The maximum number of tokens to use per GPT-3 API call")] [Comment(@"The maximum number of tokens to use per GPT-3 API call")]
public int MaxTokens { get; set; } = 100; public int MaxTokens { get; set; } = 100;
[Comment(@"The minimum number of tokens to use per GPT-3 API call, such that chat history is removed to make room.")]
public int MinTokens { get; set; } = 30;
} }
[Cloneable] [Cloneable]
@@ -151,10 +159,9 @@ public enum ChatBotImplementation
Gpt3 Gpt3
} }
public enum Gpt3Model public enum ChatGptModel
{ {
Ada001, Gpt35Turbo,
Babbage001, Gpt4,
Curie001, Gpt432k
Davinci003
} }

View File

@@ -28,20 +28,33 @@ public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
long.TryParse, long.TryParse,
ConfigPrinters.ToString, ConfigPrinters.ToString,
val => val >= 0); val => val >= 0);
AddParsedProp("chatbot", AddParsedProp("chatbot",
gs => gs.ChatBot, gs => gs.ChatBot,
ConfigParsers.InsensitiveEnum, ConfigParsers.InsensitiveEnum,
ConfigPrinters.ToString); ConfigPrinters.ToString);
AddParsedProp("gpt.model", AddParsedProp("gpt.modelName",
gs => gs.ChatGpt.Model, gs => gs.ChatGpt.ModelName,
ConfigParsers.InsensitiveEnum, ConfigParsers.InsensitiveEnum,
ConfigPrinters.ToString); ConfigPrinters.ToString);
AddParsedProp("gpt.personality",
gs => gs.ChatGpt.PersonalityPrompt,
ConfigParsers.String,
ConfigPrinters.ToString);
AddParsedProp("gpt.chathistory",
gs => gs.ChatGpt.ChatHistory,
int.TryParse,
ConfigPrinters.ToString,
val => val > 0);
AddParsedProp("gpt.max_tokens", AddParsedProp("gpt.max_tokens",
gs => gs.ChatGpt.MaxTokens, gs => gs.ChatGpt.MaxTokens,
int.TryParse, int.TryParse,
ConfigPrinters.ToString, ConfigPrinters.ToString,
val => val > 0); val => val > 0);
AddParsedProp("gpt.min_tokens",
gs => gs.ChatGpt.MinTokens,
int.TryParse,
ConfigPrinters.ToString,
val => val > 0);
Migrate(); Migrate();
} }
@@ -65,7 +78,16 @@ public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
ModifyConfig(c => ModifyConfig(c =>
{ {
c.Version = 2; c.Version = 2;
c.ChatBot = ChatBotImplementation.Cleverbot; c.ChatBot = ChatBotImplementation.Cleverbot;
});
}
if (data.Version < 3)
{
ModifyConfig(c =>
{
c.Version = 3;
c.ChatGpt.ModelName = ChatGptModel.Gpt35Turbo;
}); });
} }
} }

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
@@ -14,8 +14,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CoreCLR-NCalc" Version="2.2.110"/> <PackageReference Include="CoreCLR-NCalc" Version="3.0.203" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0"/> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="SharpToken" Version="1.2.14" />
</ItemGroup> </ItemGroup>

View File

@@ -76,10 +76,10 @@ public sealed class Help : NadekoModule<HelpService>
var topLevelModules = new List<ModuleInfo>(); var topLevelModules = new List<ModuleInfo>();
foreach (var m in _cmds.Modules.GroupBy(x => x.GetTopLevelModule()).Select(x => x.Key)) foreach (var m in _cmds.Modules.GroupBy(x => x.GetTopLevelModule()).Select(x => x.Key))
{ {
var result = await _perms.CheckAsync(ctx.Guild, ctx.Channel, ctx.User, var result = await _perms.CheckPermsAsync(ctx.Guild, ctx.Channel, ctx.User,
m.Name, null); m.Name, null);
if (result.IsT0) if (result.IsAllowed)
topLevelModules.Add(m); topLevelModules.Add(m);
} }
@@ -222,9 +222,10 @@ public sealed class Help : NadekoModule<HelpService>
.Name .Name
.StartsWith(module, StringComparison.InvariantCultureIgnoreCase))) .StartsWith(module, StringComparison.InvariantCultureIgnoreCase)))
{ {
var result = await _perms.CheckAsync(ctx.Guild, ctx.Channel, ctx.User, cmd.Module.GetTopLevelModule().Name, var result = await _perms.CheckPermsAsync(ctx.Guild, ctx.Channel, ctx.User, cmd.Module.GetTopLevelModule().Name,
cmd.Name); cmd.Name);
if (result.IsT0)
if (result.IsAllowed)
allowed.Add(cmd); allowed.Add(cmd);
} }
@@ -311,7 +312,7 @@ public sealed class Help : NadekoModule<HelpService>
.WithTitle(GetText(strs.cmd_group_commands(group.Name))) .WithTitle(GetText(strs.cmd_group_commands(group.Name)))
.WithOkColor(); .WithOkColor();
foreach (var cmd in group.Commands) foreach (var cmd in group.Commands.DistinctBy(x => x.Aliases[0]))
{ {
eb.AddField(prefix + cmd.Aliases.First(), cmd.RealSummary(_strings, _medusae, Culture, prefix)); eb.AddField(prefix + cmd.Aliases.First(), cmd.RealSummary(_strings, _medusae, Culture, prefix));
} }

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
@@ -13,7 +13,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AWSSDK.S3" Version="3.7.101.58"/> <PackageReference Include="AWSSDK.S3" Version="3.7.307" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
@@ -15,7 +15,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0"/> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
</ItemGroup> </ItemGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
@@ -14,20 +14,20 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AngleSharp" Version="1.0.1"/> <PackageReference Include="AngleSharp" Version="1.1.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/> <PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0"/> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="CsvHelper" Version="30.0.1"/> <PackageReference Include="CsvHelper" Version="31.0.2" />
<PackageReference Include="MorseCode.ITask" Version="2.0.3"/> <PackageReference Include="MorseCode.ITask" Version="2.0.3"/>
<!-- Stream Notifications --> <!-- Stream Notifications -->
<PackageReference Include="TwitchLib.Api" Version="3.4.1"/> <PackageReference Include="TwitchLib.Api" Version="3.9.0" />
<!-- Feeds, ... ? --> <!-- Feeds, ... ? -->
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.6"/> <PackageReference Include="CodeHollow.FeedReader" Version="1.2.6"/>
<!-- .hs, nothing else --> <!-- .hs, nothing else -->
<PackageReference Include="Html2Markdown" Version="5.1.0.703"/> <PackageReference Include="Html2Markdown" Version="6.2.0.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -27,6 +27,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
private readonly IPubSub _pubSub; private readonly IPubSub _pubSub;
private readonly IEmbedBuilderService _eb; private readonly IEmbedBuilderService _eb;
private readonly SearchesConfigService _config;
public TypedKey<List<StreamData>> StreamsOnlineKey { get; } public TypedKey<List<StreamData>> StreamsOnlineKey { get; }
public TypedKey<List<StreamData>> StreamsOfflineKey { get; } public TypedKey<List<StreamData>> StreamsOfflineKey { get; }
@@ -48,14 +49,16 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
IHttpClientFactory httpFactory, IHttpClientFactory httpFactory,
IBot bot, IBot bot,
IPubSub pubSub, IPubSub pubSub,
IEmbedBuilderService eb) IEmbedBuilderService eb,
SearchesConfigService config)
{ {
_db = db; _db = db;
_client = client; _client = client;
_strings = strings; _strings = strings;
_pubSub = pubSub; _pubSub = pubSub;
_eb = eb; _eb = eb;
_config = config;
_streamTracker = new(httpFactory, creds); _streamTracker = new(httpFactory, creds);
StreamsOnlineKey = new("streams.online"); StreamsOnlineKey = new("streams.online");
@@ -68,34 +71,34 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
{ {
var ids = client.GetGuildIds(); var ids = client.GetGuildIds();
var guildConfigs = uow.Set<GuildConfig>() var guildConfigs = uow.Set<GuildConfig>()
.AsQueryable() .AsQueryable()
.Include(x => x.FollowedStreams) .Include(x => x.FollowedStreams)
.Where(x => ids.Contains(x.GuildId)) .Where(x => ids.Contains(x.GuildId))
.ToList(); .ToList();
_offlineNotificationServers = new(guildConfigs _offlineNotificationServers = new(guildConfigs
.Where(gc => gc.NotifyStreamOffline) .Where(gc => gc.NotifyStreamOffline)
.Select(x => x.GuildId) .Select(x => x.GuildId)
.ToList()); .ToList());
_deleteOnOfflineServers = new(guildConfigs _deleteOnOfflineServers = new(guildConfigs
.Where(gc => gc.DeleteStreamOnlineMessage) .Where(gc => gc.DeleteStreamOnlineMessage)
.Select(x => x.GuildId) .Select(x => x.GuildId)
.ToList()); .ToList());
var followedStreams = guildConfigs.SelectMany(x => x.FollowedStreams).ToList(); var followedStreams = guildConfigs.SelectMany(x => x.FollowedStreams).ToList();
_shardTrackedStreams = followedStreams.GroupBy(x => new _shardTrackedStreams = followedStreams.GroupBy(x => new
{ {
x.Type, x.Type,
Name = x.Username.ToLower() Name = x.Username.ToLower()
}) })
.ToList() .ToList()
.ToDictionary( .ToDictionary(
x => new StreamDataKey(x.Key.Type, x.Key.Name.ToLower()), x => new StreamDataKey(x.Key.Type, x.Key.Name.ToLower()),
x => x.GroupBy(y => y.GuildId) x => x.GroupBy(y => y.GuildId)
.ToDictionary(y => y.Key, .ToDictionary(y => y.Key,
y => y.AsEnumerable().ToHashSet())); y => y.AsEnumerable().ToHashSet()));
// shard 0 will keep track of when there are no more guilds which track a stream // shard 0 will keep track of when there are no more guilds which track a stream
if (client.ShardId == 0) if (client.ShardId == 0)
@@ -106,12 +109,12 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
_streamTracker.AddLastData(fs.CreateKey(), null, false); _streamTracker.AddLastData(fs.CreateKey(), null, false);
_trackCounter = allFollowedStreams.GroupBy(x => new _trackCounter = allFollowedStreams.GroupBy(x => new
{ {
x.Type, x.Type,
Name = x.Username.ToLower() Name = x.Username.ToLower()
}) })
.ToDictionary(x => new StreamDataKey(x.Key.Type, x.Key.Name), .ToDictionary(x => new StreamDataKey(x.Key.Type, x.Key.Name),
x => x.Select(fs => fs.GuildId).ToHashSet()); x => x.Select(fs => fs.GuildId).ToHashSet());
} }
} }
@@ -151,7 +154,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
continue; continue;
var deleteGroups = failingStreams.GroupBy(x => x.Type) var deleteGroups = failingStreams.GroupBy(x => x.Type)
.ToDictionary(x => x.Key, x => x.Select(y => y.Name).ToList()); .ToDictionary(x => x.Key, x => x.Select(y => y.Name).ToList());
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
foreach (var kvp in deleteGroups) foreach (var kvp in deleteGroups)
@@ -164,9 +167,9 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
string.Join(", ", kvp.Value)); string.Join(", ", kvp.Value));
var toDelete = uow.Set<FollowedStream>() var toDelete = uow.Set<FollowedStream>()
.AsQueryable() .AsQueryable()
.Where(x => x.Type == kvp.Key && kvp.Value.Contains(x.Username)) .Where(x => x.Type == kvp.Key && kvp.Value.Contains(x.Username))
.ToList(); .ToList();
uow.RemoveRange(toDelete); uow.RemoveRange(toDelete);
await uow.SaveChangesAsync(); await uow.SaveChangesAsync();
@@ -245,17 +248,17 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
if (_shardTrackedStreams.TryGetValue(key, out var fss)) if (_shardTrackedStreams.TryGetValue(key, out var fss))
{ {
await fss await fss
// send offline stream notifications only to guilds which enable it with .stoff // send offline stream notifications only to guilds which enable it with .stoff
.SelectMany(x => x.Value) .SelectMany(x => x.Value)
.Where(x => _offlineNotificationServers.Contains(x.GuildId)) .Where(x => _offlineNotificationServers.Contains(x.GuildId))
.Select(fs => _client.GetGuild(fs.GuildId) .Select(fs => _client.GetGuild(fs.GuildId)
?.GetTextChannel(fs.ChannelId) ?.GetTextChannel(fs.ChannelId)
?.EmbedAsync(GetEmbed(fs.GuildId, stream))) ?.EmbedAsync(GetEmbed(fs.GuildId, stream)))
.WhenAll(); .WhenAll();
} }
} }
} }
private async ValueTask HandleStreamsOnline(List<StreamData> onlineStreams) private async ValueTask HandleStreamsOnline(List<StreamData> onlineStreams)
{ {
@@ -265,30 +268,30 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
if (_shardTrackedStreams.TryGetValue(key, out var fss)) if (_shardTrackedStreams.TryGetValue(key, out var fss))
{ {
var messages = await fss.SelectMany(x => x.Value) var messages = await fss.SelectMany(x => x.Value)
.Select(async fs => .Select(async fs =>
{ {
var textChannel = _client.GetGuild(fs.GuildId)?.GetTextChannel(fs.ChannelId); var textChannel = _client.GetGuild(fs.GuildId)?.GetTextChannel(fs.ChannelId);
if (textChannel is null) if (textChannel is null)
return default; return default;
var rep = new ReplacementBuilder().WithOverride("%user%", () => fs.Username) var rep = new ReplacementBuilder().WithOverride("%user%", () => fs.Username)
.WithOverride("%platform%", () => fs.Type.ToString()) .WithOverride("%platform%", () => fs.Type.ToString())
.Build(); .Build();
var message = string.IsNullOrWhiteSpace(fs.Message) ? "" : rep.Replace(fs.Message); var message = string.IsNullOrWhiteSpace(fs.Message) ? "" : rep.Replace(fs.Message);
var msg = await textChannel.EmbedAsync(GetEmbed(fs.GuildId, stream, false), message); var msg = await textChannel.EmbedAsync(GetEmbed(fs.GuildId, stream, false), message);
// only cache the ids of channel/message pairs
if (_deleteOnOfflineServers.Contains(fs.GuildId))
return (textChannel.Id, msg.Id);
else
return default;
})
.WhenAll();
// only cache the ids of channel/message pairs
if(_deleteOnOfflineServers.Contains(fs.GuildId))
return (textChannel.Id, msg.Id);
else
return default;
})
.WhenAll();
// push online stream messages to redis // push online stream messages to redis
// when streams go offline, any server which // when streams go offline, any server which
// has the online stream message deletion feature // has the online stream message deletion feature
@@ -296,16 +299,15 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
try try
{ {
var pairs = messages var pairs = messages
.Where(x => x != default) .Where(x => x != default)
.Select(x => (x.Item1, x.Item2)) .Select(x => (x.Item1, x.Item2))
.ToList(); .ToList();
if (pairs.Count > 0) if (pairs.Count > 0)
await OnlineMessagesSent(key.Type, key.Name, pairs); await OnlineMessagesSent(key.Type, key.Name, pairs);
} }
catch catch
{ {
} }
} }
} }
@@ -383,10 +385,10 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var fss = uow.Set<FollowedStream>() var fss = uow.Set<FollowedStream>()
.AsQueryable() .AsQueryable()
.Where(x => x.GuildId == guildId) .Where(x => x.GuildId == guildId)
.OrderBy(x => x.Id) .OrderBy(x => x.Id)
.ToList(); .ToList();
// out of range // out of range
if (fss.Count <= index) if (fss.Count <= index)
@@ -449,7 +451,9 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
GuildId = guildId GuildId = guildId
}; };
if (gc.FollowedStreams.Count >= 10) var config = _config.Data;
if (config.FollowedStreams.MaxCount is not -1
&& gc.FollowedStreams.Count >= config.FollowedStreams.MaxCount)
return null; return null;
gc.FollowedStreams.Add(fs); gc.FollowedStreams.Add(fs);
@@ -474,10 +478,10 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
public IEmbedBuilder GetEmbed(ulong guildId, StreamData status, bool showViewers = true) public IEmbedBuilder GetEmbed(ulong guildId, StreamData status, bool showViewers = true)
{ {
var embed = _eb.Create() var embed = _eb.Create()
.WithTitle(status.Name) .WithTitle(status.Name)
.WithUrl(status.StreamUrl) .WithUrl(status.StreamUrl)
.WithDescription(status.StreamUrl) .WithDescription(status.StreamUrl)
.AddField(GetText(guildId, strs.status), status.IsLive ? "🟢 Online" : "🔴 Offline", true); .AddField(GetText(guildId, strs.status), status.IsLive ? "🟢 Online" : "🔴 Offline", true);
if (showViewers) if (showViewers)
{ {
@@ -526,7 +530,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
return newValue; return newValue;
} }
public bool ToggleStreamOnlineDelete(ulong guildId) public bool ToggleStreamOnlineDelete(ulong guildId)
{ {
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();

View File

@@ -8,63 +8,72 @@ public partial class SearchesConfig : ICloneable<SearchesConfig>
{ {
[Comment("DO NOT CHANGE")] [Comment("DO NOT CHANGE")]
public int Version { get; set; } = 0; public int Version { get; set; } = 0;
[Comment("""
Which engine should .search command
'google_scrape' - default. Scrapes the webpage for results. May break. Requires no api keys.
'google' - official google api. Requires googleApiKey and google.searchId set in creds.yml
'searx' - requires at least one searx instance specified in the 'searxInstances' property below
""")]
public WebSearchEngine WebSearchEngine { get; set; } = WebSearchEngine.Google_Scrape;
[Comment("""
Which engine should .image command use
'google'- official google api. googleApiKey and google.imageSearchId set in creds.yml
'searx' requires at least one searx instance specified in the 'searxInstances' property below
""")]
public ImgSearchEngine ImgSearchEngine { get; set; } = ImgSearchEngine.Google;
[Comment(""" [Comment("""
Which search provider will be used for the `.youtube` command. Which engine should .search command
'google_scrape' - default. Scrapes the webpage for results. May break. Requires no api keys.
- `ytDataApiv3` - uses google's official youtube data api. Requires `GoogleApiKey` set in creds and youtube data api enabled in developers console 'google' - official google api. Requires googleApiKey and google.searchId set in creds.yml
'searx' - requires at least one searx instance specified in the 'searxInstances' property below
- `ytdl` - default, uses youtube-dl. Requires `youtube-dl` to be installed and it's path added to env variables. Slow. """)]
public WebSearchEngine WebSearchEngine { get; set; } = WebSearchEngine.Google_Scrape;
- `ytdlp` - recommended easy, uses `yt-dlp`. Requires `yt-dlp` to be installed and it's path added to env variables
[Comment("""
- `invidious` - recommended advanced, uses invidious api. Requires at least one invidious instance specified in the `invidiousInstances` property Which engine should .image command use
""")] 'google'- official google api. googleApiKey and google.imageSearchId set in creds.yml
'searx' requires at least one searx instance specified in the 'searxInstances' property below
""")]
public ImgSearchEngine ImgSearchEngine { get; set; } = ImgSearchEngine.Google;
[Comment("""
Which search provider will be used for the `.youtube` command.
- `ytDataApiv3` - uses google's official youtube data api. Requires `GoogleApiKey` set in creds and youtube data api enabled in developers console
- `ytdl` - default, uses youtube-dl. Requires `youtube-dl` to be installed and it's path added to env variables. Slow.
- `ytdlp` - recommended easy, uses `yt-dlp`. Requires `yt-dlp` to be installed and it's path added to env variables
- `invidious` - recommended advanced, uses invidious api. Requires at least one invidious instance specified in the `invidiousInstances` property
""")]
public YoutubeSearcher YtProvider { get; set; } = YoutubeSearcher.Ytdlp; public YoutubeSearcher YtProvider { get; set; } = YoutubeSearcher.Ytdlp;
[Comment(""" [Comment("""
Set the searx instance urls in case you want to use 'searx' for either img or web search. Set the searx instance urls in case you want to use 'searx' for either img or web search.
Nadeko will use a random one for each request. Nadeko will use a random one for each request.
Use a fully qualified url. Example: `https://my-searx-instance.mydomain.com` Use a fully qualified url. Example: `https://my-searx-instance.mydomain.com`
Instances specified must support 'format=json' query parameter. Instances specified must support 'format=json' query parameter.
- In case you're running your own searx instance, set - In case you're running your own searx instance, set
search: search:
formats: formats:
- json - json
in 'searxng/settings.yml' on your server in 'searxng/settings.yml' on your server
- If you're using a public instance, make sure that the instance you're using supports it (they usually don't) - If you're using a public instance, make sure that the instance you're using supports it (they usually don't)
""")] """)]
public List<string> SearxInstances { get; set; } = new List<string>(); public List<string> SearxInstances { get; set; } = new List<string>();
[Comment(""" [Comment("""
Set the invidious instance urls in case you want to use 'invidious' for `.youtube` search Set the invidious instance urls in case you want to use 'invidious' for `.youtube` search
Nadeko will use a random one for each request. Nadeko will use a random one for each request.
These instances may be used for music queue functionality in the future. These instances may be used for music queue functionality in the future.
Use a fully qualified url. Example: https://my-invidious-instance.mydomain.com Use a fully qualified url. Example: https://my-invidious-instance.mydomain.com
Instances specified must have api available. Instances specified must have api available.
You check that by opening an api endpoint in your browser. For example: https://my-invidious-instance.mydomain.com/api/v1/trending You check that by opening an api endpoint in your browser. For example: https://my-invidious-instance.mydomain.com/api/v1/trending
""")] """)]
public List<string> InvidiousInstances { get; set; } = new List<string>(); public List<string> InvidiousInstances { get; set; } = new List<string>();
[Comment("Maximum number of followed streams per server")]
public FollowedStreamConfig FollowedStreams { get; set; } = new FollowedStreamConfig();
}
public sealed class FollowedStreamConfig
{
[Comment("Maximum number of streams that each server can follow. -1 for infinite")]
public int MaxCount { get; set; } = 10;
} }
public enum YoutubeSearcher public enum YoutubeSearcher

View File

@@ -17,17 +17,22 @@ public class SearchesConfigService : ConfigServiceBase<SearchesConfig>
sc => sc.WebSearchEngine, sc => sc.WebSearchEngine,
ConfigParsers.InsensitiveEnum, ConfigParsers.InsensitiveEnum,
ConfigPrinters.ToString); ConfigPrinters.ToString);
AddParsedProp("imgEngine", AddParsedProp("imgEngine",
sc => sc.ImgSearchEngine, sc => sc.ImgSearchEngine,
ConfigParsers.InsensitiveEnum, ConfigParsers.InsensitiveEnum,
ConfigPrinters.ToString); ConfigPrinters.ToString);
AddParsedProp("ytProvider", AddParsedProp("ytProvider",
sc => sc.YtProvider, sc => sc.YtProvider,
ConfigParsers.InsensitiveEnum, ConfigParsers.InsensitiveEnum,
ConfigPrinters.ToString); ConfigPrinters.ToString);
AddParsedProp("followedStreams.maxCount",
sc => sc.FollowedStreams.MaxCount,
int.TryParse,
ConfigPrinters.ToString);
Migrate(); Migrate();
} }
@@ -41,5 +46,13 @@ public class SearchesConfigService : ConfigServiceBase<SearchesConfig>
c.WebSearchEngine = WebSearchEngine.Google_Scrape; c.WebSearchEngine = WebSearchEngine.Google_Scrape;
}); });
} }
if (data.Version < 2)
{
ModifyConfig(c =>
{
c.Version = 2;
});
}
} }
} }

View File

@@ -1,19 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<!-- .eval --> <!-- .eval -->
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.5.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.5.0" />
<!-- .calc --> <!-- .calc -->
<PackageReference Include="CoreCLR-NCalc" Version="2.2.110" /> <PackageReference Include="CoreCLR-NCalc" Version="3.0.203" />
</ItemGroup> </ItemGroup>

View File

@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="linq2db.EntityFrameworkCore" Version="7.3.0"/> <PackageReference Include="linq2db.EntityFrameworkCore" Version="8.1.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0"/> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,17 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="NonBlocking" Version="2.1.1" /> <PackageReference Include="NonBlocking" Version="2.1.2" />
<PackageReference Include="OneOf" Version="3.0.243" /> <PackageReference Include="OneOf" Version="3.0.263" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
@@ -10,8 +10,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net.Core" Version="3.203.0" /> <PackageReference Include="Discord.Net.Core" Version="3.203.0" />
<PackageReference Include="Serilog" Version="2.12.0" /> <PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="YamlDotNet" Version="13.0.2" /> <PackageReference Include="YamlDotNet" Version="15.1.2" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Version)' == '' "> <PropertyGroup Condition=" '$(Version)' == '' ">

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<NoWarn>CS8981</NoWarn> <NoWarn>CS8981</NoWarn>
</PropertyGroup> </PropertyGroup>
@@ -10,7 +10,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.52.0" /> <PackageReference Include="Grpc.AspNetCore" Version="2.61.0" />
<PackageReference Include="Serilog" Version="2.12.0" /> <PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup> </PropertyGroup>

View File

@@ -125,10 +125,10 @@ public sealed class Bot : IBot
var svcs = new StandardKernel(new NinjectSettings() var svcs = new StandardKernel(new NinjectSettings()
{ {
ThrowOnGetServiceNotFound = true, // ThrowOnGetServiceNotFound = true,
ActivationCacheDisabled = true, ActivationCacheDisabled = true,
}); });
// this is required in order for medusa unloading to work // this is required in order for medusa unloading to work
svcs.Components.Remove<IPlanner, Planner>(); svcs.Components.Remove<IPlanner, Planner>();
svcs.Components.Add<IPlanner, RemovablePlanner>(); svcs.Components.Add<IPlanner, RemovablePlanner>();
@@ -197,7 +197,7 @@ public sealed class Bot : IBot
private void LoadTypeReaders(Assembly assembly) private void LoadTypeReaders(Assembly assembly)
{ {
var filteredTypes = assembly.GetTypes() var filteredTypes = assembly.GetExportedTypes()
.Where(x => x.IsSubclassOf(typeof(TypeReader)) .Where(x => x.IsSubclassOf(typeof(TypeReader))
&& x.BaseType?.GetGenericArguments().Length > 0 && x.BaseType?.GetGenericArguments().Length > 0
&& !x.IsAbstract); && !x.IsAbstract);
@@ -208,6 +208,7 @@ public sealed class Bot : IBot
if (baseType is null) if (baseType is null)
continue; continue;
Log.Information(ft.Name);
var typeReader = (TypeReader)ActivatorUtilities.CreateInstance(Services, ft); var typeReader = (TypeReader)ActivatorUtilities.CreateInstance(Services, ft);
var typeArgs = baseType.GetGenericArguments(); var typeArgs = baseType.GetGenericArguments();
_commandService.AddTypeReader(typeArgs[0], typeReader); _commandService.AddTypeReader(typeArgs[0], typeReader);

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings> <ImplicitUsings>true</ImplicitUsings>
@@ -20,15 +20,15 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AngleSharp" Version="1.0.1"> <PackageReference Include="AngleSharp" Version="1.1.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<Publish>True</Publish> <Publish>True</Publish>
</PackageReference> </PackageReference>
<PackageReference Include="AWSSDK.S3" Version="3.7.101.58" /> <PackageReference Include="AWSSDK.S3" Version="3.7.307" />
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.6" /> <PackageReference Include="CodeHollow.FeedReader" Version="1.2.6" />
<PackageReference Include="CommandLineParser" Version="2.9.1" /> <PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Discord.Net" Version="3.203.0" /> <PackageReference Include="Discord.Net" Version="3.203.0" />
<PackageReference Include="CoreCLR-NCalc" Version="2.2.110" /> <PackageReference Include="CoreCLR-NCalc" Version="3.0.203" />
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" /> <PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" />
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.62.1.3205" /> <PackageReference Include="Google.Apis.YouTube.v3" Version="1.62.1.3205" />
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084" /> <PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084" />
@@ -38,12 +38,12 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Html2Markdown" Version="5.1.0.703" /> <PackageReference Include="Html2Markdown" Version="6.2.0.3" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.5.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.5.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="MorseCode.ITask" Version="2.0.3" /> <PackageReference Include="MorseCode.ITask" Version="2.0.3" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.0.0" /> <PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.0.0" />
@@ -51,51 +51,53 @@
<!-- DI --> <!-- DI -->
<PackageReference Include="Ninject" Version="3.3.6" /> <PackageReference Include="Ninject" Version="3.3.6" />
<PackageReference Include="Ninject.Extensions.Conventions" Version="3.3.0" /> <PackageReference Include="Ninject.Extensions.Conventions" Version="3.3.0" />
<!-- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />--> <!-- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />-->
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<!-- <PackageReference Include="Scrutor" Version="4.2.0" />--> <!-- <PackageReference Include="Scrutor" Version="4.2.0" />-->
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" /> <PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NonBlocking" Version="2.1.1" /> <PackageReference Include="NonBlocking" Version="2.1.2" />
<PackageReference Include="OneOf" Version="3.0.243" /> <PackageReference Include="OneOf" Version="3.0.263" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.Seq" Version="5.2.2" /> <PackageReference Include="Serilog.Sinks.Seq" Version="5.2.2" />
<PackageReference Include="SharpToken" Version="1.2.14" />
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta17" /> <PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta17" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" /> <PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14" /> <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0009" /> <PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0009" />
<PackageReference Include="StackExchange.Redis" Version="2.6.104" /> <PackageReference Include="StackExchange.Redis" Version="2.6.122" />
<PackageReference Include="YamlDotNet" Version="13.0.2" /> <PackageReference Include="YamlDotNet" Version="15.1.2" />
<PackageReference Include="Humanizer" Version="2.14.1"> <PackageReference Include="Humanizer" Version="2.14.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<Publish>True</Publish> <Publish>True</Publish>
</PackageReference> </PackageReference>
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" /> <PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
<!-- Db-related packages --> <!-- Db-related packages -->
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.4"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.3">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="linq2db.EntityFrameworkCore" Version="7.3.0" /> <PackageReference Include="linq2db.EntityFrameworkCore" Version="8.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.3" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
<PackageReference Include="EFCore.NamingConventions" Version="7.0.2" /> <PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
<!-- Used by stream notifications --> <!-- Used by stream notifications -->
<PackageReference Include="TwitchLib.Api" Version="3.4.1" /> <!-- <PackageReference Include="TwitchLib.Api" Version="[3.4.1]" />-->
</ItemGroup> </ItemGroup>
@@ -111,9 +113,9 @@
<ProjectReference Include="..\Nadeko.Bot.Modules.Permisssions\Nadeko.Bot.Modules.Permisssions.csproj" /> <ProjectReference Include="..\Nadeko.Bot.Modules.Permisssions\Nadeko.Bot.Modules.Permisssions.csproj" />
<ProjectReference Include="..\Nadeko.Bot.Modules.Utility\Nadeko.Bot.Modules.Utility.csproj" /> <ProjectReference Include="..\Nadeko.Bot.Modules.Utility\Nadeko.Bot.Modules.Utility.csproj" />
<ProjectReference Include="..\Nadeko.Bot.Modules.Xp\Nadeko.Bot.Modules.Xp.csproj" /> <ProjectReference Include="..\Nadeko.Bot.Modules.Xp\Nadeko.Bot.Modules.Xp.csproj" />
<!-- <ProjectReference Include="..\Nadeko.Common\Nadeko.Common.csproj" />-->
<ProjectReference Include="..\Nadeko.Bot.Modules.Expresssions\Nadeko.Bot.Modules.Expresssions.csproj" /> <ProjectReference Include="..\Nadeko.Bot.Modules.Expresssions\Nadeko.Bot.Modules.Expresssions.csproj" />
<ProjectReference Include="..\Nadeko.Bot.Modules.Gambling\Nadeko.Bot.Modules.Gambling.csproj" /> <ProjectReference Include="..\Nadeko.Bot.Modules.Gambling\Nadeko.Bot.Modules.Gambling.csproj" />
<ProjectReference Include="..\Nadeko.Bot.Modules.Searches\Nadeko.Bot.Modules.Searches.csproj" />
<ProjectReference Include="..\Nadeko.Bot.Generators.Cloneable\Nadeko.Bot.Generators.Cloneable.csproj" OutputItemType="Analyzer" /> <ProjectReference Include="..\Nadeko.Bot.Generators.Cloneable\Nadeko.Bot.Generators.Cloneable.csproj" OutputItemType="Analyzer" />
</ItemGroup> </ItemGroup>

View File

@@ -14,9 +14,10 @@ public sealed class PermissionChecker : IPermissionChecker, INService
private readonly IEmbedBuilderService _ebs; private readonly IEmbedBuilderService _ebs;
private readonly CommandHandler _ch; private readonly CommandHandler _ch;
// todo .GetPrefix should be in a different place public PermissionChecker(
public PermissionChecker(PermissionService perms, PermissionService perms,
GlobalPermissionService gperm, CmdCdService cmdCds, GlobalPermissionService gperm,
CmdCdService cmdCds,
IEmbedBuilderService ebs, IEmbedBuilderService ebs,
CommandHandler ch) CommandHandler ch)
{ {
@@ -27,58 +28,41 @@ public sealed class PermissionChecker : IPermissionChecker, INService
_ch = ch; _ch = ch;
} }
public async Task<OneOf<Success, Error<LocStr>>> CheckAsync( public async Task<PermCheckResult> CheckPermsAsync(
IGuild guild, IGuild guild,
IMessageChannel channel, IMessageChannel channel,
IUser author, IUser author,
string module, string module,
string? cmd) string? cmdName)
{ {
module = module.ToLowerInvariant(); module = module.ToLowerInvariant();
cmd = cmd?.ToLowerInvariant(); cmdName = cmdName?.ToLowerInvariant();
// todo add proper string
if (cmd is not null && await _cmdCds.TryBlock(guild, author, cmd)) if (cmdName is not null && await _cmdCds.TryBlock(guild, author, cmdName))
return new Error<LocStr>(new()); {
return new PermCooldown();
}
try try
{ {
if (_gperm.BlockedModules.Contains(module)) if (_gperm.BlockedModules.Contains(module))
{ {
Log.Information("u:{UserId} tried to use module {Module} which is globally disabled.", return new PermGlobalBlock();
author.Id, }
module
); if (cmdName is not null && _gperm.BlockedCommands.Contains(cmdName))
{
return new Success(); return new PermGlobalBlock();
} }
// todo check if this even works
if (guild is SocketGuild sg) if (guild is SocketGuild sg)
{ {
var pc = _perms.GetCacheFor(guild.Id); var pc = _perms.GetCacheFor(sg.Id);
if (!pc.Permissions.CheckPermissions(author, channel, cmd, module, out var index)) if (!pc.Permissions.CheckPermissions(author, channel, cmdName, module, out var index))
{ {
if (pc.Verbose) return new PermDisallowed(index,
{ pc.Permissions[index].GetCommand(_ch.GetPrefix(guild), sg),
// todo fix pc.Verbose);
var permissionMessage = strs.perm_prevent(index + 1,
Format.Bold(pc.Permissions[index].GetCommand(_ch.GetPrefix(guild), sg)));
try
{
// await channel.SendErrorAsync(_ebs,
// title: null,
// text: GettextpermissionMessage);
}
catch
{
}
Log.Information("{PermissionMessage}", permissionMessage);
}
// todo add proper string
return new Error<LocStr>(new());
} }
} }
} }
@@ -86,6 +70,6 @@ public sealed class PermissionChecker : IPermissionChecker, INService
{ {
} }
return new Success(); return new PermAllowed();
} }
} }

View File

@@ -20,7 +20,9 @@ public sealed class RedisPubSub : IPubSub
{ {
var serialized = _serializer.Serialize(data); var serialized = _serializer.Serialize(data);
return _multi.GetSubscriber() return _multi.GetSubscriber()
.PublishAsync($"{_creds.RedisKey()}:{key.Key}", serialized, CommandFlags.FireAndForget); .PublishAsync(new RedisChannel($"{_creds.RedisKey()}:{key.Key}", RedisChannel.PatternMode.Literal),
serialized,
CommandFlags.FireAndForget);
} }
public Task Sub<TData>(in TypedKey<TData> key, Func<TData, ValueTask> action) public Task Sub<TData>(in TypedKey<TData> key, Func<TData, ValueTask> action)
@@ -47,6 +49,9 @@ public sealed class RedisPubSub : IPubSub
} }
} }
return _multi.GetSubscriber().SubscribeAsync($"{_creds.RedisKey()}:{eventName}", OnSubscribeHandler); return _multi.GetSubscriber()
.SubscribeAsync(
new RedisChannel($"{_creds.RedisKey()}:{eventName}", RedisChannel.PatternMode.Literal),
OnSubscribeHandler);
} }
} }

View File

@@ -1,5 +1,5 @@
# DO NOT CHANGE # DO NOT CHANGE
version: 2 version: 3
# Hangman related settings (.hangman command) # Hangman related settings (.hangman command)
hangman: hangman:
# The amount of currency awarded to the winner of a hangman game # The amount of currency awarded to the winner of a hangman game
@@ -57,14 +57,19 @@ raceAnimals:
# Which chatbot API should bot use. # Which chatbot API should bot use.
# 'cleverbot' - bot will use Cleverbot API. # 'cleverbot' - bot will use Cleverbot API.
# 'gpt3' - bot will use GPT-3 API # 'gpt3' - bot will use GPT-3 API
chatBot: gpt3 chatBot: Gpt3
chatGpt: chatGpt:
# Which GPT-3 Model should bot use. # Which GPT-3 Model should bot use.
# 'ada001' - cheapest and fastest # gpt35turbo - cheapest
# 'babbage001' - 2nd option # gpt4 - 30x more expensive, higher quality
# 'curie001' - 3rd option # gp432k - same model as above, but with a 32k token limit
# 'davinci003' - Most expensive, slowest modelName: Gpt35Turbo
model: davinci003 # How should the chat bot behave, whats its personality? (Usage of this counts towards the max tokens)
personalityPrompt: You are a chat bot willing to have a conversation with anyone about anything.
# The maximum number of messages in a conversation that can be remembered. (This will increase the number of tokens used)
chatHistory: 5
# The maximum number of tokens to use per GPT-3 API call # The maximum number of tokens to use per GPT-3 API call
maxTokens: 100 maxTokens: 100
# The minimum number of tokens to use per GPT-3 API call, such that chat history is removed to make room.
minTokens: 30

View File

@@ -1,5 +1,5 @@
# DO NOT CHANGE # DO NOT CHANGE
version: 1 version: 2
# Which engine should .search command # Which engine should .search command
# 'google_scrape' - default. Scrapes the webpage for results. May break. Requires no api keys. # 'google_scrape' - default. Scrapes the webpage for results. May break. Requires no api keys.
# 'google' - official google api. Requires googleApiKey and google.searchId set in creds.yml # 'google' - official google api. Requires googleApiKey and google.searchId set in creds.yml
@@ -41,3 +41,7 @@ searxInstances: []
# Instances specified must have api available. # Instances specified must have api available.
# You check that by opening an api endpoint in your browser. For example: https://my-invidious-instance.mydomain.com/api/v1/trending # You check that by opening an api endpoint in your browser. For example: https://my-invidious-instance.mydomain.com/api/v1/trending
invidiousInstances: [] invidiousInstances: []
# Maximum number of followed streams per server
followedStreams:
# Maximum number of streams that each server can follow. -1 for infinite
maxCount: 10

View File

@@ -8,7 +8,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog" Version="2.12.0" /> <PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="System.Threading.Channels" Version="7.0.0" /> <PackageReference Include="System.Threading.Channels" Version="8.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>