public static bool TryParseAbsoluteReminder(Match timerMatch, BotMessageData messageData, out string query) { query = string.Empty; string toMatch = timerMatch.Groups["target"].ToString().Trim(); string to = toMatch == "me" ? messageData.UserName : toMatch; string req = to == messageData.UserName ? string.Empty : messageData.UserName; string durationStr = string.Empty; long duration = 0; GroupCollection matchGroups = timerMatch.Groups; string reason = matchGroups["reason"].ToString(); var dateTimeString = matchGroups["time"].ToString(); if (matchGroups["date"].Success) { dateTimeString = matchGroups["date"] + " " + dateTimeString; } if (DateTime.TryParse(dateTimeString, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal, out DateTime dt)) { duration = (long)dt.Subtract(DateTime.UtcNow).TotalSeconds; durationStr = $"{duration}s"; } if (duration < 10 || duration > TOOLONG) { return(false); } query = $"timer for:\"{to}\" {durationStr} {reason}"; return(true); }
public DiscordBotContext(DiscordSocketClient client, SocketInteraction interaction, IUserMessage message) { this.Client = client; this.Interaction = interaction; this.MessageData = BotMessageData.Create(interaction, message, this.Settings); this.Message = message; }
/// <summary> /// Sends farewells and mod log messages, if configured. /// </summary> private async Task HandleUserLeftAsync(SocketGuildUser guildUser) { var settings = SettingsConfig.GetSettings(guildUser.Guild.Id); if (!string.IsNullOrEmpty(settings.Farewell) && settings.FarewellId != 0) { var farewell = settings.Farewell.Replace("%user%", guildUser.Mention); farewell = farewell.Replace("%username%", $"{guildUser}"); farewell = Consts.ChannelRegex.Replace(farewell, new MatchEvaluator((Match chanMatch) => { string channelName = chanMatch.Groups[1].Value; var channel = guildUser.Guild.Channels.Where(c => c is ITextChannel && c.Name.Equals(channelName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); return((channel as ITextChannel)?.Mention ?? $"#{channelName}"); })); var farewellChannel = this.Client.GetChannel(settings.FarewellId) as ITextChannel ?? guildUser.Guild.DefaultChannel; if (farewellChannel.GetCurrentUserPermissions().SendMessages) { await farewellChannel.SendMessageAsync(farewell); } } // mod log if (settings.HasFlag(ModOptions.Mod_LogUserLeave) && this.Client.GetChannel(settings.Mod_LogId) is ITextChannel modLogChannel && modLogChannel.GetCurrentUserPermissions().SendMessages) { this.BatchSendMessageAsync(modLogChannel, $"{guildUser.Mention} ({guildUser}) left."); } var messageData = BotMessageData.Create(guildUser, settings); messageData.Content = ".timer clear"; messageData.Prefix = "."; await this.BotApi.IssueRequestAsync(messageData); }
public static BotMessageData Create(IUserMessage message, Settings serverSettings) { var preferEmbeds = ((message.Channel as SocketTextChannel)?.GetCurrentUserPermissions().EmbedLinks ?? false) && serverSettings.PreferEmbeds; var messageData = new BotMessageData(BotType.Discord) { DiscordMessageData = message, UserName = message.Author.Username, UserId = message.Author.Id.ToString(), UserHost = message.Author.Id.ToString(), Channel = message.Channel.Id.ToString(), Server = (message.Channel as IGuildChannel)?.GuildId.ToString() ?? "private", MessageId = message.Id.ToString(), Content = message.Content, Format = preferEmbeds ? "embed" : string.Empty, Sasshat = serverSettings.SasshatEnabled, Prefix = serverSettings.Prefix, }; // if the user does not have @everyone permissions, block its use if (!(message.Author as SocketGuildUser)?.GetPermissions(message.Channel as SocketGuildChannel).MentionEveryone ?? false) { messageData.Content = messageData.Content.Replace("@everyone", "@every\x200Bone").Replace("@here", "@he\x200Bre"); } return(messageData); }
// TODO: IRC library needs... some improvements. public async void OnIrcEventAsync(MessageData data, IrcClient client) { var responses = new List <string>(); var settings = new Settings(); if (data.Verb == ReplyCode.RPL_ENDOFMOTD || data.Verb == ReplyCode.RPL_NOMOTD) // motd end or motd missing { foreach (string channel in this.Config.Irc.Servers.Where(s => s.Host == client.Id).First().Channels) { serverData[client.Host].Channels[channel.ToLowerInvariant()] = new ChannelData(); client.Command("JOIN", channel, string.Empty); } } if (data.Verb == "PRIVMSG") { string query = string.Empty; var props = new Dictionary <string, string> { { "server", client.Host.ToLowerInvariant() }, { "channel", data.Target.ToLowerInvariant() }, }; this.TrackEvent("messageReceived", props); // TODO: put this in config // twitch parses "." prefix as internal commands; so we have to remap it :( if (client.Host == "irc.chat.twitch.tv") { settings.Prefix = "^"; } var messageData = BotMessageData.Create(data, client, settings); await this.PreProcessMessage(messageData, settings); responses.AddRange((await this.ProcessMessageAsync(messageData, settings)).Responses); foreach (string response in responses) { client.Command("PRIVMSG", data.Target, response); this.TrackEvent("messageSent", props); } } // TODO: This stuff should be handled by the IRC library... else if (data.Verb == "353") { var namesMatch = namesRegex.Match(data.Text); var channel = namesMatch.Groups[1].ToString().ToLowerInvariant(); var userList = namesMatch.Groups[2].ToString(); var users = userList.Split(new[] { ' ' }); if (!serverData[client.Host].Channels.ContainsKey(channel)) { this.serverData[client.Host].Channels[channel] = new ChannelData(); } this.serverData[client.Host].Channels[channel].Users = new HashSet <string>(users); } }
protected async Task PreProcessMessage(BotMessageData messageData, Settings settings) { this.messageCount++; // Update the seen data this.UpdateSeen(messageData.Channel + messageData.UserName, new SeenUserData { Name = messageData.UserId ?? messageData.UserName, Channel = messageData.Channel, Server = messageData.Server, //Text = messageData.Content, removed due to discord tos }); var httpMatch = Consts.HttpRegex.Match(messageData.Content); if (httpMatch.Success) { this.urls[messageData.Channel] = httpMatch.Value; } if (settings.FunResponsesEnabled && !string.IsNullOrEmpty(messageData.Content)) { var repeat = repeatData.GetOrAdd(messageData.Channel + messageData.Server, new RepeatData()); if (string.Equals(repeat.Text, messageData.Content, StringComparison.OrdinalIgnoreCase)) { if (!repeat.Nicks.Contains(messageData.UserId ?? messageData.UserName)) { repeat.Nicks.Add(messageData.UserId ?? messageData.UserName); } if (repeat.Nicks.Count == 3) { await this.RespondAsync(messageData, messageData.Content); repeat.Reset(string.Empty, string.Empty); } } else { repeat.Reset(messageData.UserId ?? messageData.UserName, messageData.Content); } } string command = messageData.Command; if (!CommandsConfig.Instance.Commands.ContainsKey(command)) { command = string.Empty; } if (string.IsNullOrEmpty(command)) { // match fun bool mentionsBot = messageData.MentionsBot(this.Config.Name, Convert.ToUInt64(this.UserId)); if (CommandsConfig.Instance.TryParseForCommand(messageData.Content, mentionsBot, out string parsedCommand, out string query)) { messageData.Query = query; } } }
public async Task HandleMessage(MessageEvent messageEvent) { try { if (messageEvent.IsSystemMessage || messageEvent.CreatedByWebhook != null || messageEvent.CreatedBy == this.client.Me.Id || messageEvent.IsReply) { return; } if (this.Throttler.IsThrottled(messageEvent.CreatedBy.ToString(), ThrottleType.User)) { Log.Debug($"messaging throttle from user: {messageEvent.CreatedBy} on chan {messageEvent.ChannelId} server {messageEvent.ServerId}"); return; } if (this.Throttler.IsThrottled(messageEvent.ServerId.ToString(), ThrottleType.Guild)) { Log.Debug($"messaging throttle from guild: {messageEvent.CreatedBy} on chan {messageEvent.ChannelId} server {messageEvent.ServerId}"); return; } var settings = new Settings { FunResponsesEnabled = true, AutoTitlesEnabled = true, PreferEmbeds = true, }; var messageData = BotMessageData.Create(messageEvent.Message, settings); await this.PreProcessMessage(messageData, settings); BotResponseData responseData = await this.ProcessMessageAsync(messageData, settings); if (responseData.Embed != null) { await this.RespondAsync(messageEvent.Message, string.Empty, bypassEdit : true, responseData.Embed.CreateGuildedEmbed()); } else { foreach (string response in responseData.Responses) { await this.RespondAsync(messageEvent.Message, response, bypassEdit : responseData.Responses.Count > 1); } } } catch (Exception ex) { Log.Warning(ex, $"Error in HandleMessage"); this.AppInsights?.TrackException(ex); } }
public static BotMessageData Create(SocketUser user, SocketGuild guild, Settings serverSettings) { var messageData = new BotMessageData(BotType.Discord) { UserName = user.Username, UserId = user.Id.ToString(), UserHost = user.Id.ToString(), Server = guild.Id.ToString(), Prefix = serverSettings.Prefix, }; return(messageData); }
protected override Task RespondAsync(BotMessageData messageData, string text) { this.ircClients[messageData.Server]?.Command("PRIVMSG", messageData.Channel, text); var props = new Dictionary <string, string> { { "server", messageData.Server.ToLowerInvariant() }, { "channel", messageData.Channel.ToLowerInvariant() }, }; this.TrackEvent("messageSent", props); return(Task.CompletedTask); }
public async Task <BotResponseData> IssueRequestAsync(BotMessageData messageData) { try { var response = await new Uri($"{this.apiEndpoint}/{messageData.Command}").PostJsonAsync(messageData); return(JsonConvert.DeserializeObject <BotResponseData>(await response.Content.ReadAsStringAsync())); } catch (Exception ex) { Log.Error(ex, "Failed to parse {Endpoint}", this.apiEndpoint.ToString()); return(new BotResponseData()); } }
public static BotMessageData Create(SocketInteraction interaction, IUserMessage message, Settings serverSettings) { var preferEmbeds = ((interaction.Channel as SocketTextChannel)?.GetCurrentUserPermissions().EmbedLinks ?? false) && serverSettings.PreferEmbeds; var messageData = new BotMessageData(BotType.Discord) { DiscordInteraction = interaction, UserName = interaction.User.Username, UserId = interaction.User.Id.ToString(), UserHost = interaction.User.Id.ToString(), Channel = interaction.Channel.Id.ToString(), Server = (interaction.Channel as IGuildChannel)?.GuildId.ToString() ?? "private", MessageId = message?.Id.ToString(), Format = preferEmbeds ? "embed" : string.Empty, Sasshat = serverSettings.SasshatEnabled, Content = message?.Content ?? serverSettings.Prefix + ((interaction as SocketCommandBase)?.CommandName ?? (interaction as SocketMessageComponent)?.Data.CustomId), Prefix = serverSettings.Prefix, TargetUserName = (interaction as SocketUserCommand)?.Data.Member.UserOrNickname(), TargetUserId = (interaction as SocketUserCommand)?.Data.Member.Id.ToString(), }; if (interaction is SocketMessageCommand messageCommand) { messageData.Content = messageCommand.Data.Message.Content; } else if (interaction is SocketSlashCommand slashCommand) { messageData.RequestOptions = new Dictionary <string, string>(); foreach (var option in slashCommand.Data.Options) { if (option.Type == ApplicationCommandOptionType.SubCommand) { messageData.Content += $" {option.Name}"; messageData.RequestOptions.Add(option.Name, option.Name); } else { messageData.Content += $" {option.Value}"; messageData.RequestOptions.Add(option.Name, option.Value.ToString()); } } } // if the user does not have @everyone permissions, block its use if (!(interaction.User as SocketGuildUser)?.GetPermissions(interaction.Channel as SocketGuildChannel).MentionEveryone ?? false) { messageData.Content = messageData.Content.Replace("@everyone", "@every\x200Bone").Replace("@here", "@he\x200Bre"); } return(messageData); }
public async Task <BotResponseData> IssueRequestAsync(BotMessageData messageData) { try { var response = await new Uri($"{this.apiEndpoint}/{messageData.Command}").PostJsonAsync(messageData); return(JsonConvert.DeserializeObject <BotResponseData>(await response.Content.ReadAsStringAsync())); } catch (Exception ex) { // TODO: proper logger Console.WriteLine($"Failed to parse {this.apiEndpoint}: "); Console.WriteLine(ex); return(new BotResponseData()); } }
public DiscordBotContext(DiscordSocketClient client, SocketUserMessage message) { this.Client = client; this.Message = message; this.MessageData = BotMessageData.Create(Message, this.Settings); }
protected override async Task RespondAsync(BotMessageData messageData, string response) { await this.RespondAsync(messageData.DiscordMessageData, response); }
protected override async Task RespondAsync(BotMessageData messageData, string response) { await this.RespondAsync(messageData.DiscordMessageData, response, rateLimitChecked : messageData.RateLimitChecked); }
public async Task <BotResponseData> IssueRequestAsync(BotMessageData messageData, string query) { var responses = new List <string>(); var responseData = new BotResponseData(); string requestUrl = string.Format("{0}?apikey={1}&nick={2}&host={3}&server={4}&channel={5}&bottype={6}&userId={7}&format={8}&query={9}", this.apiEndpoint, this.apiKey, WebUtility.UrlEncode(messageData.UserName), WebUtility.UrlEncode(messageData.UserHost ?? messageData.UserId), messageData.Server, WebUtility.UrlEncode(messageData.Channel), this.botType.ToString().ToLowerInvariant(), messageData.UserId, messageData.Format, WebUtility.UrlEncode(query)); var botResponse = await Utilities.GetApiResponseAsync <BotApiResponse>(new Uri(requestUrl)); if (botResponse != null) { if (botResponse.Embed != null) { responseData.Embed = botResponse.Embed; } else { // TODO: fix API stupidity and just return the array var botResponses = botResponse.Msgs.Length > 0 ? botResponse.Msgs : new string[] { botResponse.Msg }; if (botResponses[0] != null) { char a = (char)1; char b = (char)2; char c = (char)3; foreach (var response in botResponses) { if (response != null) { responses.Add(response.Replace("%a", a.ToString()).Replace("%b", b.ToString()).Replace("%c", c.ToString())); } } if (this.botType == BotType.Discord) { string response = string.Join("\n", responses); // Extra processing for figlet/cowsay on Discord if (query.StartsWith("cowsay", StringComparison.OrdinalIgnoreCase) || query.StartsWith("figlet", StringComparison.OrdinalIgnoreCase)) { // use a non printable character to force preceeding whitespace to display correctly response = "```" + (char)1 + response + "```"; } responses = new List <string> { response }; } } } responseData.Responses = responses; } return(responseData); }
protected override async Task RespondAsync(BotMessageData messageData, string text) { await this.RespondAsync(messageData.GuildedMessageData, text); }
// TODO: // this is icky // it's getting worse...TODO: read the above todo and fix it internal DiscordCommands(DiscordSocketClient client, AudioManager audioManager, BotApi botApi) { this.client = client; this.audioManager = audioManager; this.botApi = botApi; this.Commands = new Dictionary <string, Func <SocketUserMessage, Task <CommandResponse> > >(); this.CreateScriptOptions(); Commands.Add("debug", (message) => { var serverId = (message.Channel as IGuildChannel)?.GuildId.ToString() ?? "n/a"; var botVersion = Assembly.GetEntryAssembly().GetName().Version.ToString(); var response = new CommandResponse { Text = $"```Server ID: {serverId} | Channel ID: {message.Channel.Id} | Your ID: {message.Author.Id} | Shard ID: {client.ShardId} | Version: {botVersion} | Discord.NET Version: {DiscordSocketConfig.Version}```" }; return(Task.FromResult(response)); }); Commands.Add("seen", async(message) => { if (message.Channel is IGuildChannel guildChannel) { var settings = SettingsConfig.GetSettings(guildChannel.GuildId.ToString()); if (!settings.SeenEnabled) { return(new CommandResponse { Text = "Seen data is not being tracked for this server. Enable it in the admin settings panel." }); } string[] parts = message.Content.Split(new[] { ' ' }, 2); if (parts.Length != 2) { return(new CommandResponse { Text = "Usage: .seen username" }); } var targetUser = (await guildChannel.Guild.GetUsersAsync()).Find(parts[1]).FirstOrDefault(); if (targetUser != null) { if (targetUser.Id == client.CurrentUser.Id) { return(new CommandResponse { Text = $"I was last seen...wait...seriously? Ain't no one got time for your shit, {message.Author.Username}." }); } if (targetUser.Id == message.Author.Id) { return(new CommandResponse { Text = $"You were last seen now, saying: ... god DAMN it {message.Author.Username}, quit wasting my time" }); } string query = $"seen {targetUser.Id} {targetUser.Username}"; var messageData = BotMessageData.Create(message, query, settings); var response = (await this.botApi.IssueRequestAsync(messageData, query)).Responses.FirstOrDefault(); if (response != null) { return(new CommandResponse { Text = response }); } } return(new CommandResponse { Text = $"I...omg...I have not seen {parts[1]} in this channel :X I AM SOOOOOO SORRY" }); } return(null); }); Commands.Add("remove", async(message) => { if (message.Channel is IGuildChannel guildChannel) { if (message.Author.Id != guildChannel.Guild.OwnerId) { return(new CommandResponse { Text = "Restricted to server owner." }); } var settings = SettingsConfig.GetSettings(guildChannel.GuildId.ToString()); string[] parts = message.Content.Split(new[] { ' ' }, 3); if (parts.Length != 3) { return(new CommandResponse { Text = "Usage: .remove type #; valid types are timer and wc" }); } var type = parts[1].ToLowerInvariant(); var id = parts[2]; string query = $"remove {CommandsConfig.Instance.HelperKey} {type} {id}"; var messageData = BotMessageData.Create(message, query, settings); var response = (await this.botApi.IssueRequestAsync(messageData, query)).Responses.FirstOrDefault(); if (response != null) { return(new CommandResponse { Text = response }); } } return(null); }); Commands.Add("status", async(message) => { var serversStatus = await Utilities.GetApiResponseAsync <HeartbeatData[]>(BotConfig.Instance.HeartbeatEndpoint); var dataSb = new StringBuilder(); dataSb.Append("```cs\n" + "type shard server count users voice count\n"); int serverTotal = 0; int userTotal = 0; int voiceTotal = 0; foreach (HeartbeatData heartbeat in serversStatus) { serverTotal += heartbeat.ServerCount; userTotal += heartbeat.UserCount; voiceTotal += heartbeat.VoiceChannelCount; var botType = heartbeat.BotType.PadRight(11); var shard = heartbeat.Shard.ToString().PadLeft(4); var servers = heartbeat.ServerCount.ToString().PadLeft(13); var users = heartbeat.UserCount.ToString().PadLeft(10); var voice = heartbeat.VoiceChannelCount.ToString().PadLeft(13); dataSb.Append($"{botType} {shard} {servers} {users} {voice}\n"); } // add up totals dataSb.Append($"-------\n"); dataSb.Append($"Total: {serverTotal.ToString().PadLeft(13)} {userTotal.ToString().PadLeft(10)} {voiceTotal.ToString().PadLeft(13)}\n"); dataSb.Append("```"); return(new CommandResponse { Text = dataSb.ToString() }); }); Commands.Add("voice", (message) => { var channel = (message.Author as IGuildUser)?.VoiceChannel; if (channel == null) { return(Task.FromResult(new CommandResponse { Text = "Join a voice channel first" })); } Task.Run(async() => { try { await audioManager.JoinAudioAsync(channel); } catch (Exception ex) { // TODO: proper logging Console.WriteLine(ex); } }).Forget(); return(Task.FromResult((CommandResponse)null)); }); Commands.Add("dvoice", (message) => { if (message.Channel is IGuildChannel channel) { Task.Run(async() => { try { await audioManager.LeaveAudioAsync(channel); } catch (Exception ex) { // TODO: proper logging Console.WriteLine(ex); } }).Forget(); } return(Task.FromResult((CommandResponse)null)); }); Commands.Add("devoice", (message) => { if (message.Channel is IGuildChannel channel) { Task.Run(async() => { try { await audioManager.LeaveAudioAsync(channel); } catch (Exception ex) { // TODO: proper logging Console.WriteLine(ex); } }).Forget(); } return(Task.FromResult((CommandResponse)null)); }); Commands.Add("clear", async(message) => { if (message.Channel is IDMChannel) { return(null); } var guildUser = message.Author as IGuildUser; if (!guildUser.GuildPermissions.ManageMessages) { return(new CommandResponse { Text = "you don't have permissions to clear messages, fartface" }); } var guildChannel = message.Channel as IGuildChannel; string[] parts = message.Content.Split(new[] { ' ' }, 3); if (parts.Length != 2 && parts.Length != 3) { return(new CommandResponse { Text = "Usage: .clear #; Usage for user specific messages: .clear # username" }); } IUser deletionUser = null; if (parts.Length == 3) { if (ulong.TryParse(parts[2], out ulong userId)) { deletionUser = await message.Channel.GetUserAsync(userId); } else { deletionUser = (await guildUser.Guild.GetUsersAsync().ConfigureAwait(false)).Find(parts[2]).FirstOrDefault(); } if (deletionUser == null) { return(new CommandResponse { Text = "Couldn't find the specified user. Try their ID if nick matching is struggling" }); } } var botGuildUser = await guildChannel.GetUserAsync(client.CurrentUser.Id); bool botOnly = deletionUser == botGuildUser; if (!botOnly && !botGuildUser.GetPermissions(guildChannel).ManageMessages) { return(new CommandResponse { Text = "yeah I don't have the permissions to delete messages, buttwad." }); } if (int.TryParse(parts[1], out int count)) { var textChannel = message.Channel as ITextChannel; // +1 for the current .clear message if (deletionUser == null) { count = Math.Min(99, count) + 1; } else { count = Math.Min(100, count); } // download messages until we've hit the limit var msgsToDelete = new List <IMessage>(); var msgsToDeleteCount = 0; ulong?lastMsgId = null; var i = 0; while (msgsToDeleteCount < count) { i++; IEnumerable <IMessage> downloadedMsgs; try { if (!lastMsgId.HasValue) { downloadedMsgs = await textChannel.GetMessagesAsync(count).Flatten(); } else { downloadedMsgs = await textChannel.GetMessagesAsync(lastMsgId.Value, Direction.Before, count).Flatten(); } } catch (Exception ex) { downloadedMsgs = new IMessage[0]; Console.WriteLine(ex); } if (downloadedMsgs.Count() > 0) { lastMsgId = downloadedMsgs.Last().Id; var msgs = downloadedMsgs.Where(m => (deletionUser == null || m.Author?.Id == deletionUser.Id)).Take(count - msgsToDeleteCount); msgsToDeleteCount += msgs.Count(); msgsToDelete.AddRange(msgs); } else { break; } if (i >= 5) { break; } } var settings = SettingsConfig.GetSettings(guildUser.GuildId.ToString()); if (settings.HasFlag(ModOptions.Mod_LogDelete) && this.client.GetChannel(settings.Mod_LogId) is ITextChannel modLogChannel && modLogChannel.GetCurrentUserPermissions().SendMessages) { modLogChannel.SendMessageAsync($"{guildUser.Username}#{guildUser.Discriminator} cleared {msgsToDeleteCount} messages from {textChannel.Mention}").Forget(); } try { await(message.Channel as ITextChannel).DeleteMessagesAsync(msgsToDelete); } catch (ArgumentOutOfRangeException) { return(new CommandResponse { Text = "Bots cannot delete messages older than 2 weeks." }); } return(null); } else { return(new CommandResponse { Text = "Usage: .clear #" }); } }); Commands.Add("jpeg", async(message) => { var messageParts = message.Content.Split(new[] { ' ' }, 2); var fileName = "moar.jpeg"; var url = string.Empty; if (messageParts.Length == 2 && Uri.IsWellFormedUriString(messageParts[1], UriKind.Absolute)) { url = messageParts[1]; } else { Attachment img = message.Attachments.FirstOrDefault(); if (img != null || DiscordBot.imageUrls.TryGetValue(message.Channel.Id.ToString(), out img)) { url = img.Url; fileName = img.Filename; } } if (!string.IsNullOrEmpty(url)) { using (var httpClient = new HttpClient()) { var response = await httpClient.GetAsync(CommandsConfig.Instance.JpegEndpoint.AppendQueryParam("url", url)); var stream = await response.Content.ReadAsStreamAsync(); return(new CommandResponse { Attachment = new FileResponse { Name = fileName, Stream = stream, } }); } } return(null); }); Commands.Add("userinfo", async(message) => { SocketUser targetUser = message.MentionedUsers?.FirstOrDefault(); if (targetUser == null) { var messageParts = message.Content.Split(new[] { ' ' }, 2); if (messageParts.Length == 2) { if (message.Channel is IGuildChannel guildChannel) { targetUser = (await guildChannel.Guild.GetUsersAsync()).Find(messageParts[1]).FirstOrDefault() as SocketUser; } if (targetUser == null) { return(new CommandResponse { Text = "User not found. Try a direct mention." }); } } else { targetUser = message.Author; } } var guildUser = targetUser as IGuildUser; if (guildUser == null) { return(null); } var userInfo = new { Title = $"UserInfo for {targetUser.Username}#{targetUser.Discriminator}", AvatarUrl = targetUser.GetAvatarUrl(), NicknameInfo = !string.IsNullOrEmpty(guildUser.Nickname) ? $" aka {guildUser.Nickname}" : "", Footnote = CommandsConfig.Instance.UserInfoSnippets.Random(), Created = $"{targetUser.GetCreatedDate().ToString("dd MMM yyyy")} {targetUser.GetCreatedDate().ToString("hh:mm:ss tt")} UTC", Joined = guildUser.JoinedAt.HasValue ? $"{guildUser.JoinedAt.Value.ToString("dd MMM yyyy")} {guildUser.JoinedAt.Value.ToString("hh:mm:ss tt")} UTC" : "[data temporarily missing]", Id = targetUser.Id.ToString(), }; EmbedBuilder embedBuilder = null; string text = string.Empty; var settings = SettingsConfig.GetSettings(guildUser.GuildId.ToString()); if ((message.Channel as ITextChannel).GetCurrentUserPermissions().EmbedLinks&& settings.PreferEmbeds) { embedBuilder = new EmbedBuilder { Title = userInfo.Title, ThumbnailUrl = userInfo.AvatarUrl, }; if (!string.IsNullOrEmpty(userInfo.NicknameInfo)) { embedBuilder.Description = userInfo.NicknameInfo; } embedBuilder.Footer = new EmbedFooterBuilder { Text = userInfo.Footnote, }; embedBuilder.AddField((field) => { field.IsInline = true; field.Name = "created"; field.Value = userInfo.Created; }); if (guildUser.JoinedAt.HasValue) { embedBuilder.AddField((field) => { field.IsInline = true; field.Name = "joined"; field.Value = userInfo.Joined; }); } var roles = new List <string>(); foreach (ulong roleId in guildUser.RoleIds) { roles.Add(guildUser.Guild.Roles.First(g => g.Id == roleId).Name.TrimStart('@')); } embedBuilder.AddField((field) => { field.IsInline = false; field.Name = "roles"; field.Value = string.Join(", ", roles); }); embedBuilder.AddField((field) => { field.IsInline = true; field.Name = "Id"; field.Value = userInfo.Id; }); } else { text = $"{userInfo.Title}{userInfo.NicknameInfo}: ID: {userInfo.Id} | Created: {userInfo.Created} | Joined: {userInfo.Joined} | word on the street: {userInfo.Footnote}"; } return(new CommandResponse { Text = text, Embed = embedBuilder }); }); Commands.Add("serverinfo", async(message) => { EmbedBuilder embedBuilder = null; string text = string.Empty; if (message.Channel is IGuildChannel guildChannel && message.Channel is ITextChannel textChannel) { var guild = guildChannel.Guild; var emojiCount = guild.Emotes.Count(); var emojiText = "no custom emojis? I am ASHAMED to be here"; if (emojiCount > 50) { emojiText = $"...{emojiCount} emojis? hackers"; } else if (emojiCount == 50) { emojiText = "wow 50 custom emojis! that's the max"; } else if (emojiCount >= 40) { emojiText = $"{emojiCount} custom emojis in here. impressive...most impressive..."; } else if (emojiCount > 25) { emojiText = $"the custom emoji force is strong with this guild. {emojiCount} is over halfway to the max."; } else if (emojiCount > 10) { emojiText = $"{emojiCount} custom emoji is...passable"; } else if (emojiCount > 0) { emojiText = $"really, only {emojiCount} custom emoji? tsk tsk."; } await(message.Channel as SocketGuildChannel).Guild.DownloadUsersAsync(); var serverInfo = new { Title = $"Server Info for {guild.Name}", UserCount = (await guild.GetUsersAsync()).Count(), Owner = (await guild.GetOwnerAsync()).Username, Created = $"{guild.CreatedAt.ToString("dd MMM yyyy")} {guild.CreatedAt.ToString("hh:mm:ss tt")} UTC", EmojiText = emojiText, }; var settings = SettingsConfig.GetSettings(guildChannel.GuildId.ToString()); if (textChannel.GetCurrentUserPermissions().EmbedLinks&& settings.PreferEmbeds) { embedBuilder = new EmbedBuilder { Title = serverInfo.Title, ThumbnailUrl = guildChannel.Guild.IconUrl, }; embedBuilder.AddField((field) => { field.IsInline = true; field.Name = "Users"; field.Value = serverInfo.UserCount; }); embedBuilder.AddField((field) => { field.IsInline = true; field.Name = "Owner"; field.Value = serverInfo.Owner; }); embedBuilder.AddField((field) => { field.IsInline = true; field.Name = "Id"; field.Value = guild.Id; }); embedBuilder.AddField((field) => { field.IsInline = true; field.Name = "created"; field.Value = serverInfo.Created; }); embedBuilder.Footer = new EmbedFooterBuilder { Text = serverInfo.EmojiText, }; if (!string.IsNullOrEmpty(guild.SplashUrl)) { embedBuilder.Footer.IconUrl = guild.SplashUrl; } } else { text = $"{serverInfo.Title}: Users: {serverInfo.UserCount} | Owner: {serverInfo.Owner} | Id: {guild.Id} | Created: {serverInfo.Created} | {serverInfo.EmojiText}"; } }
protected async Task <BotResponseData> ProcessMessageAsync(BotMessageData messageData, Settings settings) { var responses = new List <string>(); var responseData = new BotResponseData { Responses = responses }; if (this.BotApi != null) { // if an explicit command is being used, it wins out over any implicitly parsed command string query = messageData.Query; string command = messageData.Command; string[] contentParts = messageData.Content.Split(new[] { ' ' }); if (string.IsNullOrEmpty(command)) { if (messageData.Content.ToLowerInvariant().Contains("remind ")) { // check for reminders Match timerAtMatch = Consts.TimerOnRegex.Match(messageData.Content); Match timerAt2Match = Consts.TimerOn2Regex.Match(messageData.Content); if (timerAtMatch.Success && Utilities.TryParseAbsoluteReminder(timerAtMatch, messageData, out query) || timerAt2Match.Success && Utilities.TryParseAbsoluteReminder(timerAt2Match, messageData, out query)) { command = "timer"; } else // try relative timers if absolute had no match { Match timerMatch = Consts.TimerRegex.Match(messageData.Content); Match timer2Match = Consts.Timer2Regex.Match(messageData.Content); if (timerMatch.Success || timer2Match.Success) { Match matchToUse = timerMatch.Success && !timerMatch.Groups["prep"].Value.All(char.IsDigit) ? timerMatch : timer2Match; if (Utilities.TryParseReminder(matchToUse, messageData, out query)) { command = "timer"; } } } } if (string.IsNullOrEmpty(command)) { if (settings.AutoTitlesEnabled && CommandsConfig.Instance.AutoTitleMatches.Any(t => messageData.Content.Contains(t))) { Match match = Consts.HttpRegex.Match(messageData.Content); string matchValue = !string.IsNullOrEmpty(match.Value) ? match.Value : Consts.RedditRegex.Match(messageData.Content).Value; if (!string.IsNullOrEmpty(matchValue)) { command = "title"; query = $"{command} {matchValue}"; } } else if ((settings.FunResponsesEnabled || IsAuthorOwner(messageData)) && contentParts.Length > 1 && contentParts[1] == "face") { command = "face"; query = $"{command} {contentParts[0]}"; } } } // Ignore if the command is disabled on this server if (settings.IsCommandDisabled(CommandsConfig.Instance, command) && !IsAuthorOwner(messageData)) { return(responseData); } if (CommandsConfig.Instance.Commands.ContainsKey(command)) { if (!messageData.RateLimitChecked) { // make sure we're not rate limited var commandKey = command + messageData.Server; var commandCount = this.commandsIssued.AddOrUpdate(commandKey, 1, (key, val) => { return(val + 1); }); if (commandCount > 12) { return(responseData); } else if (commandCount > 10) { responses.Add("rate limited try later"); return(responseData); } } var props = new Dictionary <string, string> { { "serverId", messageData.Server }, }; this.AppInsights?.TrackEvent(command.ToLowerInvariant(), props); // extra processing on ".title" command if ((command == "title" || command == "t") && messageData.Content.EndsWith(command) && urls.ContainsKey(messageData.Channel)) { query += $" {this.urls[messageData.Channel]}"; } responseData = await this.BotApi.IssueRequestAsync(messageData, query); } } if (responseData.Responses.Count == 0 && responseData.Embed == null) { string response = null; if (messageData.MentionsBot(this.Config.Name, Convert.ToUInt64(this.UserId))) { var responseValue = PhrasesConfig.Instance.PartialMentionPhrases.Where(kvp => messageData.Content.ToLowerInvariant().Contains(kvp.Key.ToLowerInvariant())).FirstOrDefault().Value; if (!string.IsNullOrEmpty(responseValue)) { response = PhrasesConfig.Instance.Responses[responseValue].Random(); } } if (response == null && (settings.FunResponsesEnabled || IsAuthorOwner(messageData)) && PhrasesConfig.Instance.ExactPhrases.ContainsKey(messageData.Content) && new Random().Next(1, 100) <= settings.FunResponseChance) { response = PhrasesConfig.Instance.Responses[PhrasesConfig.Instance.ExactPhrases[messageData.Content]].Random(); } if (response != null) { response = response.Replace("%from%", messageData.UserName); string[] resps = response.Split(new char[] { '|' }); responseData.Responses.AddRange(resps); } } return(responseData); }
protected async Task PreProcessMessage(BotMessageData messageData, Settings settings) { this.messageCount++; if (settings.SeenEnabled) { var userKey = messageData.UserId ?? messageData.UserName; this.UpdateSeen(messageData.Server + userKey, new SeenUserData { Name = userKey, Channel = messageData.Channel, Server = messageData.Server, }); } var httpMatch = Consts.UrlRegex.Match(messageData.Content); if (httpMatch.Success) { this.urls[messageData.Channel] = httpMatch.Value; } if (settings.FunResponsesEnabled && !string.IsNullOrEmpty(messageData.Content)) { var repeat = repeatData.GetOrAdd(messageData.Channel + messageData.Server, new RepeatData()); if (string.Equals(repeat.Text, messageData.Content, StringComparison.OrdinalIgnoreCase)) { if (!repeat.Nicks.Contains(messageData.UserId ?? messageData.UserName)) { repeat.Nicks.Add(messageData.UserId ?? messageData.UserName); } if (repeat.Nicks.Count == 3) { var commandKey = $"{messageData.Channel}_{messageData.Server}"; this.Throttler.Increment(commandKey, ThrottleType.Repeat); if (!this.Throttler.IsThrottled(commandKey, ThrottleType.Repeat)) { await this.RespondAsync(messageData, messageData.Content); } else { Log.Debug("repeat throttled"); } repeat.Reset(string.Empty, string.Empty); } } else { repeat.Reset(messageData.UserId ?? messageData.UserName, messageData.Content); } } string command = messageData.Command; if (!CommandsConfig.Instance.Commands.ContainsKey(command)) { command = string.Empty; } if (string.IsNullOrEmpty(command)) { // match fun bool mentionsBot = messageData.MentionsBot(this.Config.Name, Convert.ToUInt64(this.UserId)); if (CommandsConfig.Instance.TryParseForCommand(messageData.Content, mentionsBot, out _, out string query)) { messageData.Content = $"{settings.Prefix}{query}"; } } }
protected async Task <BotResponseData> ProcessMessageAsync(BotMessageData messageData, Settings settings) { var responses = new List <string>(); var responseData = new BotResponseData { Responses = responses }; if (this.BotApi != null) { // if an explicit command is being used, it wins out over any implicitly parsed command string query = messageData.Query; string command = messageData.Command; string[] contentParts = messageData.Content.Split(new[] { ' ' }); if (string.IsNullOrEmpty(command)) { if (messageData.Content.IContains("remind ")) { // check for reminders Match timerAtMatch = Consts.TimerOnRegex.Match(messageData.Content); Match timerAt2Match = Consts.TimerOn2Regex.Match(messageData.Content); if (timerAtMatch.Success && Utilities.TryParseAbsoluteReminder(timerAtMatch, messageData, out query) || timerAt2Match.Success && Utilities.TryParseAbsoluteReminder(timerAt2Match, messageData, out query)) { command = "timer"; } else // try relative timers if absolute had no match { Match timerMatch = Consts.TimerRegex.Match(messageData.Content); Match timer2Match = Consts.Timer2Regex.Match(messageData.Content); if (timerMatch.Success || timer2Match.Success) { Match matchToUse = timerMatch.Success && !timerMatch.Groups["prep"].Value.All(char.IsDigit) ? timerMatch : timer2Match; if (Utilities.TryParseReminder(matchToUse, messageData, out query)) { command = "timer"; } } } } if (string.IsNullOrEmpty(command)) { if (settings.AutoTitlesEnabled && (CommandsConfig.Instance.AutoTitleMatches.Any(t => messageData.Content.Contains(t)) || Consts.RedditRegex.IsMatch(messageData.Content))) { Match match = Consts.HttpRegex.Match(messageData.Content); string matchValue = !string.IsNullOrEmpty(match.Value) ? match.Value : Consts.RedditRegex.Match(messageData.Content).Value; if (!string.IsNullOrEmpty(matchValue)) { command = "title"; query = $"{command} {matchValue}"; } } else if ((settings.FunResponsesEnabled || IsAuthorOwner(messageData)) && contentParts.Length > 1 && contentParts[1] == "face") { command = "face"; query = $"{command} {contentParts[0]}"; } } } // Ignore if the command is disabled on this server if (settings.IsCommandDisabled(CommandsConfig.Instance, command) && !IsAuthorOwner(messageData)) { return(responseData); } if (CommandsConfig.Instance.Commands.ContainsKey(command)) { if (!messageData.RateLimitChecked) { // make sure we're not rate limited var commandKey = command + messageData.Server; if (this.Throttler.IsThrottled(commandKey, ThrottleType.Command)) { return(responseData); } this.Throttler.Increment(commandKey, ThrottleType.Command); // if we're now throttled after this increment, return a "rate limited" message if (this.Throttler.IsThrottled(commandKey, ThrottleType.Command)) { responses.Add("rate limited try later"); return(responseData); } // increment the user/guild throttlers as well this.Throttler.Increment(messageData.UserId, ThrottleType.User); this.Throttler.Increment(messageData.Server, ThrottleType.Guild); messageData.RateLimitChecked = true; } var props = new Dictionary <string, string> { { "command", command.ToLowerInvariant() }, { "server", messageData.Server }, { "channel", messageData.Channel } }; this.TrackEvent("commandProcessed", props); // extra processing on ".title" command if ((command == "title" || command == "t") && messageData.Content.EndsWith(command) && urls.ContainsKey(messageData.Channel)) { query += $" {this.urls[messageData.Channel]}"; } using (DogStatsd.StartTimer("commandDuration", tags: new[] { $"shard:{this.Shard}", $"command:{command.ToLowerInvariant()}", $"{this.BotType}" })) { messageData.Content = $"{settings.Prefix}{query}"; responseData = await this.BotApi.IssueRequestAsync(messageData); } } } if (responseData.Responses.Count == 0 && responseData.Embed == null) { string response = null; if (messageData.MentionsBot(this.Config.Name, Convert.ToUInt64(this.UserId))) { var responseValue = PhrasesConfig.Instance.PartialMentionPhrases.FirstOrDefault(kvp => messageData.Content.IContains(kvp.Key)).Value; if (!string.IsNullOrEmpty(responseValue)) { response = PhrasesConfig.Instance.Responses[responseValue].Random(); } } if (response == null && PhrasesConfig.Instance.ExactPhrases.ContainsKey(messageData.Content) && (settings.FunResponsesEnabled && new Random().Next(1, 100) <= settings.FunResponseChance || IsAuthorOwner(messageData))) { response = PhrasesConfig.Instance.Responses[PhrasesConfig.Instance.ExactPhrases[messageData.Content]].Random(); } if (response == null) { response = settings.CustomCommands.FirstOrDefault(c => c.IsExactMatch && c.Command == messageData.Content || !c.IsExactMatch && messageData.Content.IContains(c.Command))?.Response; } if (response != null) { response = response.Replace("%from%", messageData.UserName); string[] resps = response.Split(new char[] { '|' }); responseData.Responses.AddRange(resps); } } return(responseData); }
protected override Task RespondAsync(BotMessageData messageData, string text) { this.ircClients[messageData.Server]?.Command("PRIVMSG", messageData.Channel, text); return(Task.CompletedTask); }
protected abstract Task RespondAsync(BotMessageData messageData, string text);
/// <summary> /// Handles responses for messages. /// TODO: This method is way too huge. /// </summary> private async Task HandleMessageReceivedAsync(SocketMessage socketMessage, string reactionType = null, IUser reactionUser = null) { // Ignore system and our own messages. var message = socketMessage as SocketUserMessage; bool isOutbound = false; // replicate to webhook, if configured this.CallOutgoingWebhookAsync(message).Forget(); if (message == null || (isOutbound = message.Author.Id == this.Client.CurrentUser.Id)) { if (isOutbound) { if (message.Embeds?.Count > 0) { this.Logger.Log(LogType.Outgoing, $"\tSending [embed content] to {message.Channel.Name}"); } else { this.Logger.Log(LogType.Outgoing, $"\tSending to {message.Channel.Name}: {message.Content}"); } } return; } // Ignore other bots if (message.Author.IsBot) { return; } // grab the settings for this server var botGuildUser = (message.Channel as SocketGuildChannel)?.Guild.CurrentUser; var guildUser = message.Author as IGuildUser; var guildId = (guildUser != null && guildUser.IsWebhook) ? null : guildUser?.GuildId; var settings = SettingsConfig.GetSettings(guildId?.ToString()); // if it's a globally blocked server, ignore it unless it's the owner if (message.Author.Id != this.Config.Discord.OwnerId && guildId != null && this.Config.Discord.BlockedServers.Contains(guildId.Value)) { return; } // if the user is blocked based on role, return var botlessRoleId = guildUser?.Guild?.Roles?.FirstOrDefault(r => r.Name?.ToLowerInvariant() == "botless")?.Id; if ((message.Author as IGuildUser)?.RoleIds.Any(r => botlessRoleId != null && r == botlessRoleId.Value) ?? false) { return; } // Bail out with help info if it's a PM if (message.Channel is IDMChannel && (message.Content.Contains("help") || message.Content.Contains("info") || message.Content.Contains("commands"))) { await this.RespondAsync(message, "Info and commands can be found at: https://ub3r-b0t.com"); return; } // check for word censors if (botGuildUser?.GuildPermissions.ManageMessages ?? false) { if (settings.TriggersCensor(message.Content, out string offendingWord)) { offendingWord = offendingWord != null ? $"`{offendingWord}`" : "*FANCY lanuage filters*"; await message.DeleteAsync(); var dmChannel = await message.Author.GetOrCreateDMChannelAsync(); await dmChannel.SendMessageAsync($"hi uh sorry but your most recent message was tripped up by {offendingWord} and thusly was deleted. complain to management, i'm just the enforcer"); return; } } var textChannel = message.Channel as ITextChannel; if (botGuildUser != null && !botGuildUser.GetPermissions(textChannel).SendMessages) { return; } // special case FAQ channel if (message.Channel.Id == this.Config.FaqChannel && message.Content.EndsWith("?") && this.Config.FaqEndpoint != null) { string content = message.Content.Replace("<@85614143951892480>", "ub3r-b0t"); var result = await this.Config.FaqEndpoint.ToString().WithHeader("Ocp-Apim-Subscription-Key", this.Config.FaqKey).PostJsonAsync(new { question = content }); if (result.IsSuccessStatusCode) { var response = await result.Content.ReadAsStringAsync(); var qnaData = JsonConvert.DeserializeObject <QnAMakerData>(response); var score = Math.Floor(qnaData.Score); var answer = WebUtility.HtmlDecode(qnaData.Answer); await message.Channel.SendMessageAsync($"{answer} ({score}% match)"); } else { await message.Channel.SendMessageAsync("An error occurred while fetching data"); } return; } string messageContent = message.Content; // OCR for fun if requested (patrons only) // TODO: need to drive this via config // TODO: Need to generalize even further due to more reaction types // TODO: oh my god stop writing TODOs and just make the code less awful if (!string.IsNullOrEmpty(reactionType)) { string newMessageContent = string.Empty; if (reactionType == "💬" || reactionType == "🗨️") { newMessageContent = $".quote add \"{messageContent}\" - userid:{message.Author.Id} {message.Author.Username}"; await message.AddReactionAsync(new Emoji("💬")); } else if (string.IsNullOrEmpty(message.Content) && message.Attachments?.FirstOrDefault()?.Url is string attachmentUrl) { if (reactionType == "👁") { var result = await this.Config.OcrEndpoint.ToString() .WithHeader("Ocp-Apim-Subscription-Key", this.Config.VisionKey) .PostJsonAsync(new { url = attachmentUrl }); if (result.IsSuccessStatusCode) { var response = await result.Content.ReadAsStringAsync(); var ocrData = JsonConvert.DeserializeObject <OcrData>(response); if (!string.IsNullOrEmpty(ocrData.GetText())) { newMessageContent = ocrData.GetText(); } } } else if (reactionType == "🖼") { var analyzeResult = await this.Config.AnalyzeEndpoint.ToString() .WithHeader("Ocp-Apim-Subscription-Key", this.Config.VisionKey) .PostJsonAsync(new { url = attachmentUrl }); if (analyzeResult.IsSuccessStatusCode) { var response = await analyzeResult.Content.ReadAsStringAsync(); var analyzeData = JsonConvert.DeserializeObject <AnalyzeImageData>(response); if (analyzeData.Description.Tags.Contains("ball")) { newMessageContent = ".8ball foo"; } else if (analyzeData.Description.Tags.Contains("outdoor")) { newMessageContent = ".fw"; } } } } messageContent = newMessageContent ?? messageContent; } // If it's a command, match that before anything else. string query = string.Empty; bool hasBotMention = message.MentionedUsers.Any(u => u.Id == this.Client.CurrentUser.Id); int argPos = 0; if (message.HasMentionPrefix(this.Client.CurrentUser, ref argPos)) { query = messageContent.Substring(argPos); } else if (messageContent.StartsWith(settings.Prefix)) { query = messageContent.Substring(settings.Prefix.Length); } var messageData = BotMessageData.Create(message, query, settings); messageData.Content = messageContent; await this.PreProcessMessage(messageData, settings); string command = messageData.Command; if (message.Attachments.FirstOrDefault() is Attachment attachment) { imageUrls[messageData.Channel] = attachment; } // if it's a blocked command, bail if (settings.IsCommandDisabled(CommandsConfig.Instance, command) && !IsAuthorOwner(message)) { return; } // Check discord specific commands prior to general ones. if (discordCommands.Commands.ContainsKey(command)) { var response = await discordCommands.Commands[command].Invoke(message).ConfigureAwait(false); if (response != null) { if (response.Attachment != null) { var sentMessage = await message.Channel.SendFileAsync(response.Attachment.Stream, response.Attachment.Name, response.Text); this.botResponsesCache.Add(message.Id, sentMessage); } else if (!string.IsNullOrEmpty(response.Text) || response.Embed != null) { var sentMessage = await this.RespondAsync(message, response.Text, response.Embed); this.botResponsesCache.Add(message.Id, sentMessage); } } } else { IDisposable typingState = null; if (CommandsConfig.Instance.Commands.ContainsKey(command)) { // possible bug with typing state Console.WriteLine($"typing triggered by {command}"); typingState = message.Channel.EnterTypingState(); } if (messageData.Command == "quote" && reactionUser != null) { messageData.UserName = reactionUser.Username; } try { BotResponseData responseData = await this.ProcessMessageAsync(messageData, settings); if (responseData.Embed != null) { var sentMessage = await this.RespondAsync(message, string.Empty, responseData.Embed.CreateEmbedBuilder()); this.botResponsesCache.Add(message.Id, sentMessage); } else { foreach (string response in responseData.Responses) { if (!string.IsNullOrEmpty(response)) { // if sending a multi part message, skip the edit optimization. var sentMessage = await this.RespondAsync(message, response, embedResponse : null, bypassEdit : responseData.Responses.Count > 1); this.botResponsesCache.Add(message.Id, sentMessage); } } } } finally { typingState?.Dispose(); } } }
// Whether or not the message author is the bot owner (will only return true in Discord scenarios). private bool IsAuthorOwner(BotMessageData messageData) { return(!string.IsNullOrEmpty(messageData.UserId) && messageData.UserId == this.Config.Discord?.OwnerId.ToString()); }
public static bool TryParseReminder(Match timerMatch, BotMessageData messageData, out string query) { query = string.Empty; string toMatch = timerMatch.Groups["target"].ToString().Trim(); string to = toMatch == "me" ? messageData.UserName : toMatch; string req = to == messageData.UserName ? string.Empty : messageData.UserName; string durationStr = string.Empty; long duration = 0; GroupCollection matchGroups = timerMatch.Groups; string reason = matchGroups["reason"].ToString(); if (matchGroups["years"].Success) { string yearString = timerMatch.Groups["years"].ToString(); if (int.TryParse(yearString.Remove(yearString.Length - 5, 5), out int yearValue)) { duration += yearValue * ONEYEAR; durationStr = $"{yearValue}y"; } } if (matchGroups["weeks"].Success) { string weekString = matchGroups["weeks"].ToString(); if (int.TryParse(weekString.Remove(weekString.Length - 5, 5), out int weekValue)) { duration += weekValue * ONEWEEK; durationStr += $"{weekValue}w"; } } if (matchGroups["days"].Success) { string dayString = matchGroups["days"].ToString(); if (int.TryParse(dayString.Remove(dayString.Length - 4, 4), out int dayValue)) { duration += dayValue * ONEDAY; durationStr += $"{dayValue}d"; } } if (matchGroups["hours"].Success) { string hourString = matchGroups["hours"].ToString(); if (int.TryParse(hourString.Remove(hourString.Length - 5, 5), out int hourValue)) { duration += hourValue * ONEHOUR; durationStr += $"{hourValue}h"; } } if (matchGroups["minutes"].Success) { string minuteString = matchGroups["minutes"].ToString(); if (int.TryParse(minuteString.Remove(minuteString.Length - 7, 7), out int minuteValue)) { duration += minuteValue * 60; durationStr += $"{minuteValue}m"; } } if (matchGroups["seconds"].Success) { string secongString = matchGroups["seconds"].ToString(); if (int.TryParse(secongString.Remove(secongString.Length - 8, 8), out int secondValue)) { duration += secondValue; durationStr += $"{secondValue}s"; } } if (duration < 10 || duration > TOOLONG) { return(false); } query = $"timer for:\"{to}\" {durationStr} {reason}"; return(true); }