/// <summary> /// Add a new expected reply to the stack. Should be called internally only - New messages should be sent via TelegramAPI.GetExpectedReply /// </summary> /// <param name="e"></param> /// <param name="trySendImmediately">Try and send the message immediately, assuming nothing is outstanding. Will jump the queue, but not override any existing messages</param> /// <returns>An integer specifying the message id. -1 indicates it is queueed, long.MinValue indicates a failure</returns> public long newExpectedReply(ExpectedReply e, bool trySendImmediately) { //flag the user as present in the chat if (e.isPrivateMessage) { markPresence(e.userID, e.chatID, e.userName); } //check if we can send it? Get the messageID back long messageID = -1; //is this a message to a group? if (!e.isPrivateMessage) { //send, dont queue. //TODO - doesnt handle group PMs messageID = e.sendMessage(); } //this is a PM. Does the user have anything in the queue? else if (!userHasOutstandingMessages(e.userID)) { //send the message. messageID = e.sendMessage(); //queue if it was a question if (e.expectsReply) { expectedReplies.Add(e); } } //If they have messages in the queue, and we want to jump ahead, has one already been asked, or are we open? else if (trySendImmediately && !userHasOutstandingQuestions(e.userID)) { Roboto.log.log("Message jumping queue due to immediatemode", logging.loglevel.verbose); //send the message, grab the ID. messageID = e.sendMessage(); //did it work/ if (messageID == long.MinValue) { Roboto.log.log("Tried to send message using immediateMode, but it failed.", logging.loglevel.warn); return(messageID); } //queue if it was a question else if (e.expectsReply) { expectedReplies.Add(e); } } else { //chuck it on the stack if its going to be queued expectedReplies.Add(e); } //make sure we are in a safe state. This will make sure if we sent a message-only, that the next message(s) are processed. expectedReplyBackgroundProcessing(); return(messageID); }
/// <summary> /// Do a healthcheck, and archive any old presence data /// Called from mod_standard's backgorund loop. /// </summary> public void expectedReplyBackgroundProcessing() { RecentChatMembers.RemoveAll(x => x.chatID == x.userID); //TODO <- this should be a startup housekeeping check only. //TODO - are we calling this whole thing every loop at the moment? Move to mod_standard.background? //Remove any stale presence info RecentChatMembers.RemoveAll(x => x.lastSeen < DateTime.Now.Subtract(new TimeSpan(chatPresenceExpiresAfterHours, 0, 0))); Roboto.log.log("There are " + expectedReplies.Count() + " expected replies on the stack", logging.loglevel.verbose); //main processing try { //Remove any ERs that are for dead chats List <ExpectedReply> deadERs = new List <ExpectedReply>(); foreach (ExpectedReply er in expectedReplies) { chat c = getChat(er.chatID); if (c == null) { deadERs.Add(er); } } foreach (ExpectedReply er in deadERs) { expectedReplies.Remove(er); } Roboto.log.log("Removed " + deadERs.Count() + " dead expected replies, now " + expectedReplies.Count() + " remain", deadERs.Count() == 0 ? logging.loglevel.verbose : logging.loglevel.warn); //remove any expired ones int i = expectedReplies.RemoveAll(x => x.timeLogged < DateTime.Now.Subtract(TimeSpan.FromDays(Roboto.Settings.killInactiveChatsAfterXDays))); Roboto.log.log("Removed " + i + " expected replies, now " + expectedReplies.Count() + " remain", i == 0 ? logging.loglevel.verbose : logging.loglevel.warn); //Build up a list of user IDs List <long> userIDs = expectedReplies.Select(e => e.userID).Distinct().ToList <long>(); //remove any invalid messages List <ExpectedReply> messagesToRemove = expectedReplies.Where(e => e.outboundMessageID > 0 && e.expectsReply == false).ToList(); if (messagesToRemove.Count > 0) { Roboto.log.log("Removing " + messagesToRemove.Count() + " messages from queue as they are sent and dont require a reply", logging.loglevel.warn); } foreach (ExpectedReply e in messagesToRemove) { expectedReplies.Remove(e); } foreach (long userID in userIDs) { bool retry = true; while (retry) { //for each user, check if a message has been sent, and track the oldest message ExpectedReply oldest = null; List <ExpectedReply> userReplies = expectedReplies.Where(e => e.userID == userID).ToList(); //try find a message to send. Drop out if we already have a sent message on the stack (waiting for a reply) bool sent = false; foreach (ExpectedReply e in userReplies) { if (e.isSent()) { sent = true; } //message is waiting else { if (oldest == null || e.timeLogged < oldest.timeLogged) { oldest = e; } } } //send the message if neccessary if (!sent && oldest != null) { oldest.sendMessage(); if (!oldest.expectsReply) { expectedReplies.Remove(oldest); } //make sure we are in a safe state. This will make sure if we sent a message-only, that the next message(s) are processed. } //what do we do next? if (sent == true) { retry = false; } // drop out if we have a message awaiting an answer else if (oldest == null) { retry = false; } // drop out if we have no messages to send else if (oldest.expectsReply) { retry = false; } //drop out if we sent a message that expects a reply } } } catch (Exception e) { Roboto.log.log("Error during expected reply housekeeping " + e.ToString(), logging.loglevel.critical); } }