private static BubbleGroup AddNewInternal(VisualBubble newBubble, bool raiseBubbleInserted) { var group = new BubbleGroup(newBubble, null, false); BubbleGroupSettingsManager.SetUnreadIndicatorGuid(group, group.LastBubbleSafe().ID, true); if (ServiceManager.IsRunning(@group.Service)) { newBubble.Service.NewBubbleGroupCreated(@group).ContinueWith(x => { // force the UI to refetch the photo @group.IsPhotoSetFromService = false; BubbleManager.SendSubscribe(@group, true); BubbleGroupUpdater.Update(@group); }); } BubbleGroupManager.BubbleGroupsAdd(@group); BubbleGroupDatabase.AddBubble(@group, newBubble); if (raiseBubbleInserted) { try { BubbleGroupEvents.RaiseBubbleInserted(newBubble, @group); } catch (Exception ex) { Utils.DebugPrint( "Error in notifying the interface that the bubble group has been updated (" + newBubble.Service.Information.ServiceName + "): " + ex.Message); } } BubbleGroupUpdater.Update(@group); return(@group); }
internal static BubbleGroup Group(VisualBubble vb, bool resend = false, bool insertAtBottom = false) { lock (BubbleGroupDatabase.OperationLock) { Utils.DebugPrint("Grouping an " + vb.Direction + " bubble on service " + vb.Service.Information.ServiceName); var theGroup = BubbleGroupManager.FindWithAddress(vb.Service, vb.Address); BubbleGroupFactory.LoadFullyIfNeeded(theGroup); var duplicate = false; var newGroup = false; if (theGroup == null) { Utils.DebugPrint(vb.Service.Information.ServiceName + " unable to find suitable group. Creating a new one."); theGroup = new BubbleGroup(vb, null, false); newGroup = true; Utils.DebugPrint("GUID of new group: " + theGroup.ID); vb.Service.NewBubbleGroupCreated(theGroup).ContinueWith(x => { // force the UI to refetch the photo theGroup.IsPhotoSetFromService = false; SendSubscribe(theGroup, true); BubbleGroupUpdater.Update(theGroup); }); BubbleGroupManager.BubbleGroupsAdd(theGroup); } else { if (resend) { if (vb.Status == Bubble.BubbleStatus.Failed) { UpdateStatus(vb, Bubble.BubbleStatus.Waiting, theGroup); } return(theGroup); } var visualBubbleServiceId = vb.Service as IVisualBubbleServiceId; if (visualBubbleServiceId != null && visualBubbleServiceId.DisctinctIncomingVisualBubbleIdServices()) { if (vb.IdService != null) { duplicate = theGroup.Bubbles.FirstOrDefault(x => x.GetType() == vb.GetType() && x.IdService == vb.IdService) != null; } if (!duplicate && vb.IdService2 != null) { duplicate = theGroup.Bubbles.FirstOrDefault(x => x.GetType() == vb.GetType() && x.IdService2 == vb.IdService2) != null; } } if (!duplicate) { Utils.DebugPrint(vb.Service.Information.ServiceName + " found a group. Adding."); if (insertAtBottom) { var lastBubble = theGroup.LastBubbleSafe(); if (lastBubble.Time > vb.Time) { vb.Time = lastBubble.Time; } } theGroup.InsertByTime(vb); } else { Utils.DebugPrint("Yuck. It's a duplicate bubble. No need to readd: " + vb.IdService + ", " + vb.IdService2); } } if (!duplicate) { Utils.DebugPrint("Inserting bubble into database group!"); try { if (newGroup) { BubbleGroupDatabase.AddBubble(theGroup, vb); } else { BubbleGroupDatabase.InsertBubbleByTime(theGroup, vb); } } catch (Exception ex) { Utils.DebugPrint("Bubble failed to be inserting/added into the group " + theGroup.ID + ": " + ex); } try { BubbleGroupEvents.RaiseBubbleInserted(vb, theGroup); } catch (Exception ex) { Utils.DebugPrint( "Error in notifying the interface that the bubble group has been updated (" + vb.Service.Information.ServiceName + "): " + ex.Message); } } return(theGroup); } }
internal static BubbleGroup Group(VisualBubble vb, bool resend = false, bool insertAtBottom = false) { lock (BubbleGroupDatabase.OperationLock) { Utils.DebugPrint("Grouping an " + vb.Direction + " bubble on service " + vb.Service.Information.ServiceName); var theGroup = BubbleGroupManager.FindWithAddress(vb.Service, vb.Address); BubbleGroupFactory.LoadFullyIfNeeded(theGroup); var duplicate = false; var newGroup = false; if (theGroup == null) { Utils.DebugPrint(vb.Service.Information.ServiceName + " unable to find suitable group. Creating a new one."); theGroup = new BubbleGroup(vb, null, false); newGroup = true; Utils.DebugPrint("GUID of new group: " + theGroup.ID); BubbleGroupSettingsManager.SetUnreadIndicatorGuid(theGroup, theGroup.LastBubbleSafe().ID, true); vb.Service.NewBubbleGroupCreated(theGroup).ContinueWith(x => { // force the UI to refetch the photo theGroup.IsPhotoSetFromService = false; SendSubscribe(theGroup, true); BubbleGroupUpdater.Update(theGroup); }); BubbleGroupManager.BubbleGroupsAdd(theGroup); } else { if (resend) { if (vb.Status == Bubble.BubbleStatus.Failed) { UpdateStatus(vb, Bubble.BubbleStatus.Waiting, theGroup); } return(theGroup); } var visualBubbleServiceId = vb.Service as IVisualBubbleServiceId; if (visualBubbleServiceId != null && visualBubbleServiceId.DisctinctIncomingVisualBubbleIdServices()) { if (vb.IdService != null) { duplicate = theGroup.Bubbles.FirstOrDefault(x => x.GetType() == vb.GetType() && x.IdService == vb.IdService) != null; } if (!duplicate && vb.IdService2 != null) { duplicate = theGroup.Bubbles.FirstOrDefault(x => x.GetType() == vb.GetType() && x.IdService2 == vb.IdService2) != null; } } if (!duplicate) { Utils.DebugPrint(vb.Service.Information.ServiceName + " found a group. Adding."); if (insertAtBottom) { var lastBubble = theGroup.LastBubbleSafe(); if (lastBubble.Time > vb.Time) { vb.Time = lastBubble.Time; } } theGroup.InsertByTime(vb); } else { Utils.DebugPrint("Yuck. It's a duplicate bubble. No need to readd: " + vb.IdService + ", " + vb.IdService2); } } try { if (theGroup.IsParty && !string.IsNullOrWhiteSpace(vb.ParticipantAddressNickname)) { var participantAddressNicknamesArray = theGroup.ParticipantNicknames; if (participantAddressNicknamesArray == null) { participantAddressNicknamesArray = new DisaParticipantNickname[0]; } var participantAddressNicknames = participantAddressNicknamesArray.ToList(); var changed = false; var adding = true; foreach (var participantAddressNickname in participantAddressNicknames) { if (theGroup.Service.BubbleGroupComparer(participantAddressNickname.Address, vb.ParticipantAddress)) { if (participantAddressNickname.Nickname != vb.ParticipantAddressNickname) { participantAddressNickname.Nickname = vb.ParticipantAddressNickname; changed = true; } adding = false; break; } } if (adding) { participantAddressNicknames.Add(new DisaParticipantNickname { Address = vb.ParticipantAddress, Nickname = vb.ParticipantAddressNickname, }); } if (changed || adding) { theGroup.ParticipantNicknames = participantAddressNicknames.ToArray(); } } } catch (Exception ex) { Utils.DebugPrint("Failed to insert/update participant nickname into cache: " + ex); } if (!duplicate) { Utils.DebugPrint("Inserting bubble into database group!"); try { if (newGroup) { BubbleGroupDatabase.AddBubble(theGroup, vb); } else { BubbleGroupDatabase.InsertBubbleByTime(theGroup, vb); } } catch (Exception ex) { Utils.DebugPrint("Bubble failed to be inserting/added into the group " + theGroup.ID + ": " + ex); } try { BubbleGroupEvents.RaiseBubbleInserted(vb, theGroup); } catch (Exception ex) { Utils.DebugPrint( "Error in notifying the interface that the bubble group has been updated (" + vb.Service.Information.ServiceName + "): " + ex.Message); } } return(theGroup); } }
private static Task <bool> Send(Bubble b, BubbleGroup group, bool resend) { return(Task <bool> .Factory.StartNew(() => { var vb = b as VisualBubble; if (vb != null) { if (vb.Status == Bubble.BubbleStatus.Sent) { Utils.DebugPrint("Trying to send a bubble that is already sent! On " + vb.Service.Information.ServiceName); return true; } Func <bool> restartServiceIfNeeded = () => { if (!ServiceManager.IsRegistered(b.Service) || ServiceManager.IsRunning(b.Service) || ServiceManager.IsAborted(b.Service)) { return false; } Utils.DebugPrint( "For f***s sakes. The scheduler isn't doing it's job properly, or " + "you're sending a message to it at a weird time. Starting it up bra (" + b.Service.Information.ServiceName + ")."); ServiceManager.AbortAndRestart(b.Service); return true; }; var visualBubbleServiceId = vb.Service as IVisualBubbleServiceId; if (vb.IdService == null && vb.IdService2 == null && visualBubbleServiceId != null) { visualBubbleServiceId.AddVisualBubbleIdServices(vb); } try { @group = Group(vb, resend, true); } catch (Exception ex) { Utils.DebugPrint("Problem in Send:GroupBubble from service " + vb.Service.Information.ServiceName + ": " + ex.Message); return false; } if (@group == null) { Utils.DebugPrint("Could not find a suitable group for bubble " + vb.ID + " on " + vb.Service.Information.ServiceName); return false; } var shouldQueue = vb.Service.QueuedBubblesParameters == null || !vb.Service.QueuedBubblesParameters.BubblesNotToQueue.Contains(vb.GetType()); try { if (shouldQueue && !resend && BubbleQueueManager.HasQueuedBubbles(vb.Service.Information.ServiceName, true, false)) { BubbleQueueManager.JustQueue(group, vb); restartServiceIfNeeded(); return false; } if (shouldQueue) { Monitor.Enter(vb.Service.SendBubbleLock); } using (var queued = new BubbleQueueManager.InsertBubble(group, vb, shouldQueue)) { Action checkForQueued = () => { if (!resend && BubbleQueueManager.HasQueuedBubbles(vb.Service.Information.ServiceName, true, true)) { BubbleQueueManager.Send(new [] { vb.Service.Information.ServiceName }); } }; try { FailBubbleIfPathDoesntExist(vb); SendBubbleInternal(b); } catch (ServiceQueueBubbleException ex) { Utils.DebugPrint("Queuing visual bubble on service " + vb.Service.Information.ServiceName + ": " + ex.Message); UpdateStatus(vb, Bubble.BubbleStatus.Waiting, @group); if (!restartServiceIfNeeded()) { checkForQueued(); } return false; } catch (Exception ex) { queued.CancelQueue(); Utils.DebugPrint("Visual bubble on " + vb.Service.Information.ServiceName + " failed to be sent: " + ex); UpdateStatus(vb, Bubble.BubbleStatus.Failed, @group); BubbleGroupEvents.RaiseBubbleFailed(vb, @group); if (!restartServiceIfNeeded()) { checkForQueued(); } //FIXME: if the bubble fails to send, allow the queue manager to continue. if (resend) { return true; } return false; } queued.CancelQueue(); lock (BubbleGroupManager.LastBubbleSentTimestamps) { BubbleGroupManager.LastBubbleSentTimestamps[group.ID] = Time.GetNowUnixTimestamp(); } if (vb.Status == Bubble.BubbleStatus.Delivered) { Utils.DebugPrint( "************ Race condition. The server set the status to delivered/read before we could send it to sent. :')"); checkForQueued(); return true; } UpdateStatus(vb, Bubble.BubbleStatus.Sent, @group); checkForQueued(); return true; } } finally { if (shouldQueue) { Monitor.Exit(vb.Service.SendBubbleLock); } } } else { var composeBubble = b as ComposeBubble; if (composeBubble != null) { var bubbleToSend = composeBubble.BubbleToSend; var visualBubbleServiceId = bubbleToSend.Service as IVisualBubbleServiceId; if (bubbleToSend.IdService == null && bubbleToSend.IdService2 == null && visualBubbleServiceId != null) { visualBubbleServiceId.AddVisualBubbleIdServices(bubbleToSend); } @group.InsertByTime(bubbleToSend); try { BubbleGroupEvents.RaiseBubbleInserted(bubbleToSend, @group); } catch { // do nothing } } try { SendBubbleInternal(b); } catch (ServiceBubbleGroupAddressException ex) { if (!String.IsNullOrWhiteSpace(ex.Address)) { if (composeBubble != null) { composeBubble.BubbleToSend.Address = ex.Address; composeBubble.BubbleToSend.Status = Bubble.BubbleStatus.Sent; var actualGroup = Group(composeBubble.BubbleToSend, resend, true); ServiceEvents.RaiseComposeFinished( @group as ComposeBubbleGroup, actualGroup); return true; } } composeBubble.BubbleToSend.Status = Bubble.BubbleStatus.Failed; return false; } catch (Exception ex) { Utils.DebugPrint("Failed to send bubble on service " + b.Service.Information.ServiceName); if (composeBubble != null) { composeBubble.BubbleToSend.Status = Bubble.BubbleStatus.Failed; } return false; } if (composeBubble != null) { composeBubble.BubbleToSend.Status = Bubble.BubbleStatus.Failed; return false; } return true; } })); }
internal static BubbleGroup Group(VisualBubble vb, bool resend = false, bool insertAtBottom = false) { lock (BubbleGroupDatabase.OperationLock) { Utils.DebugPrint("Grouping an " + vb.Direction + " bubble on service " + vb.Service.Information.ServiceName); AddUrlMarkupIfNeeded(vb); var theGroup = BubbleGroupManager.FindWithAddress(vb.Service, vb.Address); BubbleGroupFactory.LoadFullyIfNeeded(theGroup); var duplicate = false; var newGroup = false; if (theGroup == null) { Utils.DebugPrint(vb.Service.Information.ServiceName + " unable to find suitable group. Creating a new one."); theGroup = new BubbleGroup(vb, null, false); newGroup = true; Utils.DebugPrint("GUID of new group: " + theGroup.ID); BubbleGroupSettingsManager.SetUnreadIndicatorGuid(theGroup, theGroup.LastBubbleSafe().ID, true); vb.Service.NewBubbleGroupCreated(theGroup).ContinueWith(x => { // force the UI to refetch the photo theGroup.IsPhotoSetFromService = false; SendSubscribe(theGroup, true); BubbleGroupUpdater.Update(theGroup); }); BubbleGroupManager.BubbleGroupsAdd(theGroup); } else { if (resend) { if (vb.Status == Bubble.BubbleStatus.Failed) { UpdateStatus(vb, Bubble.BubbleStatus.Waiting, theGroup); } return(theGroup); } // Does the Service for this VisualBubble require that the VisualBubble's IdService and IdService2 // be distinct? var visualBubbleServiceId = vb.Service as IVisualBubbleServiceId; if (visualBubbleServiceId != null && visualBubbleServiceId.DisctinctIncomingVisualBubbleIdServices()) { // Ok, we need to be distinct, BUT do the VisualBubble Type's have to be distinct as well? var checkType = true; if (!DisaFrameworkMethods.Missing(vb.Service, DisaFrameworkMethods.IVisualBubbleServiceIdCheckType)) { checkType = visualBubbleServiceId.CheckType(); } // Ok, now does the Service have special additional logic it wants to use for the distinction comparison? // Example: For Telegram, we allow an ImageBubble immediately followed by a TextBubble to have the // same VisualBubble.IdService - as this represents an image with a caption in Telegram. if (DisaFrameworkMethods.Missing(vb.Service, DisaFrameworkMethods.IVisualBubbleServiceIdVisualBubbleIdComparer)) { // Normal distinction checks if (vb.IdService != null) { duplicate = theGroup.Bubbles.FirstOrDefault(x => (!checkType || x.GetType() == vb.GetType()) && x.IdService == vb.IdService) != null; } if (!duplicate && vb.IdService2 != null) { duplicate = theGroup.Bubbles.FirstOrDefault(x => (!checkType || x.GetType() == vb.GetType()) && x.IdService2 == vb.IdService2) != null; } } else { // Special additional Service defined distinction checks if (vb.IdService != null) { var duplicateBubble = theGroup.Bubbles.FirstOrDefault(x => (!checkType || x.GetType() == vb.GetType()) && x.IdService == vb.IdService); duplicate = duplicateBubble == null ? false : visualBubbleServiceId.VisualBubbleIdComparer(left: duplicateBubble, right: vb); } if (!duplicate && vb.IdService2 != null) { var duplicateBubble = theGroup.Bubbles.FirstOrDefault(x => (!checkType || x.GetType() == vb.GetType()) && x.IdService2 == vb.IdService2); duplicate = duplicateBubble == null ? false : visualBubbleServiceId.VisualBubbleIdComparer(duplicateBubble, vb); } } } if (!duplicate) { Utils.DebugPrint(vb.Service.Information.ServiceName + " found a group. Adding."); if (insertAtBottom) { var lastBubble = theGroup.LastBubbleSafe(); if (lastBubble.Time > vb.Time) { vb.Time = lastBubble.Time; } } theGroup.InsertByTime(vb); } else { Utils.DebugPrint("Yuck. It's a duplicate bubble. No need to readd: " + vb.IdService + ", " + vb.IdService2); } } try { if (theGroup.IsParty && !string.IsNullOrWhiteSpace(vb.ParticipantAddressNickname)) { var participantAddressNicknamesArray = theGroup.ParticipantNicknames; if (participantAddressNicknamesArray == null) { participantAddressNicknamesArray = new DisaParticipantNickname[0]; } var participantAddressNicknames = participantAddressNicknamesArray.ToList(); var changed = false; var adding = true; foreach (var participantAddressNickname in participantAddressNicknames) { if (theGroup.Service.BubbleGroupComparer(participantAddressNickname.Address, vb.ParticipantAddress)) { if (participantAddressNickname.Nickname != vb.ParticipantAddressNickname) { participantAddressNickname.Nickname = vb.ParticipantAddressNickname; changed = true; } adding = false; break; } } if (adding) { participantAddressNicknames.Add(new DisaParticipantNickname { Address = vb.ParticipantAddress, Nickname = vb.ParticipantAddressNickname, }); } if (changed || adding) { theGroup.ParticipantNicknames = participantAddressNicknames.ToArray(); } } } catch (Exception ex) { Utils.DebugPrint("Failed to insert/update participant nickname into cache: " + ex); } if (!duplicate) { Utils.DebugPrint("Inserting bubble into database group!"); try { if (newGroup) { BubbleGroupDatabase.AddBubble(theGroup, vb); } else { BubbleGroupDatabase.InsertBubbleByTime(theGroup, vb); } } catch (Exception ex) { Utils.DebugPrint("Bubble failed to be inserting/added into the group " + theGroup.ID + ": " + ex); } try { BubbleGroupEvents.RaiseBubbleInserted(vb, theGroup); } catch (Exception ex) { Utils.DebugPrint( "Error in notifying the interface that the bubble group has been updated (" + vb.Service.Information.ServiceName + "): " + ex.Message); } } return(theGroup); } }