public async Task Command(SocketGuildUser user, [Remainder] string reason = null) { Server server = await DatabaseQueries.GetOrCreateServerAsync(Context.Guild.Id); IRole role = Context.Guild.Roles.FirstOrDefault(x => x.Name == SB_ROLE); if (role == null) { await SendBasicErrorEmbedAsync($"The role `{SB_ROLE}` does not exist, therefore there are no active shadowbans."); return; } if (!user.Roles.Contains(role)) { await SendBasicErrorEmbedAsync($"{user.Mention} is not shadowbanned."); return; } reason ??= "<No reason provided>"; await user.RemoveRoleAsync(role); await ReplyAsync($"{Context.User.Mention} Successfully unshadowbanned `{user}`."); KaguyaEvents.TriggerUnshadowban(new ModeratorEventArgs(server, Context.Guild, user, (SocketGuildUser)Context.User, reason, null)); }
private async Task ReactionReply(SocketGuildUser user, IReadOnlyCollection <WarnedUser> warnings, Embed embed, int warnCount, Server server, string reason) { Emoji[] emojis = GlobalProperties.EmojisOneThroughNine(); var data = new ReactionCallbackData("", embed, false, false, TimeSpan.FromSeconds(300)); var callbacks = new List <(IEmote, Func <SocketCommandContext, SocketReaction, Task>)>(); for (int j = 0; j < warnCount; j++) { int j1 = j; callbacks.Add((emojis[j], async(c, r) => { var uwArgs = new ModeratorEventArgs(server, Context.Guild, user, (SocketGuildUser)Context.User, reason, null); KaguyaEvents.TriggerUnwarn(uwArgs); await DatabaseQueries.DeleteAsync(warnings.ElementAt(j1)); await c.Channel.SendMessageAsync($"{r.User.Value.Mention} " + $"`Successfully removed warning #{j1 + 1}`"); })); } data.SetCallbacks(callbacks); await InlineReactionReplyAsync(data); }
public async Task ShadowbanUser(SocketGuildUser user, [Remainder] string reason = null) { SocketGuild guild = Context.Guild; IRole role = guild.Roles.FirstOrDefault(x => x.Name == SB_ROLE); Server server = await DatabaseQueries.GetOrCreateServerAsync(Context.Guild.Id); if (role == null) { await ReplyAsync($"{Context.User.Mention} Could not find role `{SB_ROLE}`. Creating..."); RestRole newRole = await guild.CreateRoleAsync(SB_ROLE, GuildPermissions.None, null, false, false, null); role = newRole; await ReplyAsync($"{Context.User.Mention} Created role `{SB_ROLE}` with permissions: `none`."); await ReplyAsync($"{Context.User.Mention} Scanning permission overwrites for channels..."); } if (string.IsNullOrWhiteSpace(reason)) { reason = "<No reason provided>"; } try { await ScanChannelsForPermissions(role); if (user.Roles.Contains(role)) { await SendBasicErrorEmbedAsync($"{user.Mention} already has the role {role.Mention}."); return; } await user.AddRoleAsync(role); } catch (Exception e) { throw new KaguyaSupportException("Failed to add `kaguya-mute` role to user!\n\n" + $"Error Log: ```{e}```"); } IEnumerable <SocketRole> roles = user.Roles.Where(x => !x.IsManaged && x.Name != "@everyone"); await user.RemoveRolesAsync(roles); var successEmbed = new KaguyaEmbedBuilder { Description = $"`{user}` has been transported to the shadowlands...", Footer = new EmbedFooterBuilder { Text = "In the shadowlands, users may not interact with any text or voice channel, " + "or view who is in the server.\n\n" + "Use the unshadowban command to undo this action." } }; KaguyaEvents.TriggerShadowban(new ModeratorEventArgs(server, guild, user, (SocketGuildUser)Context.User, reason, null)); await ReplyAsync(embed : successEmbed.Build()); }
public async Task UnmuteUser(SocketGuildUser user, [Remainder] string reason = null) { Server server = await DatabaseQueries.GetOrCreateServerAsync(Context.Guild.Id); var mutedObject = await DatabaseQueries.GetFirstMatchAsync <MutedUser>(x => x.UserId == user.Id && x.ServerId == server.ServerId); reason ??= "<No reason provided>"; if (mutedObject != null) { await DatabaseQueries.DeleteAsync(mutedObject); } if (server.IsPremium) { await DatabaseQueries.UpdateAsync(server); } try { SocketRole muteRole = Context.Guild.Roles.FirstOrDefault(x => x.Name.ToLower() == "kaguya-mute"); if (!user.Roles.Contains(muteRole)) { await ReplyAsync($"{Context.User.Mention} **{user}** is not muted."); return; } await user.RemoveRoleAsync(muteRole); await ReplyAsync($"{Context.User.Mention} Successfully unmuted **{user}**."); KaguyaEvents.TriggerUnmute(new ModeratorEventArgs(server, Context.Guild, user, (SocketGuildUser)Context.User, reason, null)); } catch (NullReferenceException) { var errorEmbed = new KaguyaEmbedBuilder { Description = "User was never muted because the mute role doesn't exist.", Footer = new EmbedFooterBuilder { Text = "Use the mute command to generate the mute role." } }; errorEmbed.SetColor(EmbedColor.RED); await ReplyAsync(embed : errorEmbed.Build()); } catch (Exception e) { throw new KaguyaSupportException($"An unexpected error occurred.\n\nError Log: {e}"); } }
public static Task Initialize() { var timer = new Timer(5000) { AutoReset = true, Enabled = true }; timer.Elapsed += async(_, _) => { List <MutedUser> curMutedUsers = await DatabaseQueries.GetAllAsync <MutedUser>(x => x.ExpiresAt < DateTime.Now.ToOADate()); foreach (MutedUser mutedUser in curMutedUsers) { SocketGuild guild = ConfigProperties.Client.GetGuild(mutedUser.ServerId); if (guild == null) { goto RemoveFromDB; } Server server = await DatabaseQueries.GetOrCreateServerAsync(guild.Id); SocketGuildUser user = guild.GetUser(mutedUser.UserId); SocketGuildUser selfUser = guild.GetUser(ConfigProperties.Client.CurrentUser.Id); try { SocketRole muteRole = guild.Roles.FirstOrDefault(x => x.Name == "kaguya-mute"); await user.RemoveRoleAsync(muteRole); KaguyaEvents.TriggerUnmute(new ModeratorEventArgs(server, guild, user, selfUser, "Automatic unmute (timed mute has expired)", null)); } catch (Exception) { await ConsoleLogger.LogAsync($"Exception handled when unmuting a user in guild [Name: {guild.Name} | ID: {guild.Id}]", LogLvl.WARN); } await DatabaseQueries.UpdateAsync(server); RemoveFromDB: await DatabaseQueries.DeleteAsync(mutedUser); await ConsoleLogger.LogAsync($"User [ID: {mutedUser.UserId}] has been automatically unmuted.", LogLvl.DEBUG); } }; return(Task.CompletedTask); }
public async Task AddWarn(SocketGuildUser user, [Remainder] string reason = null) { Server server = await DatabaseQueries.GetOrCreateServerAsync(Context.Guild.Id); if (reason == null) { reason = "No reason specified."; } var wu = new WarnedUser { ServerId = Context.Guild.Id, UserId = user.Id, ModeratorName = Context.User.ToString(), Reason = reason, Date = DateTime.Now.ToOADate() }; await DatabaseQueries.InsertAsync(wu); KaguyaEvents.TriggerWarning(new ModeratorEventArgs(server, Context.Guild, user, (SocketGuildUser)Context.User, reason, null)); try { await user.SendMessageAsync(embed : (await WarnEmbed(wu, Context)).Build()); } catch (Exception) { await ConsoleLogger.LogAsync($"Failed to DM user {user.Id} that they have been " + $"warned in guild {server.ServerId}. " + $"Database updated regardless.", LogLvl.DEBUG); } await ReplyAsync(embed : (await Reply(wu, user)).Build()); await DatabaseQueries.UpdateAsync(server); }
public async Task MuteUser(SocketGuildUser user, string duration = null, [Remainder] string reason = null) { TimeSpan? timeSpan = null; SocketGuild guild = Context.Guild; Server server = await DatabaseQueries.GetOrCreateServerAsync(guild.Id); string muteString = ""; reason ??= "<No reason provided>"; if (duration != null) { if (!duration.Any(x => x.Equals('s') || x.Equals('m') || x.Equals('h') || x.Equals('d'))) { throw new FormatException("You did not specify a proper mute time.\nThe proper format is " + "`<user> <dhms>`.\nExample: `<user> 30m`"); } timeSpan = duration.ParseToTimespan(); double time = DateTime.Now.Add(timeSpan.Value).ToOADate(); muteString = $"User will be unmuted `{DateTime.FromOADate(time).Humanize(false)}`"; var muteObject = new MutedUser { ServerId = guild.Id, UserId = user.Id, ExpiresAt = time }; if (await DatabaseQueries.GetAllForServerAsync <MutedUser>(server.ServerId) != null) { var existingObject = await DatabaseQueries.GetFirstMatchAsync <MutedUser>(x => x.UserId == user.Id && x.ServerId == server.ServerId); if (existingObject != null) { await DatabaseQueries.DeleteAsync(existingObject); await ConsoleLogger.LogAsync($"Removed duplicate mute entry for user {user.Id} in guild {guild.Id}.", LogLvl.DEBUG); } } await DatabaseQueries.InsertAsync(muteObject); } SocketRole muteRole = guild.Roles.FirstOrDefault(x => x?.Name.ToLower() == "kaguya-mute"); if (muteRole == null) { await Context.Guild.CreateRoleAsync("kaguya-mute", GuildPermissions.None, Color.Default, false, false, null); await ConsoleLogger.LogAsync($"New mute role created in guild [Name: {guild.Name} | ID: {guild.Id}]", LogLvl.DEBUG); /* * We redefine guild because the object * must now have an updated role collection * in order for this to work. */ guild = Context.Guild; muteRole = guild.Roles.FirstOrDefault(x => x.Name.ToLower() == "kaguya-mute"); await ReplyAsync($"{Context.User.Mention} Mute role not found, so I created one.\n" + "Updating channel permissions. Please wait..."); } int failCount = 0; foreach (SocketGuildChannel channel in guild.Channels) { if (channel.GetPermissionOverwrite(muteRole).HasValue) { continue; } try { await channel.AddPermissionOverwriteAsync(muteRole, OverwritePermissions.InheritAll); await channel.AddPermissionOverwriteAsync(muteRole, new OverwritePermissions( addReactions : PermValue.Deny, speak : PermValue.Deny, sendTTSMessages : PermValue.Deny, connect : PermValue.Deny, createInstantInvite : PermValue.Deny, sendMessages : PermValue.Deny)); await ConsoleLogger.LogAsync($"Permission overwrite added for guild channel.\n" + $"Guild: [Name: {guild.Name} | ID: {guild.Id}]\n" + $"Channel: [Name: {channel.Name} | ID: {channel.Id}]", LogLvl.TRACE); } catch (Exception) { if (failCount >= 3) { await ReplyAsync($"{Context.User.Mention} Could not update permissions for several channels! " + $"**Aborting mute!**"); return; } await ReplyAsync($"{Context.User.Mention} Could not update permissions for {channel}! Muted user " + $"can still type in this channel!"); failCount++; } } try { await user.AddRoleAsync(muteRole); } catch (Exception) { await ReplyAsync($"{Context.User.Mention} failed to mute user. Please ensure my 'Kaguya' role" + $" is at the top of the role hierarchy. I cannot mute users who have a role " + $"higher than me."); } await ConsoleLogger.LogAsync($"User muted. Guild: [Name: {guild.Name} | ID: {guild.Id}] " + $"User: [Name: {user} | ID: {user.Id}]", LogLvl.DEBUG); await ReplyAsync($"{Context.User.Mention} Successfully muted **{user}**. {muteString}"); KaguyaEvents.TriggerMute(new ModeratorEventArgs(server, guild, user, (SocketGuildUser)Context.User, reason, timeSpan)); }
public async Task Command() { // Cooldown in seconds. const int FISHING_COOLDOWN = 15; const int FISHING_COOLDOWN_PREMIUM = 5; User user = await DatabaseQueries.GetOrCreateUserAsync(Context.User.Id); Server server = await DatabaseQueries.GetOrCreateServerAsync(Context.Guild.Id); if (user.Points < user.FishCost()) { var baitEmbed = new KaguyaEmbedBuilder(EmbedColor.RED) { Description = $"You do not have enough points to play the fishing game.", Footer = new EmbedFooterBuilder { Text = $"You need {user.FishCost()} points to play. You have {user.Points} points." } }; await SendEmbedAsync(baitEmbed); return; } bool isPremium = user.IsPremium; if (user.LastFished >= DateTime.Now.AddSeconds(-FISHING_COOLDOWN).ToOADate() && !isPremium || user.LastFished >= DateTime.Now.AddSeconds(-FISHING_COOLDOWN_PREMIUM).ToOADate() && isPremium) { TimeSpan ts = DateTime.FromOADate(user.LastFished) - DateTime.Now.AddSeconds(-15); if (isPremium) { ts -= TimeSpan.FromSeconds(10); } var errorEmbed = new KaguyaEmbedBuilder(EmbedColor.RED) { Description = $"Please wait `{ts.Humanize(minUnit: TimeUnit.Second)}` before fishing again." }; await ReplyAndDeleteAsync("", false, errorEmbed.Build(), TimeSpan.FromSeconds(3)); return; } int value; var embed = new KaguyaEmbedBuilder { Description = $"🎣 | {Context.User.Mention} " }; var r = new Random(); double roll = r.NextDouble(); int fishId = r.Next(int.MaxValue); int fishExp; while (await DatabaseQueries.ItemExistsAsync <Fish>(x => x.FishId == fishId)) { fishId = r.Next(int.MaxValue); } var bonuses = new FishHandler.FishLevelBonuses(user.FishExp); roll *= 1 - (bonuses.BonusLuckPercent / 100); if (isPremium) { roll *= 0.95; } FishType fishType = GetFishType(roll); switch (fishType) { case FishType.SEAWEED: value = 2; fishExp = 0; embed.Description += $"Aw man, you caught `seaweed`. Better luck next time!"; embed.SetColor(EmbedColor.GRAY); break; case FishType.PINFISH: value = 15; fishExp = r.Next(1, 3); embed.Description += $"you caught a `pinfish`!"; embed.SetColor(EmbedColor.GRAY); break; case FishType.SMALL_BASS: value = 25; fishExp = r.Next(2, 6); embed.Description += $"you caught a `small bass`!"; embed.SetColor(EmbedColor.GREEN); break; case FishType.SMALL_SALMON: value = 25; fishExp = r.Next(2, 6); embed.Description += $"you caught a `small salmon`!"; embed.SetColor(EmbedColor.GREEN); break; case FishType.CATFISH: value = 75; fishExp = r.Next(5, 9); embed.Description += $"you caught a `catfish`!"; embed.SetColor(EmbedColor.GREEN); break; case FishType.LARGE_BASS: value = 150; fishExp = r.Next(7, 11); embed.Description += $"Wow, you caught a `large bass`!"; embed.SetColor(EmbedColor.LIGHT_BLUE); break; case FishType.LARGE_SALMON: value = 150; fishExp = r.Next(7, 11); embed.Description += $"Wow, you caught a `large salmon`!"; embed.SetColor(EmbedColor.LIGHT_BLUE); break; case FishType.RED_DRUM: value = 200; fishExp = r.Next(7, 20); embed.Description += $"Holy smokes, you caught a `red drum`!"; embed.SetColor(EmbedColor.RED); break; case FishType.TRIGGERFISH: value = 350; fishExp = r.Next(11, 30); embed.Description += $"Holy smokes, you caught a `triggerfish`!"; embed.SetColor(EmbedColor.LIGHT_PURPLE); break; case FishType.GIANT_SEA_BASS: value = 500; fishExp = r.Next(18, 36); embed.Description += $"No way, you caught a `giant sea bass`! Nice work!"; embed.SetColor(EmbedColor.LIGHT_PURPLE); break; case FishType.SMALLTOOTH_SAWFISH: value = 1000; fishExp = r.Next(29, 42); embed.Description += $"No way, you caught a `smalltooth sawfish`! Nice work!"; embed.SetColor(EmbedColor.LIGHT_PURPLE); break; case FishType.DEVILS_HOLE_PUPFISH: value = 2500; fishExp = r.Next(40, 95); embed.Description += $"I can't believe my eyes!! you caught a `devils hold pupfish`! You're crazy!"; embed.SetColor(EmbedColor.VIOLET); break; case FishType.ORANTE_SLEEPER_RAY: value = 5000; fishExp = r.Next(75, 325); embed.Description += $"Hot diggity dog, you caught an `orante sleeper ray`! This is unbelievable!"; embed.SetColor(EmbedColor.ORANGE); break; case FishType.GIANT_SQUID: value = 20000; fishExp = r.Next(400, 900); embed.Description += $"Well butter my buttcheeks and call me a biscuit, you caught the second " + $"rarest fish in the sea! It's a `giant squid`!! Congratulations!"; embed.SetColor(EmbedColor.ORANGE); break; case FishType.BIG_KAHUNA: value = 50000; fishExp = r.Next(1250, 4500); embed.Description += $"<a:siren:429784681316220939> NO WAY! You hit the jackpot " + $"and caught the **Legendary `BIG KAHUNA`**!!!! " + $"What an incredible moment this is! <a:siren:429784681316220939>"; embed.SetColor(EmbedColor.GOLD); break; default: value = 0; fishExp = 0; embed.Description += $"Oh no, it took your bait! Better luck next time..."; embed.SetColor(EmbedColor.GRAY); break; } user.FishExp += fishExp; user.Points -= user.FishCost(); user.LastFished = DateTime.Now.ToOADate(); var fish = new Fish { FishId = fishId, UserId = Context.User.Id, ServerId = Context.Guild.Id, TimeCaught = DateTime.Now.ToOADate(), FishType = fishType, FishString = fishType.ToString(), Value = value, Exp = fishExp, Sold = false }; value = Fish.GetPayoutForFish(fish, user.FishExp); await DatabaseQueries.InsertAsync(fish); await DatabaseQueries.UpdateAsync(user); await KaguyaEvents.TriggerFish(new FishHandlerEventArgs(user, fish, Context)); // Triggers the fish EXP service. if (fishType != FishType.BAIT_STOLEN) { List <Fish> existingFish = (await DatabaseQueries.GetFishForUserMatchingTypeAsync(fishType, user.UserId)).ToList(); int fishCount = existingFish.Count(x => !x.Sold); string fishString = fishType.ToString().Replace("_", " ").ToLower(); embed.Description += $"\n\nFish ID: `{fishId}`\n" + $"Fish Value: `{value:N0}` points.\n" + $"Fishing Exp Earned: `{fishExp:N0} exp`\n" + $"Points Remaining: `{user.Points:N0} (-{user.FishCost()})`\n\n" + $"You now have `{fishCount}` `{fishString}`"; } else { embed.Description += $"\nPoints Remaining: `{user.Points:N0} (-{user.FishCost()})`"; } embed.Footer = new EmbedFooterBuilder { Text = $"Use the {server.CommandPrefix}myfish command to view your fishing stats!\n" + $"The {server.CommandPrefix}sellfish command may be used to sell your fish." }; // Fish Embed await ReplyAsync(embed : embed.Build()); }
private static async Task ActionUsers(IEnumerable <ulong> userIds, ulong guildId, string action) { Server server = await DatabaseQueries.GetOrCreateServerAsync(guildId); SocketGuild guild = ConfigProperties.Client.GetGuild(guildId); var guildUsers = new List <SocketGuildUser>(); foreach (ulong userId in userIds) { SocketGuildUser guildUser = guild.GetUser(userId); if (guildUser != null) { guildUsers.Add(guildUser); } } if (guildUsers.Count == 0) { await ConsoleLogger.LogAsync($"The antiraid service was triggered in guild: {guild.Id} " + "but no guild users were found from the provided set of IDs!", LogLvl.WARN); return; } KaguyaEvents.TriggerRaid(new AntiRaidEventArgs(guildUsers, guild, action)); // We need to message the actioned users, if applicable, before actioning. if (!string.IsNullOrWhiteSpace(server.AntiraidPunishmentDirectMessage)) { foreach (SocketGuildUser guildUser in guildUsers) { // content must be inside the foreach because of how we modify it below // on a per-user basis. string content = server.AntiraidPunishmentDirectMessage; var embed = new KaguyaEmbedBuilder(EmbedColor.ORANGE) { Title = "Kaguya Anti-Raid Notification", Description = content }; var sb = new StringBuilder(content); sb = sb.Replace("{USERNAME}", guildUser.UsernameAndDescriminator()); sb = sb.Replace("{USERMENTION}", guildUser.Mention); sb = sb.Replace("{SERVER}", guild.Name); sb = sb.Replace("{PUNISHMENT}", FormattedAntiraidPunishment(action.ToLower())); embed.Description = sb.ToString(); try { await guildUser.SendEmbedAsync(embed); } catch (HttpException httpEx) { await ConsoleLogger.LogAsync( $"Tried to send user {guildUser.Id} a custom anti-raid DM notification " + $"from guild [Name: {guild.Name} | ID: {guild.Id}] but failed to do so due to " + $"an Http Exception (Failure Reason: {httpEx.Reason})", LogLvl.WARN); } catch (Exception ex) { await ConsoleLogger.LogAsync(ex, $"An unexpected error occurred when attempting to send user {guildUser.Id} a custom " + $"anti-raid DM notification from guild [Name: {guild.Name} | ID: {guild.Id}].\n"); } } } switch (server.AntiRaid.Action.ToLower()) { case "mute": var mute = new Mute(); foreach (SocketGuildUser user in guildUsers) { try { await mute.AutoMute(user); } catch (Exception) { await ConsoleLogger.LogAsync("Attempted to auto-mute user " + $"{user.ToString() ?? "NULL"} as " + "part of the antiraid service, but " + "an exception was thrown!!", LogLvl.ERROR); } } break; case "kick": var kick = new Kick(); foreach (SocketGuildUser user in guildUsers) { try { await kick.AutoKickUserAsync(user, "Kaguya Anti-Raid protection."); } catch (Exception) { await ConsoleLogger.LogAsync("Attempted to auto-kick user " + $"{user.ToString() ?? "NULL"} as " + "part of the antiraid service, but " + "an exception was thrown!!", LogLvl.ERROR); } } break; case "shadowban": var sb = new Shadowban(); foreach (SocketGuildUser user in guildUsers) { try { await sb.AutoShadowbanUserAsync(user); } catch (Exception) { await ConsoleLogger.LogAsync("Attempted to auto-shadowban user " + $"{user.ToString() ?? "NULL"} as " + "part of the antiraid service, but " + "an exception was thrown!!", LogLvl.ERROR); } } break; case "ban": var ban = new Ban(); foreach (SocketGuildUser user in guildUsers) { try { await ban.AutoBanUserAsync(user, "Kaguya Anti-Raid protection."); } catch (Exception) { await ConsoleLogger.LogAsync("Attempted to auto-ban user " + $"{user.ToString() ?? "NULL"} as " + "part of the antiraid service, but " + "an exception was thrown!!", LogLvl.ERROR); } } break; default: await ConsoleLogger.LogAsync("Antiraid service triggered, but no users actioned. " + "Antiraid action string is different than expected. " + "Expected \"mute\" \"kick\" \"shadowban\" OR \"ban\". " + $"Received: '{action.ToLower()}'. " + $"Guild: {guildId}.", LogLvl.ERROR); break; } await ConsoleLogger.LogAsync($"Antiraid: Successfully actioned {guildUsers.Count:N0} users in guild {guild.Id}.", LogLvl.INFO); }