diff --git a/ProjectMakoto/Commands/Configuration/JoinCommand.cs b/ProjectMakoto/Commands/Configuration/JoinCommand.cs index 24606e26..d04b1ff5 100644 --- a/ProjectMakoto/Commands/Configuration/JoinCommand.cs +++ b/ProjectMakoto/Commands/Configuration/JoinCommand.cs @@ -26,6 +26,8 @@ internal sealed class JoinCommand : BaseCommand var pad = TranslationUtil.CalculatePadding(ctx.DbUser, CommandKey.Autoban, CommandKey.JoinLogChannel, + CommandKey.UserCountChannel, + CommandKey.UserCountChannelFormat, CommandKey.Role, CommandKey.ReApplyRoles, CommandKey.ReApplyNickname, @@ -35,6 +37,7 @@ internal sealed class JoinCommand : BaseCommand return $"{"🌐".UnicodeToEmoji()} `{CommandKey.Autoban.Get(ctx.DbUser).PadRight(pad)}`: {ctx.DbGuild.Join.AutoBanGlobalBans.ToEmote(ctx.Bot)}\n" + $"{"👋".UnicodeToEmoji()} `{CommandKey.JoinLogChannel.Get(ctx.DbUser).PadRight(pad)}`: {(ctx.DbGuild.Join.JoinlogChannelId != 0 ? $"<#{ctx.DbGuild.Join.JoinlogChannelId}>" : false.ToEmote(ctx.Bot))}\n" + + $"{"🔢".UnicodeToEmoji()} `{CommandKey.UserCountChannel.Get(ctx.DbUser).PadRight(pad)}`: {(ctx.DbGuild.Join.UserCountChannelId != 0 ? $"<#{ctx.DbGuild.Join.UserCountChannelId}> (`{(ctx.DbGuild.Join.UserCountChannelFormat is not null ? ctx.DbGuild.Join.UserCountChannelFormat : "Users: %s")}`)" : false.ToEmote(ctx.Bot))}\n" + $"{"👤".UnicodeToEmoji()} `{CommandKey.Role.Get(ctx.DbUser).PadRight(pad)}`: {(ctx.DbGuild.Join.AutoAssignRoleId != 0 ? $"<@&{ctx.DbGuild.Join.AutoAssignRoleId}>" : false.ToEmote(ctx.Bot))}\n" + $"{"👥".UnicodeToEmoji()} `{CommandKey.ReApplyRoles.Get(ctx.DbUser).PadRight(pad)}`: {ctx.DbGuild.Join.ReApplyRoles.ToEmote(ctx.Bot)}\n" + $"{"💬".UnicodeToEmoji()} `{CommandKey.ReApplyNickname.Get(ctx.DbUser).PadRight(pad)}`: {ctx.DbGuild.Join.ReApplyNickname.ToEmote(ctx.Bot)}\n\n" + @@ -57,6 +60,8 @@ internal sealed class JoinCommand : BaseCommand var ToggleGlobalban = new DiscordButtonComponent((ctx.DbGuild.Join.AutoBanGlobalBans ? ButtonStyle.Danger : ButtonStyle.Success), Guid.NewGuid().ToString(), this.GetString(CommandKey.ToggleGlobalBansButton), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("🌐"))); var ChangeJoinlogChannel = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), this.GetString(CommandKey.ChangeJoinlogChannelButton), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("👋"))); + var ChangeUserCountChannel = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), this.GetString(CommandKey.ChangeUserCountChannel), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("🔢"))); + var ChangeUserCountFormat = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), this.GetString(CommandKey.ChangeUserCountChannelFormat), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("🔢"))); var ChangeRoleOnJoin = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), this.GetString(CommandKey.ChangeRoleButton), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("👤"))); var ToggleReApplyRoles = new DiscordButtonComponent((ctx.DbGuild.Join.ReApplyRoles ? ButtonStyle.Danger : ButtonStyle.Success), Guid.NewGuid().ToString(), this.GetString(CommandKey.ToggleReApplyRole), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("👥"))); var ToggleReApplyName = new DiscordButtonComponent((ctx.DbGuild.Join.ReApplyNickname ? ButtonStyle.Danger : ButtonStyle.Success), Guid.NewGuid().ToString(), this.GetString(CommandKey.ToggleReApplyNickname), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("💬"))); @@ -76,6 +81,8 @@ internal sealed class JoinCommand : BaseCommand { ChangeJoinlogChannel, ChangeRoleOnJoin, + ChangeUserCountChannel, + ChangeUserCountFormat, }) .AddComponents(new List { @@ -93,7 +100,8 @@ internal sealed class JoinCommand : BaseCommand return; } - _ = e.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + if (e.GetCustomId() != ChangeUserCountFormat.CustomId) + _ = e.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); if (e.GetCustomId() == ToggleGlobalban.CustomId) { @@ -156,6 +164,81 @@ internal sealed class JoinCommand : BaseCommand await this.ExecuteCommand(ctx, arguments); return; } + else if (e.GetCustomId() == ChangeUserCountChannel.CustomId) + { + var ChannelResult = await this.PromptChannelSelection(ChannelType.Text, new ChannelPromptConfiguration + { + CreateChannelOption = new() + { + Name = "tmp-usercount", + ChannelType = ChannelType.Text + }, + DisableOption = this.GetString(CommandKey.DisableUserCountChannel) + }); + + if (ChannelResult.TimedOut) + { + this.ModifyToTimedOut(true); + return; + } + else if (ChannelResult.Cancelled) + { + await this.ExecuteCommand(ctx, arguments); + return; + } + else if (ChannelResult.Failed) + { + if (ChannelResult.Exception.GetType() == typeof(NullReferenceException)) + { + _ = await this.RespondOrEdit(new DiscordEmbedBuilder().AsError(ctx).WithDescription(this.GetString(this.t.Commands.Common.Errors.NoChannels))); + await Task.Delay(3000); + await this.ExecuteCommand(ctx, arguments); + return; + } + + throw ChannelResult.Exception; + } + + ctx.DbGuild.Join.UserCountChannelId = ChannelResult.Result is null ? 0 : ChannelResult.Result.Id; + + await this.ExecuteCommand(ctx, arguments); + return; + } + else if (e.GetCustomId() == ChangeUserCountFormat.CustomId) + { + var modelResult = await this.PromptModalWithRetry(e.Result.Interaction, + new DiscordInteractionModalBuilder(this.GetString(CommandKey.ChangeUserCountChannelFormat)) + .AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "new_format", this.GetString(CommandKey.ChangeUserCountChannelFormatModal), null, 2, 16)), + false); + + if (modelResult.TimedOut) + { + this.ModifyToTimedOut(true); + return; + } + else if (modelResult.Cancelled) + { + await this.ExecuteCommand(ctx, arguments); + return; + } + else if (modelResult.Failed) + { + if (modelResult.Exception.GetType() == typeof(NullReferenceException)) + { + _ = await this.RespondOrEdit(new DiscordEmbedBuilder().AsError(ctx).WithDescription(this.GetString(this.t.Commands.Common.Errors.NoChannels))); + await Task.Delay(3000); + await this.ExecuteCommand(ctx, arguments); + return; + } + + throw modelResult.Exception; + } + + ctx.DbGuild.Join.UserCountChannelFormat = modelResult.Result.Interaction.GetModalValueByCustomId("new_format"); + + await this.ExecuteCommand(ctx, arguments); + return; + } else if (e.GetCustomId() == ChangeRoleOnJoin.CustomId) { var RoleResult = await this.PromptRoleSelection(new RolePromptConfiguration { CreateRoleOption = this.GetString(CommandKey.AutoAssignRoleName), DisableOption = this.GetString(CommandKey.DisableRoleOnJoin) }); diff --git a/ProjectMakoto/Entities/Guilds/JoinSettings.cs b/ProjectMakoto/Entities/Guilds/JoinSettings.cs index f58c9d9a..925aee97 100644 --- a/ProjectMakoto/Entities/Guilds/JoinSettings.cs +++ b/ProjectMakoto/Entities/Guilds/JoinSettings.cs @@ -24,6 +24,27 @@ public sealed class JoinSettings(Bot bot, Guild parent) : RequiresParent( get => this.Bot.DatabaseClient.GetValue("guilds", "serverid", this.Parent.Id, "joinlog_channel_id", this.Bot.DatabaseClient.mainDatabaseConnection); set => _ = this.Bot.DatabaseClient.SetValue("guilds", "serverid", this.Parent.Id, "joinlog_channel_id", value, this.Bot.DatabaseClient.mainDatabaseConnection); } + + [ColumnName("usercount_channel_last_edit"), ColumnType(ColumnTypes.BigInt), Default("0")] + public DateTime UserCountChannelLastEdit + { + get => this.Bot.DatabaseClient.GetValue("guilds", "serverid", this.Parent.Id, "usercount_channel_last_edit", this.Bot.DatabaseClient.mainDatabaseConnection); + set => _ = this.Bot.DatabaseClient.SetValue("guilds", "serverid", this.Parent.Id, "usercount_channel_last_edit", value, this.Bot.DatabaseClient.mainDatabaseConnection); + } + + [ColumnName("usercount_channel_id"), ColumnType(ColumnTypes.BigInt), Default("0")] + public ulong UserCountChannelId + { + get => this.Bot.DatabaseClient.GetValue("guilds", "serverid", this.Parent.Id, "usercount_channel_id", this.Bot.DatabaseClient.mainDatabaseConnection); + set => _ = this.Bot.DatabaseClient.SetValue("guilds", "serverid", this.Parent.Id, "usercount_channel_id", value, this.Bot.DatabaseClient.mainDatabaseConnection); + } + + [ColumnName("usercount_channel_format"), ColumnType(ColumnTypes.Text), Nullable] + public string? UserCountChannelFormat + { + get => this.Bot.DatabaseClient.GetValue("guilds", "serverid", this.Parent.Id, "usercount_channel_format", this.Bot.DatabaseClient.mainDatabaseConnection); + set => _ = this.Bot.DatabaseClient.SetValue("guilds", "serverid", this.Parent.Id, "usercount_channel_format", value, this.Bot.DatabaseClient.mainDatabaseConnection); + } [ColumnName("autoban_global_ban"), ColumnType(ColumnTypes.TinyInt), Default("0")] public bool AutoBanGlobalBans diff --git a/ProjectMakoto/Entities/Translation/Translations.cs b/ProjectMakoto/Entities/Translation/Translations.cs index 0bfc5e2a..186fbf5e 100644 --- a/ProjectMakoto/Entities/Translation/Translations.cs +++ b/ProjectMakoto/Entities/Translation/Translations.cs @@ -380,11 +380,15 @@ public class Translations : ITranslations public SingleTranslationKey CantUseRole; public SingleTranslationKey DisableRoleOnJoin; public SingleTranslationKey AutoAssignRoleName; + public SingleTranslationKey DisableUserCountChannel; public SingleTranslationKey DisableJoinlog; public SingleTranslationKey JoinLogChannelName; public SingleTranslationKey ToggleReApplyNickname; public SingleTranslationKey ToggleReApplyRole; public SingleTranslationKey ChangeRoleButton; + public SingleTranslationKey ChangeUserCountChannelFormatModal; + public SingleTranslationKey ChangeUserCountChannelFormat; + public SingleTranslationKey ChangeUserCountChannel; public SingleTranslationKey ChangeJoinlogChannelButton; public SingleTranslationKey ToggleGlobalBansButton; public SingleTranslationKey TimeNotice; @@ -392,6 +396,8 @@ public class Translations : ITranslations public SingleTranslationKey ReApplyNickname; public SingleTranslationKey ReApplyRoles; public SingleTranslationKey Role; + public SingleTranslationKey UserCountChannelFormat; + public SingleTranslationKey UserCountChannel; public SingleTranslationKey JoinLogChannel; public SingleTranslationKey Autoban; public SingleTranslationKey Title; diff --git a/ProjectMakoto/Events/JoinEvents.cs b/ProjectMakoto/Events/JoinEvents.cs index 29e90c85..4034eeca 100644 --- a/ProjectMakoto/Events/JoinEvents.cs +++ b/ProjectMakoto/Events/JoinEvents.cs @@ -54,6 +54,7 @@ internal sealed class JoinEvents(Bot bot) : RequiresTranslation(bot) } } + await this.RunUserCountUpdater(e.Guild); await this.Bot.Guilds[e.Guild.Id].Members[e.Member.Id].PerformAutoKickChecks(e.Guild, e.Member); } @@ -81,5 +82,39 @@ internal sealed class JoinEvents(Bot bot) : RequiresTranslation(bot) }); } } + + await this.RunUserCountUpdater(e.Guild); + } + + private async Task RunUserCountUpdater(DiscordGuild Guild) + { + if (this.Bot.Guilds[Guild.Id].Join.UserCountChannelId != 0) + { + if (Guild.Channels.ContainsKey(this.Bot.Guilds[Guild.Id].Join.UserCountChannelId)) + { + foreach (var b in ScheduledTaskExtensions.GetScheduledTasks()) + { + if (b.CustomData is not ScheduledTaskIdentifier scheduledTaskIdentifier) + continue; + + if (scheduledTaskIdentifier.Snowflake == Guild.Id && scheduledTaskIdentifier.Type == "usercount") + b.Delete(); + } + + _ = new Func(async () => + { + _ = Guild.GetChannel(this.Bot.Guilds[Guild.Id].Join.UserCountChannelId).ModifyAsync(x => + { + x.Name = (this.Bot.Guilds[Guild.Id].Join.UserCountChannelFormat is null ? "Count: %s" : this.Bot.Guilds[Guild.Id].Join.UserCountChannelFormat) + .Replace("%s", Guild.MemberCount); + }) + .ContinueWith(x => + { + this.Bot.Guilds[Guild.Id].Join.UserCountChannelLastEdit = DateTime.UtcNow; + }); + }).CreateScheduledTask(this.Bot.Guilds[Guild.Id].Join.UserCountChannelLastEdit.AddMinutes(5), + new ScheduledTaskIdentifier(Guild.Id, "", "usercount")); + } + } } } diff --git a/ProjectMakoto/Translations/strings.json b/ProjectMakoto/Translations/strings.json index 63f43207..5f7a1544 100644 --- a/ProjectMakoto/Translations/strings.json +++ b/ProjectMakoto/Translations/strings.json @@ -3780,6 +3780,14 @@ "en": "Joinlog Channel", "de": "Beitrittsprotokoll-Kanal" }, + "UserCountChannel": { + "en": "User Count Channel", + "de": "Benutzeranzahl-Kanal" + }, + "UserCountChannelFormat": { + "en": "User Count Format", + "de": "Benutzeranzahl-Format" + }, "Role": { "en": "Role On Join", "de": "Rolle beim Beitritt" @@ -3808,6 +3816,18 @@ "en": "Change Joinlog Channel", "de": "Beitrittsprotokoll-Kanal ändern" }, + "ChangeUserCountChannel": { + "en": "Change User Count Channel", + "de": "Benutzeranzahl-Kanal ändern" + }, + "ChangeUserCountChannelFormat": { + "en": "Change User Count Format", + "de": "Benutzeranzahl-Format ändern" + }, + "ChangeUserCountChannelFormatModal": { + "en": "User Count Format | %s as placeholder", + "de": "Benutzeranzahl-Format | %s für Platzhalter" + }, "ChangeRoleButton": { "en": "Change Role assigned on join", "de": "Beim Beitritt zugewiesene Rolle ändern" @@ -3828,6 +3848,10 @@ "en": "Disable Joinlog", "de": "Beitrittsprotokoll deaktivieren" }, + "DisableUserCountChannel": { + "en": "Disable User Count Channel", + "de": "Benutzeranzahl-Kanal deaktivieren" + }, "AutoAssignRoleName": { "en": "Automatically Assigned Role", "de": "Automatisch zugewiesene Rolle"