internal async Task MessageReceivedAsync(SocketMessage s) { if (!(s is SocketUserMessage msg)) { return; // If this is not a message (could be a TTS, Image, File, etc) } if (msg.Author.IsBot || msg.Author.IsWebhook) { return; // Ignore messages from bot users, which includes the bot itself. } int argPos = 0; ShardedCommandContext context = new ShardedCommandContext(discord, msg); // If DM Channel, ignore all database based things. if (msg.Channel is IDMChannel || msg.Channel is IGroupChannel) { if (msg.HasStringPrefix(GlobalConfig.Prefix, ref argPos)) { var result = await commandService.ExecuteAsync(context, argPos, service); if (!result.IsSuccess) // If not successful, reply with the error. { await context.Channel.SendMessageAsync(result.ToString()); } return; } } // Get the active database information for the current guild, or create it if it doesn't exist (for some reason) var guild = RavenDb.GetGuild(context.Guild.Id) ?? RavenDb.CreateNewGuild(context.Guild.Id, context.Guild.Name); if (!context.Guild.CurrentUser.GuildPermissions.Administrator && msg.HasStringPrefix(guild.GuildSettings.Prefix, ref argPos)) { await context.Channel.SendMessageAsync("The bot is not currently set as an administrator." + "Commands will be ignored until the bot is granted the Administrator permission."); return; } if (msg.Content.Contains("discord.gg/") && !((SocketGuildUser)context.User).GuildPermissions.ManageGuild && guild.GuildSettings.AutoblockInviteLinks) { await msg.DeleteAsync(); await context.Channel.SendMessageAsync("This server does not allow the posting of Discord server invites by non-moderators."); return; } // If the level settings are not disabled, we want to do our level processing. if (guild.GuildSettings.LevelConfig.LevelSettings != LevelSettings.Disabled) { // Get the global database entry for the user, or create it if it doesn't exist. var user = RavenDb.GetUser(context.User.Id) ?? RavenDb.CreateNewUser(context.User.Id, context.User.Username, context.User.DiscriminatorValue); // Is the user ready for extra XP? if (user.XpLastUpdated.AddSeconds(RavenDb.GlobalLevelConfig.SecondsBetweenXpGiven) < DateTime.UtcNow) { user.XpLastUpdated = DateTime.UtcNow; // We are giving them XP so let's update the time stamp user.Xp = Convert.ToUInt64(new Random().Next(RavenDb.GlobalLevelConfig.MinXpGenerated, RavenDb.GlobalLevelConfig.MaxXpGenerated + 1)) + user.Xp; // Generate a value between our two clamps, a little RNG. if (user.Xp > user.RequiredXp) // Are they ready for a level up? { user = PostLevelProcessing(user, out Embed embed); // Level them up // Don't send the global message, maybe a setting in the future? } user.Save(); // Save the global user } // Are they allowing guild leveling? if (guild.GuildSettings.LevelConfig.LevelSettings == LevelSettings.GuildLeveling) { // Get the user or create them if they don't exist. RavenUser guildUser = guild.GetUser(context.User.Id) ?? guild.CreateNewUser(context.User.Id, context.User.Username, context.User.DiscriminatorValue); if (guildUser.UserId == 0) { // This is weird unintentional behaviour, but sometimes it happens // Need to investigate further await context.Channel.SendMessageAsync("Your user ID was 0 for some reason. Please try again."); return; } // Check if they area ready for XP on a guild level if (guildUser.XpLastUpdated.AddSeconds(guild.GuildSettings.LevelConfig.SecondsBetweenXpGiven) < DateTime.UtcNow) { guildUser.XpLastUpdated = DateTime.UtcNow; // They are so we update the timestamp guildUser.Xp = Convert.ToUInt64(new Random().Next(guild.GuildSettings.LevelConfig.MinXpGenerated, guild.GuildSettings.LevelConfig.MaxXpGenerated + 1)) + guildUser.Xp; // Generate a value between our two clamps if (guildUser.Xp > guildUser.RequiredXp) // If they are ready to level up { // Get the first role they are assigned that has a non-default colour SocketRole role = ((SocketGuildUser)msg.Author).Roles.FirstOrDefault(x => x.Color.ToString() != "#0"); Color? color = role?.Color; // Get the colour from the role, or null if we didn't find a colour. guildUser = PostLevelProcessing(guildUser, out Embed embed, color); // Pass it in to get the result await context.Channel.SendMessageAsync("", false, embed); // Post it } int index = guild.Users.FindIndex(x => x.UserId == context.User.Id); if (index != -1) // I don't think this should ever happend, but better safe than sorry { guild.Users[index] = guildUser; // Update it } guild.Save(); // Save the db entry } } } // If the mention the bot directly, tell them the prefix. If they type just the word prefix, tell them. if ((msg.MentionedUsers.All(x => discord.Shards.Any(y => y.CurrentUser.Id == x.Id)) && msg.MentionedUsers.Count > 0) || msg.Content == "prefix") { await context.Channel.SendMessageAsync("This guild's prefix is: " + guild.GuildSettings.Prefix); return; } // Ignore string prefixes if the person was currently in a menu else if (msg.HasStringPrefix(guild.GuildSettings.Prefix, ref argPos)) { if (!guild.UserConfiguration.ContainsKey(context.User.Id)) { var result = await commandService.ExecuteAsync(context, argPos, service); if (!result.IsSuccess) { await context.Channel.SendMessageAsync(result.ToString()); } return; } else { await context.Channel.SendMessageAsync("You are currently in a menu. Respond to it or type 'exit' to leave it."); } } // Are they currently in a menu if (guild.UserConfiguration.ContainsKey(context.User.Id)) { string[] args = msg.Content.Split(' '); // They want off this wild ride if (msg.Content == "exit" || msg.Content.StartsWith("exit")) { guild.UserConfiguration.Remove(context.User.Id); // Remove their menu entry guild.Save(); // Save await context.Channel.SendMessageAsync("Exited out of menu."); // Goodbye user return; } else if (msg.Content == "back" || msg.Content.StartsWith("back")) { switch (guild.UserConfiguration[context.User.Id]) { case MessageBox.BaseMenu: guild.UserConfiguration.Remove(context.User.Id); // Remove their menu entry guild.Save(); // Save await context.Channel.SendMessageAsync("Exited out of menu."); // Goodbye user return; case MessageBox.LsSettingSubmenu: guild.UserConfiguration[context.User.Id] = MessageBox.LevelSettings; break; // By default we assume they are one menu deep default: guild.UserConfiguration[context.User.Id] = MessageBox.BaseMenu; break; } guild.Save(); await ConfigHandler.SelectSubMenu(guild, context.User.Id, context.Guild.GetTextChannel(context.Channel.Id), guild.UserConfiguration[context.User.Id]); return; } // Otherwise we see if they specified a valid option else if (int.TryParse(args[0], out int option)) { // Handle it a bit differently if it's a different route if (guild.UserConfiguration[context.User.Id] is MessageBox.BaseMenu) { // Normally we wouldn't use IsDefined due to it's lack of scalability, // But for the root menu it actually scales rather well. Just need to watch out for the 2000 character limit. if (Enum.IsDefined(typeof(MessageBox), option)) { // Literally makes no difference in preformance, just trying to keep this file clean. // Using this method, they can technically, if they know the submenu values, // skip the parent menus and go straight to the sub menus. I don't really see this as an issue, to be honest. await ConfigHandler.SelectSubMenu(guild, context.User.Id, context.Guild.GetTextChannel(context.Channel.Id), (MessageBox)option); return; } else // They didn't so lets give them another chance { await context.Channel.SendMessageAsync("The option you specified doesn't exist. The option should be" + " just the number of the option you are trying to pick. Try again."); return; } } else { await ConfigHandler.SelectOption(guild, context.User.Id, context.Guild.GetTextChannel(context.Channel.Id), args); } } } }
/// <summary>Called when a user joins the server. </summary> internal async Task GuildUserJoinAsync(SocketGuildUser user) { foreach (PluginInfo plugin in GlobalConfig.PluginInfo) { if (plugin.MessageReceivedAsync != null) { if (GlobalConfig.RunPluginFunctionsAsynchronously) #pragma warning disable 4014 { plugin.GuildUserLeave.Invoke(user); } #pragma warning restore 4014 else { await plugin.GuildUserLeave(user); } } } // Add it to the global database if they don't exist if (RavenDb.GetUser(user.Id) is null) { RavenDb.CreateNewUser(user.Id, user.Username, user.DiscriminatorValue, user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl()); } // Get the guild this user is in RavenGuild guild = RavenDb.GetGuild(user.Guild.Id) ?? RavenDb.CreateNewGuild(user.Guild.Id, user.Guild.Name); // Update the total amount of users guild.TotalUsers = (uint)user.Guild.Users.Count; // If they rejoined, we'll store their old name to log bool rejoined = false; string username = string.Empty; // Get the user from that guild RavenUser guildUser = guild.GetUser(user.Id); if (guildUser is null) // if they don't exist, we'll need to create them { guild.CreateNewUser(user.Id, user.Username, user.DiscriminatorValue, user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl()); } else { // They rejoined, update their name in case/discrim in case it changed rejoined = true; username = guildUser.Username + "#" + guildUser.Discriminator; guildUser.Username = user.Username; guildUser.Discriminator = user.DiscriminatorValue; guild.Save(); // Save the updated information, while storing the old one for logging purposes } // Process welcome message if one is set if (guild.GuildSettings.WelcomeMessage.Enabled) { // If the targeted channel is null or no longer exists or the message itself is undefined if (guild.GuildSettings.WelcomeMessage.ChannelId is null || user.Guild.GetTextChannel(guild.GuildSettings.WelcomeMessage.ChannelId.GetValueOrDefault()) is null || string.IsNullOrWhiteSpace(guild.GuildSettings.WelcomeMessage.Message)) { // If the logging channel is setup, exists, and is enabled if (!(guild.LoggingSettings.ChannelId is null) && !(user.Guild.GetTextChannel(guild.LoggingSettings.ChannelId.GetValueOrDefault()) is null) && guild.LoggingSettings.Enabled) { // Log to the logging channel if it has been set await user.Guild.GetTextChannel(guild.LoggingSettings.ChannelId.Value).SendMessageAsync(null, false, new EmbedBuilder() { Title = "Warning!", Color = new Color(255, 128, 0), // Orange Description = "Unable to send welcome message. Channel or message are currently null. Please reconfigure it.", Footer = new EmbedFooterBuilder() { Text = $"{DateTime.UtcNow:ddd MMM d yyyy HH mm}" } }.Build()); } } else { // Send the welcome message and repalce the server or user tags if they are present await user.Guild.GetTextChannel(guild.GuildSettings.WelcomeMessage.ChannelId.Value) .SendMessageAsync(guild.GuildSettings.WelcomeMessage.Message .Replace("%SERVER%", user.Guild.Name) .Replace("%USER%", user.Username)); } }
internal async Task MessageReceivedAsync(SocketMessage s) { foreach (PluginInfo plugin in GlobalConfig.PluginInfo) { if (plugin.MessageReceivedAsync != null) { if (GlobalConfig.RunPluginFunctionsAsynchronously) #pragma warning disable 4014 { plugin.MessageReceivedAsync.Invoke(s); } #pragma warning restore 4014 else { await plugin.MessageReceivedAsync(s); } } } if (!(s is SocketUserMessage msg)) { return; // If this is not a message (could be a TTS, Image, File, etc) } if (msg.Author.IsBot || msg.Author.IsWebhook) { return; // Ignore messages from bot users, which includes the bot itself. } int argPos = 0; ShardedCommandContext context = new ShardedCommandContext(discord, msg); // If DM Channel, ignore all database based things. if (msg.Channel is IDMChannel || msg.Channel is IGroupChannel) { if (msg.HasStringPrefix(GlobalConfig.Prefix, ref argPos)) { var result = await commandService.ExecuteAsync(context, argPos, service); if (!result.IsSuccess) // If not successful, reply with the error. { await context.Channel.SendMessageAsync(result.ToString()); } return; } } // Get the active database information for the current guild, or create it if it doesn't exist (for some reason) var guild = RavenDb.GetGuild(context.Guild.Id) ?? RavenDb.CreateNewGuild(context.Guild.Id, context.Guild.Name); guild.TotalMessages++; if (msg.Attachments.Count > 0) { foreach (var x in msg.Attachments) { if (x.Height != null) { guild.TotalImages++; } } } guild.Save(); if (!context.Guild.CurrentUser.GuildPermissions.Administrator && msg.HasStringPrefix(guild.GuildSettings.Prefix, ref argPos)) { await context.Channel.SendMessageAsync("The bot is not currently set as an administrator." + "Commands will be ignored until the bot is granted the Administrator permission."); return; } if (msg.Content.Contains("discord.gg/") && !((SocketGuildUser)context.User).GuildPermissions.ManageGuild && guild.GuildSettings.AutoblockInviteLinks) { await msg.DeleteAsync(); await context.Channel.SendMessageAsync("This server does not allow the posting of Discord server invites by non-moderators."); return; } // If the level settings are not disabled, we want to do our level processing. if (guild.GuildSettings.LevelConfig.LevelSettings != LevelSettings.Disabled) { // Get the global database entry for the user, or create it if it doesn't exist. var user = RavenDb.GetUser(context.User.Id) ?? RavenDb.CreateNewUser(context.User.Id, context.User.Username, context.User.DiscriminatorValue, context.User.GetAvatarUrl() ?? context.User.GetDefaultAvatarUrl()); // Is the user ready for extra XP? if (user.XpLastUpdated.AddSeconds(RavenDb.GlobalLevelConfig.SecondsBetweenXpGiven) < DateTime.UtcNow) { user.XpLastUpdated = DateTime.UtcNow; // We are giving them XP so let's update the time stamp user.Xp = Convert.ToUInt64(new Random().Next(RavenDb.GlobalLevelConfig.MinXpGenerated, RavenDb.GlobalLevelConfig.MaxXpGenerated + 1)) + user.Xp; // Generate a value between our two clamps, a little RNG. if (user.Xp > user.RequiredXp) // Are they ready for a level up? { user = PostLevelProcessing(user, out Embed embed); // Level them up // Don't send the global message, maybe a setting in the future? } user.Save(); // Save the global user } // Are they allowing guild leveling? if (guild.GuildSettings.LevelConfig.LevelSettings == LevelSettings.GuildLeveling) { // Get the user or create them if they don't exist. RavenUser guildUser = guild.GetUser(context.User.Id) ?? guild.CreateNewUser(context.User.Id, context.User.Username, context.User.DiscriminatorValue, context.User.GetAvatarUrl() ?? context.User.GetDefaultAvatarUrl()); if (guildUser.UserId == 0) { // This is weird unintentional behaviour, but sometimes it happens // Need to investigate further await context.Channel.SendMessageAsync("Your user ID was 0 for some reason. Please try again."); return; } // Check if they area ready for XP on a guild level if (guildUser.XpLastUpdated.AddSeconds(guild.GuildSettings.LevelConfig.SecondsBetweenXpGiven) < DateTime.UtcNow) { guildUser.XpLastUpdated = DateTime.UtcNow; // They are so we update the timestamp guildUser.Xp = Convert.ToUInt64(new Random().Next(guild.GuildSettings.LevelConfig.MinXpGenerated, guild.GuildSettings.LevelConfig.MaxXpGenerated + 1)) + guildUser.Xp; // Generate a value between our two clamps if (guildUser.Xp >= guildUser.RequiredXp) // If they are ready to level up { // Get the first role they are assigned that has a non-default colour SocketRole role = ((SocketGuildUser)msg.Author).Roles.FirstOrDefault(x => x.Color.ToString() != "#0"); Color? color = role?.Color; // Get the colour from the role, or null if we didn't find a colour. guildUser = PostLevelProcessing(guildUser, out Embed embed, color, guild); // Pass it in to get the result await context.Channel.SendMessageAsync("", false, embed); // Post it } int index = guild.Users.FindIndex(x => x.UserId == context.User.Id); if (index != -1) // I don't think this should ever happend, but better safe than sorry { guild.Users[index] = guildUser; // Update it } guild.Save(); // Save the db entry } } } // If the mention the bot directly, tell them the prefix. If they type just the word prefix, tell them. if ((msg.MentionedUsers.All(x => discord.Shards.Any(y => y.CurrentUser.Id == x.Id)) && MentionUtils.TryParseUser(msg.Content, out ulong a)) || msg.Content.ToLower() == "prefix") { await context.Channel.SendMessageAsync("This guild's prefix is: " + guild.GuildSettings.Prefix); return; }