public static void SetNotQueuedToFailures(BubbleGroup group) { var groups = BubbleGroupManager.GetInner(@group).Where(x => !x.PartiallyLoaded && (x.Service.QueuedBubblesParameters != null && x.Service.QueuedBubblesParameters.SendingBubblesToFailOnServiceStart != null && x.Service.QueuedBubblesParameters.SendingBubblesToFailOnServiceStart.Any())); var failed = new List <Tuple <BubbleGroup, VisualBubble> >(); foreach (var innerGroup in groups) { foreach (var bubble in innerGroup) { if (bubble.Direction == Bubble.BubbleDirection.Outgoing && bubble.Status == Bubble.BubbleStatus.Waiting) { if (innerGroup .Service.QueuedBubblesParameters.SendingBubblesToFailOnServiceStart .FirstOrDefault(x => x == bubble.GetType()) != null) { failed.Add(new Tuple <BubbleGroup, VisualBubble>(innerGroup, bubble)); } } } } if (!failed.Any()) { return; } var somethingUpdated = false; var failuresGroupedByBubbleGroup = failed.GroupBy(x => x.Item1); foreach (var failureGroup in failuresGroupedByBubbleGroup) { var groupOfBubbles = failureGroup.First().Item1; var bubbles = failureGroup.Select(x => x.Item2).ToArray(); foreach (var bubble in bubbles) { bubble.Status = Bubble.BubbleStatus.Failed; } BubbleGroupDatabase.UpdateBubble(groupOfBubbles, bubbles, groupOfBubbles.Bubbles.Count + 100); // 100 is really a tolerance here (just in case) foreach (var bubble in bubbles) { BubbleGroupEvents.RaiseBubbleFailed(bubble, groupOfBubbles); } somethingUpdated = true; } if (somethingUpdated) { BubbleGroupEvents.RaiseBubblesUpdated(@group); BubbleGroupEvents.RaiseRefreshed(@group); } }
public static bool LoadFullyIfNeeded(BubbleGroup group, bool sync = false) { if (@group == null) { return(false); } var loadedSomething = false; lock (BubbleGroupDatabase.OperationLock) { var unifiedGroup = @group as UnifiedBubbleGroup; var associatedGroups = unifiedGroup != null ? unifiedGroup.Groups.ToList() : new[] { @group }.ToList(); var associatedPartiallyLoadedGroups = associatedGroups.Where(x => x.PartiallyLoaded).ToList(); foreach (var partiallyLoadedGroup in associatedPartiallyLoadedGroups) { loadedSomething = true; var partiallyLoadedBubblesToRemove = partiallyLoadedGroup.Bubbles.ToList(); foreach (var bubble in BubbleGroupDatabase.FetchBubbles(partiallyLoadedGroup).Reverse()) { partiallyLoadedGroup.Bubbles.Add(bubble); } foreach (var partiallyLoadedBubbleToRemove in partiallyLoadedBubblesToRemove) { partiallyLoadedGroup.Bubbles.Remove(partiallyLoadedBubbleToRemove); } partiallyLoadedGroup.PartiallyLoaded = false; } if (unifiedGroup != null && !unifiedGroup.UnifiedGroupLoaded) { Populate(unifiedGroup); } } if (!sync && loadedSomething) { Task.Factory.StartNew(() => { var unifiedGroup = @group as UnifiedBubbleGroup; if (unifiedGroup != null) { BubbleQueueManager.Send(unifiedGroup.Groups.Where(x => ServiceManager.IsRunning(x.Service)) .Select(x => x.Service.Information.ServiceName).ToArray()); } else if (ServiceManager.IsRunning(@group.Service)) { BubbleQueueManager.Send(new[] { @group.Service.Information.ServiceName }); } BubbleManager.SetNotQueuedToFailures(@group); }); } return(loadedSomething); }
public static IEnumerable <VisualBubble> ReadBubblesFromDatabase(BubbleGroup group) { return(BubbleGroupDatabase.FetchBubbles(group, SearchDepth, false)); }
internal static void LoadAllPartiallyIfPossible(int bubblesPerGroup = 100) { var corruptedGroups = new List <string>(); var bubbleGroupsLocations = Directory.GetFiles(BubbleGroupDatabase.GetBaseLocation(), "*.*", SearchOption.AllDirectories); var bubbleGroupsLocationsSorted = bubbleGroupsLocations.OrderByDescending(x => Time.GetUnixTimestamp(File.GetLastWriteTime(x))).ToList(); foreach (var bubbleGroupLocation in bubbleGroupsLocationsSorted) { String groupId = null; try { var groupHeader = Path.GetFileNameWithoutExtension(bubbleGroupLocation); var groupDelimeter = groupHeader.IndexOf("^", StringComparison.Ordinal); var serviceName = groupHeader.Substring(0, groupDelimeter); groupId = groupHeader.Substring(groupDelimeter + 1); var service = ServiceManager.GetByName(serviceName); if (service == null) { Utils.DebugPrint("Service " + serviceName + " not found in AllServices!"); continue; } var deserializedBubbleGroup = LoadPartiallyIfPossible(bubbleGroupLocation, service, groupId, bubblesPerGroup); if (deserializedBubbleGroup == null) { throw new Exception("DeserializedBubbleGroup is nothing."); } BubbleGroupManager.BubbleGroupsAdd(deserializedBubbleGroup); } catch (Exception ex) { Utils.DebugPrint("Group " + bubbleGroupLocation + " is corrupt. Deleting. " + ex); File.Delete(bubbleGroupLocation); if (groupId != null) { corruptedGroups.Add(groupId); } } } var migrationResaveNeeded = false; var corruptedUnifiedGroups = new List <SimpleDatabase <UnifiedBubbleGroup, DisaUnifiedBubbleGroupEntry> .Container>(); var removeFromRuntime = new List <SimpleDatabase <UnifiedBubbleGroup, DisaUnifiedBubbleGroupEntry> .Container>(); foreach (var group in UnifiedBubbleGroupsDatabase) { var innerGroupCorrupt = false; var innerGroups = new List <BubbleGroup>(); foreach (var innerGroupId in @group.Serializable.GroupIds) { var innerGroup = BubbleGroupManager.Find(innerGroupId); if (innerGroup == null) { Utils.DebugPrint("Unified group, inner group " + innerGroupId + " could not be related."); if (corruptedGroups.Contains(innerGroupId)) { Utils.DebugPrint( "It was detected that this inner group was corrupted and deleted. Will delete unified group."); innerGroupCorrupt = true; corruptedUnifiedGroups.Add(@group); } } else { innerGroups.Add(innerGroup); } } if (!innerGroups.Any()) { Utils.DebugPrint("Yuck. This unified group has no inner groups. Removing from runtime."); removeFromRuntime.Add(@group); continue; } if (innerGroupCorrupt) { continue; } var primaryGroup = innerGroups.FirstOrDefault(x => x.ID == @group.Serializable.PrimaryGroupId); if (primaryGroup == null) { Utils.DebugPrint("Unified group, primary group " + @group.Serializable.PrimaryGroupId + " could not be related. Removing from runtime."); removeFromRuntime.Add(@group); continue; } var id = @group.Serializable.Id; var unifiedGroup = CreateUnifiedInternal(innerGroups, primaryGroup, id); if (id == null) { migrationResaveNeeded = true; @group.Serializable.Id = unifiedGroup.ID; } var sendingGroup = innerGroups.FirstOrDefault(x => x.ID == @group.Serializable.SendingGroupId); if (sendingGroup != null) { unifiedGroup.SendingGroup = sendingGroup; } @group.Object = unifiedGroup; BubbleGroupManager.BubbleGroupsAdd(unifiedGroup); } if (removeFromRuntime.Any()) { foreach (var group in removeFromRuntime) { UnifiedBubbleGroupsDatabase.Remove(@group); } } if (corruptedUnifiedGroups.Any()) { foreach (var group in corruptedUnifiedGroups) { UnifiedBubbleGroupsDatabase.Remove(@group); } } if (migrationResaveNeeded) { Utils.DebugPrint("It was detected that we need to save migration changes for UnifiedBubbleGroups."); UnifiedBubbleGroupsDatabase.SaveChanges(); } try { foreach (var groupCache in BubbleGroupCacheManager.Load()) { var associatedGroup = BubbleGroupManager.Find(groupCache.Guid); if (associatedGroup == null) { continue; } var unifiedGroup = associatedGroup as UnifiedBubbleGroup; if (unifiedGroup != null) { associatedGroup = unifiedGroup.PrimaryGroup; } associatedGroup.Title = groupCache.Name; associatedGroup.Photo = groupCache.Photo; associatedGroup.IsPhotoSetInitiallyFromCache = true; if (groupCache.Participants != null) { associatedGroup.Participants = groupCache.Participants.ToSynchronizedCollection(); foreach (var participant in associatedGroup.Participants) { participant.IsPhotoSetInitiallyFromCache = true; } } } } catch (Exception ex) { Utils.DebugPrint("Failed to load bubble groups!: " + ex); } BubbleGroupSettingsManager.Load(); }
public static Task Sync(BubbleGroup group, bool force = false) { return(Task.Factory.StartNew(() => { Utils.DebugPrint("Syncing convo " + group.ID); lock (SyncLock) { Utils.DebugPrint("Syncing convo " + group.ID + " (after lock)"); var somethingSynced = true; var groupsToSync = new List <BubbleGroup>(); var unifiedGroup = group as UnifiedBubbleGroup; if (unifiedGroup != null) { groupsToSync.AddRange(unifiedGroup.Groups); } else { groupsToSync.Add(group); } foreach (var groupToSync in groupsToSync) { if (!groupToSync.NeedsSync && !force) { continue; } var groupToSyncAgent = groupToSync.Service as Agent; if (groupToSyncAgent == null) { continue; } if (!ServiceManager.IsRunning(groupToSync.Service)) { continue; } try { var syncTask = groupToSyncAgent.Sync(groupToSync, Database.GetActionId(groupToSync)); syncTask.Wait(); var syncResult = syncTask.Result; if (!syncResult.EmptyResult && !syncResult.NullActionId && !syncResult.JustRefresh) { lock (BubbleGroupDatabase.OperationLock) { if (syncResult.ResultType == Result.Type.Purge) { Utils.DebugPrint("Sync is purging the database for bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); BubbleGroupDatabase.Kill(groupToSync); } if (!syncResult.EmptyInserts) { Utils.DebugPrint("Sync is inserting " + syncResult.Inserts.Length + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); if (syncResult.ResultType == Result.Type.Purge) { syncResult.Inserts.TimSort((x, y) => x.Time.CompareTo(y.Time)); BubbleGroupDatabase.AddBubbles(groupToSync, syncResult.Inserts); } else { BubbleGroupDatabase.InsertBubblesByTime(groupToSync, syncResult.Inserts, SearchDepth); } } if (!syncResult.EmptyUpdates) { Utils.DebugPrint("Sync is updating " + syncResult.Updates.Length + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); BubbleManager.Update(groupToSync, syncResult.Updates, SearchDepth); } Database.SetActionId(groupToSync, syncResult.NewActionId); } } else { Utils.DebugPrint("Sync for bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName + " returned an empty result (" + syncResult.ResultType.ToString() + ")."); somethingSynced = syncResult.JustRefresh; } groupToSync.NeedsSync = false; } catch (Exception ex) { Utils.DebugPrint("Failed to sync bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName + ": " + ex); somethingSynced = false; } } if (somethingSynced) { lock (BubbleGroupDatabase.OperationLock) { var sendingBubbles = BubbleManager.FetchAllSendingAndDownloading(group).ToList(); BubbleGroupFactory.UnloadFullLoad(group); BubbleGroupFactory.LoadFullyIfNeeded(group, true); if (sendingBubbles.Any()) { BubbleManager.Replace(group, sendingBubbles); } group.RaiseBubblesSynced(); } } } })); }
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); } }