Files
nadekobot/src/NadekoBot/Modules/Games/ChatterBot/ChatterBotService.cs
Kwoth ab93380d7c 5.1
2024-06-13 18:54:21 +00:00

195 lines
6.4 KiB
C#

#nullable disable
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db.Models;
using NadekoBot.Modules.Games.Common;
using NadekoBot.Modules.Games.Common.ChatterBot;
using NadekoBot.Modules.Patronage;
using NadekoBot.Modules.Permissions;
namespace NadekoBot.Modules.Games.Services;
public class ChatterBotService : IExecOnMessage
{
public ConcurrentDictionary<ulong, Lazy<IChatterBotSession>> ChatterBotGuilds { get; }
public int Priority
=> 1;
private readonly DiscordSocketClient _client;
private readonly IPermissionChecker _perms;
private readonly IBotCredentials _creds;
private readonly IHttpClientFactory _httpFactory;
private readonly GamesConfigService _gcs;
private readonly IMessageSenderService _sender;
public readonly IPatronageService _ps;
public ChatterBotService(
DiscordSocketClient client,
IPermissionChecker perms,
IBot bot,
IPatronageService ps,
IHttpClientFactory factory,
IBotCredentials creds,
GamesConfigService gcs,
IMessageSenderService sender)
{
_client = client;
_perms = perms;
_creds = creds;
_sender = sender;
_httpFactory = factory;
_perms = perms;
_gcs = gcs;
_ps = ps;
ChatterBotGuilds = new(bot.AllGuildConfigs
.Where(gc => gc.CleverbotEnabled)
.ToDictionary(gc => gc.GuildId,
_ => new Lazy<IChatterBotSession>(() => CreateSession(), true)));
}
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.Gpt:
if (!string.IsNullOrWhiteSpace(_creds.Gpt3ApiKey))
return new OfficialGptSession(_creds.Gpt3ApiKey,
_gcs.Data.ChatGpt.ModelName,
_gcs.Data.ChatGpt.ChatHistory,
_gcs.Data.ChatGpt.MaxTokens,
_gcs.Data.ChatGpt.MinTokens,
_gcs.Data.ChatGpt.PersonalityPrompt,
_client.CurrentUser.Username,
_httpFactory);
Log.Information("Gpt3 will 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
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(LimitedFeatureName.ChatBot, usrMsg.Author.Id, 2048 / 2))
{
// limit exceeded
return false;
}
_ = channel.TriggerTypingAsync();
var response = await chatBot.Think(message, usrMsg.Author.ToString());
if (response.TryPickT0(out var result, out var error))
{
// calculate the diff in case we overestimated user's usage
var inTokens = (result.TokensIn - 2048) / 2;
// add the output tokens to the limit
await _ps.LimitForceHit(LimitedFeatureName.ChatBot,
usrMsg.Author.Id,
(inTokens) + (result.TokensOut / 2 * 3));
await _sender.Response(channel)
.Confirm(result.Text)
.SendAsync();
}
else
{
Log.Warning("Error in chatterbot: {Error}", error);
}
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;
}
}