mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	.atl / .at reworked
This commit is contained in:
		@@ -19,6 +19,7 @@ using NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
using NadekoBot.Common.Configs;
 | 
			
		||||
using NadekoBot.Db;
 | 
			
		||||
using NadekoBot.Modules.Administration.Services;
 | 
			
		||||
using NadekoBot.Modules.Searches;
 | 
			
		||||
using Serilog;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								src/NadekoBot/Db/Models/AutoTranslateChannel.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/NadekoBot/Db/Models/AutoTranslateChannel.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Services.Database.Models
 | 
			
		||||
{
 | 
			
		||||
    public class AutoTranslateChannel : DbEntity
 | 
			
		||||
    {
 | 
			
		||||
        public ulong GuildId { get; set; }
 | 
			
		||||
        public ulong ChannelId { get; set; }
 | 
			
		||||
        public bool AutoDelete { get; set; }
 | 
			
		||||
        public IList<AutoTranslateUser> Users { get; set; } = new List<AutoTranslateUser>();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/NadekoBot/Db/Models/AutoTranslateUser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/NadekoBot/Db/Models/AutoTranslateUser.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
namespace NadekoBot.Services.Database.Models
 | 
			
		||||
{
 | 
			
		||||
    public class AutoTranslateUser : DbEntity
 | 
			
		||||
    {
 | 
			
		||||
        public int ChannelId { get; set; }
 | 
			
		||||
        public AutoTranslateChannel Channel { get; set; }
 | 
			
		||||
        public ulong UserId { get; set; }
 | 
			
		||||
        public string Source { get; set; }
 | 
			
		||||
        public string Target { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -60,6 +60,8 @@ namespace NadekoBot.Services.Database
 | 
			
		||||
        public DbSet<WaifuInfo> WaifuInfo { get; set; }
 | 
			
		||||
        public DbSet<ImageOnlyChannel> ImageOnlyChannels { get; set; }
 | 
			
		||||
        public DbSet<NsfwBlacklistedTag> NsfwBlacklistedTags { get; set; }
 | 
			
		||||
        public DbSet<AutoTranslateChannel> AutoTranslateChannels { get; set; }
 | 
			
		||||
        public DbSet<AutoTranslateUser> AutoTranslateUsers { get; set; }
 | 
			
		||||
 | 
			
		||||
        public NadekoContext(DbContextOptions<NadekoContext> options) : base(options)
 | 
			
		||||
        {
 | 
			
		||||
@@ -368,6 +370,21 @@ namespace NadekoBot.Services.Database
 | 
			
		||||
            modelBuilder.Entity<NsfwBlacklistedTag>(nbt => nbt
 | 
			
		||||
                .HasIndex(x => x.GuildId)
 | 
			
		||||
                .IsUnique(false));
 | 
			
		||||
 | 
			
		||||
            var atch = modelBuilder.Entity<AutoTranslateChannel>();
 | 
			
		||||
            atch.HasIndex(x => x.GuildId)
 | 
			
		||||
                .IsUnique(false);
 | 
			
		||||
 | 
			
		||||
            atch.HasIndex(x => x.ChannelId)
 | 
			
		||||
                .IsUnique();
 | 
			
		||||
 | 
			
		||||
            atch
 | 
			
		||||
                .HasMany(x => x.Users)
 | 
			
		||||
                .WithOne(x => x.Channel)
 | 
			
		||||
                .OnDelete(DeleteBehavior.Cascade);
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity<AutoTranslateUser>(atu => atu
 | 
			
		||||
                .HasAlternateKey(x => new { x.ChannelId, x.UserId }));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2725
									
								
								src/NadekoBot/Migrations/20211213145407_atl-rework.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										2725
									
								
								src/NadekoBot/Migrations/20211213145407_atl-rework.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										71
									
								
								src/NadekoBot/Migrations/20211213145407_atl-rework.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/NadekoBot/Migrations/20211213145407_atl-rework.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Migrations;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Migrations
 | 
			
		||||
{
 | 
			
		||||
    public partial class atlrework : Migration
 | 
			
		||||
    {
 | 
			
		||||
        protected override void Up(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.CreateTable(
 | 
			
		||||
                name: "AutoTranslateChannels",
 | 
			
		||||
                columns: table => new
 | 
			
		||||
                {
 | 
			
		||||
                    Id = table.Column<int>(type: "INTEGER", nullable: false)
 | 
			
		||||
                        .Annotation("Sqlite:Autoincrement", true),
 | 
			
		||||
                    GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
 | 
			
		||||
                    ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
 | 
			
		||||
                    AutoDelete = table.Column<bool>(type: "INTEGER", nullable: false),
 | 
			
		||||
                    DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
 | 
			
		||||
                },
 | 
			
		||||
                constraints: table =>
 | 
			
		||||
                {
 | 
			
		||||
                    table.PrimaryKey("PK_AutoTranslateChannels", x => x.Id);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateTable(
 | 
			
		||||
                name: "AutoTranslateUsers",
 | 
			
		||||
                columns: table => new
 | 
			
		||||
                {
 | 
			
		||||
                    Id = table.Column<int>(type: "INTEGER", nullable: false)
 | 
			
		||||
                        .Annotation("Sqlite:Autoincrement", true),
 | 
			
		||||
                    ChannelId = table.Column<int>(type: "INTEGER", nullable: false),
 | 
			
		||||
                    UserId = table.Column<ulong>(type: "INTEGER", nullable: false),
 | 
			
		||||
                    Source = table.Column<string>(type: "TEXT", nullable: true),
 | 
			
		||||
                    Target = table.Column<string>(type: "TEXT", nullable: true),
 | 
			
		||||
                    DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
 | 
			
		||||
                },
 | 
			
		||||
                constraints: table =>
 | 
			
		||||
                {
 | 
			
		||||
                    table.PrimaryKey("PK_AutoTranslateUsers", x => x.Id);
 | 
			
		||||
                    table.UniqueConstraint("AK_AutoTranslateUsers_ChannelId_UserId", x => new { x.ChannelId, x.UserId });
 | 
			
		||||
                    table.ForeignKey(
 | 
			
		||||
                        name: "FK_AutoTranslateUsers_AutoTranslateChannels_ChannelId",
 | 
			
		||||
                        column: x => x.ChannelId,
 | 
			
		||||
                        principalTable: "AutoTranslateChannels",
 | 
			
		||||
                        principalColumn: "Id",
 | 
			
		||||
                        onDelete: ReferentialAction.Cascade);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateIndex(
 | 
			
		||||
                name: "IX_AutoTranslateChannels_ChannelId",
 | 
			
		||||
                table: "AutoTranslateChannels",
 | 
			
		||||
                column: "ChannelId",
 | 
			
		||||
                unique: true);
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateIndex(
 | 
			
		||||
                name: "IX_AutoTranslateChannels_GuildId",
 | 
			
		||||
                table: "AutoTranslateChannels",
 | 
			
		||||
                column: "GuildId");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void Down(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DropTable(
 | 
			
		||||
                name: "AutoTranslateUsers");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropTable(
 | 
			
		||||
                name: "AutoTranslateChannels");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -340,6 +340,62 @@ namespace NadekoBot.Migrations
 | 
			
		||||
                    b.ToTable("AutoCommands");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<int>("Id")
 | 
			
		||||
                        .ValueGeneratedOnAdd()
 | 
			
		||||
                        .HasColumnType("INTEGER");
 | 
			
		||||
 | 
			
		||||
                    b.Property<bool>("AutoDelete")
 | 
			
		||||
                        .HasColumnType("INTEGER");
 | 
			
		||||
 | 
			
		||||
                    b.Property<ulong>("ChannelId")
 | 
			
		||||
                        .HasColumnType("INTEGER");
 | 
			
		||||
 | 
			
		||||
                    b.Property<DateTime?>("DateAdded")
 | 
			
		||||
                        .HasColumnType("TEXT");
 | 
			
		||||
 | 
			
		||||
                    b.Property<ulong>("GuildId")
 | 
			
		||||
                        .HasColumnType("INTEGER");
 | 
			
		||||
 | 
			
		||||
                    b.HasKey("Id");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("ChannelId")
 | 
			
		||||
                        .IsUnique();
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("GuildId");
 | 
			
		||||
 | 
			
		||||
                    b.ToTable("AutoTranslateChannels");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<int>("Id")
 | 
			
		||||
                        .ValueGeneratedOnAdd()
 | 
			
		||||
                        .HasColumnType("INTEGER");
 | 
			
		||||
 | 
			
		||||
                    b.Property<int>("ChannelId")
 | 
			
		||||
                        .HasColumnType("INTEGER");
 | 
			
		||||
 | 
			
		||||
                    b.Property<DateTime?>("DateAdded")
 | 
			
		||||
                        .HasColumnType("TEXT");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Source")
 | 
			
		||||
                        .HasColumnType("TEXT");
 | 
			
		||||
 | 
			
		||||
                    b.Property<string>("Target")
 | 
			
		||||
                        .HasColumnType("TEXT");
 | 
			
		||||
 | 
			
		||||
                    b.Property<ulong>("UserId")
 | 
			
		||||
                        .HasColumnType("INTEGER");
 | 
			
		||||
 | 
			
		||||
                    b.HasKey("Id");
 | 
			
		||||
 | 
			
		||||
                    b.HasAlternateKey("ChannelId", "UserId");
 | 
			
		||||
 | 
			
		||||
                    b.ToTable("AutoTranslateUsers");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<int>("Id")
 | 
			
		||||
@@ -2194,6 +2250,17 @@ namespace NadekoBot.Migrations
 | 
			
		||||
                    b.Navigation("GuildConfig");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.HasOne("NadekoBot.Services.Database.Models.AutoTranslateChannel", "Channel")
 | 
			
		||||
                        .WithMany("Users")
 | 
			
		||||
                        .HasForeignKey("ChannelId")
 | 
			
		||||
                        .OnDelete(DeleteBehavior.Cascade)
 | 
			
		||||
                        .IsRequired();
 | 
			
		||||
 | 
			
		||||
                    b.Navigation("Channel");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", null)
 | 
			
		||||
@@ -2541,6 +2608,11 @@ namespace NadekoBot.Migrations
 | 
			
		||||
                    b.Navigation("IgnoredChannels");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Navigation("Users");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Navigation("AntiAltSetting");
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								src/NadekoBot/Modules/Searches/Services/AtlExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/NadekoBot/Modules/Searches/Services/AtlExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Searches
 | 
			
		||||
{
 | 
			
		||||
    public static class AtlExtensions
 | 
			
		||||
    {
 | 
			
		||||
        public static Task<AutoTranslateChannel> GetByChannelId(this IQueryable<AutoTranslateChannel> set, ulong channelId)
 | 
			
		||||
            => set.FirstOrDefaultAsyncLinqToDB(x => x.ChannelId == channelId);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/NadekoBot/Modules/Searches/Services/ITranslateService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/NadekoBot/Modules/Searches/Services/ITranslateService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +1,6 @@
 | 
			
		||||
using Discord;
 | 
			
		||||
using Discord.WebSocket;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Common;
 | 
			
		||||
using NadekoBot.Common;
 | 
			
		||||
using NadekoBot.Modules.Searches.Common;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using Newtonsoft.Json.Linq;
 | 
			
		||||
@@ -13,18 +9,14 @@ using SixLabors.ImageSharp.Drawing.Processing;
 | 
			
		||||
using SixLabors.ImageSharp.PixelFormats;
 | 
			
		||||
using SixLabors.ImageSharp.Processing;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using System.Net.Http;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using AngleSharp.Html.Dom;
 | 
			
		||||
using AngleSharp.Html.Parser;
 | 
			
		||||
using NadekoBot.Db;
 | 
			
		||||
using NadekoBot.Modules.Administration;
 | 
			
		||||
using Serilog;
 | 
			
		||||
using HorizontalAlignment = SixLabors.Fonts.HorizontalAlignment;
 | 
			
		||||
using Image = SixLabors.ImageSharp.Image;
 | 
			
		||||
@@ -34,71 +26,31 @@ namespace NadekoBot.Modules.Searches.Services
 | 
			
		||||
    public class SearchesService : INService
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IHttpClientFactory _httpFactory;
 | 
			
		||||
        private readonly DiscordSocketClient _client;
 | 
			
		||||
        private readonly IGoogleApiService _google;
 | 
			
		||||
        private readonly DbService _db;
 | 
			
		||||
        private readonly IImageCache _imgs;
 | 
			
		||||
        private readonly IDataCache _cache;
 | 
			
		||||
        private readonly FontProvider _fonts;
 | 
			
		||||
        private readonly IBotCredentials _creds;
 | 
			
		||||
        private readonly IEmbedBuilderService _eb;
 | 
			
		||||
        private readonly NadekoRandom _rng;
 | 
			
		||||
 | 
			
		||||
        public ConcurrentDictionary<ulong, bool> TranslatedChannels { get; } = new ConcurrentDictionary<ulong, bool>();
 | 
			
		||||
        // (userId, channelId)
 | 
			
		||||
        public ConcurrentDictionary<(ulong UserId, ulong ChannelId), string> UserLanguages { get; } = new ConcurrentDictionary<(ulong, ulong), string>();
 | 
			
		||||
 | 
			
		||||
        public List<WoWJoke> WowJokes { get; } = new List<WoWJoke>();
 | 
			
		||||
        public List<MagicItem> MagicItems { get; } = new List<MagicItem>();
 | 
			
		||||
        private readonly List<string> _yomamaJokes;
 | 
			
		||||
 | 
			
		||||
        public SearchesService(DiscordSocketClient client, IGoogleApiService google,
 | 
			
		||||
            DbService db, Bot bot, IDataCache cache, IHttpClientFactory factory,
 | 
			
		||||
            FontProvider fonts, IBotCredentials creds, IEmbedBuilderService eb)
 | 
			
		||||
        public SearchesService(IGoogleApiService google, 
 | 
			
		||||
            IDataCache cache,
 | 
			
		||||
            IHttpClientFactory factory,
 | 
			
		||||
            FontProvider fonts,
 | 
			
		||||
            IBotCredentials creds)
 | 
			
		||||
        {
 | 
			
		||||
            _httpFactory = factory;
 | 
			
		||||
            _client = client;
 | 
			
		||||
            _google = google;
 | 
			
		||||
            _db = db;
 | 
			
		||||
            _imgs = cache.LocalImages;
 | 
			
		||||
            _cache = cache;
 | 
			
		||||
            _fonts = fonts;
 | 
			
		||||
            _creds = creds;
 | 
			
		||||
            _eb = eb;
 | 
			
		||||
            _rng = new NadekoRandom();
 | 
			
		||||
 | 
			
		||||
            //translate commands
 | 
			
		||||
            _client.MessageReceived += (msg) =>
 | 
			
		||||
            {
 | 
			
		||||
                var _ = Task.Run(async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        if (!(msg is SocketUserMessage umsg))
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
                        if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out var autoDelete))
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
                        var key = (umsg.Author.Id, umsg.Channel.Id);
 | 
			
		||||
 | 
			
		||||
                        if (!UserLanguages.TryGetValue(key, out string langs))
 | 
			
		||||
                            return;
 | 
			
		||||
 | 
			
		||||
                        var text = await Translate(langs, umsg.Resolve(TagHandling.Ignore))
 | 
			
		||||
                                            .ConfigureAwait(false);
 | 
			
		||||
                        if (autoDelete)
 | 
			
		||||
                            try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { }
 | 
			
		||||
                        
 | 
			
		||||
                        await umsg.Channel.SendConfirmAsync(_eb, $"{umsg.Author.Mention} `:` "
 | 
			
		||||
                            + text.Replace("<@ ", "<@", StringComparison.InvariantCulture)
 | 
			
		||||
                                  .Replace("<@! ", "<@!", StringComparison.InvariantCulture)).ConfigureAwait(false);
 | 
			
		||||
                    }
 | 
			
		||||
                    catch { }
 | 
			
		||||
                });
 | 
			
		||||
                return Task.CompletedTask;
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            //joke commands
 | 
			
		||||
            if (File.Exists("data/wowjokes.json"))
 | 
			
		||||
            {
 | 
			
		||||
@@ -340,19 +292,6 @@ namespace NadekoBot.Modules.Searches.Services
 | 
			
		||||
                _rng.Next(1, max).ToString("000") + ".png";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<string> Translate(string langs, string text = null)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrWhiteSpace(text))
 | 
			
		||||
                throw new ArgumentException("Text is empty or null", nameof(text));
 | 
			
		||||
            var langarr = langs.ToLowerInvariant().Split('>');
 | 
			
		||||
            if (langarr.Length != 2)
 | 
			
		||||
                throw new ArgumentException("Langs does not have 2 parts separated by a >", nameof(langs));
 | 
			
		||||
            var from = langarr[0];
 | 
			
		||||
            var to = langarr[1];
 | 
			
		||||
            text = text?.Trim();
 | 
			
		||||
            return (await _google.Translate(text, from, to).ConfigureAwait(false)).SanitizeMentions(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private readonly object yomamaLock = new object();
 | 
			
		||||
        private int yomamaJokeIndex = 0;
 | 
			
		||||
        public Task<string> GetYomamaJoke()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										216
									
								
								src/NadekoBot/Modules/Searches/Services/TranslateService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								src/NadekoBot/Modules/Searches/Services/TranslateService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,216 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Discord;
 | 
			
		||||
using Discord.Net;
 | 
			
		||||
using LinqToDB;
 | 
			
		||||
using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Searches
 | 
			
		||||
{
 | 
			
		||||
    public sealed class TranslateService : ITranslateService, ILateExecutor, IReadyExecutor, INService
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IGoogleApiService _google;
 | 
			
		||||
        private readonly DbService _db;
 | 
			
		||||
        private readonly IEmbedBuilderService _eb;
 | 
			
		||||
        private readonly Bot _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,
 | 
			
		||||
            Bot bot)
 | 
			
		||||
        {
 | 
			
		||||
            _google = google;
 | 
			
		||||
            _db = db;
 | 
			
		||||
            _eb = eb;
 | 
			
		||||
            _bot = bot;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task OnReadyAsync()
 | 
			
		||||
        {
 | 
			
		||||
            var ctx = _db.GetDbContext();
 | 
			
		||||
 | 
			
		||||
            var guilds = _bot.AllGuildConfigs.Select(x => x.GuildId).ToList();
 | 
			
		||||
            var cs = await ctx.AutoTranslateChannels
 | 
			
		||||
                .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, x.Target)));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        public async Task LateExecute(IGuild guild, IUserMessage msg)
 | 
			
		||||
        {
 | 
			
		||||
            if (msg is IUserMessage { 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))
 | 
			
		||||
                    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());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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).ConfigureAwait(false);
 | 
			
		||||
            return res.SanitizeMentions(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<bool> ToggleAtl(ulong guildId, ulong channelId, bool autoDelete)
 | 
			
		||||
        {
 | 
			
		||||
            var ctx = _db.GetDbContext();
 | 
			
		||||
 | 
			
		||||
            var old = await ctx.AutoTranslateChannels
 | 
			
		||||
                .ToLinqToDBTable()
 | 
			
		||||
                .FirstOrDefaultAsyncLinqToDB(x => x.ChannelId == channelId);
 | 
			
		||||
            
 | 
			
		||||
            if (old is null)
 | 
			
		||||
            {
 | 
			
		||||
                ctx.AutoTranslateChannels
 | 
			
		||||
                    .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.AutoTranslateChannels
 | 
			
		||||
                .ToLinqToDBTable()
 | 
			
		||||
                .DeleteAsync(x => x.ChannelId == channelId);
 | 
			
		||||
            
 | 
			
		||||
            await ctx.SaveChangesAsync();
 | 
			
		||||
            _atcs.TryRemove(channelId, out _);
 | 
			
		||||
            _users.TryRemove(channelId, out _);
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public async Task<bool?> RegisterUserAsync(ulong userId, ulong channelId, string from, string to)
 | 
			
		||||
        {
 | 
			
		||||
            var ctx = _db.GetDbContext();
 | 
			
		||||
            var ch = await ctx.AutoTranslateChannels
 | 
			
		||||
                .ToLinqToDBTable()
 | 
			
		||||
                .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();
 | 
			
		||||
 | 
			
		||||
                var dict = _users.GetOrAdd(channelId, new ConcurrentDictionary<ulong, (string, string)>());
 | 
			
		||||
                dict[userId] = (from, to);
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ctx.AutoTranslateUsers.Remove(user);
 | 
			
		||||
            await ctx.SaveChangesAsync();
 | 
			
		||||
            
 | 
			
		||||
            if (_users.TryGetValue(channelId, out var inner))
 | 
			
		||||
                inner.TryRemove(userId, out _);
 | 
			
		||||
            
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<bool> UnregisterUser(ulong channelId, ulong userId)
 | 
			
		||||
        {
 | 
			
		||||
            var ctx = _db.GetDbContext();
 | 
			
		||||
            var rows = await ctx.AutoTranslateUsers
 | 
			
		||||
                .ToLinqToDBTable()
 | 
			
		||||
                .DeleteAsync(x => x.UserId == userId &&
 | 
			
		||||
                                  x.Channel.ChannelId == channelId);
 | 
			
		||||
 | 
			
		||||
            if (_users.TryGetValue(channelId, out var inner))
 | 
			
		||||
                inner.TryRemove(userId, out _);
 | 
			
		||||
            
 | 
			
		||||
            await ctx.SaveChangesAsync();
 | 
			
		||||
            return rows > 0;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        public IEnumerable<string> GetLanguages() => _google.Languages;
 | 
			
		||||
    } 
 | 
			
		||||
}
 | 
			
		||||
@@ -2,35 +2,29 @@
 | 
			
		||||
using Discord.Commands;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using NadekoBot.Common.Attributes;
 | 
			
		||||
using NadekoBot.Services;
 | 
			
		||||
using NadekoBot.Modules.Searches.Services;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Searches
 | 
			
		||||
{
 | 
			
		||||
    public partial class Searches
 | 
			
		||||
    {
 | 
			
		||||
        [Group]
 | 
			
		||||
        public class TranslateCommands : NadekoSubmodule
 | 
			
		||||
        public class TranslateCommands : NadekoSubmodule<ITranslateService>
 | 
			
		||||
        {
 | 
			
		||||
            private readonly SearchesService _searches;
 | 
			
		||||
            private readonly IGoogleApiService _google;
 | 
			
		||||
 | 
			
		||||
            public TranslateCommands(SearchesService searches, IGoogleApiService google)
 | 
			
		||||
            {
 | 
			
		||||
                _searches = searches;
 | 
			
		||||
                _google = google;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            [NadekoCommand, Aliases]
 | 
			
		||||
            public async Task Translate(string langs, [Leftover] string text = null)
 | 
			
		||||
            public async Task Translate(string from, string to, [Leftover] string text = null)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    await ctx.Channel.TriggerTypingAsync().ConfigureAwait(false);
 | 
			
		||||
                    var translation = await _searches.Translate(langs, text).ConfigureAwait(false);
 | 
			
		||||
                    await SendConfirmAsync(GetText(strs.translation) + " " + langs, translation).ConfigureAwait(false);
 | 
			
		||||
                    var translation = await _service.Translate(from, to, text).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
                    var embed = _eb.Create(ctx)
 | 
			
		||||
                        .WithOkColor()
 | 
			
		||||
                        .AddField(from, text, false)
 | 
			
		||||
                        .AddField(to, translation, false);
 | 
			
		||||
 | 
			
		||||
                    await ctx.Channel.EmbedAsync(embed);
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
@@ -38,27 +32,6 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //[NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
            //[OwnerOnly]
 | 
			
		||||
            //public async Task Obfuscate([Leftover] string txt)
 | 
			
		||||
            //{
 | 
			
		||||
            //    var lastItem = "en";
 | 
			
		||||
            //    foreach (var item in _google.Languages.Except(new[] { "en" }).Where(x => x.Length < 4))
 | 
			
		||||
            //    {
 | 
			
		||||
            //        var txt2 = await _searches.Translate(lastItem + ">" + item, txt);
 | 
			
		||||
            //        await ctx.Channel.EmbedAsync(_eb.Create()
 | 
			
		||||
            //            .WithOkColor()
 | 
			
		||||
            //            .WithTitle(lastItem + ">" + item)
 | 
			
		||||
            //            .AddField("Input", txt)
 | 
			
		||||
            //            .AddField("Output", txt2));
 | 
			
		||||
            //        txt = txt2;
 | 
			
		||||
            //        await Task.Delay(500);
 | 
			
		||||
            //        lastItem = item;
 | 
			
		||||
            //    }
 | 
			
		||||
            //    txt = await _searches.Translate(lastItem + ">en", txt);
 | 
			
		||||
            //    await SendConfirmAsync("Final output:\n\n" + txt);
 | 
			
		||||
            //}
 | 
			
		||||
 | 
			
		||||
            public enum AutoDeleteAutoTranslate
 | 
			
		||||
            {
 | 
			
		||||
                Del,
 | 
			
		||||
@@ -68,56 +41,49 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
            [NadekoCommand, Aliases]
 | 
			
		||||
            [RequireContext(ContextType.Guild)]
 | 
			
		||||
            [UserPerm(GuildPerm.Administrator)]
 | 
			
		||||
            [BotPerm(ChannelPerm.ManageMessages)]
 | 
			
		||||
            [OwnerOnly]
 | 
			
		||||
            public async Task AutoTranslate(AutoDeleteAutoTranslate autoDelete = AutoDeleteAutoTranslate.Nodel)
 | 
			
		||||
            {
 | 
			
		||||
                var channel = (ITextChannel)ctx.Channel;
 | 
			
		||||
 | 
			
		||||
                if (autoDelete == AutoDeleteAutoTranslate.Del)
 | 
			
		||||
                {
 | 
			
		||||
                    _searches.TranslatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true);
 | 
			
		||||
                    await ReplyConfirmLocalizedAsync(strs.atl_ad_started).ConfigureAwait(false);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                if (_searches.TranslatedChannels.TryRemove(channel.Id, out _))
 | 
			
		||||
                {
 | 
			
		||||
                    await ReplyConfirmLocalizedAsync(strs.atl_stopped).ConfigureAwait(false);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                if (_searches.TranslatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del))
 | 
			
		||||
                var toggle = await _service.ToggleAtl(ctx.Guild.Id, ctx.Channel.Id, autoDelete == AutoDeleteAutoTranslate.Del);
 | 
			
		||||
                if (toggle)
 | 
			
		||||
                {
 | 
			
		||||
                    await ReplyConfirmLocalizedAsync(strs.atl_started).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    await ReplyConfirmLocalizedAsync(strs.atl_stopped).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            [NadekoCommand, Aliases]
 | 
			
		||||
            [RequireContext(ContextType.Guild)]
 | 
			
		||||
            public async Task AutoTransLang([Leftover] string langs = null)
 | 
			
		||||
            public async Task AutoTransLang()
 | 
			
		||||
            {
 | 
			
		||||
                var ucp = (ctx.User.Id, ctx.Channel.Id);
 | 
			
		||||
 | 
			
		||||
                if (string.IsNullOrWhiteSpace(langs))
 | 
			
		||||
                if (await _service.UnregisterUser(ctx.Channel.Id, ctx.User.Id))
 | 
			
		||||
                {
 | 
			
		||||
                    if (_searches.UserLanguages.TryRemove(ucp, out langs))
 | 
			
		||||
                        await ReplyConfirmLocalizedAsync(strs.atl_removed).ConfigureAwait(false);
 | 
			
		||||
                    await ReplyConfirmLocalizedAsync(strs.atl_removed).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            [NadekoCommand, Aliases]
 | 
			
		||||
            [RequireContext(ContextType.Guild)]
 | 
			
		||||
            public async Task AutoTransLang(string from, string to)
 | 
			
		||||
            {
 | 
			
		||||
                var succ = await _service.RegisterUserAsync(ctx.User.Id, ctx.Channel.Id, from, to);
 | 
			
		||||
 | 
			
		||||
                if (succ is null)
 | 
			
		||||
                {
 | 
			
		||||
                    await ReplyErrorLocalizedAsync(strs.atl_not_enabled);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var langarr = langs.ToLowerInvariant().Split('>');
 | 
			
		||||
                if (langarr.Length != 2)
 | 
			
		||||
                    return;
 | 
			
		||||
                var from = langarr[0];
 | 
			
		||||
                var to = langarr[1];
 | 
			
		||||
 | 
			
		||||
                if (!_google.Languages.Contains(from) || !_google.Languages.Contains(to))
 | 
			
		||||
                
 | 
			
		||||
                if (succ is false)
 | 
			
		||||
                {
 | 
			
		||||
                    await ReplyErrorLocalizedAsync(strs.invalid_lang).ConfigureAwait(false);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                _searches.UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
 | 
			
		||||
 | 
			
		||||
                await ReplyConfirmLocalizedAsync(strs.atl_set(from, to));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -125,7 +91,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
            [RequireContext(ContextType.Guild)]
 | 
			
		||||
            public async Task Translangs()
 | 
			
		||||
            {
 | 
			
		||||
                await ctx.Channel.SendTableAsync(_google.Languages, str => $"{str,-15}", 3).ConfigureAwait(false);
 | 
			
		||||
                await ctx.Channel.SendTableAsync(_service.GetLanguages(), str => $"{str,-15}", 3).ConfigureAwait(false);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1306,7 +1306,7 @@ poll:
 | 
			
		||||
autotranslang:
 | 
			
		||||
  desc: "Sets your source and target language to be used with `{0}at`. Specify no parameters to remove previously set value."
 | 
			
		||||
  args:
 | 
			
		||||
    - "en>fr"
 | 
			
		||||
    - "en fr"
 | 
			
		||||
autotranslate:
 | 
			
		||||
  desc: "Starts automatic translation of all messages by users who set their `{0}atl` in this channel. You can set \"del\" parameter to automatically delete all translated user messages."
 | 
			
		||||
  args:
 | 
			
		||||
 
 | 
			
		||||
@@ -450,6 +450,7 @@
 | 
			
		||||
  "atl_set": "Your auto-translate language has been set to {0}>{1}",
 | 
			
		||||
  "atl_started": "Started automatic translation of messages on this channel.",
 | 
			
		||||
  "atl_stopped": "Stopped automatic translation of messages on this channel.",
 | 
			
		||||
  "atl_not_enabled": "Automatic translation is not enabled on this channel.",
 | 
			
		||||
  "bad_input_format": "Bad input format, or something went wrong.",
 | 
			
		||||
  "card_not_found": "Couldn't find that card.",
 | 
			
		||||
  "catfact": "fact",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user