222 lines
No EOL
6.7 KiB
C#
222 lines
No EOL
6.7 KiB
C#
#nullable disable
|
|
using LinqToDB;
|
|
using LinqToDB.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using EllieBot.Common.ModuleBehaviors;
|
|
using EllieBot.Db.Models;
|
|
using System.Net;
|
|
|
|
namespace EllieBot.Modules.Searches;
|
|
|
|
public sealed class TranslateService : ITranslateService, IExecNoCommand, IReadyExecutor, IEService
|
|
{
|
|
private readonly IGoogleApiService _google;
|
|
private readonly DbService _db;
|
|
private readonly IMessageSenderService _sender;
|
|
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,
|
|
IMessageSenderService sender,
|
|
IBot bot)
|
|
{
|
|
_google = google;
|
|
_db = db;
|
|
_sender = sender;
|
|
_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 = _sender.CreateEmbed(guild?.Id).WithOkColor();
|
|
|
|
if (autoDelete)
|
|
{
|
|
embed.WithAuthor(um.Author.ToString(), um.Author.GetAvatarUrl())
|
|
.AddField(langs.From, um.Content)
|
|
.AddField(langs.To, output);
|
|
|
|
await _sender.Response(tch).Embed(embed).SendAsync();
|
|
|
|
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)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(text))
|
|
throw new ArgumentException("Text is empty or null", nameof(text));
|
|
|
|
var res = await _google.Translate(text, source.ToLowerInvariant(), target.ToLowerInvariant());
|
|
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(", ")}");
|
|
} |