private async ValueTask <bool> TryHandleCommand(MessageCreateEventArgs evt, MessageContext ctx) { var content = evt.Message.Content; if (content == null) { return(false); } var argPos = -1; // Check if message starts with the command prefix if (content.StartsWith("pk;", StringComparison.InvariantCultureIgnoreCase)) { argPos = 3; } else if (content.StartsWith("pk!", StringComparison.InvariantCultureIgnoreCase)) { argPos = 3; } else if (StringUtils.HasMentionPrefix(content, ref argPos, out var id)) // Set argPos to the proper value { if (id != _client.CurrentUser.Id) // But undo it if it's someone else's ping { argPos = -1; } } // If we didn't find a prefix, give up handling commands if (argPos == -1) { return(false); } // Trim leading whitespace from command without actually modifying the wring // This just moves the argPos pointer by however much whitespace is at the start of the post-argPos string var trimStartLengthDiff = content.Substring(argPos).Length - content.Substring(argPos).TrimStart().Length; argPos += trimStartLengthDiff; try { var system = ctx?.SystemId != null ? await _db.Execute(c => c.QuerySystem(ctx.SystemId.Value)) : null; await _tree.ExecuteCommand(new Context(_services, evt.Client, evt.Message, argPos, system, ctx)); } catch (PKError) { // Only permission errors will ever bubble this far and be caught here instead of Context.Execute // so we just catch and ignore these. TODO: this may need to change. } return(true); }
private ProxyMatch GetProxyTagMatch(string message, PKSystem system, IEnumerable <PKMember> potentialMembers) { // If the message starts with a @mention, and then proceeds to have proxy tags, // extract the mention and place it inside the inner message // eg. @Ske [text] => [@Ske text] int matchStartPosition = 0; string leadingMention = null; if (StringUtils.HasMentionPrefix(message, ref matchStartPosition, out _)) { leadingMention = message.Substring(0, matchStartPosition); message = message.Substring(matchStartPosition); } // Flatten and sort by specificity (ProxyString length desc = prefix+suffix length desc = inner message asc = more specific proxy first!) var ordered = potentialMembers.SelectMany(m => m.ProxyTags.Select(tag => (tag, m))).OrderByDescending(p => p.Item1.ProxyString.Length); foreach (var(tag, match) in ordered) { if (tag.Prefix == null && tag.Suffix == null) { continue; } var prefix = tag.Prefix ?? ""; var suffix = tag.Suffix ?? ""; var isMatch = message.Length >= prefix.Length + suffix.Length && message.StartsWith(prefix) && message.EndsWith(suffix); // Special case for image-only proxies and proxy tags with spaces if (!isMatch && message.Trim() == prefix.TrimEnd() + suffix.TrimStart()) { isMatch = true; message = prefix + suffix; // To get around substring errors } if (isMatch) { var inner = message.Substring(prefix.Length, message.Length - prefix.Length - suffix.Length); if (leadingMention != null) { inner = $"{leadingMention} {inner}"; } return(new ProxyMatch { Member = match, System = system, InnerText = inner, ProxyTags = tag }); } } return(null); }
private string?ExtractLeadingMention(ref string input) { var mentionPos = 0; if (!StringUtils.HasMentionPrefix(input, ref mentionPos, out _)) { return(null); } var leadingMention = input.Substring(0, mentionPos); input = input.Substring(mentionPos); return(leadingMention); }
public async Task HandleMessage(SocketMessage arg) { var shard = _client.GetShardFor((arg.Channel as IGuildChannel)?.Guild); if (shard.ConnectionState != ConnectionState.Connected) return; // Discard messages while the bot "catches up" to avoid unnecessary CPU pressure causing timeouts RegisterMessageMetrics(arg); // Ignore system messages (member joined, message pinned, etc) var msg = arg as SocketUserMessage; if (msg == null) return; // Fetch information about the guild early, as we need it for the logger cleanup GuildConfig cachedGuild = default; // todo: is this default correct? if (msg.Channel is ITextChannel textChannel) cachedGuild = await _cache.GetGuildDataCached(textChannel.GuildId); // Pass guild bot/WH messages onto the logger cleanup service, but otherwise ignore if ((msg.Author.IsBot || msg.Author.IsWebhook) && msg.Channel is ITextChannel) { await _loggerClean.HandleLoggerBotCleanup(arg, cachedGuild); return; } // Add message info as Sentry breadcrumb _msg = msg; _sentryScope.AddBreadcrumb(msg.Content, "event.message", data: new Dictionary<string, string> { {"user", msg.Author.Id.ToString()}, {"channel", msg.Channel.Id.ToString()}, {"guild", ((msg.Channel as IGuildChannel)?.GuildId ?? 0).ToString()}, {"message", msg.Id.ToString()}, }); _sentryScope.SetTag("shard", shard.ShardId.ToString()); // Add to last message cache _lastMessageCache.AddMessage(arg.Channel.Id, arg.Id); // We fetch information about the sending account from the cache var cachedAccount = await _cache.GetAccountDataCached(msg.Author.Id); // this ^ may be null, do remember that down the line int argPos = -1; // Check if message starts with the command prefix if (msg.Content.StartsWith("pk;", StringComparison.InvariantCultureIgnoreCase)) argPos = 3; else if (msg.Content.StartsWith("pk!", StringComparison.InvariantCultureIgnoreCase)) argPos = 3; else if (msg.Content != null && StringUtils.HasMentionPrefix(msg.Content, ref argPos, out var id)) // Set argPos to the proper value if (id != _client.CurrentUser.Id) // But undo it if it's someone else's ping argPos = -1; // If it does, try executing a command if (argPos > -1) { _logger.Verbose("Parsing command {Command} from message {Channel}-{Message}", msg.Content, msg.Channel.Id, msg.Id); // Essentially move the argPos pointer by however much whitespace is at the start of the post-argPos string var trimStartLengthDiff = msg.Content.Substring(argPos).Length - msg.Content.Substring(argPos).TrimStart().Length; argPos += trimStartLengthDiff; await _tree.ExecuteCommand(new Context(_services, msg, argPos, cachedAccount?.System)); } else if (cachedAccount != null) { // If not, try proxying anyway // but only if the account data we got before is present // no data = no account = no system = no proxy! try { await _proxy.HandleMessageAsync(cachedGuild, cachedAccount, msg, doAutoProxy: true); } catch (PKError e) { await arg.Channel.SendMessageAsync($"{Emojis.Error} {e.Message}"); } } }