protected virtual void HandleQuoteUserCommand(CommandMatch cmd, IUserMessageEventArgs msg) { Action <string> postReply; string location; QuoteRating rating; bool addMyRating; GetReplyActionAndLocationForMessage(msg, out postReply, out location); if (!FlagsToQuoteFilter(cmd, out rating, out addMyRating)) { // invalid flags return; } var nick = (string)cmd.Arguments[0]; string lowercaseNick = nick.ToLowerInvariant(); using (var ctx = GetNewContext()) { IQueryable <Quote> quotesWithVotes = ctx.Quotes .Include(q => q.Votes) .Where(q => q.Author.ToLower() == lowercaseNick); PostRandomQuote(msg.SenderNickname, location, quotesWithVotes, rating, addMyRating, postReply); } }
protected virtual void HandleNextQuoteCommand(CommandMatch cmd, IUserMessageEventArgs msg) { Action <string> postReply; string location; QuoteRating rating; bool addMyRating; GetReplyActionAndLocationForMessage(msg, out postReply, out location); if (!FlagsToQuoteFilter(cmd, out rating, out addMyRating)) { // invalid flags return; } using (var ctx = GetNewContext()) { Quote quote = null; switch (rating) { case QuoteRating.Any: if (ShuffledAnyQuotes == null) { ShuffledAnyQuotes = GetFilteredQuotes(ctx.Quotes.Include(q => q.Votes), QuoteRating.Any) .ToShuffledList(); ShuffledAnyQuotesIndex = 0; } quote = ShuffledAnyQuotes[ShuffledAnyQuotesIndex++]; ShuffledAnyQuotesIndex %= ShuffledAnyQuotes.Count; break; case QuoteRating.High: if (ShuffledGoodQuotes == null) { ShuffledGoodQuotes = GetFilteredQuotes(ctx.Quotes.Include(q => q.Votes), QuoteRating.High) .ToShuffledList(); ShuffledGoodQuotesIndex = 0; } quote = ShuffledGoodQuotes[ShuffledGoodQuotesIndex++]; ShuffledGoodQuotesIndex %= ShuffledGoodQuotes.Count; break; case QuoteRating.Low: if (ShuffledBadQuotes == null) { ShuffledBadQuotes = GetFilteredQuotes(ctx.Quotes.Include(q => q.Votes), QuoteRating.Low) .ToShuffledList(); ShuffledBadQuotesIndex = 0; } quote = ShuffledBadQuotes[ShuffledBadQuotesIndex++]; ShuffledBadQuotesIndex %= ShuffledBadQuotes.Count; break; default: Debug.Fail("unexpected quote rating"); break; } PostQuote(quote, msg.SenderNickname, location, addMyRating, postReply); } }
protected virtual void ActuallyHandleChannelOrQueryAka([NotNull] CommandMatch cmd, [NotNull] IUserMessageEventArgs e, [NotNull] Action <string> respond) { string nickToSearch = (string)cmd.Arguments[0]; if (!NickToMostRecentHost.ContainsKey(nickToSearch)) { respond($"I don\u2019t remember {nickToSearch}."); return; } var identifier = NickToMostRecentHost[nickToSearch]; var identifierParts = identifier.Parts; ImmutableList <HashSet <string> > matches; int matchDepth = HostToNicks.GetBestMatches(identifierParts, out matches); if (matchDepth == -1) { respond($"I don\u2019t remember any other nickname from {identifier} than {nickToSearch}."); return; } ImmutableList <HashSet <string> > fuzzyMatches = null; int fuzzyMatchDepth = -1; if (matchDepth == identifierParts.Count) { // do a fuzzy match too // replace the last item in identifierParts with the empty string var fuzzyIdentifierParts = identifierParts.SetItem(identifierParts.Count - 1, ""); fuzzyMatchDepth = HostToNicks.GetBestMatches(fuzzyIdentifierParts, out fuzzyMatches); } var otherNicks = new SortedSet <string>(matches.SelectMany(x => x)); var fuzzyOtherNicks = (fuzzyMatches == null) ? null : new SortedSet <string>(fuzzyMatches.SelectMany(x => x)); fuzzyOtherNicks?.ExceptWith(otherNicks); if (matchDepth == identifierParts.Count) { if (fuzzyOtherNicks != null && fuzzyOtherNicks.Count > 0) { respond($"{identifier}: {otherNicks.StringJoin(", ")}; fuzzy match ({fuzzyMatchDepth}/{identifierParts.Count}) also: {fuzzyOtherNicks.StringJoin(", ")}"); } else { respond($"{identifier}: {otherNicks.StringJoin(", ")}"); } } else { respond($"{identifier} fuzzy match ({matchDepth}/{identifierParts.Count}): {otherNicks.StringJoin(", ")}"); } }
protected virtual void HandleDownquoteCommand(CommandMatch cmd, IUserMessageEventArgs msg) { string normalizedSender = ConnectionManager.RegisteredNameForNick(msg.SenderNickname) ?? msg.SenderNickname; Action <string> postReply; string location; GetReplyActionAndLocationForMessage(msg, out postReply, out location); if (!LastQuoteIDs.ContainsKey(location)) { postReply("You'll have to get a quote first..."); } UpsertVote(normalizedSender, LastQuoteIDs[location], -1); }
protected void GetReplyActionAndLocationForMessage(IUserMessageEventArgs msg, out Action <string> replyAction, out string location) { var channelMsg = msg as IChannelMessageEventArgs; if (channelMsg != null) { replyAction = (body) => ConnectionManager.SendChannelMessage(channelMsg.Channel, body); location = channelMsg.Channel; } else { Debug.Assert(msg is IPrivateMessageEventArgs); replyAction = (body) => ConnectionManager.SendQueryMessage(msg.SenderNickname, body); location = msg.SenderNickname; } }
protected virtual void HandleSmileysCommand(CommandMatch cmd, IUserMessageEventArgs e) { foreach (var smiley in Config.Smileys) { var smileyLine = new StringBuilder(smiley); if (smiley.Length > 1) { // escape smiley by adding ZWNBSP in between first and second character smileyLine.Append(" = "); smileyLine.Append(smiley[0]); smileyLine.Append('\uFEFF'); smileyLine.Append(smiley, 1, smiley.Length - 1); } ConnectionManager.SendQueryMessage(e.SenderNickname, smileyLine.ToString()); } }
protected void Output(Action <string> respond, IUserMessageEventArgs message, string command, ResponseManager responseManager, string targetNick) { var channelMessage = message as IChannelMessageEventArgs; string targetBody = responseManager.NextResponse(RNG); if (targetBody == null) { return; } Logger.LogDebug("{Sender} triggered {Command} in {Location}", message.SenderNickname, command, channelMessage?.Channel ?? "private message"); var response = targetBody.Replace("{{NICKNAME}}", targetNick); foreach (var line in response.Split('\n').Where(l => l.Length > 0)) { respond(line); } }
protected string VerifyIdentity(IUserMessageEventArgs message) { var username = message.SenderNickname; if (Config.UseIrcServices) { username = ConnectionManager.RegisteredNameForNick(username); if (username == null) { Logger.LogInformation("{Nickname} is not logged in; ignoring", message.SenderNickname); return(null); } } if (!Config.Puppeteers.Contains(username)) { Logger.LogInformation("{Username} is not a puppeteer; ignoring", username); return(null); } return(username); }
protected virtual bool ApplyGlobalCallbacks(CommandMatch match, IUserMessageEventArgs msg) { bool callbackSaidNo = false; foreach (GlobalCommandCallback callback in GlobalCommandCallbacks) { try { if (!callback.Invoke(match, msg)) { callbackSaidNo = true; } } catch (Exception exc) { Logger.LogError( "error when global callback {GlobalCommandCallback} was processing command {Command}: {Exception}", callback, match.CommandName, exc ); } } return(!callbackSaidNo); }
protected virtual void HandleQuoteCommand(CommandMatch cmd, IUserMessageEventArgs msg) { Action <string> postReply; string location; QuoteRating rating; bool addMyRating; GetReplyActionAndLocationForMessage(msg, out postReply, out location); if (!FlagsToQuoteFilter(cmd, out rating, out addMyRating)) { // invalid flags return; } string subject = ((string)cmd.Arguments[0]); if (subject.StartsWith(" ")) { subject = subject.Substring(1); } if (subject.Trim().Length == 0) { subject = null; } var lowercaseSubject = subject?.ToLowerInvariant(); using (var ctx = GetNewContext()) { IQueryable <Quote> quotes = (lowercaseSubject != null) ? ctx.Quotes.Where(q => q.Body.ToLower().Contains(lowercaseSubject)) : ctx.Quotes; IQueryable <Quote> quotesWithVotes = quotes.Include(q => q.Votes); PostRandomQuote(msg.SenderNickname, location, quotesWithVotes, rating, addMyRating, postReply); } }
protected virtual bool LimiterCallback(CommandMatch commandMatch, IUserMessageEventArgs msg) { // is it a fun command? if (!commandMatch.Command.Tags.Any(tag => Config.CommandTags.Contains(tag))) { // no => don't protest return(true); } // how soon is then? DateTimeOffset cutoff = DateTimeOffset.Now.AddMinutes(-Config.TimeSpanMinutes); // find the keys string channelKey; if (Config.PerChannel) { var chanMsg = msg as IChannelMessageEventArgs; if (chanMsg != null) { if (!Config.Channels.Contains(chanMsg.Channel)) { // we're not policing this channel return(true); } channelKey = chanMsg.Channel; } else { // private message if (!Config.CountPrivateMessages) { // we don't bother with those return(true); } channelKey = PrivateMessageChannelKey; } } else { if (msg is IPrivateMessageEventArgs && !Config.CountPrivateMessages) { // we don't bother with private messages return(true); } channelKey = GlobalChannelKey; } string userKey = Config.PerUser ? (Connection.RegisteredNameForNick(msg.SenderNickname) ?? msg.SenderNickname) : GlobalUserKey; // obtain the timestamps List <DateTimeOffset> timestamps = ObtainTimeStampsForChannelAndUserKeys(channelKey, userKey); // clear out the old ones timestamps.RemoveAll(ts => ts < cutoff); if (timestamps.Count > Config.MaxCountPerTime) { // too many if (Config.CountLimitedAttempts) { // count this one even though it failed >;-) timestamps.Add(DateTimeOffset.Now); } return(false); } // count this one but allow it timestamps.Add(DateTimeOffset.Now); return(true); }
protected void ActuallyHandleMessage(Action <string> respond, IUserMessageEventArgs args, MessageFlags flags) { if (flags.HasFlag(MessageFlags.UserBanned)) { return; } if (args.SenderNickname == ConnectionManager.MyNickname) { return; } var lowerBody = args.Message.ToLowerInvariant(); ResponseManager commandResponseManager; if (CommandsResponses.TryGetValue(lowerBody, out commandResponseManager)) { Output(respond, args, lowerBody, commandResponseManager, args.SenderNickname); return; } var channelNicks = new HashSet <string>(); var channelMessage = args as IChannelMessageEventArgs; if (channelMessage != null) { var nicknamesInChannel = ConnectionManager.NicknamesInChannel(channelMessage.Channel); if (nicknamesInChannel != null) { channelNicks.UnionWith(nicknamesInChannel); } } foreach (var nickCommandResponse in NicknamableCommandsResponses) { if (!lowerBody.StartsWith(nickCommandResponse.Key)) { // not this command continue; } if (lowerBody.TrimEnd() == nickCommandResponse.Key) { // command on its own; trigger for self Output(respond, args, nickCommandResponse.Key, nickCommandResponse.Value, args.SenderNickname); return; } // trigger for someone else? string targetedNickCased = args.Message.Substring(nickCommandResponse.Key.Length).Trim(); string targetedNick = lowerBody.Substring(nickCommandResponse.Key.Length).Trim(); if (targetedNickCased == "-r" || targetedNickCased == "--random") { // random pick, biased towards active users if (channelNicks.Count == 0) { // emergency trick: target the sender Output(respond, args, nickCommandResponse.Key, nickCommandResponse.Value, args.SenderNickname); return; } Debug.Assert(channelMessage != null); // each message in the last n messages leads to an entry in the pick list // this increases the chances of being picked var pickList = new List <string>(); LinkedList <string> lastMessageAuthors; if (ChannelsToLastMessageAuthors.TryGetValue(channelMessage.Channel, out lastMessageAuthors)) { foreach (string author in lastMessageAuthors) { if (channelNicks.Contains(author)) { pickList.Add(author); } } } pickList.AddRange(channelNicks); int index = RNG.Next(channelNicks.Count); string target = pickList[index]; Output(respond, args, nickCommandResponse.Key, nickCommandResponse.Value, target); return; } else if (targetedNickCased == "-R" || targetedNickCased == "--really-random") { // random pick of any user in the channel if (channelNicks.Count == 0) { // emergency trick: target the sender Output(respond, args, nickCommandResponse.Key, nickCommandResponse.Value, args.SenderNickname); return; } Debug.Assert(channelMessage != null); int index = RNG.Next(channelNicks.Count); string target = channelNicks.ElementAt(index); Output(respond, args, nickCommandResponse.Key, nickCommandResponse.Value, target); return; } foreach (string channelNick in channelNicks) { if (channelNick.ToLowerInvariant() == targetedNick) { // nickname directly from user list Output(respond, args, nickCommandResponse.Key, nickCommandResponse.Value, channelNick); return; } } // registered nickname? var registeredTargetNick = ConnectionManager.RegisteredNameForNick(targetedNick); if (registeredTargetNick == null) { // nope, targeted nick is not registered return; } foreach (string channelNick in channelNicks) { var registeredChannelNick = ConnectionManager.RegisteredNameForNick(channelNick); if (registeredChannelNick == null) { // this channel nickname is not registered continue; } if (registeredTargetNick == registeredChannelNick) { // registered nicknames match Output(respond, args, nickCommandResponse.Key, nickCommandResponse.Value, channelNick); return; } } } }