Restructured the project structure back to the way it was, there's no reasonable way to split the modules

This commit is contained in:
Kwoth
2024-04-26 22:26:24 +00:00
parent 6c9c8bf63e
commit e0819f760c
768 changed files with 192 additions and 1047 deletions

View File

@@ -0,0 +1,17 @@
#nullable disable
namespace NadekoBot.Modules.Searches;
public interface ITranslateService
{
public Task<string> Translate(string source, string target, string text = null);
Task<bool> ToggleAtl(ulong guildId, ulong channelId, bool autoDelete);
IEnumerable<string> GetLanguages();
Task<bool?> RegisterUserAsync(
ulong userId,
ulong channelId,
string from,
string to);
Task<bool> UnregisterUser(ulong channelId, ulong userId);
}

View File

@@ -0,0 +1,224 @@
#nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using Nadeko.Bot.Db.Models;
using System.Net;
namespace NadekoBot.Modules.Searches;
public sealed class TranslateService : ITranslateService, IExecNoCommand, IReadyExecutor, INService
{
private readonly IGoogleApiService _google;
private readonly DbService _db;
private readonly IEmbedBuilderService _eb;
private readonly IBot _bot;
private readonly ConcurrentDictionary<ulong, bool> _atcs = new();
private readonly ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, (string From, string To)>> _users = new();
public TranslateService(
IGoogleApiService google,
DbService db,
IEmbedBuilderService eb,
IBot bot)
{
_google = google;
_db = db;
_eb = eb;
_bot = bot;
}
public async Task OnReadyAsync()
{
List<AutoTranslateChannel> cs;
await using (var ctx = _db.GetDbContext())
{
var guilds = _bot.AllGuildConfigs.Select(x => x.GuildId).ToList();
cs = await ctx.Set<AutoTranslateChannel>().Include(x => x.Users)
.Where(x => guilds.Contains(x.GuildId))
.ToListAsyncEF();
}
foreach (var c in cs)
{
_atcs[c.ChannelId] = c.AutoDelete;
_users[c.ChannelId] =
new(c.Users.ToDictionary(x => x.UserId, x => (x.Source.ToLower(), x.Target.ToLower())));
}
}
public async Task ExecOnNoCommandAsync(IGuild guild, IUserMessage msg)
{
if (string.IsNullOrWhiteSpace(msg.Content))
return;
if (msg is { Channel: ITextChannel tch } um)
{
if (!_atcs.TryGetValue(tch.Id, out var autoDelete))
return;
if (!_users.TryGetValue(tch.Id, out var users) || !users.TryGetValue(um.Author.Id, out var langs))
return;
var output = await _google.Translate(msg.Content, langs.From, langs.To);
if (string.IsNullOrWhiteSpace(output)
|| msg.Content.Equals(output, StringComparison.InvariantCultureIgnoreCase))
return;
var embed = _eb.Create().WithOkColor();
if (autoDelete)
{
embed.WithAuthor(um.Author.ToString(), um.Author.GetAvatarUrl())
.AddField(langs.From, um.Content)
.AddField(langs.To, output);
await tch.EmbedAsync(embed);
try
{
await um.DeleteAsync();
}
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden)
{
_atcs.TryUpdate(tch.Id, false, true);
}
return;
}
await um.ReplyAsync(embed: embed.AddField(langs.To, output).Build(), allowedMentions: AllowedMentions.None);
}
}
public async Task<string> Translate(string source, string target, string text = null)
{
if (string.IsNullOrWhiteSpace(text))
throw new ArgumentException("Text is empty or null", nameof(text));
var res = await _google.Translate(text, source, target);
return res.SanitizeMentions(true);
}
public async Task<bool> ToggleAtl(ulong guildId, ulong channelId, bool autoDelete)
{
await using var ctx = _db.GetDbContext();
var old = await ctx.Set<AutoTranslateChannel>().ToLinqToDBTable()
.FirstOrDefaultAsyncLinqToDB(x => x.ChannelId == channelId);
if (old is null)
{
ctx.Set<AutoTranslateChannel>().Add(new()
{
GuildId = guildId,
ChannelId = channelId,
AutoDelete = autoDelete
});
await ctx.SaveChangesAsync();
_atcs[channelId] = autoDelete;
_users[channelId] = new();
return true;
}
// if autodelete value is different, update the autodelete value
// instead of disabling
if (old.AutoDelete != autoDelete)
{
old.AutoDelete = autoDelete;
await ctx.SaveChangesAsync();
_atcs[channelId] = autoDelete;
return true;
}
await ctx.Set<AutoTranslateChannel>().ToLinqToDBTable().DeleteAsync(x => x.ChannelId == channelId);
await ctx.SaveChangesAsync();
_atcs.TryRemove(channelId, out _);
_users.TryRemove(channelId, out _);
return false;
}
private void UpdateUser(
ulong channelId,
ulong userId,
string from,
string to)
{
var dict = _users.GetOrAdd(channelId, new ConcurrentDictionary<ulong, (string, string)>());
dict[userId] = (from, to);
}
public async Task<bool?> RegisterUserAsync(
ulong userId,
ulong channelId,
string from,
string to)
{
if (!_google.Languages.ContainsKey(from) || !_google.Languages.ContainsKey(to))
return null;
await using var ctx = _db.GetDbContext();
var ch = await ctx.Set<AutoTranslateChannel>().GetByChannelId(channelId);
if (ch is null)
return null;
var user = ch.Users.FirstOrDefault(x => x.UserId == userId);
if (user is null)
{
ch.Users.Add(user = new()
{
Source = from,
Target = to,
UserId = userId
});
await ctx.SaveChangesAsync();
UpdateUser(channelId, userId, from, to);
return true;
}
// if it's different from old settings, update
if (user.Source != from || user.Target != to)
{
user.Source = from;
user.Target = to;
await ctx.SaveChangesAsync();
UpdateUser(channelId, userId, from, to);
return true;
}
return await UnregisterUser(channelId, userId);
}
public async Task<bool> UnregisterUser(ulong channelId, ulong userId)
{
await using var ctx = _db.GetDbContext();
var rows = await ctx.Set<AutoTranslateUser>().ToLinqToDBTable()
.DeleteAsync(x => x.UserId == userId && x.Channel.ChannelId == channelId);
if (_users.TryGetValue(channelId, out var inner))
inner.TryRemove(userId, out _);
return rows > 0;
}
public IEnumerable<string> GetLanguages()
=> _google.Languages.GroupBy(x => x.Value).Select(x => $"{x.AsEnumerable().Select(y => y.Key).Join(", ")}");
}

View File

@@ -0,0 +1,95 @@
#nullable disable
namespace NadekoBot.Modules.Searches;
public partial class Searches
{
[Group]
public partial class TranslateCommands : NadekoModule<ITranslateService>
{
public enum AutoDeleteAutoTranslate
{
Del,
Nodel
}
[Cmd]
public async Task Translate(string from, string to, [Leftover] string text = null)
{
try
{
await ctx.Channel.TriggerTypingAsync();
var translation = await _service.Translate(from, to, text);
var embed = _eb.Create(ctx).WithOkColor().AddField(from, text).AddField(to, translation);
await ctx.Channel.EmbedAsync(embed);
}
catch
{
await ReplyErrorLocalizedAsync(strs.bad_input_format);
}
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[BotPerm(ChannelPerm.ManageMessages)]
[OwnerOnly]
public async Task AutoTranslate(AutoDeleteAutoTranslate autoDelete = AutoDeleteAutoTranslate.Nodel)
{
var toggle =
await _service.ToggleAtl(ctx.Guild.Id, ctx.Channel.Id, autoDelete == AutoDeleteAutoTranslate.Del);
if (toggle)
await ReplyConfirmLocalizedAsync(strs.atl_started);
else
await ReplyConfirmLocalizedAsync(strs.atl_stopped);
}
[Cmd]
[RequireContext(ContextType.Guild)]
public async Task AutoTransLang()
{
if (await _service.UnregisterUser(ctx.Channel.Id, ctx.User.Id))
await ReplyConfirmLocalizedAsync(strs.atl_removed);
}
[Cmd]
[RequireContext(ContextType.Guild)]
public async Task AutoTransLang(string from, string to)
{
var succ = await _service.RegisterUserAsync(ctx.User.Id, ctx.Channel.Id, from.ToLower(), to.ToLower());
if (succ is null)
{
await ReplyErrorLocalizedAsync(strs.atl_not_enabled);
return;
}
if (succ is false)
{
await ReplyErrorLocalizedAsync(strs.invalid_lang);
return;
}
await ReplyConfirmLocalizedAsync(strs.atl_set(from, to));
}
[Cmd]
[RequireContext(ContextType.Guild)]
public async Task Translangs()
{
var langs = _service.GetLanguages().ToList();
var eb = _eb.Create()
.WithTitle(GetText(strs.supported_languages))
.WithOkColor();
foreach (var chunk in langs.Chunk(15))
{
eb.AddField("󠀁", chunk.Join("\n"), isInline: true);
}
await ctx.Channel.EmbedAsync(eb);
}
}
}