Removed discrim from the database

Added .translateflags command
Added captcha to timely, configurable in .conf gambling
Changed bonuses for patreon rewards
Fixed nunchi message color
This commit is contained in:
Toastie (DCS Team) 2024-11-03 23:50:08 +13:00
parent 41f1c7aa11
commit d1bc423b99
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
29 changed files with 12080 additions and 4732 deletions

View file

@ -2,20 +2,49 @@
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except date format. a-c-f-r-o Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except date format. a-c-f-r-o
## [5.1.16] - 29.10.2024 ## [5.1.18] - 03.11.2024
### Added
- Added `.translateflags` / `.trfl` command.
- Enable on a per-channel basis.
- Reacting on any message in that channel with a flag emoji will post the translation of that message in the
language of that country
- 5 second cooldown per user
- The message can only be translated once per language (counter resets every 24h)
- `.timely` now has a captcha. Togglable via `.conf gambling`
## Changed
- [public bot] Patreon reward bonus for flowers reduced. Timely bonuses stay the same
- discriminators removed from the databases. All users who had ???? as discriminator have been renamed to ??username.
- all new unknown users will have ??Unknown as their name
## Fixed
- nunchi join game message is now ok color instead of error color
## [5.1.17] - 29.10.2024
### Fixed
- fix: Bot will now not accept .aar Role if that Role is higher than or equal to bot's role. Previously bot would just
fail silently, now there is a proper error message.
## [5.1.16] - 28.10.2024
## Added ## Added
- Added .ncanvas and related commands. - Added .ncanvas and related commands.
- You can set pixel colors (and text) on a 500x350 canvas, pepega version of r/place - You can set pixel colors (and text) on a 500x350 canvas, pepega version of r/place
- You use currency to set pixels. - You use currency to set pixels.
- Commands: - Commands:
- see the entire canvas: `.nc` - see the entire canvas: `.nc`
- zoom: `.ncz <pos>` or `.ncz x y` - zoom: `.ncz <pos>` or `.ncz x y`
- set pixel: `.ncsp <pos> <color> <text?>` - set pixel: `.ncsp <pos> <color> <text?>`
- get pixel: `.ncp <pos>` - get pixel: `.ncp <pos>`
- Owners can use .ncsetimg to set a starting image, use `.h .setimg` for instructions - Owners can use .ncsetimg to set a starting image, use `.h .setimg` for instructions
- Owners can reset the whole canvas via `.ncreset` - Owners can reset the whole canvas via `.ncreset`
## [5.1.15] - 21.10.2024 ## [5.1.15] - 21.10.2024
@ -70,8 +99,9 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
### Added ### Added
- Added `%user.displayname%` placeholder. It will show users nickname, if there is one, otherwise it will show the username. - Added `%user.displayname%` placeholder. It will show users nickname, if there is one, otherwise it will show the
- Nickname won't be shown in bye messages. username.
- Nickname won't be shown in bye messages.
- Added initial version of grpc api. Beta - Added initial version of grpc api. Beta
### Fixed ### Fixed
@ -82,9 +112,9 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
### Changed ### Changed
- Youtube now always uses `yt-dlp`. Dropped support for `youtube-dl` - Youtube now always uses `yt-dlp`. Dropped support for `youtube-dl`
- If you've previously renamed your yt-dlp file to youtube-dl, please rename it back. - If you've previously renamed your yt-dlp file to youtube-dl, please rename it back.
- ytProvider in data/searches.yml now also controls where you're getting your song streams from. - ytProvider in data/searches.yml now also controls where you're getting your song streams from.
- (Invidious support added for .q) - (Invidious support added for .q)
## [5.1.10] - 24.09.2024 ## [5.1.10] - 24.09.2024
@ -103,26 +133,28 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
- Fixed `.greettest`, and other `.*test` commands if you didn't have them enabled. - Fixed `.greettest`, and other `.*test` commands if you didn't have them enabled.
- Fixed `.greetdmtest` sending messages twice. - Fixed `.greetdmtest` sending messages twice.
- Fixed a serious bug which caused greet messages to be jumbled up, and wrong ones to be sent for the wrong events. - Fixed a serious bug which caused greet messages to be jumbled up, and wrong ones to be sent for the wrong events.
- There is no database issue, all greet messages are safe, the cache was caching any setting every 3 seconds with no regard for the type of the event - There is no database issue, all greet messages are safe, the cache was caching any setting every 3 seconds with no
- This also caused `.greetdm` messages to not be sent if `.greet` is enabled regard for the type of the event
- This bug was introduced in 5.1.8. PLEASE UPDATE if you are on 5.1.8 - This also caused `.greetdm` messages to not be sent if `.greet` is enabled
- This bug was introduced in 5.1.8. PLEASE UPDATE if you are on 5.1.8
- Selfhosters only: Fixed marmalade dependency loading - Selfhosters only: Fixed marmalade dependency loading
- Note: Make sure to not publish any other DLLs besides the ones you are sure you will need, as there can be version conflicts which didn't happen before. - Note: Make sure to not publish any other DLLs besides the ones you are sure you will need, as there can be version
conflicts which didn't happen before.
## [5.1.8] - 20.09.2024 ## [5.1.8] - 20.09.2024
### Added ### Added
- Added `.leaveunkeptservers` which will make the bot leave all servers on all shards whose owners didn't run `.keep` command. - Added `.leaveunkeptservers` which will make the bot leave all servers on all shards whose owners didn't run `.keep` command.
- This is a dangerous and irreversible command, don't use it. Meant for use on the public bot. - This is a dangerous and irreversible command, don't use it. Meant for use on the public bot.
- `.adpl` now supports custom statuses (you no longer need to specify Playing, Watching, etc...) - `.adpl` now supports custom statuses (you no longer need to specify Playing, Watching, etc...)
### Changed ### Changed
- `.quote` commands cleaned up and improved - `.quote` commands cleaned up and improved
- All quote commands now start with `.q<whatever>` and follow the same naming pattern as Expression commands - All quote commands now start with `.q<whatever>` and follow the same naming pattern as Expression commands
- `.liqu` renamed to `.qli` - `.liqu` renamed to `.qli`
- `.quotesearch` / `.qse` is now paginated for easier searching - `.quotesearch` / `.qse` is now paginated for easier searching
- `.whosplaying` is now paginated - `.whosplaying` is now paginated
- `.img` is now paginated - `.img` is now paginated
- `.setgame` renamed to`.setactivity` and now supports custom text activity. You don't have to specify playing, listening etc before the activity - `.setgame` renamed to`.setactivity` and now supports custom text activity. You don't have to specify playing, listening etc before the activity
@ -142,7 +174,7 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
### Removed ### Removed
- Removed mysql support as it didn't work for a while, and requires some special handling/maintenance - Removed mysql support as it didn't work for a while, and requires some special handling/maintenance
- Sqlite and Postgres support stays - Sqlite and Postgres support stays
## [5.1.7] - 09.08.2024 ## [5.1.7] - 09.08.2024
@ -166,15 +198,16 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
- Possible fix for `.remind` timestamp - Possible fix for `.remind` timestamp
### Removed ### Removed
- Removed old bloat / semi broken / dumb commands - Removed old bloat / semi broken / dumb commands
- `.memelist` / `.memegen` (too inconvenient to use) - `.memelist` / `.memegen` (too inconvenient to use)
- `.activity` (useless owner-only command) - `.activity` (useless owner-only command)
- `.rafflecur` (Just use raffle and then award manually instead) - `.rafflecur` (Just use raffle and then award manually instead)
- `.rollduel` (we had this command?) - `.rollduel` (we had this command?)
- You can no longer bet on `.connect4` - You can no longer bet on `.connect4`
- `.economy` Removed. - `.economy` Removed.
- Was buggy and didn.t really show the real state of the economy. - Was buggy and didn't really show the real state of the economy.
- It might come back improved in the future - It might come back improved in the future
- `.mal` Removed. Useless information / semi broken - `.mal` Removed. Useless information / semi broken
## [5.1.5] - 01.08.2024 ## [5.1.5] - 01.08.2024
@ -182,9 +215,9 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
### Added ### Added
- Added: Added a `.afk <msg>?` command which sets an afk message which will trigger whenever someone pings you - Added: Added a `.afk <msg>?` command which sets an afk message which will trigger whenever someone pings you
- Message will when you type a message in any channel that the bot sees, or after 8 hours, whichever comes first - Message will when you type a message in any channel that the bot sees, or after 8 hours, whichever comes first
- The specified message will be prefixed with "The user is afk: " - The specified message will be prefixed with "The user is afk: "
- The afk message will disappear 30 seconds after being triggered - The afk message will disappear 30 seconds after being triggered
### Changed ### Changed
@ -192,7 +225,7 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
- Updated some bet descriptions to include 'all' 'half' usage instructions - Updated some bet descriptions to include 'all' 'half' usage instructions
- Updated some command strings - Updated some command strings
- dev: Vastly simplified marmalade creation using dotnet templates, docs updated - dev: Vastly simplified marmalade creation using dotnet templates, docs updated
- Slight refactor of .wiki, .time, .catfact, .wikia, .define, .bible and .quran commands, no significant change in functionality - Slight refactor of .wiki, time, .catfact, .wikia, .define, .bible and .quran commands, no significant change in functionality
### Fixed ### Fixed
@ -211,8 +244,10 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
- Added Clubs rank in the leaderboard to `.clubinfo` - Added Clubs rank in the leaderboard to `.clubinfo`
- Bot owners can now check other people's bank balance (Not server owners, only bot owner, the person who is hosting the bot) - Bot owners can now check other people's bank balance (Not server owners, only bot owner, the person who is hosting the bot)
- You can now send multiple waifu gifts at once to waifus. For example `.waifugift 3xRose @user` will give that user 3 roses - You can now send multiple waifu gifts at once to waifus. For example `.waifugift 3xRose @user` will give that user 3 roses
- The format is `<NUMBER>x<ITEM>`, no spaces - The format is `<NUMBER>x<ITEM>`, no spaces
- Added `.boosttest` command - Added `.boosttest` command
- Added support for any openai compatible api for the chatterbot feature change:
- Changed games.yml to allow input of the apiUrl (needs to be openai compatible) and modelName as a string.
### Changed ### Changed
@ -254,9 +289,9 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
### Added ### Added
- Added `.honeypot` command, which automatically softbans (ban and immediate unban) any user who posts in that channel. - Added `.honeypot` command, which automatically softbans (ban and immediate unban) any user who posts in that channel.
- Useful to auto softban bots who spam every channel upon joining - Useful to auto softban bots who spam every channel upon joining
- Users who run commands or expressions won't be softbanned. - Users who run commands or expressions won't be softbanned.
- Users who have ban member permissions are also excluded. - Users who have ban member permissions are also excluded.
### Fixed ### Fixed
@ -270,7 +305,6 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
- Added support for `gpt-4o` in `data/games.yml` - Added support for `gpt-4o` in `data/games.yml`
- Added EllieAiToken to `creds.yml` - Added EllieAiToken to `creds.yml`
### Changed ### Changed
- Remind will now show a timestamp tag for durations - Remind will now show a timestamp tag for durations
@ -283,8 +317,8 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
- Fixed xp bg buy button not working, and possibly some other buttons too - Fixed xp bg buy button not working, and possibly some other buttons too
- Fixed shopbuy %user% placeholders and updated help text - Fixed shopbuy %user% placeholders and updated help text
- All 'feed overloads should now work" - All .feed overloads should now work"
- `'xpexclude` should will now work with forums too. If you exclude a forum you won't be able to gain xp in any of the threads. - `.xpexclude` should will now work with forums too. If you exclude a forum you won't be able to gain xp in any of the threads.
- Fixed remind not showing correct time - Fixed remind not showing correct time
### Removed ### Removed
@ -296,12 +330,14 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
### Added ### Added
- Added `'setserverbanner` and `'setservericon` commands - Added `.setserverbanner` and `.setservericon` commands
- Added overloads section to `'h command` which will show you all versions of command usage with param names - Added overloads section to `.h command` which will show you all versions of command usage with param names
- You can now check commands for submodules, for example `'cmds SelfAssignedRoles` will show brief help for each of the commands in that submodule - You can now check commands for submodules, for example `.cmds SelfAssignedRoles` will show brief help for each of the
- Added dropdown menus for 'mdls and 'cmds (both module and group versions) which will give you the option to see more detailed help for each specific module, group or command respectively commands in that submodule
- Added dropdown menus for .mdls and .cmds (both module and group versions) which will give you the option to see more
detailed help for each specific module, group or command respectively
- Self-Hosters only: - Self-Hosters only:
- Added a dangerous cleanup command that you don't have to know about - Added a dangerous cleanup command that you don't have to know about
### Changed ### Changed
@ -309,7 +345,7 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
### Fixed ### Fixed
- `'verbose` will now be respected for expression errors - `.verbose` will now be respected for expression errors
- Using `'pick` will now correctly show the name of the user who picked the currency - Using `.pick` will now correctly show the name of the user who picked the currency
- Fixed `'h` not working on some commands - Fixed `.h` not working on some commands
- `'langset` and `'langsetd` should no longer allow unsupported languages and nonsense to be typed in - `.langset` and `.langsetd` should no longer allow unsupported languages and nonsense to be typed in

View file

@ -73,6 +73,14 @@ public abstract class EllieContext : DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
#region Flag Translate
modelBuilder.Entity<FlagTranslateChannel>()
.HasIndex(x => new { x.GuildId, x.ChannelId })
.IsUnique();
#endregion
#region NCanvas #region NCanvas
modelBuilder.Entity<NCPixel>() modelBuilder.Entity<NCPixel>()

View file

@ -25,7 +25,6 @@ public static class DiscordUserExtensions
{ {
UserId = userId, UserId = userId,
Username = username, Username = username,
Discriminator = discrim,
AvatarId = avatarId, AvatarId = avatarId,
TotalXp = 0, TotalXp = 0,
CurrencyAmount = 0 CurrencyAmount = 0
@ -33,7 +32,6 @@ public static class DiscordUserExtensions
old => new() old => new()
{ {
Username = username, Username = username,
Discriminator = discrim,
AvatarId = avatarId AvatarId = avatarId
}, },
() => new() () => new()
@ -49,8 +47,7 @@ public static class DiscordUserExtensions
() => new() () => new()
{ {
UserId = userId, UserId = userId,
Username = "Unknown", Username = "??Unknown",
Discriminator = "????",
AvatarId = string.Empty, AvatarId = string.Empty,
TotalXp = 0, TotalXp = 0,
CurrencyAmount = 0 CurrencyAmount = 0

View file

@ -6,7 +6,7 @@ public class DiscordUser : DbEntity
{ {
public ulong UserId { get; set; } public ulong UserId { get; set; }
public string Username { get; set; } public string Username { get; set; }
public string Discriminator { get; set; } // public string Discriminator { get; set; }
public string AvatarId { get; set; } public string AvatarId { get; set; }
public int? ClubId { get; set; } public int? ClubId { get; set; }
@ -26,9 +26,6 @@ public class DiscordUser : DbEntity
public override string ToString() public override string ToString()
{ {
if (string.IsNullOrWhiteSpace(Discriminator) || Discriminator == "0000") return Username;
return Username;
return Username + "#" + Discriminator;
} }
} }

View file

@ -0,0 +1,8 @@
#nullable disable
namespace EllieBot.Db.Models;
public class FlagTranslateChannel : DbEntity
{
public ulong GuildId { get; set; }
public ulong ChannelId { get; set; }
}

View file

@ -5,6 +5,11 @@ namespace EllieBot.Migrations;
public static class MigrationQueries public static class MigrationQueries
{ {
public static void UpdateUsernames(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("UPDATE DiscordUser SET Username = '??' + Username WHERE Discriminator = '????';");
}
public static void MigrateRero(MigrationBuilder migrationBuilder) public static void MigrateRero(MigrationBuilder migrationBuilder)
{ {
if (migrationBuilder.IsSqlite()) if (migrationBuilder.IsSqlite())

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,56 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
/// <inheritdoc />
public partial class nodiscrimandflagtranslate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
MigrationQueries.UpdateUsernames(migrationBuilder);
migrationBuilder.DropColumn(
name: "discriminator",
table: "discorduser");
migrationBuilder.CreateTable(
name: "flagtranslatechannel",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
dateadded = table.Column<DateTime>(type: "timestamp without time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("pk_flagtranslatechannel", x => x.id);
});
migrationBuilder.CreateIndex(
name: "ix_flagtranslatechannel_guildid_channelid",
table: "flagtranslatechannel",
columns: new[] { "guildid", "channelid" },
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "flagtranslatechannel");
migrationBuilder.AddColumn<string>(
name: "discriminator",
table: "discorduser",
type: "text",
nullable: true);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,55 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EllieBot.Migrations
{
/// <inheritdoc />
public partial class nodiscrimandflagtranslate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
MigrationQueries.UpdateUsernames(migrationBuilder);
migrationBuilder.DropColumn(
name: "Discriminator",
table: "DiscordUser");
migrationBuilder.CreateTable(
name: "FlagTranslateChannel",
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),
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_FlagTranslateChannel", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_FlagTranslateChannel_GuildId_ChannelId",
table: "FlagTranslateChannel",
columns: new[] { "GuildId", "ChannelId" },
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "FlagTranslateChannel");
migrationBuilder.AddColumn<string>(
name: "Discriminator",
table: "DiscordUser",
type: "TEXT",
nullable: true);
}
}
}

View file

@ -453,7 +453,6 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, IEService
{ {
x.UserId, x.UserId,
x.Username, x.Username,
x.Discriminator
}) })
.Where(x => users.Select(y => y.Id).Contains(x.UserId)) .Where(x => users.Select(y => y.Id).Contains(x.UserId))
.ToArrayAsyncEF(); .ToArrayAsyncEF();
@ -465,12 +464,11 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, IEService
UserId = x.Id, UserId = x.Id,
AvatarId = x.AvatarId, AvatarId = x.AvatarId,
Username = x.Username, Username = x.Username,
Discriminator = x.Discriminator
}); });
var added = (await ctx.BulkCopyAsync(usersToAdd)).RowsCopied; var added = (await ctx.BulkCopyAsync(usersToAdd)).RowsCopied;
var toUpdateUserIds = presentDbUsers var toUpdateUserIds = presentDbUsers
.Where(x => x.Username == "Unknown" && x.Discriminator == "????") .Where(x => x.Username.StartsWith("??"))
.Select(x => x.UserId) .Select(x => x.UserId)
.ToArray(); .ToArray();
@ -481,7 +479,6 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, IEService
.UpdateAsync(x => new DiscordUser() .UpdateAsync(x => new DiscordUser()
{ {
Username = user.Username, Username = user.Username,
Discriminator = user.Discriminator,
// .award tends to set AvatarId and DateAdded to NULL, so account for that. // .award tends to set AvatarId and DateAdded to NULL, so account for that.
AvatarId = user.AvatarId, AvatarId = user.AvatarId,

View file

@ -603,7 +603,7 @@ public class WaifuService : IEService, IReadyExecutor
.Where(wi => wi.ClaimerId == waifuId) .Where(wi => wi.ClaimerId == waifuId)
.Select(wi => wi.WaifuId) .Select(wi => wi.WaifuId)
.Contains(x.Id)) .Contains(x.Id))
.Select(x => $"{x.Username}#{x.Discriminator}") .Select(x => x.Username)
.ToListAsyncEF(); .ToListAsyncEF();
} }
@ -615,7 +615,7 @@ public class WaifuService : IEService, IReadyExecutor
.Where(wi => wi.AffinityId == waifuId) .Where(wi => wi.AffinityId == waifuId)
.Select(wi => wi.WaifuId) .Select(wi => wi.WaifuId)
.Contains(x.Id)) .Contains(x.Id))
.Select(x => $"{x.Username}#{x.Discriminator}") .Select(x => x.Username)
.ToListAsyncEF(); .ToListAsyncEF();
} }

View file

@ -42,15 +42,12 @@ public static class WaifuExtensions
{ {
Affinity = x.Affinity == null Affinity = x.Affinity == null
? null ? null
: x.Affinity.Username : x.Affinity.Username,
+ (x.Affinity.Discriminator != "0000" ? "#" + x.Affinity.Discriminator : ""),
ClaimerName = ClaimerName =
x.Claimer == null x.Claimer == null
? null ? null
: x.Claimer.Username : x.Claimer.Username,
+ (x.Claimer.Discriminator != "0000" ? "#" + x.Claimer.Discriminator : ""), WaifuName = x.Waifu.Username,
WaifuName = x.Waifu.Username
+ (x.Waifu.Discriminator != "0000" ? "#" + x.Waifu.Discriminator : ""),
Price = x.Price Price = x.Price
}) })
.ToListAsyncEF(); .ToListAsyncEF();
@ -62,7 +59,7 @@ public static class WaifuExtensions
public static ulong GetWaifuUserId(this DbSet<WaifuInfo> waifus, ulong ownerId, string name) public static ulong GetWaifuUserId(this DbSet<WaifuInfo> waifus, ulong ownerId, string name)
=> waifus.AsQueryable() => waifus.AsQueryable()
.AsNoTracking() .AsNoTracking()
.Where(x => x.Claimer.UserId == ownerId && x.Waifu.Username + "#" + x.Waifu.Discriminator == name) .Where(x => x.Claimer.UserId == ownerId && x.Waifu.Username == name)
.Select(x => x.Waifu.UserId) .Select(x => x.Waifu.UserId)
.FirstOrDefault(); .FirstOrDefault();
@ -100,7 +97,7 @@ public static class WaifuExtensions
ctx.Set<DiscordUser>() ctx.Set<DiscordUser>()
.AsQueryable() .AsQueryable()
.Where(u => u.UserId == userId) .Where(u => u.UserId == userId)
.Select(u => u.Username + "#" + u.Discriminator) .Select(u => u.Username)
.FirstOrDefault(), .FirstOrDefault(),
AffinityCount = AffinityCount =
ctx.Set<WaifuUpdate>() ctx.Set<WaifuUpdate>()
@ -112,14 +109,14 @@ public static class WaifuExtensions
ctx.Set<DiscordUser>() ctx.Set<DiscordUser>()
.AsQueryable() .AsQueryable()
.Where(u => u.Id == w.AffinityId) .Where(u => u.Id == w.AffinityId)
.Select(u => u.Username + "#" + u.Discriminator) .Select(u => u.Username)
.FirstOrDefault(), .FirstOrDefault(),
ClaimCount = ctx.Set<WaifuInfo>().AsQueryable().Count(x => x.ClaimerId == w.WaifuId), ClaimCount = ctx.Set<WaifuInfo>().AsQueryable().Count(x => x.ClaimerId == w.WaifuId),
ClaimerName = ClaimerName =
ctx.Set<DiscordUser>() ctx.Set<DiscordUser>()
.AsQueryable() .AsQueryable()
.Where(u => u.Id == w.ClaimerId) .Where(u => u.Id == w.ClaimerId)
.Select(u => u.Username + "#" + u.Discriminator) .Select(u => u.Username)
.FirstOrDefault(), .FirstOrDefault(),
DivorceCount = DivorceCount =
ctx.Set<WaifuUpdate>() ctx.Set<WaifuUpdate>()

View file

@ -29,7 +29,7 @@ public partial class Games
if (!await nunchi.Join(ctx.User.Id, ctx.User.ToString())) if (!await nunchi.Join(ctx.User.Id, ctx.User.ToString()))
return; return;
await Response().Error(strs.nunchi_joined(nunchi.ParticipantCount)).SendAsync(); await Response().Confirm(strs.nunchi_joined(nunchi.ParticipantCount)).SendAsync();
return; return;
} }

View file

@ -122,11 +122,11 @@ public sealed class CurrencyRewardService : IEService, IReadyExecutor
var dollarValue = pledgeCents / 100; var dollarValue = pledgeCents / 100;
percentBonus = dollarValue switch percentBonus = dollarValue switch
{ {
>= 100 => 100, >= 100 => 20,
>= 50 => 50, >= 50 => 10,
>= 20 => 20, >= 20 => 5,
>= 10 => 10, >= 10 => 3,
>= 5 => 5, >= 5 => 1,
_ => 0 _ => 0
}; };
return (long)(modifiedAmount * (1 + (percentBonus / 100.0f))); return (long)(modifiedAmount * (1 + (percentBonus / 100.0f)));

View file

@ -0,0 +1,191 @@
#nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
using System.Collections.Frozen;
namespace EllieBot.Modules.Searches;
public sealed partial class FlagTranslateService : IReadyExecutor, IEService
{
private readonly IBotCreds _creds;
private readonly DiscordSocketClient _client;
private readonly TranslateService _ts;
private readonly IMessageSenderService _sender;
private IReadOnlyDictionary<string, string> _supportedFlags;
private readonly DbService _db;
private ConcurrentHashSet<ulong> _enabledChannels;
private readonly IBotCache _cache;
// disallow same message being translated multiple times to the same language
private readonly ConcurrentHashSet<(ulong, string)> _msgLangs = new();
public FlagTranslateService(
IBotCreds creds,
DiscordSocketClient client,
TranslateService ts,
IMessageSenderService sender,
DbService db,
IBotCache cache)
{
_creds = creds;
_client = client;
_ts = ts;
_sender = sender;
_db = db;
_cache = cache;
}
public async Task OnReadyAsync()
{
_supportedFlags = COUNTRIES
.Split('\n')
.Select(x => x.Split(' '))
.ToDictionary(x => x[0], x => x[1].TrimEnd())
.ToFrozenDictionary();
await using (var uow = _db.GetDbContext())
{
_enabledChannels = (await uow.GetTable<FlagTranslateChannel>()
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId,
_creds.TotalShards,
_client.ShardId))
.Select(x => new
{
x.ChannelId,
x.GuildId
})
.ToListAsyncLinqToDB())
.Select(x => x.ChannelId)
.ToHashSet()
.ToConcurrentSet();
}
_client.ReactionAdded += OnReactionAdded;
var periodicCleanup = new PeriodicTimer(TimeSpan.FromHours(24));
while (await periodicCleanup.WaitForNextTickAsync())
{
_msgLangs.Clear();
}
}
private const int FLAG_START = 127462;
private static TypedKey<bool> CdKey(ulong userId)
=> new($"flagtranslate:{userId}");
private Task OnReactionAdded(
Cacheable<IUserMessage, ulong> arg1,
Cacheable<IMessageChannel, ulong> arg2,
SocketReaction reaction)
{
if (!_enabledChannels.Contains(reaction.Channel.Id))
return Task.CompletedTask;
var runes = reaction.Emote.Name.EnumerateRunes();
if (!runes.MoveNext()
|| runes.Current is not { Value: >= 127462 and <= 127487 } l1
|| !runes.MoveNext()
|| runes.Current is not { Value: >= 127462 and <= 127487 } l2)
{
return Task.CompletedTask;
}
_ = Task.Run(async () =>
{
if (reaction.Channel is not SocketTextChannel tc)
return;
var user = await ((IGuild)tc.Guild).GetUserAsync(reaction.UserId);
if (user is null)
return;
if (!user.GetPermissions(tc).SendMessages)
return;
if (!tc.Guild.CurrentUser.GetPermissions(tc).SendMessages
|| !tc.Guild.CurrentUser.GetPermissions(tc).EmbedLinks)
{
await Disable(tc.Guild.Id, tc.Id);
return;
}
var c1 = (char)(l1.Value - FLAG_START + 65);
var c2 = (char)(l2.Value - FLAG_START + 65);
var code = $"{c1}{c2}".ToUpper();
if (!_supportedFlags.TryGetValue(code, out var lang))
return;
if (!_msgLangs.Add((reaction.MessageId, lang)))
return;
var result = await _cache.GetAsync(CdKey(reaction.UserId));
if (result.TryPickT0(out _, out _))
return;
await _cache.AddAsync(CdKey(reaction.UserId), true, TimeSpan.FromSeconds(5));
var msg = await arg1.GetOrDownloadAsync();
var response = await _ts.Translate("", lang, msg.Content).ConfigureAwait(false);
await msg.ReplyAsync(embed: _sender.CreateEmbed()
.WithOkColor()
.WithFooter(user.ToString() ?? reaction.UserId.ToString(),
user.RealAvatarUrl().ToString())
.WithDescription(response)
.WithAuthor(reaction.Emote.ToString())
.Build(),
allowedMentions: AllowedMentions.None
);
});
return Task.CompletedTask;
}
public async Task Disable(ulong guildId, ulong tcId)
{
if (!_enabledChannels.TryRemove(tcId))
return;
await using var uow = _db.GetDbContext();
await uow.GetTable<FlagTranslateChannel>()
.Where(x => x.GuildId == guildId
&& x.ChannelId == tcId)
.DeleteAsync();
}
public async Task<bool> Toggle(ulong guildId, ulong tcId)
{
if (_enabledChannels.Contains(tcId))
{
await Disable(guildId, tcId);
return false;
}
await Enable(guildId, tcId);
return true;
}
public async Task Enable(ulong guildId, ulong tcId)
{
if (!_enabledChannels.Add(tcId))
return;
await using var uow = _db.GetDbContext();
await uow.GetTable<FlagTranslateChannel>()
.InsertAsync(() => new FlagTranslateChannel
{
GuildId = guildId,
ChannelId = tcId
});
}
}

View file

@ -0,0 +1,73 @@
namespace EllieBot.Modules.Searches;
public partial class FlagTranslateService
{
private const string COUNTRIES = """
CN zh
IN hi
US en
ID id
PK ur
BR pt
NG ha
BD bn
RU ru
JP ja
MX es
PH tl
VN vi
EG ar
ET am
DE de
IR fa
TR tr
TH th
FR fr
CD fr
MM my
UG en
MZ pt
ZA zu
CO es
BG bg
HR hr
MY ms
NL nl
RO ro
CZ cs
GR el
SK sk
PT pt
KR ko
IT it
ES es
RS sr
TN ar
PL pl
SD ar
CM fr
SN fr
ML fr
NE ha
BI fr
AO pt
AF ps
MA ar
DZ ar
GB en
AR es
ZW ny
KE sw
GH en
SA ar
IL he
IQ ar
UA ua
LY ar
KW ar
OM ar
YE ar
AL sq
AE ar
""";
}

View file

@ -44,12 +44,10 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
foreach (var c in cs) foreach (var c in cs)
{ {
_atcs[c.ChannelId] = c.AutoDelete; _atcs[c.ChannelId] = c.AutoDelete;
_users[c.ChannelId] = _users[c.ChannelId] = new(c.Users.ToDictionary(x => x.UserId, x => (x.Source.ToLower(), x.Target.ToLower())));
new(c.Users.ToDictionary(x => x.UserId, x => (x.Source.ToLower(), x.Target.ToLower())));
} }
} }
public async Task ExecOnNoCommandAsync(IGuild guild, IUserMessage msg) public async Task ExecOnNoCommandAsync(IGuild guild, IUserMessage msg)
{ {
if (string.IsNullOrWhiteSpace(msg.Content)) if (string.IsNullOrWhiteSpace(msg.Content))
@ -95,7 +93,7 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
} }
} }
public async Task<string> Translate(string source, string target, string text = null) public async Task<string> Translate(string source, string target, string text)
{ {
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
throw new ArgumentException("Text is empty or null", nameof(text)); throw new ArgumentException("Text is empty or null", nameof(text));

View file

@ -6,6 +6,14 @@ public partial class Searches
[Group] [Group]
public partial class TranslateCommands : EllieModule<ITranslateService> public partial class TranslateCommands : EllieModule<ITranslateService>
{ {
private readonly FlagTranslateService _flagSvc;
public TranslateCommands(FlagTranslateService flagSvc)
{
_flagSvc = flagSvc;
}
public enum AutoDeleteAutoTranslate public enum AutoDeleteAutoTranslate
{ {
Del, Del,
@ -91,5 +99,18 @@ public partial class Searches
await Response().Embed(eb).SendAsync(); await Response().Embed(eb).SendAsync();
} }
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(ChannelPermission.ManageChannels)]
[BotPerm(ChannelPermission.SendMessages | ChannelPermission.EmbedLinks)]
public async Task TranslateFlags()
{
var enabled = await _flagSvc.Toggle(ctx.Guild.Id, ctx.Channel.Id);
if (enabled)
await Response().Confirm(strs.trfl_enabled).SendAsync();
else
await Response().Confirm(strs.trfl_disabled).SendAsync();
}
} }
} }

View file

@ -28,6 +28,8 @@ public class XpSvc : GrpcXp.GrpcXpBase, IGrpcSvc, IEService
GetXpSettingsRequest request, GetXpSettingsRequest request,
ServerCallContext context) ServerCallContext context)
{ {
await Task.Yield();
var guild = _client.GetGuild(request.GuildId); var guild = _client.GetGuild(request.GuildId);
if (guild is null) if (guild is null)
@ -54,6 +56,26 @@ public class XpSvc : GrpcXp.GrpcXpBase, IGrpcSvc, IEService
Name = guild.GetRole(x)?.Name ?? "????" Name = guild.GetRole(x)?.Name ?? "????"
}))); })));
var curRews = _xp.GetCurrencyRewards(request.GuildId);
var roleRews = _xp.GetRoleRewards(request.GuildId);
var rews = curRews.Select(x => new RewItemReply()
{
Level = x.Level,
Type = "Currency",
Value = x.Amount.ToString()
});
rews = rews.Concat(roleRews.Select(x => new RewItemReply()
{
Level = x.Level,
Type = "Role",
Value = guild.GetRole(x.RoleId)?.ToString() ?? x.RoleId.ToString()
}))
.OrderBy(x => x.Level);
reply.Rewards.AddRange(rews);
reply.ServerExcluded = isServerExcluded; reply.ServerExcluded = isServerExcluded;
return reply; return reply;

View file

@ -201,9 +201,12 @@ public sealed partial class GoogleApiService : IGoogleApiService, IEService
{ {
string text; string text;
if (!Languages.ContainsKey(sourceLanguage) || !Languages.ContainsKey(targetLanguage)) if (!Languages.ContainsKey(targetLanguage))
throw new ArgumentException(nameof(sourceLanguage) + "/" + nameof(targetLanguage)); throw new ArgumentException(nameof(sourceLanguage) + "/" + nameof(targetLanguage));
if (string.IsNullOrWhiteSpace(sourceLanguage) || !Languages.ContainsKey(sourceLanguage))
sourceLanguage = "auto";
var url = new Uri(string.Format( var url = new Uri(string.Format(
"https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}", "https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}",
@ -223,7 +226,7 @@ public sealed partial class GoogleApiService : IGoogleApiService, IEService
private string ConvertToLanguageCode(string language) private string ConvertToLanguageCode(string language)
{ {
Languages.TryGetValue(language, out var mode); Languages.TryGetValue(language, out var mode);
return mode; return string.IsNullOrWhiteSpace(mode) ? language : mode;
} }
} }

View file

@ -154,7 +154,6 @@ public sealed partial class GoogleApiService
} }
Languages = langs; Languages = langs;
} }
} }

View file

@ -77,8 +77,7 @@ public class DefaultWallet : IWallet
.InsertOrUpdateAsync(() => new() .InsertOrUpdateAsync(() => new()
{ {
UserId = userId, UserId = userId,
Username = "Unknown", Username = "??Unknown",
Discriminator = "????",
CurrencyAmount = amount, CurrencyAmount = amount,
}, },
(old) => new() (old) => new()

View file

@ -193,7 +193,7 @@ public sealed class StatsService : IStatsService, IReadyExecutor, IEService
Id = g.Id, Id = g.Id,
IconUrl = g.IconUrl, IconUrl = g.IconUrl,
Name = g.Name, Name = g.Name,
Owner = (await ig.GetUserAsync(g.OwnerId))?.Username ?? "Unknown", Owner = (await ig.GetUserAsync(g.OwnerId))?.Username ?? "??Unknown",
OwnerId = g.OwnerId, OwnerId = g.OwnerId,
CreatedAt = g.CreatedAt.UtcDateTime, CreatedAt = g.CreatedAt.UtcDateTime,
VoiceChannels = g.VoiceChannels.Count, VoiceChannels = g.VoiceChannels.Count,

View file

@ -1445,3 +1445,8 @@ ncpixel:
- ncgp - ncgp
ncreset: ncreset:
- ncreset - ncreset
translateflags:
- translateflags
- trfl
- fltr
- transflags

View file

@ -4634,3 +4634,11 @@ ncreset:
- '' - ''
params: params:
- { } - { }
translateflags:
desc: |-
Toggles translate flags on the current channel.
Reacting with a country flag will translate the message to that country's language.
ex:
- ''
params:
- { }

View file

@ -1111,5 +1111,7 @@
"nc_hint": "Use `{0}nczoom x y` command to zoom in. Image is {1}x{2} pixels.", "nc_hint": "Use `{0}nczoom x y` command to zoom in. Image is {1}x{2} pixels.",
"nc_insuff_payment": "Invalid payment.", "nc_insuff_payment": "Invalid payment.",
"invalid_img_size": "Image must to be {0}x{1} pixels.", "invalid_img_size": "Image must to be {0}x{1} pixels.",
"no_attach_found": "No attachment found. Please send the image along with this command." "no_attach_found": "No attachment found. Please send the image along with this command.",
"trfl_enabled": "Flag translation enabled on this channel. Reacting to a message with a flag will translate it to that language.",
"trfl_disabled": "Flag translation disabled."
} }