static async Task MainAsync(string[] _) { string token; var json = ""; string configFile = "config.json"; #if DEBUG configFile = "config.dev.json"; #endif using (var fs = File.OpenRead(configFile)) using (var sr = new StreamReader(fs, new UTF8Encoding(false))) json = await sr.ReadToEndAsync(); cfgjson = JsonConvert.DeserializeObject <ConfigJson>(json); var keys = cfgjson.WordListList.Keys; foreach (string key in keys) { var listOutput = File.ReadAllLines($"Lists/{key}"); cfgjson.WordListList[key].Words = listOutput; } if (Environment.GetEnvironmentVariable("FORTEBASS_TOKEN") != null) { token = Environment.GetEnvironmentVariable("FORTEBASS_TOKEN"); } else { token = cfgjson.Core.Token; } string redisHost; if (Environment.GetEnvironmentVariable("REDIS_DOCKER_OVERRIDE") != null) { redisHost = "redis"; } else { redisHost = cfgjson.Redis.Host; } redis = ConnectionMultiplexer.Connect($"{redisHost}:{cfgjson.Redis.Port}"); db = redis.GetDatabase(); db.KeyDelete("messages"); discord = new DiscordClient(new DiscordConfiguration { Token = token, TokenType = TokenType.Bot, MinimumLogLevel = Microsoft.Extensions.Logging.LogLevel.Debug, Intents = DiscordIntents.All }); #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed async Task OnReady(DiscordClient client, ReadyEventArgs e) { Console.WriteLine($"Logged in as {client.CurrentUser.Username}#{client.CurrentUser.Discriminator}"); logChannel = await discord.GetChannelAsync(cfgjson.LogChannel); Mutes.CheckMutesAsync(); ModCmds.CheckBansAsync(); ModCmds.CheckRemindersAsync(); string commitHash = "aaaaaaa"; string commitMessage = "N/A"; string commitTime = "0000-00-00 00:00:00 +0000"; if (File.Exists("CommitHash.txt")) { using var sr = new StreamReader("CommitHash.txt"); commitHash = sr.ReadToEnd(); } if (File.Exists("CommitMessage.txt")) { using var sr = new StreamReader("CommitMessage.txt"); commitMessage = sr.ReadToEnd(); } if (File.Exists("CommitTime.txt")) { using var sr = new StreamReader("CommitTime.txt"); commitTime = sr.ReadToEnd(); } var FortebassChannel = await client.GetChannelAsync(cfgjson.HomeChannel); FortebassChannel.SendMessageAsync($"{cfgjson.Emoji.Connected} Fortebass connected successfully!\n\n" + $"**Version**: `{commitHash}`\n" + $"**Version timestamp**: `{commitTime}`\n**Framework**: `{RuntimeInformation.FrameworkDescription}`\n**Platform**: `{RuntimeInformation.OSDescription}`\n\n" + $"Most recent commit message:\n" + $"```\n" + $"{commitMessage}\n" + $"```"); } async Task MessageCreated(DiscordClient client, MessageCreateEventArgs e) { if (e.Channel.IsPrivate || e.Guild.Id != cfgjson.ServerID || e.Author.IsBot) { return; } if (processedMessages.Contains(e.Message.Id)) { return; } else { processedMessages.Add(e.Message.Id); } DiscordMember member = await e.Guild.GetMemberAsync(e.Author.Id); if (Warnings.GetPermLevel(member) >= ServerPermLevel.CommunityManager) { return; } bool match = false; // Matching word list var wordListKeys = cfgjson.WordListList.Keys; foreach (string key in wordListKeys) { if (CheckForNaughtyWords(e.Message.Content.ToLower(), cfgjson.WordListList[key])) { try { e.Message.DeleteAsync(); DiscordChannel logChannel = await discord.GetChannelAsync(Program.cfgjson.LogChannel); var embed = new DiscordEmbedBuilder() .WithDescription(e.Message.Content) .WithColor(new DiscordColor(0xf03916)) .WithTimestamp(e.Message.Timestamp) .WithFooter( $"User ID: {e.Author.Id}", null ) .WithAuthor( $"{e.Author.Username}#{e.Author.Discriminator} in #{e.Channel.Name}", null, e.Author.AvatarUrl ); logChannel.SendMessageAsync($"{cfgjson.Emoji.Denied} Deleted infringing message by {e.Author.Mention} in {e.Channel.Mention}:", embed); } catch { // still warn anyway } match = true; string reason = cfgjson.WordListList[key].Reason; DiscordMessage msg = await e.Channel.SendMessageAsync($"{cfgjson.Emoji.Denied} {e.Message.Author.Mention} was automatically warned: **{reason.Replace("`", "\\`").Replace("*", "\\*")}**"); Warnings.GiveWarningAsync(e.Message.Author, discord.CurrentUser, reason, contextLink: Warnings.MessageLink(msg), e.Channel); return; } if (match) { return; } } if (match) { return; } // Mass mentions if (e.Message.MentionedUsers.Count >= cfgjson.MassMentionThreshold && Warnings.GetPermLevel(member) < ServerPermLevel.Mod) { DiscordChannel logChannel = await discord.GetChannelAsync(cfgjson.LogChannel); try { e.Message.DeleteAsync(); var embed = new DiscordEmbedBuilder() .WithDescription(e.Message.Content) .WithColor(new DiscordColor(0xf03916)) .WithTimestamp(e.Message.Timestamp) .WithFooter( $"User ID: {e.Author.Id}", null ) .WithAuthor( $"{e.Author.Username}#{e.Author.Discriminator} in #{e.Channel.Name}", null, e.Author.AvatarUrl ); logChannel.SendMessageAsync($"{cfgjson.Emoji.Denied} Deleted infringing message by {e.Author.Mention} in {e.Channel.Mention}:", embed); } catch { // still warn anyway } string reason = "Mass mentions"; DiscordMessage msg = await e.Channel.SendMessageAsync($"{cfgjson.Emoji.Denied} {e.Message.Author.Mention} was automatically warned: **{reason.Replace("`", "\\`").Replace("*", "\\*")}**"); await Warnings.GiveWarningAsync(e.Message.Author, discord.CurrentUser, reason, contextLink : Warnings.MessageLink(msg), e.Channel); return; } // Unapproved invites if (Warnings.GetPermLevel(member) < (ServerPermLevel)cfgjson.InviteTierRequirement) { string inviteExclusion = cfgjson.InviteExclusion; if (cfgjson.InviteExclusion != null) { inviteExclusion = cfgjson.InviteExclusion; } string checkedMessage = e.Message.Content.Replace($"discord.gg/{inviteExclusion}", "").Replace($"discord.com/invite/{inviteExclusion}", "").Replace('\\', '/'); if (checkedMessage.Contains("discord.gg/") || checkedMessage.Contains("discord.com/invite/")) { try { e.Message.DeleteAsync(); var embed = new DiscordEmbedBuilder() .WithDescription(e.Message.Content) .WithColor(new DiscordColor(0xf03916)) .WithTimestamp(e.Message.Timestamp) .WithFooter( $"User ID: {e.Author.Id}", null ) .WithAuthor( $"{e.Author.Username}#{e.Author.Discriminator} in #{e.Channel.Name}", null, e.Author.AvatarUrl ); logChannel.SendMessageAsync($"{cfgjson.Emoji.Denied} Deleted infringing message by {e.Author.Mention} in {e.Channel.Mention}:", embed); } catch { // still warn anyway } string reason = "Sent an invite"; DiscordMessage msg = await e.Channel.SendMessageAsync($"{Program.cfgjson.Emoji.Denied} {e.Message.Author.Mention} was automatically warned: **{reason.Replace("`", "\\`").Replace("*", "\\*")}**"); await Warnings.GiveWarningAsync(e.Message.Author, discord.CurrentUser, reason, contextLink : Warnings.MessageLink(msg), e.Channel); return; } } // Mass emoji if (!cfgjson.UnrestrictedEmojiChannels.Contains(e.Message.ChannelId) && e.Message.Content.Length >= cfgjson.MassEmojiThreshold) { char[] tempArray = e.Message.Content.Replace("🏻", "").Replace("🏼", "").Replace("🏽", "").Replace("🏾", "").Replace("🏿", "").ToCharArray(); int pos = 0; foreach (char c in tempArray) { if (c == '™' || c == '®' || c == '©') { tempArray[pos] = ' '; } if (c == '\u200d') { tempArray[pos] = ' '; tempArray[pos + 1] = ' '; } ++pos; } string input = new string(tempArray); var matches = emoji_rx.Matches(input); if (matches.Count > cfgjson.MassEmojiThreshold) { e.Message.DeleteAsync(); if (Warnings.GetPermLevel(member) == ServerPermLevel.nothing && !db.HashExists("emojiPardoned", e.Message.Author.Id.ToString())) { await db.HashSetAsync("emojiPardoned", member.Id.ToString(), false); await e.Channel.SendMessageAsync($"{cfgjson.Emoji.Information} {e.Author.Mention}, if you want to play around with lots of emoji, please use <#{cfgjson.UnrestrictedEmojiChannels[0]}> to avoid punishment."); return; } string reason = "Mass emoji"; string output = $"{Program.cfgjson.Emoji.Denied} {e.Message.Author.Mention} was automatically warned: **{reason.Replace("`", "\\`").Replace("*", "\\*")}**"; if (!db.HashExists("emojiPardoned", e.Author.Id.ToString()) || db.HashGet("emojiPardoned", e.Message.Author.Id.ToString()) == false) { output += $"\nIf you want to play around with lots of emoji, please use <#{cfgjson.UnrestrictedEmojiChannels[0]}> to avoid punishment."; await db.HashSetAsync("emojiPardoned", member.Id.ToString(), true); } DiscordMessage msg = await e.Channel.SendMessageAsync(output); await Warnings.GiveWarningAsync(e.Message.Author, discord.CurrentUser, reason, contextLink : Warnings.MessageLink(msg), e.Channel); return; } } } async Task GuildMemberAdded(DiscordClient client, GuildMemberAddEventArgs e) { if (e.Guild.Id != cfgjson.ServerID) { return; } if (await db.HashExistsAsync("mutes", e.Member.Id)) { // todo: store per-guild DiscordRole mutedRole = e.Guild.GetRole(cfgjson.MutedRole); await e.Member.GrantRoleAsync(mutedRole, "Reapplying mute: possible mute evasion."); } } discord.Ready += OnReady; discord.MessageCreated += MessageCreated; discord.GuildMemberAdded += GuildMemberAdded; discord.MessageReactionAdded += OnReaction; commands = discord.UseCommandsNext(new CommandsNextConfiguration { StringPrefixes = cfgjson.Core.Prefixes });; commands.RegisterCommands <Warnings>(); commands.RegisterCommands <MuteCmds>(); commands.RegisterCommands <UserRoleCmds>(); commands.RegisterCommands <ModCmds>(); await discord.ConnectAsync(); while (true) { await Task.Delay(10000); Mutes.CheckMutesAsync(); ModCmds.CheckBansAsync(); ModCmds.CheckRemindersAsync(); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed } }
static async Task MainAsync(string[] _) { var json = ""; using (var fs = File.OpenRead("config.json")) using (var sr = new StreamReader(fs, new UTF8Encoding(false))) json = await sr.ReadToEndAsync(); cfgjson = JsonConvert.DeserializeObject <ConfigJson>(json); var keys = cfgjson.WordListList.Keys; foreach (string key in keys) { var listOutput = File.ReadAllLines($"Lists/{key}"); cfgjson.WordListList[key].Words = listOutput; } string redisHost; if (Environment.GetEnvironmentVariable("REDIS_DOCKER_OVERRIDE") != null) { redisHost = "redis"; } else { redisHost = cfgjson.Redis.Host; } redis = ConnectionMultiplexer.Connect($"{redisHost}:{cfgjson.Redis.Port}"); db = redis.GetDatabase(); db.KeyDelete("messages"); discord = new DiscordClient(new DiscordConfiguration { Token = cfgjson.Core.Token, TokenType = TokenType.Bot, MinimumLogLevel = Microsoft.Extensions.Logging.LogLevel.Debug, Intents = DiscordIntents.All }); #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed async Task OnReady(DiscordClient client, ReadyEventArgs e) { Console.WriteLine($"Logged in as {client.CurrentUser.Username}#{client.CurrentUser.Discriminator}"); logChannel = await discord.GetChannelAsync(cfgjson.LogChannel); Mutes.CheckMutesAsync(); ModCmds.CheckBansAsync(); } async Task MessageCreated(DiscordClient client, MessageCreateEventArgs e) { if (e.Channel.IsPrivate || e.Guild.Id != cfgjson.ServerID || e.Author.IsBot) { return; } if (processedMessages.Contains(e.Message.Id)) { return; } else { processedMessages.Add(e.Message.Id); } DiscordMember member = await e.Guild.GetMemberAsync(e.Author.Id); if (Warnings.GetPermLevel(member) >= ServerPermLevel.TrialMod) { return; } bool match = false; // Matching word list var wordListKeys = cfgjson.WordListList.Keys; foreach (string key in wordListKeys) { if (CheckForNaughtyWords(e.Message.Content, cfgjson.WordListList[key])) { try { e.Message.DeleteAsync(); DiscordChannel logChannel = await discord.GetChannelAsync(Program.cfgjson.LogChannel); var embed = new DiscordEmbedBuilder() .WithDescription(e.Message.Content) .WithColor(new DiscordColor(0xf03916)) .WithTimestamp(e.Message.Timestamp) .WithFooter( $"User ID: {e.Author.Id}", null ) .WithAuthor( $"{e.Author.Username}#{e.Author.Discriminator} in #{e.Channel.Name}", null, e.Author.AvatarUrl ); logChannel.SendMessageAsync($"{cfgjson.Emoji.Denied} Deleted infringing message by {e.Author.Mention} in {e.Channel.Mention}:", false, embed); } catch { // still warn anyway } match = true; string reason = cfgjson.WordListList[key].Reason; DiscordMessage msg = await e.Channel.SendMessageAsync($"{cfgjson.Emoji.Denied} {e.Message.Author.Mention} was warned: **{reason.Replace("`", "\\`").Replace("*", "\\*")}**"); Warnings.GiveWarningAsync(e.Message.Author, discord.CurrentUser, reason, contextLink: Warnings.MessageLink(msg), e.Channel); return; } if (match) { return; } } if (match) { return; } // Mass emoji if (e.Message.MentionedUsers.Count >= cfgjson.MassMentionThreshold) { DiscordChannel logChannel = await discord.GetChannelAsync(Program.cfgjson.LogChannel); try { e.Message.DeleteAsync(); var embed = new DiscordEmbedBuilder() .WithDescription(e.Message.Content) .WithColor(new DiscordColor(0xf03916)) .WithTimestamp(e.Message.Timestamp) .WithFooter( $"User ID: {e.Author.Id}", null ) .WithAuthor( $"{e.Author.Username}#{e.Author.Discriminator} in #{e.Channel.Name}", null, e.Author.AvatarUrl ); logChannel.SendMessageAsync($"{cfgjson.Emoji.Denied} Deleted infringing message by {e.Author.Mention} in {e.Channel.Mention}:", false, embed); } catch { // still warn anyway } string reason = "Mass mentions"; DiscordMessage msg = await e.Channel.SendMessageAsync($"{cfgjson.Emoji.Denied} {e.Message.Author.Mention} was warned: **{reason.Replace("`", "\\`").Replace("*", "\\*")}**"); await Warnings.GiveWarningAsync(e.Message.Author, discord.CurrentUser, reason, contextLink : Warnings.MessageLink(msg), e.Channel); return; } // Unapproved invites if (Warnings.GetPermLevel(member) < (ServerPermLevel)cfgjson.InviteTierRequirement) { string inviteExclusion = "microsoft"; if (cfgjson.InviteExclusion != null) { inviteExclusion = cfgjson.InviteExclusion; } string checkedMessage = e.Message.Content.Replace($"discord.gg/{inviteExclusion}", "").Replace($"discord.com/invite/{inviteExclusion}", ""); if (checkedMessage.Contains("discord.gg/") || checkedMessage.Contains("discord.com/invite/")) { try { e.Message.DeleteAsync(); var embed = new DiscordEmbedBuilder() .WithDescription(e.Message.Content) .WithColor(new DiscordColor(0xf03916)) .WithTimestamp(e.Message.Timestamp) .WithFooter( $"User ID: {e.Author.Id}", null ) .WithAuthor( $"{e.Author.Username}#{e.Author.Discriminator} in #{e.Channel.Name}", null, e.Author.AvatarUrl ); logChannel.SendMessageAsync($"{cfgjson.Emoji.Denied} Deleted infringing message by {e.Author.Mention} in {e.Channel.Mention}:", false, embed); } catch { // still warn anyway } string reason = "Sent an invite"; DiscordMessage msg = await e.Channel.SendMessageAsync($"{Program.cfgjson.Emoji.Denied} {e.Message.Author.Mention} was warned: **{reason.Replace("`", "\\`").Replace("*", "\\*")}**"); await Warnings.GiveWarningAsync(e.Message.Author, discord.CurrentUser, reason, contextLink : Warnings.MessageLink(msg), e.Channel); return; } } // Mass emoji if (!cfgjson.UnrestrictedEmojiChannels.Contains(e.Message.ChannelId) && e.Message.Content.Length >= cfgjson.MassEmojiThreshold) { char[] tempArray = e.Message.Content.Replace("🏻", "").Replace("🏼", "").Replace("🏽", "").Replace("🏾", "").Replace("🏿", "").ToCharArray(); int pos = 0; foreach (char c in tempArray) { if (c == '™' || c == '®' || c == '©') { tempArray[pos] = ' '; } if (c == '\u200d') { tempArray[pos] = ' '; tempArray[pos + 1] = ' '; } ++pos; } string input = new string(tempArray); var matches = emoji_rx.Matches(input); if (matches.Count > cfgjson.MassEmojiThreshold) { e.Message.DeleteAsync(); string reason = "Mass emoji"; DiscordMessage msg = await e.Channel.SendMessageAsync($"{Program.cfgjson.Emoji.Denied} {e.Message.Author.Mention} was warned: **{reason.Replace("`", "\\`").Replace("*", "\\*")}**"); await Warnings.GiveWarningAsync(e.Message.Author, discord.CurrentUser, reason, contextLink : Warnings.MessageLink(msg), e.Channel); return; } } } async Task GuildMemberAdded(DiscordClient client, GuildMemberAddEventArgs e) { if (e.Guild.Id != cfgjson.ServerID) { return; } if (await db.HashExistsAsync("mutes", e.Member.Id)) { // todo: store per-guild DiscordRole mutedRole = e.Guild.GetRole(cfgjson.MutedRole); await e.Member.GrantRoleAsync(mutedRole); } } discord.Ready += OnReady; discord.MessageCreated += MessageCreated; discord.GuildMemberAdded += GuildMemberAdded; commands = discord.UseCommandsNext(new CommandsNextConfiguration { StringPrefixes = cfgjson.Core.Prefixes });; commands.RegisterCommands <Warnings>(); commands.RegisterCommands <MuteCmds>(); commands.RegisterCommands <UserRoleCmds>(); commands.RegisterCommands <ModCmds>(); await discord.ConnectAsync(); while (true) { await Task.Delay(60000); Mutes.CheckMutesAsync(); ModCmds.CheckBansAsync(); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed } }
static async Task MainAsync(string[] _) { var json = ""; using (var fs = File.OpenRead("config.json")) using (var sr = new StreamReader(fs, new UTF8Encoding(false))) json = await sr.ReadToEndAsync(); cfgjson = JsonConvert.DeserializeObject <ConfigJson>(json); string redisHost; if (Environment.GetEnvironmentVariable("REDIS_DOCKER_OVERRIDE") != null) { redisHost = "redis"; } else { redisHost = cfgjson.Redis.Host; } redis = ConnectionMultiplexer.Connect($"{redisHost}:{cfgjson.Redis.Port}"); db = redis.GetDatabase(); db.KeyDelete("messages"); discord = new DiscordClient(new DiscordConfiguration { Token = cfgjson.Core.Token, TokenType = TokenType.Bot, UseInternalLogHandler = true, LogLevel = LogLevel.Debug }); #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed discord.Ready += async e => { Console.WriteLine($"Logged in as {e.Client.CurrentUser.Username}#{e.Client.CurrentUser.Discriminator}"); logChannel = await discord.GetChannelAsync(cfgjson.LogChannel); await Task.Delay(4000); Mutes.CheckMutesAsync(); }; discord.GuildMemberAdded += async e => { if (e.Guild.Id != cfgjson.ServerID) { return; } if (await db.HashExistsAsync("mutes", e.Member.Id)) { // todo: store per-guild DiscordRole mutedRole = e.Guild.GetRole(cfgjson.MutedRole); await e.Member.GrantRoleAsync(mutedRole); } }; commands = discord.UseCommandsNext(new CommandsNextConfiguration { StringPrefixes = cfgjson.Core.Prefixes });; commands.RegisterCommands <Warnings>(); commands.RegisterCommands <MuteCmds>(); await discord.ConnectAsync(); while (true) { await Task.Delay(60000); Mutes.CheckMutesAsync(); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed } }