/// <summary> /// This is called every so often to check if users Names or Email address has changed, or User has been Deleted /// </summary> /// <param name="campfireInfo"></param> private static void Work_ScanForUserChanges(CampfireState campfireInfo, ICampfireAPI api) { // iterate of all known users... and see if name or email address has changed // Must do this in a thread safe fashion by first getting all the ids. List <int> userIds = campfireInfo.UserIds(); foreach (int uid in userIds) { User user = api.GetUser(uid); if (user != null && !string.IsNullOrEmpty(user.Name) && !string.IsNullOrEmpty(user.Email)) { campfireInfo.UpdateUser(user.Id, user.Name, user.Email); } else { campfireInfo.DeleteUser(uid); } } }
/// <summary> /// This is called every so often to update the known list of rooms /// </summary> /// <param name="campfireInfo"></param> private static void Work_ScanForAddOrRemoveRooms(CampfireState campfireInfo, ICampfireAPI api) { List <int> roomIds = campfireInfo.RoomIds(); List <Room> rooms = api.Rooms(); foreach (Room r in rooms) { // since r.id exists, remove it from 'roomIds' roomIds.Remove(r.Id); } List <int> roomsToDelete = roomIds; foreach (int roomToDeleteId in roomsToDelete) { campfireInfo.DeleteRoom(roomToDeleteId); } campfireInfo.AddRooms(rooms); }
/// <summary> /// This is called every so often to check if users Names or Email address has changed, or User has been Deleted /// </summary> /// <param name="campfireInfo"></param> private static void Work_ScanForUserChanges(CampfireState campfireInfo, ICampfireAPI api) { // iterate of all known users... and see if name or email address has changed // Must do this in a thread safe fashion by first getting all the ids. List<int> userIds = campfireInfo.UserIds(); foreach (int uid in userIds) { User user = api.GetUser(uid); if (user != null && !string.IsNullOrEmpty(user.Name) && !string.IsNullOrEmpty(user.Email)) { campfireInfo.UpdateUser(user.Id, user.Name, user.Email); } else { campfireInfo.DeleteUser(uid); } } }
/// <summary> /// This is called every so often to update the known list of rooms /// </summary> /// <param name="campfireInfo"></param> private static void Work_ScanForAddOrRemoveRooms(CampfireState campfireInfo, ICampfireAPI api) { List<int> roomIds = campfireInfo.RoomIds(); List<Room> rooms = api.Rooms(); foreach (Room r in rooms) { // since r.id exists, remove it from 'roomIds' roomIds.Remove(r.Id); } List<int> roomsToDelete = roomIds; foreach (int roomToDeleteId in roomsToDelete) { campfireInfo.DeleteRoom(roomToDeleteId); } campfireInfo.AddRooms(rooms); }
private static void ProcessEnterMessage(CampfireState campfireInfo, Message msg, ICampfireAPI api) { // remove any pending notifications for this user in the room in which this Enter message appeared campfireInfo.RemovePendingNotification(msg.UserId, msg.RoomId, msg.PostedAt, true); }
private static void ProcessTextMessage(CampfireState campfireInfo, Message msg, ICampfireAPI api) { // The person that posted this message... If they have a pending notification in the room... then cancel it... they've spoken campfireInfo.RemovePendingNotification(msg.UserId, msg.RoomId, msg.PostedAt, true); IList<CampfireState.UserInfo> allUsers = campfireInfo.Users; IList<CampfireState.UserReference> lazyNotificationUsers; IList<CampfireState.UserReference> immediateNotificationUsers; IList<CampfireState.UserReference> smokeSignalReferences; Utils.FindUserReferences(msg.Body, allUsers, out lazyNotificationUsers, out immediateNotificationUsers, out smokeSignalReferences); CampfireState.UserInfo source = allUsers.FirstOrDefault(u => u.Id == msg.UserId); CampfireState.RoomInfo room = CampfireState.Instance.Rooms.FirstOrDefault(r => r.Id == msg.RoomId); // special smoke signal commands only make sense if a legitimate user issued them if (source != null) { foreach (CampfireState.UserReference ri in smokeSignalReferences) { ri.SourceUser = source; ri.Room = room; ProcessSmokeSignalCommands(source.Id, ri); } } foreach (CampfireState.UserReference ri in immediateNotificationUsers) { ri.SourceUser = source; ri.Room = room; campfireInfo.AddPendingNotification(ri, msg.PostedAt.AddSeconds(0)); } foreach (CampfireState.UserReference ri in lazyNotificationUsers) { ri.SourceUser = source; ri.Room = room; int delay = ri.TargetUser.DelayInMinutes > 0 ? ri.TargetUser.DelayInMinutes : SmokeSignalConfig.Instance.DelayBeforeSmokeSignalInMinutes; campfireInfo.AddPendingNotification(ri, msg.PostedAt.AddSeconds(delay * 60)); } }
private static void CheckForUnknownUserOrRoom(CampfireState campfireInfo, Message msg, ICampfireAPI api) { // If this userId isn't already known... if (!campfireInfo.IsUserKnown(msg.UserId)) { // fetch all the user Info and then add User newUser = api.GetUser(msg.UserId); if ((newUser != null) && (newUser.Type == User.UserType.Member) && !string.IsNullOrEmpty(newUser.Name) && !string.IsNullOrEmpty(newUser.Email)) { Utils.TraceVerboseMessage(string.Format("Found a new User: {0}, Id: {1}", newUser.Name, newUser.Id)); campfireInfo.AddUser(newUser.Id, newUser.Name, newUser.Email, string.Empty); } } // If this roomId isn't already known... if (!campfireInfo.IsRoomKnown(msg.RoomId)) { // fetch all the user Info and then add Room newRoom = api.GetRoom(msg.RoomId); if (newRoom != null) { Utils.TraceVerboseMessage(string.Format("Found a new Room: {0}, Id: {1}", newRoom.Name, newRoom.Id)); campfireInfo.AddRoom(newRoom.Id, newRoom.Name, 0); } } }
private static void ProcessMessages_ProcessQueuedMessages(CampfireState campfireInfo, ICampfireAPI api) { /* For each message that comes from any room: Remove any entry in “PendingNotification” collection where PendingNotification.user is message.author (and room's match) (e.g he’s already participating after being called out so no need to notify) If there’s a User Reference match Enter (user, time, room) into a PendingNotification collection (unless user/room already exists in collection) For each entry in PendingNotification If entry.timestamp is < Now – entry.user.ConfiguredDelay then Send that user a notification that he’s been called out in entry.room */ Message msg; while ((msg = campfireInfo.PopMessage()) != null) { CheckForUnknownUserOrRoom(campfireInfo, msg, api); if (msg.Type == Message.MType.EnterMessage) { ProcessEnterMessage(campfireInfo, msg, api); } else if (msg.Type == Message.MType.TextMessage) { ProcessTextMessage(campfireInfo, msg, api); } } TriggerNotifications(campfireInfo); // Now look to fire any pending notifications }
private static void ProcessMessages_FetchNewMessages(CampfireState campfireInfo, ICampfireAPI api) { IList<CampfireState.RoomInfo> rooms = campfireInfo.Rooms; foreach (CampfireState.RoomInfo room in rooms) { List<Message> msgs; try { msgs = api.RecentMessages(room.Id, room.LastMessageId, Message.MType.TextMessage | Message.MType.EnterMessage); } catch (System.Net.WebException ex) { // oops. Let's break out and then try again later Utils.TraceException(TraceLevel.Warning, ex, "Exception calling API.RecentMessages"); break; } int lastMsgId = 0; foreach (Message msg in msgs) { lastMsgId = msg.Id; QueueMessage(campfireInfo, msg); } if (lastMsgId != 0) { // remember that we've now processed through this message Id. So next time we're only fetch newer messages campfireInfo.UpdateLastMessageId(room.Id, lastMsgId); } } }
/// <summary> /// Retrieve all 'new' messages from each room. And then "process" them. /// </summary> /// <param name="campfireInfo"></param> /// <param name="api"></param> private static void Work_ProcessNewMessagesForAllRooms(CampfireState campfireInfo, ICampfireAPI api) { /* * 2 phases: * - fetch all new message for all the rooms. Plase messages into appropriate queue * - Process all those queue up messages, in order * * Structuring this as 2 phases in case they are even executes by different threads. E.g. a thread for each room "waiting" on messages. */ ProcessMessages_FetchNewMessages(campfireInfo, api); ProcessMessages_ProcessQueuedMessages(campfireInfo, api); }
private static void ProcessTextMessage(CampfireState campfireInfo, Message msg, ICampfireAPI api) { // The person that posted this message... If they have a pending notification in the room... then cancel it... they've spoken campfireInfo.RemovePendingNotification(msg.UserId, msg.RoomId, msg.PostedAt, true); IList <CampfireState.UserInfo> allUsers = campfireInfo.Users; IList <CampfireState.UserReference> lazyNotificationUsers; IList <CampfireState.UserReference> immediateNotificationUsers; IList <CampfireState.UserReference> smokeSignalReferences; Utils.FindUserReferences(msg.Body, allUsers, out lazyNotificationUsers, out immediateNotificationUsers, out smokeSignalReferences); CampfireState.UserInfo source = allUsers.FirstOrDefault(u => u.Id == msg.UserId); CampfireState.RoomInfo room = CampfireState.Instance.Rooms.FirstOrDefault(r => r.Id == msg.RoomId); // special smoke signal commands only make sense if a legitimate user issued them if (source != null) { foreach (CampfireState.UserReference ri in smokeSignalReferences) { ri.SourceUser = source; ri.Room = room; ProcessSmokeSignalCommands(source.Id, ri); } } foreach (CampfireState.UserReference ri in immediateNotificationUsers) { ri.SourceUser = source; ri.Room = room; campfireInfo.AddPendingNotification(ri, msg.PostedAt.AddSeconds(0)); } foreach (CampfireState.UserReference ri in lazyNotificationUsers) { ri.SourceUser = source; ri.Room = room; int delay = ri.TargetUser.DelayInMinutes > 0 ? ri.TargetUser.DelayInMinutes : SmokeSignalConfig.Instance.DelayBeforeSmokeSignalInMinutes; campfireInfo.AddPendingNotification(ri, msg.PostedAt.AddSeconds(delay * 60)); } }
private static void ProcessMessages_ProcessQueuedMessages(CampfireState campfireInfo, ICampfireAPI api) { /* * For each message that comes from any room: * Remove any entry in “PendingNotification” collection where PendingNotification.user is message.author (and room's match) * (e.g he’s already participating after being called out so no need to notify) * If there’s a User Reference match * Enter (user, time, room) into a PendingNotification collection * (unless user/room already exists in collection) * * For each entry in PendingNotification * If entry.timestamp is < Now – entry.user.ConfiguredDelay then * Send that user a notification that he’s been called out in entry.room */ Message msg; while ((msg = campfireInfo.PopMessage()) != null) { CheckForUnknownUserOrRoom(campfireInfo, msg, api); if (msg.Type == Message.MType.EnterMessage) { ProcessEnterMessage(campfireInfo, msg, api); } else if (msg.Type == Message.MType.TextMessage) { ProcessTextMessage(campfireInfo, msg, api); } } TriggerNotifications(campfireInfo); // Now look to fire any pending notifications }
private static void ProcessMessages_FetchNewMessages(CampfireState campfireInfo, ICampfireAPI api) { IList <CampfireState.RoomInfo> rooms = campfireInfo.Rooms; foreach (CampfireState.RoomInfo room in rooms) { List <Message> msgs; try { msgs = api.RecentMessages(room.Id, room.LastMessageId, Message.MType.TextMessage | Message.MType.EnterMessage); } catch (System.Net.WebException ex) { // oops. Let's break out and then try again later Utils.TraceException(TraceLevel.Warning, ex, "Exception calling API.RecentMessages"); break; } int lastMsgId = 0; foreach (Message msg in msgs) { lastMsgId = msg.Id; QueueMessage(campfireInfo, msg); } if (lastMsgId != 0) { // remember that we've now processed through this message Id. So next time we're only fetch newer messages campfireInfo.UpdateLastMessageId(room.Id, lastMsgId); } } }