mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2024-10-02 20:13:13 +00:00
.atl / .at reworked
This commit is contained in:
parent
fcc49dbbdb
commit
3c0768a372
15 changed files with 3207 additions and 137 deletions
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -9,6 +9,18 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
|
||||||
- Added slots.currencyFontColor to gambling.yml
|
- Added slots.currencyFontColor to gambling.yml
|
||||||
- Added `.qexport` and `.qimport` commands which allow you to export and import quotes just like `.crsexport`
|
- Added `.qexport` and `.qimport` commands which allow you to export and import quotes just like `.crsexport`
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- `.at` and `.atl` commands reworked
|
||||||
|
- Persist restarts
|
||||||
|
- Will now only translate non-commands
|
||||||
|
- You can switch between `.at del` and `.at` without clearing the user language registrations
|
||||||
|
- Disabling `.at` will clear all user language registrations on that channel
|
||||||
|
- Users can't register languages if the `.at` is not enabled
|
||||||
|
- Looks much nicer
|
||||||
|
- Bot will now reply to user messages with a translation if `del` is disabled
|
||||||
|
- Bot will make an embed with original and translated text with user avatar and name if `del` is enabled
|
||||||
|
- If the bot is unable to delete messages while having `del` enabled, it will reset back to the no-del behavior for the current session
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- `.crypto` now supports top 5000 coins
|
- `.crypto` now supports top 5000 coins
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ using NadekoBot.Common.ModuleBehaviors;
|
||||||
using NadekoBot.Common.Configs;
|
using NadekoBot.Common.Configs;
|
||||||
using NadekoBot.Db;
|
using NadekoBot.Db;
|
||||||
using NadekoBot.Modules.Administration.Services;
|
using NadekoBot.Modules.Administration.Services;
|
||||||
|
using NadekoBot.Modules.Searches;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace NadekoBot
|
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<WaifuInfo> WaifuInfo { get; set; }
|
||||||
public DbSet<ImageOnlyChannel> ImageOnlyChannels { get; set; }
|
public DbSet<ImageOnlyChannel> ImageOnlyChannels { get; set; }
|
||||||
public DbSet<NsfwBlacklistedTag> NsfwBlacklistedTags { 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)
|
public NadekoContext(DbContextOptions<NadekoContext> options) : base(options)
|
||||||
{
|
{
|
||||||
|
@ -368,6 +370,21 @@ namespace NadekoBot.Services.Database
|
||||||
modelBuilder.Entity<NsfwBlacklistedTag>(nbt => nbt
|
modelBuilder.Entity<NsfwBlacklistedTag>(nbt => nbt
|
||||||
.HasIndex(x => x.GuildId)
|
.HasIndex(x => x.GuildId)
|
||||||
.IsUnique(false));
|
.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");
|
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 =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
|
@ -2194,6 +2250,17 @@ namespace NadekoBot.Migrations
|
||||||
b.Navigation("GuildConfig");
|
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 =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", null)
|
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", null)
|
||||||
|
@ -2541,6 +2608,11 @@ namespace NadekoBot.Migrations
|
||||||
b.Navigation("IgnoredChannels");
|
b.Navigation("IgnoredChannels");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Users");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("AntiAltSetting");
|
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 NadekoBot.Common;
|
||||||
using Discord.WebSocket;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using NadekoBot.Common;
|
|
||||||
using NadekoBot.Modules.Searches.Common;
|
using NadekoBot.Modules.Searches.Common;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
using NadekoBot.Services.Database.Models;
|
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
@ -13,18 +9,14 @@ using SixLabors.ImageSharp.Drawing.Processing;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
using SixLabors.ImageSharp.Processing;
|
using SixLabors.ImageSharp.Processing;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AngleSharp.Html.Dom;
|
using AngleSharp.Html.Dom;
|
||||||
using AngleSharp.Html.Parser;
|
using AngleSharp.Html.Parser;
|
||||||
using NadekoBot.Db;
|
|
||||||
using NadekoBot.Modules.Administration;
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using HorizontalAlignment = SixLabors.Fonts.HorizontalAlignment;
|
using HorizontalAlignment = SixLabors.Fonts.HorizontalAlignment;
|
||||||
using Image = SixLabors.ImageSharp.Image;
|
using Image = SixLabors.ImageSharp.Image;
|
||||||
|
@ -34,71 +26,31 @@ namespace NadekoBot.Modules.Searches.Services
|
||||||
public class SearchesService : INService
|
public class SearchesService : INService
|
||||||
{
|
{
|
||||||
private readonly IHttpClientFactory _httpFactory;
|
private readonly IHttpClientFactory _httpFactory;
|
||||||
private readonly DiscordSocketClient _client;
|
|
||||||
private readonly IGoogleApiService _google;
|
private readonly IGoogleApiService _google;
|
||||||
private readonly DbService _db;
|
|
||||||
private readonly IImageCache _imgs;
|
private readonly IImageCache _imgs;
|
||||||
private readonly IDataCache _cache;
|
private readonly IDataCache _cache;
|
||||||
private readonly FontProvider _fonts;
|
private readonly FontProvider _fonts;
|
||||||
private readonly IBotCredentials _creds;
|
private readonly IBotCredentials _creds;
|
||||||
private readonly IEmbedBuilderService _eb;
|
|
||||||
private readonly NadekoRandom _rng;
|
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<WoWJoke> WowJokes { get; } = new List<WoWJoke>();
|
||||||
public List<MagicItem> MagicItems { get; } = new List<MagicItem>();
|
public List<MagicItem> MagicItems { get; } = new List<MagicItem>();
|
||||||
private readonly List<string> _yomamaJokes;
|
private readonly List<string> _yomamaJokes;
|
||||||
|
|
||||||
public SearchesService(DiscordSocketClient client, IGoogleApiService google,
|
public SearchesService(IGoogleApiService google,
|
||||||
DbService db, Bot bot, IDataCache cache, IHttpClientFactory factory,
|
IDataCache cache,
|
||||||
FontProvider fonts, IBotCredentials creds, IEmbedBuilderService eb)
|
IHttpClientFactory factory,
|
||||||
|
FontProvider fonts,
|
||||||
|
IBotCredentials creds)
|
||||||
{
|
{
|
||||||
_httpFactory = factory;
|
_httpFactory = factory;
|
||||||
_client = client;
|
|
||||||
_google = google;
|
_google = google;
|
||||||
_db = db;
|
|
||||||
_imgs = cache.LocalImages;
|
_imgs = cache.LocalImages;
|
||||||
_cache = cache;
|
_cache = cache;
|
||||||
_fonts = fonts;
|
_fonts = fonts;
|
||||||
_creds = creds;
|
_creds = creds;
|
||||||
_eb = eb;
|
|
||||||
_rng = new NadekoRandom();
|
_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
|
//joke commands
|
||||||
if (File.Exists("data/wowjokes.json"))
|
if (File.Exists("data/wowjokes.json"))
|
||||||
{
|
{
|
||||||
|
@ -340,19 +292,6 @@ namespace NadekoBot.Modules.Searches.Services
|
||||||
_rng.Next(1, max).ToString("000") + ".png";
|
_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 readonly object yomamaLock = new object();
|
||||||
private int yomamaJokeIndex = 0;
|
private int yomamaJokeIndex = 0;
|
||||||
public Task<string> GetYomamaJoke()
|
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 Discord.Commands;
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Linq;
|
|
||||||
using NadekoBot.Common.Attributes;
|
using NadekoBot.Common.Attributes;
|
||||||
using NadekoBot.Services;
|
|
||||||
using NadekoBot.Modules.Searches.Services;
|
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Searches
|
namespace NadekoBot.Modules.Searches
|
||||||
{
|
{
|
||||||
public partial class Searches
|
public partial class Searches
|
||||||
{
|
{
|
||||||
[Group]
|
[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]
|
[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
|
try
|
||||||
{
|
{
|
||||||
await ctx.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
await ctx.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||||
var translation = await _searches.Translate(langs, text).ConfigureAwait(false);
|
var translation = await _service.Translate(from, to, text).ConfigureAwait(false);
|
||||||
await SendConfirmAsync(GetText(strs.translation) + " " + langs, translation).ConfigureAwait(false);
|
|
||||||
|
var embed = _eb.Create(ctx)
|
||||||
|
.WithOkColor()
|
||||||
|
.AddField(from, text, false)
|
||||||
|
.AddField(to, translation, false);
|
||||||
|
|
||||||
|
await ctx.Channel.EmbedAsync(embed);
|
||||||
}
|
}
|
||||||
catch
|
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
|
public enum AutoDeleteAutoTranslate
|
||||||
{
|
{
|
||||||
Del,
|
Del,
|
||||||
|
@ -68,56 +41,49 @@ namespace NadekoBot.Modules.Searches
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
[BotPerm(ChannelPerm.ManageMessages)]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
public async Task AutoTranslate(AutoDeleteAutoTranslate autoDelete = AutoDeleteAutoTranslate.Nodel)
|
public async Task AutoTranslate(AutoDeleteAutoTranslate autoDelete = AutoDeleteAutoTranslate.Nodel)
|
||||||
{
|
{
|
||||||
var channel = (ITextChannel)ctx.Channel;
|
var toggle = await _service.ToggleAtl(ctx.Guild.Id, ctx.Channel.Id, autoDelete == AutoDeleteAutoTranslate.Del);
|
||||||
|
if (toggle)
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
await ReplyConfirmLocalizedAsync(strs.atl_started).ConfigureAwait(false);
|
await ReplyConfirmLocalizedAsync(strs.atl_started).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await ReplyConfirmLocalizedAsync(strs.atl_stopped).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Aliases]
|
[NadekoCommand, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task AutoTransLang([Leftover] string langs = null)
|
public async Task AutoTransLang()
|
||||||
{
|
{
|
||||||
var ucp = (ctx.User.Id, ctx.Channel.Id);
|
if (await _service.UnregisterUser(ctx.Channel.Id, ctx.User.Id))
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(langs))
|
|
||||||
{
|
{
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var langarr = langs.ToLowerInvariant().Split('>');
|
if (succ is false)
|
||||||
if (langarr.Length != 2)
|
|
||||||
return;
|
|
||||||
var from = langarr[0];
|
|
||||||
var to = langarr[1];
|
|
||||||
|
|
||||||
if (!_google.Languages.Contains(from) || !_google.Languages.Contains(to))
|
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.invalid_lang).ConfigureAwait(false);
|
await ReplyErrorLocalizedAsync(strs.invalid_lang).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_searches.UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
|
|
||||||
|
|
||||||
await ReplyConfirmLocalizedAsync(strs.atl_set(from, to));
|
await ReplyConfirmLocalizedAsync(strs.atl_set(from, to));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +91,7 @@ namespace NadekoBot.Modules.Searches
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Translangs()
|
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:
|
autotranslang:
|
||||||
desc: "Sets your source and target language to be used with `{0}at`. Specify no parameters to remove previously set value."
|
desc: "Sets your source and target language to be used with `{0}at`. Specify no parameters to remove previously set value."
|
||||||
args:
|
args:
|
||||||
- "en>fr"
|
- "en fr"
|
||||||
autotranslate:
|
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."
|
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:
|
args:
|
||||||
|
|
|
@ -450,6 +450,7 @@
|
||||||
"atl_set": "Your auto-translate language has been set to {0}>{1}",
|
"atl_set": "Your auto-translate language has been set to {0}>{1}",
|
||||||
"atl_started": "Started automatic translation of messages on this channel.",
|
"atl_started": "Started automatic translation of messages on this channel.",
|
||||||
"atl_stopped": "Stopped 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.",
|
"bad_input_format": "Bad input format, or something went wrong.",
|
||||||
"card_not_found": "Couldn't find that card.",
|
"card_not_found": "Couldn't find that card.",
|
||||||
"catfact": "fact",
|
"catfact": "fact",
|
||||||
|
|
Loading…
Reference in a new issue