elliebot/src/EllieBot/Modules/Games/ChatterBot/ChatterBotService.cs
2025-03-30 16:16:57 +13:00

239 lines
No EOL
7.5 KiB
C#

#nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
using EllieBot.Modules.Games.Common;
using EllieBot.Modules.Games.Common.ChatterBot;
using EllieBot.Modules.Patronage;
using EllieBot.Modules.Permissions;
namespace EllieBot.Modules.Games.Services;
public class ChatterBotService : IExecOnMessage, IReadyExecutor
{
private ConcurrentDictionary<ulong, Lazy<IChatterBotSession>> _chatterBotGuilds = [];
public int Priority
=> 1;
private readonly DiscordSocketClient _client;
private readonly IPermissionChecker _perms;
private readonly IBotCreds _creds;
private readonly IHttpClientFactory _httpFactory;
private readonly GamesConfigService _gcs;
private readonly IMessageSenderService _sender;
private readonly DbService _db;
public readonly IPatronageService _ps;
public ChatterBotService(
DiscordSocketClient client,
IPermissionChecker perms,
IPatronageService ps,
IHttpClientFactory factory,
IBotCreds creds,
GamesConfigService gcs,
IMessageSenderService sender,
DbService db)
{
_client = client;
_perms = perms;
_creds = creds;
_sender = sender;
_db = db;
_httpFactory = factory;
_perms = perms;
_gcs = gcs;
_ps = ps;
}
public IChatterBotSession CreateSession()
{
switch (_gcs.Data.ChatBot)
{
case ChatBotImplementation.Cleverbot:
if (!string.IsNullOrWhiteSpace(_creds.CleverbotApiKey))
return new OfficialCleverbotSession(_creds.CleverbotApiKey, _httpFactory);
Log.Information("Cleverbot will not work as the api key is missing");
return null;
case ChatBotImplementation.OpenAi:
var data = _gcs.Data;
if (!string.IsNullOrWhiteSpace(_creds.Gpt3ApiKey))
return new OpenAiApiSession(
data.ChatGpt.ApiUrl,
_creds.Gpt3ApiKey,
data.ChatGpt.ModelName,
data.ChatGpt.ChatHistory,
data.ChatGpt.MaxTokens,
data.ChatGpt.MinTokens,
data.ChatGpt.PersonalityPrompt,
_client.CurrentUser.Username,
_httpFactory);
Log.Information("Openai Api will likely not work as the api key is missing");
return null;
default:
return null;
}
}
public IChatterBotSession GetOrCreateSession(ulong guildId)
{
if (_chatterBotGuilds.TryGetValue(guildId, out var lazyChatBot))
return lazyChatBot.Value;
lazyChatBot = new(() => CreateSession(), true);
_chatterBotGuilds.TryAdd(guildId, lazyChatBot);
return lazyChatBot.Value;
}
public string PrepareMessage(IUserMessage msg)
{
var nadekoId = _client.CurrentUser.Id;
var normalMention = $"<@{nadekoId}> ";
var nickMention = $"<@!{nadekoId}> ";
string message;
if (msg.Content.StartsWith(normalMention, StringComparison.InvariantCulture))
message = msg.Content[normalMention.Length..].Trim();
else if (msg.Content.StartsWith(nickMention, StringComparison.InvariantCulture))
message = msg.Content[nickMention.Length..].Trim();
else if (msg.ReferencedMessage?.Author.Id == nadekoId)
message = msg.Content;
else
return null;
return message;
}
public async Task<bool> ExecOnMessageAsync(IGuild guild, IUserMessage usrMsg)
{
if (guild is not SocketGuild sg)
return false;
var channel = usrMsg.Channel as ITextChannel;
if (channel is null)
return false;
if (!_chatterBotGuilds.TryGetValue(channel.Guild.Id, out var lazyChatBot))
return false;
var chatBot = lazyChatBot.Value;
var message = PrepareMessage(usrMsg);
if (message is null)
return false;
return await RunChatterBot(sg, usrMsg, channel, chatBot, message);
}
public async Task<bool> RunChatterBot(
SocketGuild guild,
IUserMessage usrMsg,
ITextChannel channel,
IChatterBotSession chatBot,
string message)
{
try
{
var res = await _perms.CheckPermsAsync(guild,
usrMsg.Channel,
usrMsg.Author,
CleverBotResponseStr.CLEVERBOT_RESPONSE,
CleverBotResponseStr.CLEVERBOT_RESPONSE);
if (!res.IsAllowed)
return false;
if (!await _ps.LimitHitAsync("ai", guild.OwnerId, 1))
{
// limit exceeded
return false;
}
_ = channel.TriggerTypingAsync();
var response = await chatBot.Think(message, usrMsg.Author.ToString());
if (response.TryPickT0(out var result, out var error))
{
await _sender.Response(channel)
.Confirm(result.Text)
.SendAsync();
}
else
{
Log.Warning("Error in chatterbot: {Error}", error.Value);
}
Log.Information("""
CleverBot Executed
Server: {GuildName} [{GuildId}]
Channel: {ChannelName} [{ChannelId}]
UserId: {Author} [{AuthorId}]
Message: {Content}
""",
guild.Name,
guild.Id,
usrMsg.Channel?.Name,
usrMsg.Channel?.Id,
usrMsg.Author,
usrMsg.Author.Id,
usrMsg.Content);
return true;
}
catch (Exception ex)
{
Log.Warning(ex, "Error in cleverbot");
}
return false;
}
public async Task<bool> ToggleChatterBotAsync(ulong guildId)
{
if (_chatterBotGuilds.TryRemove(guildId, out _))
{
await using var uow = _db.GetDbContext();
await uow.Set<GuildConfig>()
.ToLinqToDBTable()
.Where(x => x.GuildId == guildId)
.UpdateAsync((gc) => new GuildConfig()
{
CleverbotEnabled = false
});
await uow.SaveChangesAsync();
return false;
}
_chatterBotGuilds.TryAdd(guildId, new(() => CreateSession(), true));
await using (var uow = _db.GetDbContext())
{
await uow.Set<GuildConfig>()
.ToLinqToDBTable()
.Where(x => x.GuildId == guildId)
.UpdateAsync((gc) => new GuildConfig()
{
CleverbotEnabled = true
});
await uow.SaveChangesAsync();
}
return true;
}
public async Task OnReadyAsync()
{
await using var uow = _db.GetDbContext();
_chatterBotGuilds = await uow.GuildConfigs
.AsNoTracking()
.Where(gc => gc.CleverbotEnabled)
.ToListAsyncLinqToDB()
.Fmap(x => x
.ToDictionary(gc => gc.GuildId,
_ => new Lazy<IChatterBotSession>(() => CreateSession(), true))
.ToConcurrent());
}
}