| 
									
										
										
										
											2021-01-14 12:07:05 -06:00
										 |  |  | // Copyright 2020-2021 Signal Messenger, LLC
 | 
					
						
							| 
									
										
										
										
											2020-10-30 15:34:04 -05:00
										 |  |  | // SPDX-License-Identifier: AGPL-3.0-only
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | /* eslint-disable camelcase */ | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | import { join } from 'path'; | 
					
						
							|  |  |  | import mkdirp from 'mkdirp'; | 
					
						
							|  |  |  | import rimraf from 'rimraf'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { Database, Statement } from 'better-sqlite3'; | 
					
						
							|  |  |  | import SQL from 'better-sqlite3'; | 
					
						
							| 
									
										
										
										
											2021-07-30 11:43:16 -05:00
										 |  |  | import pProps from 'p-props'; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { Dictionary } from 'lodash'; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | import { | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   forEach, | 
					
						
							|  |  |  |   fromPairs, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   groupBy, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |   isNil, | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   isNumber, | 
					
						
							|  |  |  |   isString, | 
					
						
							|  |  |  |   last, | 
					
						
							|  |  |  |   map, | 
					
						
							| 
									
										
										
										
											2021-07-30 11:43:16 -05:00
										 |  |  |   mapValues, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |   omit, | 
					
						
							| 
									
										
										
										
											2021-05-07 14:50:14 -07:00
										 |  |  |   pick, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | } from 'lodash'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | import { ReadStatus } from '../messages/MessageReadStatus'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { GroupV2MemberType } from '../model-types.d'; | 
					
						
							|  |  |  | import type { ReactionType } from '../types/Reactions'; | 
					
						
							| 
									
										
										
										
											2021-08-30 14:39:57 -07:00
										 |  |  | import { STORAGE_UI_KEYS } from '../types/StorageUIKeys'; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | import { UUID } from '../types/UUID'; | 
					
						
							|  |  |  | import type { UUIDStringType } from '../types/UUID'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { StoredJob } from '../jobs/types'; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | import { assert, assertSync } from '../util/assert'; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | import { combineNames } from '../util/combineNames'; | 
					
						
							| 
									
										
										
										
											2021-10-21 12:49:53 -07:00
										 |  |  | import { consoleLogger } from '../util/consoleLogger'; | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  | import { dropNull } from '../util/dropNull'; | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  | import { isNormalNumber } from '../util/isNormalNumber'; | 
					
						
							| 
									
										
										
										
											2021-04-29 18:02:27 -05:00
										 |  |  | import { isNotNil } from '../util/isNotNil'; | 
					
						
							| 
									
										
										
										
											2021-08-30 14:39:57 -07:00
										 |  |  | import { missingCaseError } from '../util/missingCaseError'; | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | import { parseIntOrThrow } from '../util/parseIntOrThrow'; | 
					
						
							| 
									
										
										
										
											2021-08-26 09:10:58 -05:00
										 |  |  | import * as durations from '../util/durations'; | 
					
						
							| 
									
										
										
										
											2021-07-30 11:43:16 -05:00
										 |  |  | import { formatCountForLogging } from '../logging/formatCountForLogging'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { ConversationColorType, CustomColorType } from '../types/Colors'; | 
					
						
							| 
									
										
										
										
											2021-08-20 11:06:15 -05:00
										 |  |  | import { ProcessGroupCallRingRequestResult } from '../types/Calling'; | 
					
						
							| 
									
										
										
										
											2021-08-30 14:39:57 -07:00
										 |  |  | import { RemoveAllConfiguration } from '../types/RemoveAllConfiguration'; | 
					
						
							| 
									
										
										
										
											2021-11-02 18:01:13 -05:00
										 |  |  | import type { BadgeType, BadgeImageType } from '../badges/types'; | 
					
						
							|  |  |  | import { parseBadgeCategory } from '../badges/BadgeCategory'; | 
					
						
							|  |  |  | import { parseBadgeImageTheme } from '../badges/BadgeImageTheme'; | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  | import type { LoggerType } from '../types/Logging'; | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  | import * as log from '../logging/log'; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | import type { EmptyQuery, ArrayQuery, Query, JSONRows } from './util'; | 
					
						
							|  |  |  | import { | 
					
						
							|  |  |  |   jsonToObject, | 
					
						
							|  |  |  |   objectToJSON, | 
					
						
							|  |  |  |   batchMultiVarQuery, | 
					
						
							|  |  |  |   getCountFromTable, | 
					
						
							|  |  |  |   removeById, | 
					
						
							|  |  |  |   removeAllFromTable, | 
					
						
							|  |  |  |   getAllFromTable, | 
					
						
							|  |  |  |   getById, | 
					
						
							|  |  |  |   bulkAdd, | 
					
						
							|  |  |  |   createOrUpdate, | 
					
						
							|  |  |  |   TableIterator, | 
					
						
							|  |  |  |   setUserVersion, | 
					
						
							|  |  |  |   getUserVersion, | 
					
						
							|  |  |  |   getSchemaVersion, | 
					
						
							|  |  |  | } from './util'; | 
					
						
							|  |  |  | import { updateSchema } from './migrations'; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   AllItemsType, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   AttachmentDownloadJobType, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   ConversationMetricsType, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   ConversationType, | 
					
						
							| 
									
										
										
										
											2021-08-31 14:35:01 -07:00
										 |  |  |   DeleteSentProtoRecipientOptionsType, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   EmojiType, | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |   IdentityKeyIdType, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   IdentityKeyType, | 
					
						
							| 
									
										
										
										
											2021-06-14 17:09:37 -07:00
										 |  |  |   ItemKeyType, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   ItemType, | 
					
						
							| 
									
										
										
										
											2021-12-10 14:51:54 -08:00
										 |  |  |   LastConversationMessagesType, | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   MessageMetricsType, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   MessageType, | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   MessageTypeUnhydrated, | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |   PreKeyIdType, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   PreKeyType, | 
					
						
							| 
									
										
										
										
											2021-12-10 14:51:54 -08:00
										 |  |  |   ServerSearchResultMessageType, | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |   SenderKeyIdType, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   SenderKeyType, | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   SentMessageDBType, | 
					
						
							|  |  |  |   SentMessagesType, | 
					
						
							|  |  |  |   SentProtoType, | 
					
						
							|  |  |  |   SentProtoWithMessageIdsType, | 
					
						
							|  |  |  |   SentRecipientsDBType, | 
					
						
							|  |  |  |   SentRecipientsType, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   ServerInterface, | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |   SessionIdType, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   SessionType, | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |   SignedPreKeyIdType, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   SignedPreKeyType, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   StickerPackStatusType, | 
					
						
							|  |  |  |   StickerPackType, | 
					
						
							|  |  |  |   StickerType, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   StoryDistributionMemberType, | 
					
						
							|  |  |  |   StoryDistributionType, | 
					
						
							|  |  |  |   StoryDistributionWithMembersType, | 
					
						
							|  |  |  |   StoryReadType, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   UnprocessedType, | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  |   UnprocessedUpdateType, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | } from './Interface'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | type ConversationRow = Readonly<{ | 
					
						
							|  |  |  |   json: string; | 
					
						
							|  |  |  |   profileLastFetchedAt: null | number; | 
					
						
							|  |  |  | }>; | 
					
						
							|  |  |  | type ConversationRows = Array<ConversationRow>; | 
					
						
							| 
									
										
										
										
											2021-04-07 13:00:22 -07:00
										 |  |  | type StickerRow = Readonly<{ | 
					
						
							|  |  |  |   id: number; | 
					
						
							|  |  |  |   packId: string; | 
					
						
							|  |  |  |   emoji: string | null; | 
					
						
							|  |  |  |   height: number; | 
					
						
							|  |  |  |   isCoverOnly: number; | 
					
						
							|  |  |  |   lastUsed: number; | 
					
						
							|  |  |  |   path: string; | 
					
						
							|  |  |  |   width: number; | 
					
						
							|  |  |  | }>; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | // Because we can't force this module to conform to an interface, we narrow our exports
 | 
					
						
							|  |  |  | //   to this one default export, which does conform to the interface.
 | 
					
						
							|  |  |  | // Note: In Javascript, you need to access the .default property when requiring it
 | 
					
						
							|  |  |  | // https://github.com/microsoft/TypeScript/issues/420
 | 
					
						
							|  |  |  | const dataInterface: ServerInterface = { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   close, | 
					
						
							|  |  |  |   removeDB, | 
					
						
							| 
									
										
										
										
											2018-11-01 16:40:19 -07:00
										 |  |  |   removeIndexedDBFiles, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   createOrUpdateIdentityKey, | 
					
						
							|  |  |  |   getIdentityKeyById, | 
					
						
							|  |  |  |   bulkAddIdentityKeys, | 
					
						
							|  |  |  |   removeIdentityKeyById, | 
					
						
							|  |  |  |   removeAllIdentityKeys, | 
					
						
							| 
									
										
										
										
											2019-02-04 15:54:37 -08:00
										 |  |  |   getAllIdentityKeys, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   createOrUpdatePreKey, | 
					
						
							|  |  |  |   getPreKeyById, | 
					
						
							|  |  |  |   bulkAddPreKeys, | 
					
						
							|  |  |  |   removePreKeyById, | 
					
						
							|  |  |  |   removeAllPreKeys, | 
					
						
							| 
									
										
										
										
											2019-02-04 15:54:37 -08:00
										 |  |  |   getAllPreKeys, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   createOrUpdateSignedPreKey, | 
					
						
							|  |  |  |   getSignedPreKeyById, | 
					
						
							|  |  |  |   bulkAddSignedPreKeys, | 
					
						
							|  |  |  |   removeSignedPreKeyById, | 
					
						
							|  |  |  |   removeAllSignedPreKeys, | 
					
						
							| 
									
										
										
										
											2021-12-10 14:51:54 -08:00
										 |  |  |   getAllSignedPreKeys, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   createOrUpdateItem, | 
					
						
							|  |  |  |   getItemById, | 
					
						
							|  |  |  |   removeItemById, | 
					
						
							|  |  |  |   removeAllItems, | 
					
						
							| 
									
										
										
										
											2021-12-10 14:51:54 -08:00
										 |  |  |   getAllItems, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-13 18:18:43 -07:00
										 |  |  |   createOrUpdateSenderKey, | 
					
						
							|  |  |  |   getSenderKeyById, | 
					
						
							|  |  |  |   removeAllSenderKeys, | 
					
						
							|  |  |  |   getAllSenderKeys, | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  |   removeSenderKeyById, | 
					
						
							| 
									
										
										
										
											2021-05-13 18:18:43 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   insertSentProto, | 
					
						
							|  |  |  |   deleteSentProtosOlderThan, | 
					
						
							|  |  |  |   deleteSentProtoByMessageId, | 
					
						
							|  |  |  |   insertProtoRecipients, | 
					
						
							|  |  |  |   deleteSentProtoRecipient, | 
					
						
							|  |  |  |   getSentProtoByRecipient, | 
					
						
							|  |  |  |   removeAllSentProtos, | 
					
						
							|  |  |  |   getAllSentProtos, | 
					
						
							|  |  |  |   _getAllSentProtoRecipients, | 
					
						
							|  |  |  |   _getAllSentProtoMessageIds, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   createOrUpdateSession, | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  |   createOrUpdateSessions, | 
					
						
							| 
									
										
										
										
											2021-05-17 11:03:42 -07:00
										 |  |  |   commitSessionsAndUnprocessed, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   bulkAddSessions, | 
					
						
							|  |  |  |   removeSessionById, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   removeSessionsByConversation, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   removeAllSessions, | 
					
						
							| 
									
										
										
										
											2019-02-04 15:54:37 -08:00
										 |  |  |   getAllSessions, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-10 14:51:54 -08:00
										 |  |  |   eraseStorageServiceStateFromConversations, | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   getConversationCount, | 
					
						
							|  |  |  |   saveConversation, | 
					
						
							|  |  |  |   saveConversations, | 
					
						
							|  |  |  |   getConversationById, | 
					
						
							|  |  |  |   updateConversation, | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  |   updateConversations, | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   removeConversation, | 
					
						
							| 
									
										
										
										
											2021-12-10 14:51:54 -08:00
										 |  |  |   updateAllConversationColors, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   getAllConversations, | 
					
						
							|  |  |  |   getAllConversationIds, | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   getAllGroupsInvolvingUuid, | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   searchConversations, | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |   searchMessages, | 
					
						
							|  |  |  |   searchMessagesInConversation, | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |   getMessageCount, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   saveMessage, | 
					
						
							|  |  |  |   saveMessages, | 
					
						
							|  |  |  |   removeMessage, | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  |   removeMessages, | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |   getUnreadByConversationAndMarkRead, | 
					
						
							|  |  |  |   getUnreadReactionsAndMarkRead, | 
					
						
							|  |  |  |   markReactionAsRead, | 
					
						
							|  |  |  |   addReaction, | 
					
						
							|  |  |  |   removeReactionFromConversation, | 
					
						
							| 
									
										
										
										
											2021-10-15 15:54:31 -07:00
										 |  |  |   _getAllReactions, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   _removeAllReactions, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   getMessageBySender, | 
					
						
							|  |  |  |   getMessageById, | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  |   getMessagesById, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   _getAllMessages, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   _removeAllMessages, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   getAllMessageIds, | 
					
						
							|  |  |  |   getMessagesBySentAt, | 
					
						
							|  |  |  |   getExpiredMessages, | 
					
						
							| 
									
										
										
										
											2021-06-18 14:12:04 -05:00
										 |  |  |   getMessagesUnexpectedlyMissingExpirationStartTimestamp, | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |   getSoonestMessageExpiry, | 
					
						
							| 
									
										
										
										
											2021-05-19 11:17:51 -05:00
										 |  |  |   getNextTapToViewMessageTimestampToAgeOut, | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |   getTapToViewMessagesNeedingErase, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   getOlderMessagesByConversation, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   getOlderStories, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   getNewerMessagesByConversation, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   getTotalUnreadForConversation, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   getMessageMetricsForConversation, | 
					
						
							| 
									
										
										
										
											2021-08-16 09:56:27 -07:00
										 |  |  |   getLastConversationMessages, | 
					
						
							| 
									
										
										
										
											2020-12-07 14:43:19 -06:00
										 |  |  |   hasGroupCallHistoryMessage, | 
					
						
							| 
									
										
										
										
											2020-07-10 11:28:49 -07:00
										 |  |  |   migrateConversationMessages, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-28 15:51:26 -07:00
										 |  |  |   getUnprocessedCount, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   getAllUnprocessed, | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |   updateUnprocessedWithData, | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  |   updateUnprocessedsWithData, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   getUnprocessedById, | 
					
						
							|  |  |  |   removeUnprocessed, | 
					
						
							|  |  |  |   removeAllUnprocessed, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  |   getNextAttachmentDownloadJobs, | 
					
						
							|  |  |  |   saveAttachmentDownloadJob, | 
					
						
							|  |  |  |   resetAttachmentDownloadPending, | 
					
						
							| 
									
										
										
										
											2021-12-10 14:51:54 -08:00
										 |  |  |   setAttachmentDownloadJobPending, | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  |   removeAttachmentDownloadJob, | 
					
						
							|  |  |  |   removeAllAttachmentDownloadJobs, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   createOrUpdateStickerPack, | 
					
						
							|  |  |  |   updateStickerPackStatus, | 
					
						
							|  |  |  |   createOrUpdateSticker, | 
					
						
							|  |  |  |   updateStickerLastUsed, | 
					
						
							|  |  |  |   addStickerPackReference, | 
					
						
							|  |  |  |   deleteStickerPackReference, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   getStickerCount, | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   deleteStickerPack, | 
					
						
							|  |  |  |   getAllStickerPacks, | 
					
						
							|  |  |  |   getAllStickers, | 
					
						
							|  |  |  |   getRecentStickers, | 
					
						
							| 
									
										
										
										
											2021-01-27 14:39:45 -08:00
										 |  |  |   clearAllErrorStickerPackAttempts, | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  |   updateEmojiUsage, | 
					
						
							|  |  |  |   getRecentEmojis, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-02 18:01:13 -05:00
										 |  |  |   getAllBadges, | 
					
						
							|  |  |  |   updateOrCreateBadges, | 
					
						
							|  |  |  |   badgeImageFileDownloaded, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   _getAllStoryDistributions, | 
					
						
							|  |  |  |   _getAllStoryDistributionMembers, | 
					
						
							|  |  |  |   _deleteAllStoryDistributions, | 
					
						
							|  |  |  |   createNewStoryDistribution, | 
					
						
							|  |  |  |   getAllStoryDistributionsWithMembers, | 
					
						
							| 
									
										
										
										
											2021-12-09 18:15:59 -08:00
										 |  |  |   modifyStoryDistribution, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   modifyStoryDistributionMembers, | 
					
						
							|  |  |  |   deleteStoryDistribution, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _getAllStoryReads, | 
					
						
							|  |  |  |   _deleteAllStoryReads, | 
					
						
							|  |  |  |   addNewStoryRead, | 
					
						
							|  |  |  |   getLastStoryReadsForAuthor, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   removeAll, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   removeAllConfiguration, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   getMessagesNeedingUpgrade, | 
					
						
							|  |  |  |   getMessagesWithVisualMediaAttachments, | 
					
						
							|  |  |  |   getMessagesWithFileAttachments, | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  |   getMessageServerGuidsForSpam, | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 18:02:27 -05:00
										 |  |  |   getJobsInQueue, | 
					
						
							|  |  |  |   insertJob, | 
					
						
							|  |  |  |   deleteJob, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-20 11:06:15 -05:00
										 |  |  |   processGroupCallRingRequest, | 
					
						
							|  |  |  |   processGroupCallRingCancelation, | 
					
						
							|  |  |  |   cleanExpiredGroupCallRings, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 11:45:22 -07:00
										 |  |  |   getMaxMessageCounter, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-30 11:43:16 -05:00
										 |  |  |   getStatisticsForLogging, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   // Server-only
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-21 13:13:33 -07:00
										 |  |  |   getCorruptionLog, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   initialize, | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |   initializeRenderer, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |   removeKnownAttachments, | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   removeKnownStickers, | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |   removeKnownDraftAttachments, | 
					
						
							| 
									
										
										
										
											2021-11-02 18:01:13 -05:00
										 |  |  |   getAllBadgeImageFileLocalPaths, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | export default dataInterface; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | type DatabaseQueryCache = Map<string, Statement<Array<unknown>>>; | 
					
						
							| 
									
										
										
										
											2021-04-05 16:13:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | const statementCache = new WeakMap<Database, DatabaseQueryCache>(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | function prepare<T>(db: Database, query: string): Statement<T> { | 
					
						
							| 
									
										
										
										
											2021-04-05 16:13:21 -07:00
										 |  |  |   let dbCache = statementCache.get(db); | 
					
						
							|  |  |  |   if (!dbCache) { | 
					
						
							|  |  |  |     dbCache = new Map(); | 
					
						
							|  |  |  |     statementCache.set(db, dbCache); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   let result = dbCache.get(query) as Statement<T>; | 
					
						
							| 
									
										
										
										
											2021-04-05 16:13:21 -07:00
										 |  |  |   if (!result) { | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |     result = db.prepare<T>(query); | 
					
						
							| 
									
										
										
										
											2021-04-05 16:13:21 -07:00
										 |  |  |     dbCache.set(query, result); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | function rowToConversation(row: ConversationRow): ConversationType { | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |   const parsedJson = JSON.parse(row.json); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let profileLastFetchedAt: undefined | number; | 
					
						
							|  |  |  |   if (isNormalNumber(row.profileLastFetchedAt)) { | 
					
						
							|  |  |  |     profileLastFetchedAt = row.profileLastFetchedAt; | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     assert( | 
					
						
							|  |  |  |       isNil(row.profileLastFetchedAt), | 
					
						
							|  |  |  |       'profileLastFetchedAt contained invalid data; defaulting to undefined' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     profileLastFetchedAt = undefined; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     ...parsedJson, | 
					
						
							|  |  |  |     profileLastFetchedAt, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-07 13:00:22 -07:00
										 |  |  | function rowToSticker(row: StickerRow): StickerType { | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     ...row, | 
					
						
							|  |  |  |     isCoverOnly: Boolean(row.isCoverOnly), | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  |     emoji: dropNull(row.emoji), | 
					
						
							| 
									
										
										
										
											2021-04-07 13:00:22 -07:00
										 |  |  |   }; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-15 11:43:34 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | function isRenderer() { | 
					
						
							|  |  |  |   if (typeof process === 'undefined' || !process) { | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return process.type === 'renderer'; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | function keyDatabase(db: Database, key: string): void { | 
					
						
							|  |  |  |   // https://www.zetetic.net/sqlcipher/sqlcipher-api/#key
 | 
					
						
							|  |  |  |   db.pragma(`key = "x'${key}'"`); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | function switchToWAL(db: Database): void { | 
					
						
							|  |  |  |   // https://sqlite.org/wal.html
 | 
					
						
							|  |  |  |   db.pragma('journal_mode = WAL'); | 
					
						
							|  |  |  |   db.pragma('synchronous = FULL'); | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | function migrateSchemaVersion(db: Database): void { | 
					
						
							|  |  |  |   const userVersion = getUserVersion(db); | 
					
						
							|  |  |  |   if (userVersion > 0) { | 
					
						
							| 
									
										
										
										
											2021-10-15 15:54:31 -07:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   const schemaVersion = getSchemaVersion(db); | 
					
						
							|  |  |  |   const newUserVersion = schemaVersion > 18 ? 16 : schemaVersion; | 
					
						
							|  |  |  |   logger.info( | 
					
						
							|  |  |  |     'migrateSchemaVersion: Migrating from schema_version ' + | 
					
						
							|  |  |  |       `${schemaVersion} to user_version ${newUserVersion}` | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2021-10-15 15:54:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   setUserVersion(db, newUserVersion); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-15 15:54:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | function openAndMigrateDatabase(filePath: string, key: string) { | 
					
						
							|  |  |  |   let db: Database | undefined; | 
					
						
							| 
									
										
										
										
											2021-10-15 15:54:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   // First, we try to open the database without any cipher changes
 | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     db = new SQL(filePath); | 
					
						
							|  |  |  |     keyDatabase(db, key); | 
					
						
							|  |  |  |     switchToWAL(db); | 
					
						
							|  |  |  |     migrateSchemaVersion(db); | 
					
						
							| 
									
										
										
										
											2021-10-15 15:54:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     return db; | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     if (db) { | 
					
						
							|  |  |  |       db.close(); | 
					
						
							| 
									
										
										
										
											2021-10-15 15:54:31 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     logger.info('migrateDatabase: Migration without cipher change failed'); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-10-15 15:54:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   // If that fails, we try to open the database with 3.x compatibility to extract the
 | 
					
						
							|  |  |  |   //   user_version (previously stored in schema_version, blown away by cipher_migrate).
 | 
					
						
							|  |  |  |   db = new SQL(filePath); | 
					
						
							|  |  |  |   keyDatabase(db, key); | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   // https://www.zetetic.net/blog/2018/11/30/sqlcipher-400-release/#compatability-sqlcipher-4-0-0
 | 
					
						
							|  |  |  |   db.pragma('cipher_compatibility = 3'); | 
					
						
							|  |  |  |   migrateSchemaVersion(db); | 
					
						
							|  |  |  |   db.close(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   // After migrating user_version -> schema_version, we reopen database, because we can't
 | 
					
						
							|  |  |  |   //   migrate to the latest ciphers after we've modified the defaults.
 | 
					
						
							|  |  |  |   db = new SQL(filePath); | 
					
						
							|  |  |  |   keyDatabase(db, key); | 
					
						
							| 
									
										
										
										
											2020-03-31 12:22:14 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   db.pragma('cipher_migrate'); | 
					
						
							|  |  |  |   switchToWAL(db); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return db; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | const INVALID_KEY = /[^0-9A-Fa-f]/; | 
					
						
							|  |  |  | function openAndSetUpSQLCipher(filePath: string, { key }: { key: string }) { | 
					
						
							|  |  |  |   const match = INVALID_KEY.exec(key); | 
					
						
							|  |  |  |   if (match) { | 
					
						
							|  |  |  |     throw new Error(`setupSQLCipher: key '${key}' is not valid`); | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   const db = openAndMigrateDatabase(filePath, key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Because foreign key support is not enabled by default!
 | 
					
						
							|  |  |  |   db.pragma('foreign_keys = ON'); | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return db; | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | let globalInstance: Database | undefined; | 
					
						
							| 
									
										
										
										
											2021-10-21 12:49:53 -07:00
										 |  |  | let logger = consoleLogger; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | let globalInstanceRenderer: Database | undefined; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | let databaseFilePath: string | undefined; | 
					
						
							|  |  |  | let indexedDBPath: string | undefined; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-21 13:13:33 -07:00
										 |  |  | let corruptionLog = new Array<string>(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SQL.setCorruptionLogger(line => { | 
					
						
							|  |  |  |   logger.error(`SQL corruption: ${line}`); | 
					
						
							|  |  |  |   corruptionLog.push(line); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getCorruptionLog(): string { | 
					
						
							|  |  |  |   const result = corruptionLog.join('\n'); | 
					
						
							|  |  |  |   corruptionLog = []; | 
					
						
							|  |  |  |   return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function initialize({ | 
					
						
							|  |  |  |   configDir, | 
					
						
							|  |  |  |   key, | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |   logger: suppliedLogger, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | }: { | 
					
						
							|  |  |  |   configDir: string; | 
					
						
							|  |  |  |   key: string; | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |   logger: LoggerType; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | }): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   if (globalInstance) { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     throw new Error('Cannot initialize more than once!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!isString(configDir)) { | 
					
						
							|  |  |  |     throw new Error('initialize: configDir is required!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!isString(key)) { | 
					
						
							| 
									
										
										
										
											2019-02-19 17:32:44 -08:00
										 |  |  |     throw new Error('initialize: key is required!'); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |   logger = suppliedLogger; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   indexedDBPath = join(configDir, 'IndexedDB'); | 
					
						
							| 
									
										
										
										
											2018-11-01 16:40:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const dbDir = join(configDir, 'sql'); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   mkdirp.sync(dbDir); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   databaseFilePath = join(dbDir, 'db.sqlite'); | 
					
						
							| 
									
										
										
										
											2018-11-01 16:40:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   let db: Database | undefined; | 
					
						
							| 
									
										
										
										
											2019-08-19 15:26:45 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-27 10:17:22 -08:00
										 |  |  |   try { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     db = openAndSetUpSQLCipher(databaseFilePath, { key }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // For profiling use:
 | 
					
						
							|  |  |  |     // db.pragma('cipher_profile=\'sqlcipher.log\'');
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     updateSchema(db, logger); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-19 15:26:45 -07:00
										 |  |  |     // At this point we can allow general access to the database
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     globalInstance = db; | 
					
						
							| 
									
										
										
										
											2019-08-19 15:26:45 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // test database
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     getMessageCountSync(); | 
					
						
							| 
									
										
										
										
											2019-02-19 17:32:44 -08:00
										 |  |  |   } catch (error) { | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |     logger.error('Database startup error:', error.stack); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     if (db) { | 
					
						
							|  |  |  |       db.close(); | 
					
						
							| 
									
										
										
										
											2019-02-19 17:32:44 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-02-19 17:32:44 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  | async function initializeRenderer({ | 
					
						
							|  |  |  |   configDir, | 
					
						
							|  |  |  |   key, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   configDir: string; | 
					
						
							|  |  |  |   key: string; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | }): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |   if (!isRenderer()) { | 
					
						
							|  |  |  |     throw new Error('Cannot call from main process.'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (globalInstanceRenderer) { | 
					
						
							|  |  |  |     throw new Error('Cannot initialize more than once!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!isString(configDir)) { | 
					
						
							|  |  |  |     throw new Error('initialize: configDir is required!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!isString(key)) { | 
					
						
							|  |  |  |     throw new Error('initialize: key is required!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!indexedDBPath) { | 
					
						
							|  |  |  |     indexedDBPath = join(configDir, 'IndexedDB'); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |   const dbDir = join(configDir, 'sql'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!databaseFilePath) { | 
					
						
							|  |  |  |     databaseFilePath = join(dbDir, 'db.sqlite'); | 
					
						
							| 
									
										
										
										
											2019-02-19 17:32:44 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   let promisified: Database | undefined; | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     promisified = openAndSetUpSQLCipher(databaseFilePath, { key }); | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // At this point we can allow general access to the database
 | 
					
						
							|  |  |  |     globalInstanceRenderer = promisified; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // test database
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     getMessageCountSync(); | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |   } catch (error) { | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  |     log.error('Database startup error:', error.stack); | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function close(): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-08-18 09:52:48 -07:00
										 |  |  |   for (const dbRef of [globalInstanceRenderer, globalInstance]) { | 
					
						
							| 
									
										
										
										
											2021-08-17 14:55:34 -07:00
										 |  |  |     // SQLLite documentation suggests that we run `PRAGMA optimize` right
 | 
					
						
							|  |  |  |     // before closing the database connection.
 | 
					
						
							| 
									
										
										
										
											2021-08-18 09:52:48 -07:00
										 |  |  |     dbRef?.pragma('optimize'); | 
					
						
							| 
									
										
										
										
											2021-08-17 14:55:34 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-18 09:52:48 -07:00
										 |  |  |     dbRef?.close(); | 
					
						
							| 
									
										
										
										
											2021-08-17 14:55:34 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-04-06 10:29:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-18 09:52:48 -07:00
										 |  |  |   globalInstance = undefined; | 
					
						
							|  |  |  |   globalInstanceRenderer = undefined; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function removeDB(): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   if (globalInstance) { | 
					
						
							| 
									
										
										
										
											2021-09-08 13:39:14 -07:00
										 |  |  |     try { | 
					
						
							|  |  |  |       globalInstance.close(); | 
					
						
							|  |  |  |     } catch (error) { | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |       logger.error('removeDB: Failed to close database:', error.stack); | 
					
						
							| 
									
										
										
										
											2021-09-08 13:39:14 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     globalInstance = undefined; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   if (!databaseFilePath) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'removeDB: Cannot erase database without a databaseFilePath!' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   rimraf.sync(databaseFilePath); | 
					
						
							| 
									
										
										
										
											2021-03-24 14:35:06 -07:00
										 |  |  |   rimraf.sync(`${databaseFilePath}-shm`); | 
					
						
							|  |  |  |   rimraf.sync(`${databaseFilePath}-wal`); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function removeIndexedDBFiles(): Promise<void> { | 
					
						
							| 
									
										
										
										
											2018-11-01 16:40:19 -07:00
										 |  |  |   if (!indexedDBPath) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'removeIndexedDBFiles: Need to initialize and set indexedDBPath first!' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const pattern = join(indexedDBPath, '*.leveldb'); | 
					
						
							| 
									
										
										
										
											2018-11-01 16:40:19 -07:00
										 |  |  |   rimraf.sync(pattern); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   indexedDBPath = undefined; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | function getInstance(): Database { | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |   if (isRenderer()) { | 
					
						
							|  |  |  |     if (!globalInstanceRenderer) { | 
					
						
							|  |  |  |       throw new Error('getInstance: globalInstanceRenderer not set!'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return globalInstanceRenderer; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   if (!globalInstance) { | 
					
						
							|  |  |  |     throw new Error('getInstance: globalInstance not set!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return globalInstance; | 
					
						
							| 
									
										
										
										
											2018-11-01 16:40:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | const IDENTITY_KEYS_TABLE = 'identityKeys'; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function createOrUpdateIdentityKey(data: IdentityKeyType): Promise<void> { | 
					
						
							|  |  |  |   return createOrUpdate(getInstance(), IDENTITY_KEYS_TABLE, data); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | async function getIdentityKeyById( | 
					
						
							|  |  |  |   id: IdentityKeyIdType | 
					
						
							|  |  |  | ): Promise<IdentityKeyType | undefined> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return getById(getInstance(), IDENTITY_KEYS_TABLE, id); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function bulkAddIdentityKeys( | 
					
						
							|  |  |  |   array: Array<IdentityKeyType> | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							|  |  |  |   return bulkAdd(getInstance(), IDENTITY_KEYS_TABLE, array); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | async function removeIdentityKeyById(id: IdentityKeyIdType): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return removeById(getInstance(), IDENTITY_KEYS_TABLE, id); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function removeAllIdentityKeys(): Promise<void> { | 
					
						
							|  |  |  |   return removeAllFromTable(getInstance(), IDENTITY_KEYS_TABLE); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function getAllIdentityKeys(): Promise<Array<IdentityKeyType>> { | 
					
						
							|  |  |  |   return getAllFromTable(getInstance(), IDENTITY_KEYS_TABLE); | 
					
						
							| 
									
										
										
										
											2019-02-04 15:54:37 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | const PRE_KEYS_TABLE = 'preKeys'; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function createOrUpdatePreKey(data: PreKeyType): Promise<void> { | 
					
						
							|  |  |  |   return createOrUpdate(getInstance(), PRE_KEYS_TABLE, data); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | async function getPreKeyById( | 
					
						
							|  |  |  |   id: PreKeyIdType | 
					
						
							|  |  |  | ): Promise<PreKeyType | undefined> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return getById(getInstance(), PRE_KEYS_TABLE, id); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function bulkAddPreKeys(array: Array<PreKeyType>): Promise<void> { | 
					
						
							|  |  |  |   return bulkAdd(getInstance(), PRE_KEYS_TABLE, array); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | async function removePreKeyById(id: PreKeyIdType): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return removeById(getInstance(), PRE_KEYS_TABLE, id); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function removeAllPreKeys(): Promise<void> { | 
					
						
							|  |  |  |   return removeAllFromTable(getInstance(), PRE_KEYS_TABLE); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function getAllPreKeys(): Promise<Array<PreKeyType>> { | 
					
						
							|  |  |  |   return getAllFromTable(getInstance(), PRE_KEYS_TABLE); | 
					
						
							| 
									
										
										
										
											2019-02-04 15:54:37 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | const SIGNED_PRE_KEYS_TABLE = 'signedPreKeys'; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function createOrUpdateSignedPreKey( | 
					
						
							|  |  |  |   data: SignedPreKeyType | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							|  |  |  |   return createOrUpdate(getInstance(), SIGNED_PRE_KEYS_TABLE, data); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | async function getSignedPreKeyById( | 
					
						
							|  |  |  |   id: SignedPreKeyIdType | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<SignedPreKeyType | undefined> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return getById(getInstance(), SIGNED_PRE_KEYS_TABLE, id); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function bulkAddSignedPreKeys( | 
					
						
							|  |  |  |   array: Array<SignedPreKeyType> | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							|  |  |  |   return bulkAdd(getInstance(), SIGNED_PRE_KEYS_TABLE, array); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | async function removeSignedPreKeyById(id: SignedPreKeyIdType): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return removeById(getInstance(), SIGNED_PRE_KEYS_TABLE, id); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function removeAllSignedPreKeys(): Promise<void> { | 
					
						
							|  |  |  |   return removeAllFromTable(getInstance(), SIGNED_PRE_KEYS_TABLE); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getAllSignedPreKeys(): Promise<Array<SignedPreKeyType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows: JSONRows = db | 
					
						
							|  |  |  |     .prepare<EmptyQuery>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT json | 
					
						
							|  |  |  |       FROM signedPreKeys | 
					
						
							|  |  |  |       ORDER BY id ASC; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all(); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   return rows.map(row => jsonToObject(row.json)); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | const ITEMS_TABLE = 'items'; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function createOrUpdateItem<K extends ItemKeyType>( | 
					
						
							| 
									
										
										
										
											2021-06-14 17:09:37 -07:00
										 |  |  |   data: ItemType<K> | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return createOrUpdate(getInstance(), ITEMS_TABLE, data); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | async function getItemById<K extends ItemKeyType>( | 
					
						
							| 
									
										
										
										
											2021-06-14 17:09:37 -07:00
										 |  |  |   id: K | 
					
						
							|  |  |  | ): Promise<ItemType<K> | undefined> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return getById(getInstance(), ITEMS_TABLE, id); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-06-14 17:09:37 -07:00
										 |  |  | async function getAllItems(): Promise<AllItemsType> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows: JSONRows = db | 
					
						
							|  |  |  |     .prepare<EmptyQuery>('SELECT json FROM items ORDER BY id ASC;') | 
					
						
							|  |  |  |     .all(); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   type RawItemType = { id: ItemKeyType; value: unknown }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const items = rows.map(row => jsonToObject<RawItemType>(row.json)); | 
					
						
							| 
									
										
										
										
											2021-06-14 17:09:37 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   const result: Record<ItemKeyType, unknown> = Object.create(null); | 
					
						
							| 
									
										
										
										
											2021-06-14 17:09:37 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   for (const { id, value } of items) { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     result[id] = value; | 
					
						
							| 
									
										
										
										
											2021-06-14 17:09:37 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-11 16:43:05 -06:00
										 |  |  |   return result as unknown as AllItemsType; | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | async function removeItemById(id: ItemKeyType): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return removeById(getInstance(), ITEMS_TABLE, id); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function removeAllItems(): Promise<void> { | 
					
						
							|  |  |  |   return removeAllFromTable(getInstance(), ITEMS_TABLE); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-13 18:18:43 -07:00
										 |  |  | async function createOrUpdateSenderKey(key: SenderKeyType): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   prepare( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     INSERT OR REPLACE INTO senderKeys ( | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |       senderId, | 
					
						
							|  |  |  |       distributionId, | 
					
						
							|  |  |  |       data, | 
					
						
							|  |  |  |       lastUpdatedDate | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $id, | 
					
						
							|  |  |  |       $senderId, | 
					
						
							|  |  |  |       $distributionId, | 
					
						
							|  |  |  |       $data, | 
					
						
							|  |  |  |       $lastUpdatedDate | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run(key); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function getSenderKeyById( | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |   id: SenderKeyIdType | 
					
						
							| 
									
										
										
										
											2021-05-13 18:18:43 -07:00
										 |  |  | ): Promise<SenderKeyType | undefined> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const row = prepare(db, 'SELECT * FROM senderKeys WHERE id = $id').get({ | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function removeAllSenderKeys(): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   prepare<EmptyQuery>(db, 'DELETE FROM senderKeys').run(); | 
					
						
							| 
									
										
										
										
											2021-05-13 18:18:43 -07:00
										 |  |  | } | 
					
						
							|  |  |  | async function getAllSenderKeys(): Promise<Array<SenderKeyType>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   const rows = prepare<EmptyQuery>(db, 'SELECT * FROM senderKeys').all(); | 
					
						
							| 
									
										
										
										
											2021-05-13 18:18:43 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return rows; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | async function removeSenderKeyById(id: SenderKeyIdType): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   prepare(db, 'DELETE FROM senderKeys WHERE id = $id').run({ id }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-13 18:18:43 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | async function insertSentProto( | 
					
						
							|  |  |  |   proto: SentProtoType, | 
					
						
							|  |  |  |   options: { | 
					
						
							|  |  |  |     recipients: SentRecipientsType; | 
					
						
							|  |  |  |     messageIds: SentMessagesType; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ): Promise<number> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const { recipients, messageIds } = options; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Note: we use `pluck` in this function to fetch only the first column of returned row.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return db.transaction(() => { | 
					
						
							|  |  |  |     // 1. Insert the payload, fetching its primary key id
 | 
					
						
							|  |  |  |     const info = prepare( | 
					
						
							|  |  |  |       db, | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       INSERT INTO sendLogPayloads ( | 
					
						
							|  |  |  |         contentHint, | 
					
						
							|  |  |  |         proto, | 
					
						
							|  |  |  |         timestamp | 
					
						
							|  |  |  |       ) VALUES ( | 
					
						
							|  |  |  |         $contentHint, | 
					
						
							|  |  |  |         $proto, | 
					
						
							|  |  |  |         $timestamp | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ).run(proto); | 
					
						
							|  |  |  |     const id = parseIntOrThrow( | 
					
						
							|  |  |  |       info.lastInsertRowid, | 
					
						
							|  |  |  |       'insertSentProto/lastInsertRowid' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 2. Insert a record for each recipient device.
 | 
					
						
							|  |  |  |     const recipientStatement = prepare( | 
					
						
							|  |  |  |       db, | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       INSERT INTO sendLogRecipients ( | 
					
						
							|  |  |  |         payloadId, | 
					
						
							|  |  |  |         recipientUuid, | 
					
						
							|  |  |  |         deviceId | 
					
						
							|  |  |  |       ) VALUES ( | 
					
						
							|  |  |  |         $id, | 
					
						
							|  |  |  |         $recipientUuid, | 
					
						
							|  |  |  |         $deviceId | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const recipientUuids = Object.keys(recipients); | 
					
						
							|  |  |  |     for (const recipientUuid of recipientUuids) { | 
					
						
							|  |  |  |       const deviceIds = recipients[recipientUuid]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       for (const deviceId of deviceIds) { | 
					
						
							|  |  |  |         recipientStatement.run({ | 
					
						
							|  |  |  |           id, | 
					
						
							|  |  |  |           recipientUuid, | 
					
						
							|  |  |  |           deviceId, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 2. Insert a record for each message referenced by this payload.
 | 
					
						
							|  |  |  |     const messageStatement = prepare( | 
					
						
							|  |  |  |       db, | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       INSERT INTO sendLogMessageIds ( | 
					
						
							|  |  |  |         payloadId, | 
					
						
							|  |  |  |         messageId | 
					
						
							|  |  |  |       ) VALUES ( | 
					
						
							|  |  |  |         $id, | 
					
						
							|  |  |  |         $messageId | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const messageId of messageIds) { | 
					
						
							|  |  |  |       messageStatement.run({ | 
					
						
							|  |  |  |         id, | 
					
						
							|  |  |  |         messageId, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return id; | 
					
						
							|  |  |  |   })(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function deleteSentProtosOlderThan(timestamp: number): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   prepare( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     DELETE FROM sendLogPayloads | 
					
						
							|  |  |  |     WHERE | 
					
						
							|  |  |  |       timestamp IS NULL OR | 
					
						
							|  |  |  |       timestamp < $timestamp; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     timestamp, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function deleteSentProtoByMessageId(messageId: string): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   prepare( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     DELETE FROM sendLogPayloads WHERE id IN ( | 
					
						
							|  |  |  |       SELECT payloadId FROM sendLogMessageIds | 
					
						
							|  |  |  |       WHERE messageId = $messageId | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     messageId, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function insertProtoRecipients({ | 
					
						
							|  |  |  |   id, | 
					
						
							|  |  |  |   recipientUuid, | 
					
						
							|  |  |  |   deviceIds, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   id: number; | 
					
						
							|  |  |  |   recipientUuid: string; | 
					
						
							|  |  |  |   deviceIds: Array<number>; | 
					
						
							|  |  |  | }): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   db.transaction(() => { | 
					
						
							|  |  |  |     const statement = prepare( | 
					
						
							|  |  |  |       db, | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       INSERT INTO sendLogRecipients ( | 
					
						
							|  |  |  |         payloadId, | 
					
						
							|  |  |  |         recipientUuid, | 
					
						
							|  |  |  |         deviceId | 
					
						
							|  |  |  |       ) VALUES ( | 
					
						
							|  |  |  |         $id, | 
					
						
							|  |  |  |         $recipientUuid, | 
					
						
							|  |  |  |         $deviceId | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const deviceId of deviceIds) { | 
					
						
							|  |  |  |       statement.run({ | 
					
						
							|  |  |  |         id, | 
					
						
							|  |  |  |         recipientUuid, | 
					
						
							|  |  |  |         deviceId, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   })(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-31 14:35:01 -07:00
										 |  |  | async function deleteSentProtoRecipient( | 
					
						
							|  |  |  |   options: | 
					
						
							|  |  |  |     | DeleteSentProtoRecipientOptionsType | 
					
						
							|  |  |  |     | ReadonlyArray<DeleteSentProtoRecipientOptionsType> | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-31 14:35:01 -07:00
										 |  |  |   const items = Array.isArray(options) ? options : [options]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Note: we use `pluck` in this function to fetch only the first column of
 | 
					
						
							|  |  |  |   // returned row.
 | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   db.transaction(() => { | 
					
						
							| 
									
										
										
										
											2021-08-31 14:35:01 -07:00
										 |  |  |     for (const item of items) { | 
					
						
							|  |  |  |       const { timestamp, recipientUuid, deviceId } = item; | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-31 14:35:01 -07:00
										 |  |  |       // 1. Figure out what payload we're talking about.
 | 
					
						
							|  |  |  |       const rows = prepare( | 
					
						
							|  |  |  |         db, | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         SELECT sendLogPayloads.id FROM sendLogPayloads | 
					
						
							|  |  |  |         INNER JOIN sendLogRecipients | 
					
						
							|  |  |  |           ON sendLogRecipients.payloadId = sendLogPayloads.id | 
					
						
							|  |  |  |         WHERE | 
					
						
							|  |  |  |           sendLogPayloads.timestamp = $timestamp AND | 
					
						
							|  |  |  |           sendLogRecipients.recipientUuid = $recipientUuid AND | 
					
						
							|  |  |  |           sendLogRecipients.deviceId = $deviceId; | 
					
						
							|  |  |  |        `
 | 
					
						
							|  |  |  |       ).all({ timestamp, recipientUuid, deviceId }); | 
					
						
							|  |  |  |       if (!rows.length) { | 
					
						
							|  |  |  |         continue; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (rows.length > 1) { | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |         logger.warn( | 
					
						
							| 
									
										
										
										
											2021-08-31 14:35:01 -07:00
										 |  |  |           'deleteSentProtoRecipient: More than one payload matches ' + | 
					
						
							|  |  |  |             `recipient and timestamp ${timestamp}. Using the first.` | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         continue; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-31 14:35:01 -07:00
										 |  |  |       const { id } = rows[0]; | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-31 14:35:01 -07:00
										 |  |  |       // 2. Delete the recipient/device combination in question.
 | 
					
						
							|  |  |  |       prepare( | 
					
						
							|  |  |  |         db, | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         DELETE FROM sendLogRecipients | 
					
						
							|  |  |  |         WHERE | 
					
						
							|  |  |  |           payloadId = $id AND | 
					
						
							|  |  |  |           recipientUuid = $recipientUuid AND | 
					
						
							|  |  |  |           deviceId = $deviceId; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ).run({ id, recipientUuid, deviceId }); | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-31 14:35:01 -07:00
										 |  |  |       // 3. See how many more recipient devices there were for this payload.
 | 
					
						
							|  |  |  |       const remaining = prepare( | 
					
						
							|  |  |  |         db, | 
					
						
							|  |  |  |         'SELECT count(*) FROM sendLogRecipients WHERE payloadId = $id;' | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |         .pluck(true) | 
					
						
							|  |  |  |         .get({ id }); | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-31 14:35:01 -07:00
										 |  |  |       if (!isNumber(remaining)) { | 
					
						
							|  |  |  |         throw new Error( | 
					
						
							|  |  |  |           'deleteSentProtoRecipient: select count() returned non-number!' | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-31 14:35:01 -07:00
										 |  |  |       if (remaining > 0) { | 
					
						
							|  |  |  |         continue; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // 4. Delete the entire payload if there are no more recipients left.
 | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |       logger.info( | 
					
						
							| 
									
										
										
										
											2021-08-31 14:35:01 -07:00
										 |  |  |         'deleteSentProtoRecipient: ' + | 
					
						
							|  |  |  |           `Deleting proto payload for timestamp ${timestamp}` | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       prepare(db, 'DELETE FROM sendLogPayloads WHERE id = $id;').run({ | 
					
						
							|  |  |  |         id, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   })(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function getSentProtoByRecipient({ | 
					
						
							|  |  |  |   now, | 
					
						
							|  |  |  |   recipientUuid, | 
					
						
							|  |  |  |   timestamp, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   now: number; | 
					
						
							|  |  |  |   recipientUuid: string; | 
					
						
							|  |  |  |   timestamp: number; | 
					
						
							|  |  |  | }): Promise<SentProtoWithMessageIdsType | undefined> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const HOUR = 1000 * 60 * 60; | 
					
						
							|  |  |  |   const oneDayAgo = now - HOUR * 24; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await deleteSentProtosOlderThan(oneDayAgo); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const row = prepare( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     SELECT | 
					
						
							|  |  |  |       sendLogPayloads.*, | 
					
						
							|  |  |  |       GROUP_CONCAT(DISTINCT sendLogMessageIds.messageId) AS messageIds | 
					
						
							|  |  |  |     FROM sendLogPayloads | 
					
						
							|  |  |  |     INNER JOIN sendLogRecipients ON sendLogRecipients.payloadId = sendLogPayloads.id | 
					
						
							|  |  |  |     LEFT JOIN sendLogMessageIds ON sendLogMessageIds.payloadId = sendLogPayloads.id | 
					
						
							|  |  |  |     WHERE | 
					
						
							|  |  |  |       sendLogPayloads.timestamp = $timestamp AND | 
					
						
							|  |  |  |       sendLogRecipients.recipientUuid = $recipientUuid | 
					
						
							|  |  |  |     GROUP BY sendLogPayloads.id; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).get({ | 
					
						
							|  |  |  |     timestamp, | 
					
						
							|  |  |  |     recipientUuid, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							|  |  |  |     return undefined; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const { messageIds } = row; | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     ...row, | 
					
						
							|  |  |  |     messageIds: messageIds ? messageIds.split(',') : [], | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function removeAllSentProtos(): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   prepare<EmptyQuery>(db, 'DELETE FROM sendLogPayloads;').run(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function getAllSentProtos(): Promise<Array<SentProtoType>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const rows = prepare<EmptyQuery>(db, 'SELECT * FROM sendLogPayloads;').all(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return rows; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function _getAllSentProtoRecipients(): Promise< | 
					
						
							|  |  |  |   Array<SentRecipientsDBType> | 
					
						
							|  |  |  | > { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const rows = prepare<EmptyQuery>( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     'SELECT * FROM sendLogRecipients;' | 
					
						
							|  |  |  |   ).all(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return rows; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function _getAllSentProtoMessageIds(): Promise<Array<SentMessageDBType>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const rows = prepare<EmptyQuery>( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     'SELECT * FROM sendLogMessageIds;' | 
					
						
							|  |  |  |   ).all(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return rows; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | const SESSIONS_TABLE = 'sessions'; | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | function createOrUpdateSessionSync(data: SessionType): void { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |   const { id, conversationId, ourUuid, uuid } = data; | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   if (!id) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'createOrUpdateSession: Provided data did not have a truthy id' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   if (!conversationId) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |     throw new Error( | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       'createOrUpdateSession: Provided data did not have a truthy conversationId' | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 16:13:21 -07:00
										 |  |  |   prepare( | 
					
						
							|  |  |  |     db, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     `
 | 
					
						
							|  |  |  |     INSERT OR REPLACE INTO sessions ( | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       id, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       conversationId, | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |       ourUuid, | 
					
						
							|  |  |  |       uuid, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       json | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $id, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       $conversationId, | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |       $ourUuid, | 
					
						
							|  |  |  |       $uuid, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       $json | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     ) | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     conversationId, | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  |     ourUuid, | 
					
						
							|  |  |  |     uuid, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     json: objectToJSON(data), | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | async function createOrUpdateSession(data: SessionType): Promise<void> { | 
					
						
							|  |  |  |   return createOrUpdateSessionSync(data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function createOrUpdateSessions( | 
					
						
							|  |  |  |   array: Array<SessionType> | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.transaction(() => { | 
					
						
							|  |  |  |     for (const item of array) { | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  |       assertSync(createOrUpdateSessionSync(item)); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   })(); | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 11:03:42 -07:00
										 |  |  | async function commitSessionsAndUnprocessed({ | 
					
						
							|  |  |  |   sessions, | 
					
						
							|  |  |  |   unprocessed, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   sessions: Array<SessionType>; | 
					
						
							|  |  |  |   unprocessed: Array<UnprocessedType>; | 
					
						
							|  |  |  | }): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   db.transaction(() => { | 
					
						
							|  |  |  |     for (const item of sessions) { | 
					
						
							| 
									
										
										
										
											2021-05-24 14:30:56 -07:00
										 |  |  |       assertSync(createOrUpdateSessionSync(item)); | 
					
						
							| 
									
										
										
										
											2021-05-17 11:03:42 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const item of unprocessed) { | 
					
						
							| 
									
										
										
										
											2021-05-24 14:30:56 -07:00
										 |  |  |       assertSync(saveUnprocessedSync(item)); | 
					
						
							| 
									
										
										
										
											2021-05-17 11:03:42 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   })(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function bulkAddSessions(array: Array<SessionType>): Promise<void> { | 
					
						
							|  |  |  |   return bulkAdd(getInstance(), SESSIONS_TABLE, array); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | async function removeSessionById(id: SessionIdType): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return removeById(getInstance(), SESSIONS_TABLE, id); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function removeSessionsByConversation( | 
					
						
							|  |  |  |   conversationId: string | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     DELETE FROM sessions | 
					
						
							|  |  |  |     WHERE conversationId = $conversationId; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     conversationId, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function removeAllSessions(): Promise<void> { | 
					
						
							|  |  |  |   return removeAllFromTable(getInstance(), SESSIONS_TABLE); | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function getAllSessions(): Promise<Array<SessionType>> { | 
					
						
							|  |  |  |   return getAllFromTable(getInstance(), SESSIONS_TABLE); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | // Conversations
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function getConversationCount(): Promise<number> { | 
					
						
							|  |  |  |   return getCountFromTable(getInstance(), 'conversations'); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | function getConversationMembersList({ members, membersV2 }: ConversationType) { | 
					
						
							|  |  |  |   if (membersV2) { | 
					
						
							|  |  |  |     return membersV2.map((item: GroupV2MemberType) => item.uuid).join(' '); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   if (members) { | 
					
						
							|  |  |  |     return members.join(' '); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return null; | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | function saveConversationSync( | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   data: ConversationType, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db = getInstance() | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | ): void { | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |   const { | 
					
						
							|  |  |  |     active_at, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     e164, | 
					
						
							|  |  |  |     groupId, | 
					
						
							|  |  |  |     id, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |     name, | 
					
						
							|  |  |  |     profileFamilyName, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     profileName, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |     profileLastFetchedAt, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     type, | 
					
						
							|  |  |  |     uuid, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |   } = data; | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   const membersList = getConversationMembersList(data); | 
					
						
							| 
									
										
										
										
											2020-09-08 19:25:05 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     INSERT INTO conversations ( | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |       json, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       e164, | 
					
						
							|  |  |  |       uuid, | 
					
						
							|  |  |  |       groupId, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       active_at, | 
					
						
							|  |  |  |       type, | 
					
						
							|  |  |  |       members, | 
					
						
							|  |  |  |       name, | 
					
						
							|  |  |  |       profileName, | 
					
						
							|  |  |  |       profileFamilyName, | 
					
						
							|  |  |  |       profileFullName, | 
					
						
							|  |  |  |       profileLastFetchedAt | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $id, | 
					
						
							|  |  |  |       $json, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $e164, | 
					
						
							|  |  |  |       $uuid, | 
					
						
							|  |  |  |       $groupId, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $active_at, | 
					
						
							|  |  |  |       $type, | 
					
						
							|  |  |  |       $members, | 
					
						
							|  |  |  |       $name, | 
					
						
							|  |  |  |       $profileName, | 
					
						
							|  |  |  |       $profileFamilyName, | 
					
						
							|  |  |  |       $profileFullName, | 
					
						
							|  |  |  |       $profileLastFetchedAt | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     id, | 
					
						
							| 
									
										
										
										
											2021-04-30 14:40:25 -05:00
										 |  |  |     json: objectToJSON( | 
					
						
							|  |  |  |       omit(data, ['profileLastFetchedAt', 'unblurredAvatarPath']) | 
					
						
							|  |  |  |     ), | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     e164: e164 || null, | 
					
						
							|  |  |  |     uuid: uuid || null, | 
					
						
							|  |  |  |     groupId: groupId || null, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     active_at: active_at || null, | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     type, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     members: membersList, | 
					
						
							|  |  |  |     name: name || null, | 
					
						
							|  |  |  |     profileName: profileName || null, | 
					
						
							|  |  |  |     profileFamilyName: profileFamilyName || null, | 
					
						
							|  |  |  |     profileFullName: combineNames(profileName, profileFamilyName) || null, | 
					
						
							|  |  |  |     profileLastFetchedAt: profileLastFetchedAt || null, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | async function saveConversation( | 
					
						
							|  |  |  |   data: ConversationType, | 
					
						
							|  |  |  |   db = getInstance() | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							|  |  |  |   return saveConversationSync(data, db); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function saveConversations( | 
					
						
							|  |  |  |   arrayOfConversations: Array<ConversationType> | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.transaction(() => { | 
					
						
							|  |  |  |     for (const conversation of arrayOfConversations) { | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  |       assertSync(saveConversationSync(conversation)); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   })(); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | function updateConversationSync( | 
					
						
							|  |  |  |   data: ConversationType, | 
					
						
							|  |  |  |   db = getInstance() | 
					
						
							|  |  |  | ): void { | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |   const { | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     active_at, | 
					
						
							|  |  |  |     type, | 
					
						
							|  |  |  |     name, | 
					
						
							|  |  |  |     profileName, | 
					
						
							|  |  |  |     profileFamilyName, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |     profileLastFetchedAt, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     e164, | 
					
						
							|  |  |  |     uuid, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |   } = data; | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   const membersList = getConversationMembersList(data); | 
					
						
							| 
									
										
										
										
											2020-09-08 19:25:05 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   db.prepare( | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     `
 | 
					
						
							|  |  |  |     UPDATE conversations SET | 
					
						
							| 
									
										
										
										
											2019-05-23 18:27:42 -07:00
										 |  |  |       json = $json, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       e164 = $e164, | 
					
						
							|  |  |  |       uuid = $uuid, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 18:27:42 -07:00
										 |  |  |       active_at = $active_at, | 
					
						
							|  |  |  |       type = $type, | 
					
						
							|  |  |  |       members = $members, | 
					
						
							|  |  |  |       name = $name, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |       profileName = $profileName, | 
					
						
							|  |  |  |       profileFamilyName = $profileFamilyName, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |       profileFullName = $profileFullName, | 
					
						
							|  |  |  |       profileLastFetchedAt = $profileLastFetchedAt | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     WHERE id = $id; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     id, | 
					
						
							| 
									
										
										
										
											2021-04-30 14:40:25 -05:00
										 |  |  |     json: objectToJSON( | 
					
						
							|  |  |  |       omit(data, ['profileLastFetchedAt', 'unblurredAvatarPath']) | 
					
						
							|  |  |  |     ), | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     e164: e164 || null, | 
					
						
							|  |  |  |     uuid: uuid || null, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     active_at: active_at || null, | 
					
						
							|  |  |  |     type, | 
					
						
							|  |  |  |     members: membersList, | 
					
						
							|  |  |  |     name: name || null, | 
					
						
							|  |  |  |     profileName: profileName || null, | 
					
						
							|  |  |  |     profileFamilyName: profileFamilyName || null, | 
					
						
							|  |  |  |     profileFullName: combineNames(profileName, profileFamilyName) || null, | 
					
						
							|  |  |  |     profileLastFetchedAt: profileLastFetchedAt || null, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | async function updateConversation(data: ConversationType): Promise<void> { | 
					
						
							|  |  |  |   return updateConversationSync(data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function updateConversations( | 
					
						
							|  |  |  |   array: Array<ConversationType> | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.transaction(() => { | 
					
						
							|  |  |  |     for (const item of array) { | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  |       assertSync(updateConversationSync(item)); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   })(); | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-22 11:44:51 -07:00
										 |  |  | function removeConversationsSync(ids: Array<string>): void { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-06-22 11:44:51 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Our node interface doesn't seem to allow you to replace one single ? with an array
 | 
					
						
							|  |  |  |   db.prepare<ArrayQuery>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     DELETE FROM conversations | 
					
						
							|  |  |  |     WHERE id IN ( ${ids.map(() => '?').join(', ')} ); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run(ids); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function removeConversation(id: Array<string> | string): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   if (!Array.isArray(id)) { | 
					
						
							| 
									
										
										
										
											2021-04-06 11:15:17 -07:00
										 |  |  |     db.prepare<Query>('DELETE FROM conversations WHERE id = $id;').run({ | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       id, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!id.length) { | 
					
						
							|  |  |  |     throw new Error('removeConversation: No ids to delete!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   batchMultiVarQuery(db, id, removeConversationsSync); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getConversationById( | 
					
						
							|  |  |  |   id: string | 
					
						
							|  |  |  | ): Promise<ConversationType | undefined> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const row: { json: string } = db | 
					
						
							|  |  |  |     .prepare<Query>('SELECT json FROM conversations WHERE id = $id;') | 
					
						
							|  |  |  |     .get({ id }); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     return undefined; | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return jsonToObject(row.json); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function eraseStorageServiceStateFromConversations(): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-07-24 16:32:08 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<EmptyQuery>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     UPDATE conversations | 
					
						
							|  |  |  |     SET | 
					
						
							| 
									
										
										
										
											2020-09-29 19:29:11 -04:00
										 |  |  |       json = json_remove(json, '$.storageID', '$.needsStorageServiceSync', '$.unknownFields', '$.storageProfileKey'); | 
					
						
							| 
									
										
										
										
											2020-07-24 16:32:08 -07:00
										 |  |  |     `
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   ).run(); | 
					
						
							| 
									
										
										
										
											2020-07-24 16:32:08 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | function getAllConversationsSync(db = getInstance()): Array<ConversationType> { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows: ConversationRows = db | 
					
						
							|  |  |  |     .prepare<EmptyQuery>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT json, profileLastFetchedAt | 
					
						
							|  |  |  |       FROM conversations | 
					
						
							|  |  |  |       ORDER BY id ASC; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all(); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   return rows.map(row => rowToConversation(row)); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function getAllConversations(): Promise<Array<ConversationType>> { | 
					
						
							|  |  |  |   return getAllConversationsSync(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getAllConversationIds(): Promise<Array<string>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows: Array<{ id: string }> = db | 
					
						
							|  |  |  |     .prepare<EmptyQuery>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT id FROM conversations ORDER BY id ASC; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all(); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   return rows.map(row => row.id); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function getAllGroupsInvolvingUuid( | 
					
						
							|  |  |  |   uuid: UUIDStringType | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<Array<ConversationType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows: ConversationRows = db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT json, profileLastFetchedAt | 
					
						
							|  |  |  |       FROM conversations WHERE | 
					
						
							|  |  |  |         type = 'group' AND | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |         members LIKE $uuid | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       ORDER BY id ASC; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |       uuid: `%${uuid}%`, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   return rows.map(row => rowToConversation(row)); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function searchConversations( | 
					
						
							|  |  |  |   query: string, | 
					
						
							|  |  |  |   { limit }: { limit?: number } = {} | 
					
						
							|  |  |  | ): Promise<Array<ConversationType>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows: ConversationRows = db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT json, profileLastFetchedAt | 
					
						
							|  |  |  |       FROM conversations WHERE | 
					
						
							|  |  |  |         ( | 
					
						
							|  |  |  |           e164 LIKE $query OR | 
					
						
							|  |  |  |           name LIKE $query OR | 
					
						
							|  |  |  |           profileFullName LIKE $query | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       ORDER BY active_at DESC | 
					
						
							|  |  |  |       LIMIT $limit | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ | 
					
						
							|  |  |  |       query: `%${query}%`, | 
					
						
							|  |  |  |       limit: limit || 100, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   return rows.map(row => rowToConversation(row)); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function searchMessages( | 
					
						
							|  |  |  |   query: string, | 
					
						
							| 
									
										
										
										
											2021-04-27 13:24:57 -07:00
										 |  |  |   params: { limit?: number; conversationId?: string } = {} | 
					
						
							| 
									
										
										
										
											2021-12-10 14:51:54 -08:00
										 |  |  | ): Promise<Array<ServerSearchResultMessageType>> { | 
					
						
							| 
									
										
										
										
											2021-04-27 13:24:57 -07:00
										 |  |  |   const { limit = 500, conversationId } = params; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-26 15:01:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // sqlite queries with a join on a virtual table (like FTS5) are de-optimized
 | 
					
						
							|  |  |  |   // and can't use indices for ordering results. Instead an in-memory index of
 | 
					
						
							| 
									
										
										
										
											2021-04-27 13:24:57 -07:00
										 |  |  |   // the join rows is sorted on the fly, and this becomes substantially
 | 
					
						
							|  |  |  |   // slower when there are large columns in it (like `messages.json`).
 | 
					
						
							|  |  |  |   //
 | 
					
						
							|  |  |  |   // Thus here we take an indirect approach and store `rowid`s in a temporary
 | 
					
						
							|  |  |  |   // table for all messages that match the FTS query. Then we create another
 | 
					
						
							|  |  |  |   // table to sort and limit the results, and finally join on it when fetch
 | 
					
						
							|  |  |  |   // the snippets and json. The benefit of this is that the `ORDER BY` and
 | 
					
						
							|  |  |  |   // `LIMIT` happen without virtual table and are thus covered by
 | 
					
						
							|  |  |  |   // `messages_searchOrder` index.
 | 
					
						
							|  |  |  |   return db.transaction(() => { | 
					
						
							|  |  |  |     db.exec( | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-04-27 13:24:57 -07:00
										 |  |  |       CREATE TEMP TABLE tmp_results(rowid INTEGER PRIMARY KEY ASC); | 
					
						
							|  |  |  |       CREATE TEMP TABLE tmp_filtered_results(rowid INTEGER PRIMARY KEY ASC); | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     db.prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |         INSERT INTO tmp_results (rowid) | 
					
						
							|  |  |  |         SELECT | 
					
						
							|  |  |  |           rowid | 
					
						
							|  |  |  |         FROM | 
					
						
							|  |  |  |           messages_fts | 
					
						
							| 
									
										
										
										
											2021-04-26 15:01:22 -07:00
										 |  |  |         WHERE | 
					
						
							| 
									
										
										
										
											2021-04-27 13:24:57 -07:00
										 |  |  |           messages_fts.body MATCH $query; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-04-27 13:24:57 -07:00
										 |  |  |     ).run({ query }); | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-27 13:24:57 -07:00
										 |  |  |     if (conversationId === undefined) { | 
					
						
							|  |  |  |       db.prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |           INSERT INTO tmp_filtered_results (rowid) | 
					
						
							|  |  |  |           SELECT | 
					
						
							|  |  |  |             tmp_results.rowid | 
					
						
							|  |  |  |           FROM | 
					
						
							|  |  |  |             tmp_results | 
					
						
							|  |  |  |           INNER JOIN | 
					
						
							|  |  |  |             messages ON messages.rowid = tmp_results.rowid | 
					
						
							|  |  |  |           ORDER BY messages.received_at DESC, messages.sent_at DESC | 
					
						
							|  |  |  |           LIMIT $limit; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ).run({ limit }); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       db.prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |           INSERT INTO tmp_filtered_results (rowid) | 
					
						
							|  |  |  |           SELECT | 
					
						
							|  |  |  |             tmp_results.rowid | 
					
						
							|  |  |  |           FROM | 
					
						
							|  |  |  |             tmp_results | 
					
						
							|  |  |  |           INNER JOIN | 
					
						
							|  |  |  |             messages ON messages.rowid = tmp_results.rowid | 
					
						
							|  |  |  |           WHERE | 
					
						
							|  |  |  |             messages.conversationId = $conversationId | 
					
						
							|  |  |  |           ORDER BY messages.received_at DESC, messages.sent_at DESC | 
					
						
							|  |  |  |           LIMIT $limit; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ).run({ conversationId, limit }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // The `MATCH` is necessary in order to for `snippet()` helper function to
 | 
					
						
							|  |  |  |     // give us the right results. We can't call `snippet()` in the query above
 | 
					
						
							|  |  |  |     // because it would bloat the temporary table with text data and we want
 | 
					
						
							|  |  |  |     // to keep its size minimal for `ORDER BY` + `LIMIT` to be fast.
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |     const result = db | 
					
						
							| 
									
										
										
										
											2021-04-27 13:24:57 -07:00
										 |  |  |       .prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         SELECT | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |           messages.json, | 
					
						
							| 
									
										
										
										
											2021-04-27 13:24:57 -07:00
										 |  |  |           snippet(messages_fts, -1, '<<left>>', '<<right>>', '...', 10) | 
					
						
							|  |  |  |             AS snippet | 
					
						
							|  |  |  |         FROM tmp_filtered_results | 
					
						
							|  |  |  |         INNER JOIN messages_fts | 
					
						
							|  |  |  |           ON messages_fts.rowid = tmp_filtered_results.rowid | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |         INNER JOIN messages | 
					
						
							|  |  |  |           ON messages.rowid = tmp_filtered_results.rowid | 
					
						
							| 
									
										
										
										
											2021-04-27 13:24:57 -07:00
										 |  |  |         WHERE | 
					
						
							|  |  |  |           messages_fts.body MATCH $query | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |         ORDER BY messages.received_at DESC, messages.sent_at DESC; | 
					
						
							| 
									
										
										
										
											2021-04-27 13:24:57 -07:00
										 |  |  |         `
 | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |       .all({ query }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     db.exec( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       DROP TABLE tmp_results; | 
					
						
							|  |  |  |       DROP TABLE tmp_filtered_results; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return result; | 
					
						
							|  |  |  |   })(); | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function searchMessagesInConversation( | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   query: string, | 
					
						
							|  |  |  |   conversationId: string, | 
					
						
							| 
									
										
										
										
											2021-04-27 13:24:57 -07:00
										 |  |  |   { limit = 100 }: { limit?: number } = {} | 
					
						
							| 
									
										
										
										
											2021-12-10 14:51:54 -08:00
										 |  |  | ): Promise<Array<ServerSearchResultMessageType>> { | 
					
						
							| 
									
										
										
										
											2021-04-27 13:24:57 -07:00
										 |  |  |   return searchMessages(query, { conversationId, limit }); | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | function getMessageCountSync( | 
					
						
							|  |  |  |   conversationId?: string, | 
					
						
							|  |  |  |   db = getInstance() | 
					
						
							|  |  |  | ): number { | 
					
						
							| 
									
										
										
										
											2021-07-30 11:43:16 -05:00
										 |  |  |   if (conversationId === undefined) { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     return getCountFromTable(db, 'messages'); | 
					
						
							| 
									
										
										
										
											2021-07-30 11:43:16 -05:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   const count = db | 
					
						
							| 
									
										
										
										
											2021-07-30 11:43:16 -05:00
										 |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |         SELECT count(*) | 
					
						
							|  |  |  |         FROM messages | 
					
						
							|  |  |  |         WHERE conversationId = $conversationId; | 
					
						
							|  |  |  |         `
 | 
					
						
							| 
									
										
										
										
											2021-07-30 11:43:16 -05:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     .pluck() | 
					
						
							| 
									
										
										
										
											2021-07-30 11:43:16 -05:00
										 |  |  |     .get({ conversationId }); | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return count; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function getMessageCount(conversationId?: string): Promise<number> { | 
					
						
							|  |  |  |   return getMessageCountSync(conversationId); | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-16 09:56:27 -07:00
										 |  |  | function hasUserInitiatedMessages(conversationId: string): boolean { | 
					
						
							| 
									
										
										
										
											2021-06-02 13:55:10 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const row: { count: number } = db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT COUNT(*) as count FROM | 
					
						
							|  |  |  |         ( | 
					
						
							|  |  |  |           SELECT 1 FROM messages | 
					
						
							|  |  |  |           WHERE | 
					
						
							|  |  |  |             conversationId = $conversationId AND | 
					
						
							|  |  |  |             (type IS NULL | 
					
						
							|  |  |  |               OR | 
					
						
							|  |  |  |               type NOT IN ( | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |                 'change-number-notification', | 
					
						
							| 
									
										
										
										
											2021-06-02 13:55:10 -07:00
										 |  |  |                 'group-v1-migration', | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |                 'group-v2-change', | 
					
						
							|  |  |  |                 'keychange', | 
					
						
							|  |  |  |                 'message-history-unsynced', | 
					
						
							|  |  |  |                 'profile-change', | 
					
						
							|  |  |  |                 'story', | 
					
						
							| 
									
										
										
										
											2021-08-05 16:34:49 -07:00
										 |  |  |                 'universal-timer-notification', | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |                 'verified-change' | 
					
						
							| 
									
										
										
										
											2021-06-02 13:55:10 -07:00
										 |  |  |               ) | 
					
						
							| 
									
										
										
										
											2021-08-16 09:56:27 -07:00
										 |  |  |             ) | 
					
						
							| 
									
										
										
										
											2021-06-02 13:55:10 -07:00
										 |  |  |           LIMIT 1 | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .get({ conversationId }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row.count !== 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | function saveMessageSync( | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   data: MessageType, | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   options: { | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  |     jobToInsert?: StoredJob; | 
					
						
							|  |  |  |     forceSave?: boolean; | 
					
						
							|  |  |  |     alreadyInTransaction?: boolean; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     db?: Database; | 
					
						
							|  |  |  |   } = {} | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | ): string { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   const { | 
					
						
							|  |  |  |     jobToInsert, | 
					
						
							|  |  |  |     forceSave, | 
					
						
							|  |  |  |     alreadyInTransaction, | 
					
						
							|  |  |  |     db = getInstance(), | 
					
						
							|  |  |  |   } = options; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (!alreadyInTransaction) { | 
					
						
							|  |  |  |     return db.transaction(() => { | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  |       return assertSync( | 
					
						
							|  |  |  |         saveMessageSync(data, { | 
					
						
							|  |  |  |           ...options, | 
					
						
							|  |  |  |           alreadyInTransaction: true, | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     })(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const { | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |     body, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     conversationId, | 
					
						
							|  |  |  |     hasAttachments, | 
					
						
							|  |  |  |     hasFileAttachments, | 
					
						
							|  |  |  |     hasVisualMediaAttachments, | 
					
						
							|  |  |  |     id, | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |     isErased, | 
					
						
							| 
									
										
										
										
											2019-08-05 13:53:15 -07:00
										 |  |  |     isViewOnce, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     received_at, | 
					
						
							|  |  |  |     schemaVersion, | 
					
						
							|  |  |  |     sent_at, | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  |     serverGuid, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     source, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     sourceUuid, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     sourceDevice, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |     storyId, | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |     type, | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |     readStatus, | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |     expireTimer, | 
					
						
							|  |  |  |     expirationStartTimestamp, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   } = data; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |   const payload = { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     id, | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |     json: objectToJSON(data), | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     body: body || null, | 
					
						
							|  |  |  |     conversationId, | 
					
						
							|  |  |  |     expirationStartTimestamp: expirationStartTimestamp || null, | 
					
						
							|  |  |  |     expireTimer: expireTimer || null, | 
					
						
							|  |  |  |     hasAttachments: hasAttachments ? 1 : 0, | 
					
						
							|  |  |  |     hasFileAttachments: hasFileAttachments ? 1 : 0, | 
					
						
							|  |  |  |     hasVisualMediaAttachments: hasVisualMediaAttachments ? 1 : 0, | 
					
						
							|  |  |  |     isErased: isErased ? 1 : 0, | 
					
						
							|  |  |  |     isViewOnce: isViewOnce ? 1 : 0, | 
					
						
							|  |  |  |     received_at: received_at || null, | 
					
						
							| 
									
										
										
										
											2021-06-17 10:15:10 -07:00
										 |  |  |     schemaVersion: schemaVersion || 0, | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  |     serverGuid: serverGuid || null, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     sent_at: sent_at || null, | 
					
						
							|  |  |  |     source: source || null, | 
					
						
							|  |  |  |     sourceUuid: sourceUuid || null, | 
					
						
							|  |  |  |     sourceDevice: sourceDevice || null, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |     storyId: storyId || null, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     type: type || null, | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |     readStatus: readStatus ?? null, | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   if (id && !forceSave) { | 
					
						
							| 
									
										
										
										
											2021-04-05 16:13:21 -07:00
										 |  |  |     prepare( | 
					
						
							|  |  |  |       db, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       `
 | 
					
						
							|  |  |  |       UPDATE messages SET | 
					
						
							|  |  |  |         id = $id, | 
					
						
							|  |  |  |         json = $json, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         body = $body, | 
					
						
							|  |  |  |         conversationId = $conversationId, | 
					
						
							|  |  |  |         expirationStartTimestamp = $expirationStartTimestamp, | 
					
						
							|  |  |  |         expireTimer = $expireTimer, | 
					
						
							|  |  |  |         hasAttachments = $hasAttachments, | 
					
						
							|  |  |  |         hasFileAttachments = $hasFileAttachments, | 
					
						
							|  |  |  |         hasVisualMediaAttachments = $hasVisualMediaAttachments, | 
					
						
							|  |  |  |         isErased = $isErased, | 
					
						
							|  |  |  |         isViewOnce = $isViewOnce, | 
					
						
							|  |  |  |         received_at = $received_at, | 
					
						
							|  |  |  |         schemaVersion = $schemaVersion, | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  |         serverGuid = $serverGuid, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |         sent_at = $sent_at, | 
					
						
							|  |  |  |         source = $source, | 
					
						
							|  |  |  |         sourceUuid = $sourceUuid, | 
					
						
							|  |  |  |         sourceDevice = $sourceDevice, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |         storyId = $storyId, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |         type = $type, | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |         readStatus = $readStatus | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       WHERE id = $id; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ).run(payload); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  |     if (jobToInsert) { | 
					
						
							|  |  |  |       insertJobSync(db, jobToInsert); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |     return id; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const toCreate = { | 
					
						
							|  |  |  |     ...data, | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     id: id || UUID.generate().toString(), | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   }; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   prepare( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     INSERT INTO messages ( | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |       json, | 
					
						
							| 
									
										
										
										
											2021-03-22 18:09:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       body, | 
					
						
							|  |  |  |       conversationId, | 
					
						
							|  |  |  |       expirationStartTimestamp, | 
					
						
							|  |  |  |       expireTimer, | 
					
						
							|  |  |  |       hasAttachments, | 
					
						
							|  |  |  |       hasFileAttachments, | 
					
						
							|  |  |  |       hasVisualMediaAttachments, | 
					
						
							|  |  |  |       isErased, | 
					
						
							|  |  |  |       isViewOnce, | 
					
						
							|  |  |  |       received_at, | 
					
						
							|  |  |  |       schemaVersion, | 
					
						
							|  |  |  |       serverGuid, | 
					
						
							|  |  |  |       sent_at, | 
					
						
							|  |  |  |       source, | 
					
						
							|  |  |  |       sourceUuid, | 
					
						
							|  |  |  |       sourceDevice, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       storyId, | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       type, | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |       readStatus | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |     ) values ( | 
					
						
							|  |  |  |       $id, | 
					
						
							|  |  |  |       $json, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       $body, | 
					
						
							|  |  |  |       $conversationId, | 
					
						
							|  |  |  |       $expirationStartTimestamp, | 
					
						
							|  |  |  |       $expireTimer, | 
					
						
							|  |  |  |       $hasAttachments, | 
					
						
							|  |  |  |       $hasFileAttachments, | 
					
						
							|  |  |  |       $hasVisualMediaAttachments, | 
					
						
							|  |  |  |       $isErased, | 
					
						
							|  |  |  |       $isViewOnce, | 
					
						
							|  |  |  |       $received_at, | 
					
						
							|  |  |  |       $schemaVersion, | 
					
						
							|  |  |  |       $serverGuid, | 
					
						
							|  |  |  |       $sent_at, | 
					
						
							|  |  |  |       $source, | 
					
						
							|  |  |  |       $sourceUuid, | 
					
						
							|  |  |  |       $sourceDevice, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       $storyId, | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       $type, | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |       $readStatus | 
					
						
							| 
									
										
										
										
											2021-07-09 16:38:51 -05:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     ...payload, | 
					
						
							|  |  |  |     id: toCreate.id, | 
					
						
							|  |  |  |     json: objectToJSON(toCreate), | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  |   if (jobToInsert) { | 
					
						
							|  |  |  |     insertJobSync(db, jobToInsert); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   return toCreate.id; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | async function saveMessage( | 
					
						
							|  |  |  |   data: MessageType, | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  |   options?: { | 
					
						
							|  |  |  |     jobToInsert?: StoredJob; | 
					
						
							|  |  |  |     forceSave?: boolean; | 
					
						
							|  |  |  |     alreadyInTransaction?: boolean; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | ): Promise<string> { | 
					
						
							|  |  |  |   return saveMessageSync(data, options); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function saveMessages( | 
					
						
							|  |  |  |   arrayOfMessages: Array<MessageType>, | 
					
						
							| 
									
										
										
										
											2021-07-19 13:45:18 -07:00
										 |  |  |   options?: { forceSave?: boolean } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-19 13:45:18 -07:00
										 |  |  |   const { forceSave } = options || {}; | 
					
						
							| 
									
										
										
										
											2018-07-31 19:29:51 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.transaction(() => { | 
					
						
							|  |  |  |     for (const message of arrayOfMessages) { | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  |       assertSync( | 
					
						
							|  |  |  |         saveMessageSync(message, { forceSave, alreadyInTransaction: true }) | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   })(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function removeMessage(id: string): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-06 11:15:17 -07:00
										 |  |  |   db.prepare<Query>('DELETE FROM messages WHERE id = $id;').run({ id }); | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-22 11:44:51 -07:00
										 |  |  | function removeMessagesSync(ids: Array<string>): void { | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-06 11:15:17 -07:00
										 |  |  |   db.prepare<ArrayQuery>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     DELETE FROM messages | 
					
						
							|  |  |  |     WHERE id IN ( ${ids.map(() => '?').join(', ')} ); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run(ids); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-22 11:44:51 -07:00
										 |  |  | async function removeMessages(ids: Array<string>): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   batchMultiVarQuery(getInstance(), ids, removeMessagesSync); | 
					
						
							| 
									
										
										
										
											2021-06-22 11:44:51 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getMessageById(id: string): Promise<MessageType | undefined> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const row = db | 
					
						
							|  |  |  |     .prepare<Query>('SELECT json FROM messages WHERE id = $id;') | 
					
						
							| 
									
										
										
										
											2021-05-17 18:15:09 -05:00
										 |  |  |     .get({ | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     return undefined; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   return jsonToObject(row.json); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  | async function getMessagesById( | 
					
						
							|  |  |  |   messageIds: Array<string> | 
					
						
							|  |  |  | ): Promise<Array<MessageType>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return batchMultiVarQuery( | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     db, | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  |     messageIds, | 
					
						
							|  |  |  |     (batch: Array<string>): Array<MessageType> => { | 
					
						
							|  |  |  |       const query = db.prepare<ArrayQuery>( | 
					
						
							|  |  |  |         `SELECT json FROM messages WHERE id IN (${Array(batch.length) | 
					
						
							|  |  |  |           .fill('?') | 
					
						
							|  |  |  |           .join(',')});`
 | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       const rows: JSONRows = query.all(batch); | 
					
						
							|  |  |  |       return rows.map(row => jsonToObject(row.json)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function _getAllMessages(): Promise<Array<MessageType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const rows: JSONRows = db | 
					
						
							|  |  |  |     .prepare<EmptyQuery>('SELECT json FROM messages ORDER BY id ASC;') | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     .all(); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   return rows.map(row => jsonToObject(row.json)); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  | async function _removeAllMessages(): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   db.prepare<EmptyQuery>('DELETE from messages;').run(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getAllMessageIds(): Promise<Array<string>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows: Array<{ id: string }> = db | 
					
						
							|  |  |  |     .prepare<EmptyQuery>('SELECT id FROM messages ORDER BY id ASC;') | 
					
						
							|  |  |  |     .all(); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   return rows.map(row => row.id); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  | async function getMessageBySender({ | 
					
						
							|  |  |  |   source, | 
					
						
							|  |  |  |   sourceUuid, | 
					
						
							|  |  |  |   sourceDevice, | 
					
						
							|  |  |  |   sent_at, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | }: { | 
					
						
							|  |  |  |   source: string; | 
					
						
							|  |  |  |   sourceUuid: string; | 
					
						
							| 
									
										
										
										
											2021-08-02 14:55:31 -07:00
										 |  |  |   sourceDevice: number; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   sent_at: number; | 
					
						
							| 
									
										
										
										
											2021-12-10 14:51:54 -08:00
										 |  |  | }): Promise<MessageType | undefined> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const rows: JSONRows = prepare( | 
					
						
							| 
									
										
										
										
											2021-04-05 16:13:21 -07:00
										 |  |  |     db, | 
					
						
							|  |  |  |     `
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |     SELECT json FROM messages WHERE | 
					
						
							| 
									
										
										
										
											2021-04-05 16:13:21 -07:00
										 |  |  |       (source = $source OR sourceUuid = $sourceUuid) AND | 
					
						
							|  |  |  |       sourceDevice = $sourceDevice AND | 
					
						
							| 
									
										
										
										
											2021-12-10 14:51:54 -08:00
										 |  |  |       sent_at = $sent_at | 
					
						
							|  |  |  |     LIMIT 2; | 
					
						
							| 
									
										
										
										
											2021-04-05 16:13:21 -07:00
										 |  |  |     `
 | 
					
						
							|  |  |  |   ).all({ | 
					
						
							|  |  |  |     source, | 
					
						
							|  |  |  |     sourceUuid, | 
					
						
							|  |  |  |     sourceDevice, | 
					
						
							|  |  |  |     sent_at, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-10 14:51:54 -08:00
										 |  |  |   if (rows.length > 1) { | 
					
						
							|  |  |  |     log.warn('getMessageBySender: More than one message found for', { | 
					
						
							|  |  |  |       sent_at, | 
					
						
							|  |  |  |       source, | 
					
						
							|  |  |  |       sourceUuid, | 
					
						
							|  |  |  |       sourceDevice, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (rows.length < 1) { | 
					
						
							|  |  |  |     return undefined; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return jsonToObject(rows[0].json); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  | async function getUnreadByConversationAndMarkRead({ | 
					
						
							|  |  |  |   conversationId, | 
					
						
							|  |  |  |   newestUnreadAt, | 
					
						
							|  |  |  |   storyId, | 
					
						
							|  |  |  |   readAt, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   conversationId: string; | 
					
						
							|  |  |  |   newestUnreadAt: number; | 
					
						
							|  |  |  |   storyId?: UUIDStringType; | 
					
						
							|  |  |  |   readAt?: number; | 
					
						
							|  |  |  | }): Promise< | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |   Array<Pick<MessageType, 'id' | 'source' | 'sourceUuid' | 'sent_at' | 'type'>> | 
					
						
							|  |  |  | > { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   return db.transaction(() => { | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |     const expirationStartTimestamp = Math.min(Date.now(), readAt ?? Infinity); | 
					
						
							|  |  |  |     db.prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       UPDATE messages | 
					
						
							| 
									
										
										
										
											2021-06-29 09:45:31 -07:00
										 |  |  |       INDEXED BY expiring_message_by_conversation_and_received_at | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |       SET | 
					
						
							|  |  |  |         expirationStartTimestamp = $expirationStartTimestamp, | 
					
						
							|  |  |  |         json = json_patch(json, $jsonPatch) | 
					
						
							|  |  |  |       WHERE | 
					
						
							|  |  |  |         ( | 
					
						
							|  |  |  |           expirationStartTimestamp IS NULL OR | 
					
						
							|  |  |  |           expirationStartTimestamp > $expirationStartTimestamp | 
					
						
							|  |  |  |         ) AND | 
					
						
							|  |  |  |         expireTimer IS NOT NULL AND | 
					
						
							|  |  |  |         conversationId = $conversationId AND | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |         storyId IS $storyId AND | 
					
						
							|  |  |  |         received_at <= $newestUnreadAt; | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |       `
 | 
					
						
							|  |  |  |     ).run({ | 
					
						
							|  |  |  |       conversationId, | 
					
						
							|  |  |  |       expirationStartTimestamp, | 
					
						
							|  |  |  |       jsonPatch: JSON.stringify({ expirationStartTimestamp }), | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       newestUnreadAt, | 
					
						
							|  |  |  |       storyId: storyId || null, | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |     const rows = db | 
					
						
							|  |  |  |       .prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							| 
									
										
										
										
											2021-06-29 09:45:31 -07:00
										 |  |  |         SELECT id, json FROM messages | 
					
						
							|  |  |  |         INDEXED BY messages_unread | 
					
						
							|  |  |  |         WHERE | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |           readStatus = ${ReadStatus.Unread} AND | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |           conversationId = $conversationId AND | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |           storyId IS $storyId AND | 
					
						
							|  |  |  |           received_at <= $newestUnreadAt | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |         ORDER BY received_at DESC, sent_at DESC; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |       .all({ | 
					
						
							|  |  |  |         conversationId, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |         newestUnreadAt, | 
					
						
							|  |  |  |         storyId: storyId || null, | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-07 14:50:14 -07:00
										 |  |  |     db.prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |         UPDATE messages | 
					
						
							|  |  |  |         SET | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |           readStatus = ${ReadStatus.Read}, | 
					
						
							| 
									
										
										
										
											2021-05-07 14:50:14 -07:00
										 |  |  |           json = json_patch(json, $jsonPatch) | 
					
						
							|  |  |  |         WHERE | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |           readStatus = ${ReadStatus.Unread} AND | 
					
						
							| 
									
										
										
										
											2021-05-07 14:50:14 -07:00
										 |  |  |           conversationId = $conversationId AND | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |           storyId IS $storyId AND | 
					
						
							|  |  |  |           received_at <= $newestUnreadAt; | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |         `
 | 
					
						
							| 
									
										
										
										
											2021-05-07 14:50:14 -07:00
										 |  |  |     ).run({ | 
					
						
							|  |  |  |       conversationId, | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |       jsonPatch: JSON.stringify({ readStatus: ReadStatus.Read }), | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       newestUnreadAt, | 
					
						
							|  |  |  |       storyId: storyId || null, | 
					
						
							| 
									
										
										
										
											2021-05-07 14:50:14 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return rows.map(row => { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |       const json = jsonToObject<MessageType>(row.json); | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       return { | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |         readStatus: ReadStatus.Read, | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |         ...pick(json, [ | 
					
						
							|  |  |  |           'expirationStartTimestamp', | 
					
						
							|  |  |  |           'id', | 
					
						
							|  |  |  |           'sent_at', | 
					
						
							|  |  |  |           'source', | 
					
						
							|  |  |  |           'sourceUuid', | 
					
						
							|  |  |  |           'type', | 
					
						
							|  |  |  |         ]), | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       }; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   })(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  | type ReactionResultType = Pick< | 
					
						
							|  |  |  |   ReactionType, | 
					
						
							|  |  |  |   'targetAuthorUuid' | 'targetTimestamp' | 'messageId' | 
					
						
							|  |  |  | > & { rowid: number }; | 
					
						
							|  |  |  | async function getUnreadReactionsAndMarkRead({ | 
					
						
							|  |  |  |   conversationId, | 
					
						
							|  |  |  |   newestUnreadAt, | 
					
						
							|  |  |  |   storyId, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   conversationId: string; | 
					
						
							|  |  |  |   newestUnreadAt: number; | 
					
						
							|  |  |  |   storyId?: UUIDStringType; | 
					
						
							|  |  |  | }): Promise<Array<ReactionResultType>> { | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |   return db.transaction(() => { | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |     const unreadMessages: Array<ReactionResultType> = db | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       .prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |         SELECT rowid, targetAuthorUuid, targetTimestamp, messageId | 
					
						
							|  |  |  |         FROM reactions | 
					
						
							|  |  |  |         JOIN messages on messages.id IS reactions.messageId | 
					
						
							|  |  |  |         WHERE | 
					
						
							|  |  |  |           unread IS NOT 0 AND | 
					
						
							|  |  |  |           messages.conversationId IS $conversationId AND | 
					
						
							|  |  |  |           messages.received_at <= $newestUnreadAt AND | 
					
						
							|  |  |  |           messages.storyId IS $storyId | 
					
						
							|  |  |  |         ORDER BY messageReceivedAt DESC; | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       `
 | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |       .all({ | 
					
						
							|  |  |  |         conversationId, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |         newestUnreadAt, | 
					
						
							|  |  |  |         storyId: storyId || null, | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |     const idsToUpdate = unreadMessages.map(item => item.rowid); | 
					
						
							|  |  |  |     batchMultiVarQuery(db, idsToUpdate, (ids: Array<number>): void => { | 
					
						
							|  |  |  |       db.prepare<ArrayQuery>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         UPDATE reactions SET | 
					
						
							|  |  |  |         unread = 0 WHERE rowid IN ( ${ids.map(() => '?').join(', ')} ); | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ).run(idsToUpdate); | 
					
						
							| 
									
										
										
										
											2021-05-17 12:52:09 -04:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return unreadMessages; | 
					
						
							|  |  |  |   })(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function markReactionAsRead( | 
					
						
							|  |  |  |   targetAuthorUuid: string, | 
					
						
							|  |  |  |   targetTimestamp: number | 
					
						
							|  |  |  | ): Promise<ReactionType | undefined> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   return db.transaction(() => { | 
					
						
							|  |  |  |     const readReaction = db | 
					
						
							|  |  |  |       .prepare( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |           SELECT * | 
					
						
							|  |  |  |           FROM reactions | 
					
						
							|  |  |  |           WHERE | 
					
						
							|  |  |  |             targetAuthorUuid = $targetAuthorUuid AND | 
					
						
							|  |  |  |             targetTimestamp = $targetTimestamp AND | 
					
						
							|  |  |  |             unread = 1 | 
					
						
							|  |  |  |           ORDER BY rowId DESC | 
					
						
							|  |  |  |           LIMIT 1; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |       .get({ | 
					
						
							|  |  |  |         targetAuthorUuid, | 
					
						
							|  |  |  |         targetTimestamp, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     db.prepare( | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |         UPDATE reactions SET | 
					
						
							|  |  |  |         unread = 0 WHERE | 
					
						
							| 
									
										
										
										
											2021-05-17 12:52:09 -04:00
										 |  |  |         targetAuthorUuid = $targetAuthorUuid AND | 
					
						
							|  |  |  |         targetTimestamp = $targetTimestamp; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |     ).run({ | 
					
						
							|  |  |  |       targetAuthorUuid, | 
					
						
							|  |  |  |       targetTimestamp, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return readReaction; | 
					
						
							|  |  |  |   })(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function addReaction({ | 
					
						
							|  |  |  |   conversationId, | 
					
						
							|  |  |  |   emoji, | 
					
						
							|  |  |  |   fromId, | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   messageId, | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |   messageReceivedAt, | 
					
						
							|  |  |  |   targetAuthorUuid, | 
					
						
							|  |  |  |   targetTimestamp, | 
					
						
							|  |  |  | }: ReactionType): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   await db | 
					
						
							|  |  |  |     .prepare( | 
					
						
							|  |  |  |       `INSERT INTO reactions (
 | 
					
						
							|  |  |  |       conversationId, | 
					
						
							|  |  |  |       emoji, | 
					
						
							|  |  |  |       fromId, | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |       messageId, | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       messageReceivedAt, | 
					
						
							|  |  |  |       targetAuthorUuid, | 
					
						
							|  |  |  |       targetTimestamp, | 
					
						
							|  |  |  |       unread | 
					
						
							|  |  |  |     ) VALUES ( | 
					
						
							|  |  |  |       $conversationId, | 
					
						
							|  |  |  |       $emoji, | 
					
						
							|  |  |  |       $fromId, | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |       $messageId, | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       $messageReceivedAt, | 
					
						
							|  |  |  |       $targetAuthorUuid, | 
					
						
							|  |  |  |       $targetTimestamp, | 
					
						
							|  |  |  |       $unread | 
					
						
							|  |  |  |     );`
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |     .run({ | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       conversationId, | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       emoji, | 
					
						
							|  |  |  |       fromId, | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |       messageId, | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       messageReceivedAt, | 
					
						
							|  |  |  |       targetAuthorUuid, | 
					
						
							|  |  |  |       targetTimestamp, | 
					
						
							|  |  |  |       unread: 1, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  | async function removeReactionFromConversation({ | 
					
						
							|  |  |  |   emoji, | 
					
						
							|  |  |  |   fromId, | 
					
						
							|  |  |  |   targetAuthorUuid, | 
					
						
							|  |  |  |   targetTimestamp, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   emoji: string; | 
					
						
							|  |  |  |   fromId: string; | 
					
						
							|  |  |  |   targetAuthorUuid: string; | 
					
						
							|  |  |  |   targetTimestamp: number; | 
					
						
							|  |  |  | }): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   await db | 
					
						
							|  |  |  |     .prepare( | 
					
						
							|  |  |  |       `DELETE FROM reactions WHERE
 | 
					
						
							|  |  |  |       emoji = $emoji AND | 
					
						
							|  |  |  |       fromId = $fromId AND | 
					
						
							|  |  |  |       targetAuthorUuid = $targetAuthorUuid AND | 
					
						
							|  |  |  |       targetTimestamp = $targetTimestamp;`
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .run({ | 
					
						
							|  |  |  |       emoji, | 
					
						
							|  |  |  |       fromId, | 
					
						
							|  |  |  |       targetAuthorUuid, | 
					
						
							|  |  |  |       targetTimestamp, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-15 15:54:31 -07:00
										 |  |  | async function _getAllReactions(): Promise<Array<ReactionType>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   return db.prepare<EmptyQuery>('SELECT * from reactions;').all(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  | async function _removeAllReactions(): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   db.prepare<EmptyQuery>('DELETE from reactions;').run(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-15 15:54:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | async function getOlderMessagesByConversation( | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   conversationId: string, | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     limit = 100, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |     messageId, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     receivedAt = Number.MAX_VALUE, | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |     sentAt = Number.MAX_VALUE, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |     storyId, | 
					
						
							| 
									
										
										
										
											2020-09-24 13:57:54 -07:00
										 |  |  |   }: { | 
					
						
							|  |  |  |     limit?: number; | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |     messageId?: string; | 
					
						
							| 
									
										
										
										
											2020-09-24 13:57:54 -07:00
										 |  |  |     receivedAt?: number; | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |     sentAt?: number; | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |     storyId?: UUIDStringType; | 
					
						
							| 
									
										
										
										
											2020-09-24 13:57:54 -07:00
										 |  |  |   } = {} | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  | ): Promise<Array<MessageTypeUnhydrated>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2020-07-06 10:06:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   return db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT json FROM messages WHERE | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |         conversationId = $conversationId AND | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |         ($messageId IS NULL OR id IS NOT $messageId) AND | 
					
						
							|  |  |  |         type IS NOT 'story' AND | 
					
						
							|  |  |  |         storyId IS $storyId AND | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |         ( | 
					
						
							|  |  |  |           (received_at = $received_at AND sent_at < $sent_at) OR | 
					
						
							|  |  |  |           received_at < $received_at | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       ORDER BY received_at DESC, sent_at DESC | 
					
						
							|  |  |  |       LIMIT $limit; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ | 
					
						
							|  |  |  |       conversationId, | 
					
						
							|  |  |  |       limit, | 
					
						
							|  |  |  |       messageId: messageId || null, | 
					
						
							|  |  |  |       received_at: receivedAt, | 
					
						
							|  |  |  |       sent_at: sentAt, | 
					
						
							|  |  |  |       storyId: storyId || null, | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |     .reverse(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function getOlderStories({ | 
					
						
							|  |  |  |   conversationId, | 
					
						
							|  |  |  |   limit = 10, | 
					
						
							|  |  |  |   receivedAt = Number.MAX_VALUE, | 
					
						
							|  |  |  |   sentAt, | 
					
						
							|  |  |  |   sourceUuid, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   conversationId?: string; | 
					
						
							|  |  |  |   limit?: number; | 
					
						
							|  |  |  |   receivedAt?: number; | 
					
						
							|  |  |  |   sentAt?: number; | 
					
						
							|  |  |  |   sourceUuid?: string; | 
					
						
							|  |  |  | }): Promise<Array<MessageType>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const rows: JSONRows = db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT json | 
					
						
							|  |  |  |       FROM messages | 
					
						
							|  |  |  |       WHERE | 
					
						
							|  |  |  |         type IS 'story' AND | 
					
						
							|  |  |  |         ($conversationId IS NULL OR conversationId IS $conversationId) AND | 
					
						
							|  |  |  |         ($sourceUuid IS NULL OR sourceUuid IS $sourceUuid) AND | 
					
						
							|  |  |  |         (received_at < $receivedAt | 
					
						
							|  |  |  |           OR (received_at IS $receivedAt AND sent_at < $sentAt) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       ORDER BY received_at DESC, sent_at DESC | 
					
						
							|  |  |  |       LIMIT $limit; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ | 
					
						
							|  |  |  |       conversationId: conversationId || null, | 
					
						
							|  |  |  |       receivedAt, | 
					
						
							|  |  |  |       sentAt: sentAt || null, | 
					
						
							|  |  |  |       sourceUuid: sourceUuid || null, | 
					
						
							|  |  |  |       limit, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   return rows.map(row => jsonToObject(row.json)); | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function getNewerMessagesByConversation( | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   conversationId: string, | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |   { | 
					
						
							|  |  |  |     limit = 100, | 
					
						
							|  |  |  |     receivedAt = 0, | 
					
						
							|  |  |  |     sentAt = 0, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |     storyId, | 
					
						
							|  |  |  |   }: { | 
					
						
							|  |  |  |     limit?: number; | 
					
						
							|  |  |  |     receivedAt?: number; | 
					
						
							|  |  |  |     sentAt?: number; | 
					
						
							|  |  |  |     storyId?: UUIDStringType; | 
					
						
							|  |  |  |   } = {} | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  | ): Promise<Array<MessageTypeUnhydrated>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const rows: JSONRows = db | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       SELECT json FROM messages WHERE | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |         conversationId = $conversationId AND | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |         type IS NOT 'story' AND | 
					
						
							|  |  |  |         storyId IS $storyId AND | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |         ( | 
					
						
							|  |  |  |           (received_at = $received_at AND sent_at > $sent_at) OR | 
					
						
							|  |  |  |           received_at > $received_at | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       ORDER BY received_at ASC, sent_at ASC | 
					
						
							|  |  |  |       LIMIT $limit; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ | 
					
						
							|  |  |  |       conversationId, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       limit, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       received_at: receivedAt, | 
					
						
							|  |  |  |       sent_at: sentAt, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       storyId: storyId || null, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-07-09 16:38:51 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   return rows; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | function getOldestMessageForConversation( | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   conversationId: string, | 
					
						
							|  |  |  |   storyId?: UUIDStringType | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): MessageMetricsType | undefined { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const row = db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT * FROM messages WHERE | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |         conversationId = $conversationId AND | 
					
						
							|  |  |  |         type IS NOT 'story' AND | 
					
						
							|  |  |  |         storyId IS $storyId | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       ORDER BY received_at ASC, sent_at ASC | 
					
						
							|  |  |  |       LIMIT 1; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .get({ | 
					
						
							|  |  |  |       conversationId, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       storyId: storyId || null, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     return undefined; | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | function getNewestMessageForConversation( | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   conversationId: string, | 
					
						
							|  |  |  |   storyId?: UUIDStringType | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): MessageMetricsType | undefined { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const row = db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT * FROM messages WHERE | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |         conversationId = $conversationId AND | 
					
						
							|  |  |  |         type IS NOT 'story' AND | 
					
						
							|  |  |  |         storyId IS $storyId | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       ORDER BY received_at DESC, sent_at DESC | 
					
						
							|  |  |  |       LIMIT 1; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .get({ | 
					
						
							|  |  |  |       conversationId, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       storyId: storyId || null, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     return undefined; | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-16 09:56:27 -07:00
										 |  |  | function getLastConversationActivity({ | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  |   conversationId, | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   ourUuid, | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  | }: { | 
					
						
							|  |  |  |   conversationId: string; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   ourUuid: UUIDStringType; | 
					
						
							| 
									
										
										
										
											2021-08-16 09:56:27 -07:00
										 |  |  | }): MessageType | undefined { | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const row = prepare( | 
					
						
							| 
									
										
										
										
											2021-04-08 12:16:10 -07:00
										 |  |  |     db, | 
					
						
							|  |  |  |     `
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       SELECT json FROM messages | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       WHERE | 
					
						
							|  |  |  |         conversationId = $conversationId AND | 
					
						
							|  |  |  |         (type IS NULL | 
					
						
							|  |  |  |           OR | 
					
						
							|  |  |  |           type NOT IN ( | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |             'change-number-notification', | 
					
						
							| 
									
										
										
										
											2021-06-01 13:45:43 -07:00
										 |  |  |             'group-v1-migration', | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |             'keychange', | 
					
						
							|  |  |  |             'message-history-unsynced', | 
					
						
							|  |  |  |             'profile-change', | 
					
						
							|  |  |  |             'story', | 
					
						
							| 
									
										
										
										
											2021-08-05 16:34:49 -07:00
										 |  |  |             'universal-timer-notification', | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |             'verified-change' | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |           ) | 
					
						
							|  |  |  |         ) AND | 
					
						
							|  |  |  |         ( | 
					
						
							|  |  |  |           json_extract(json, '$.expirationTimerUpdate.fromSync') IS NULL | 
					
						
							|  |  |  |           OR | 
					
						
							|  |  |  |           json_extract(json, '$.expirationTimerUpdate.fromSync') != 1 | 
					
						
							|  |  |  |         ) AND NOT | 
					
						
							|  |  |  |         ( | 
					
						
							| 
									
										
										
										
											2021-11-18 15:15:38 -08:00
										 |  |  |           type IS 'group-v2-change' AND | 
					
						
							|  |  |  |           json_extract(json, '$.groupV2Change.from') IS NOT $ourUuid AND | 
					
						
							|  |  |  |           json_extract(json, '$.groupV2Change.details.length') IS 1 AND | 
					
						
							|  |  |  |           json_extract(json, '$.groupV2Change.details[0].type') IS 'member-remove' AND | 
					
						
							|  |  |  |           json_extract(json, '$.groupV2Change.details[0].uuid') IS NOT $ourUuid | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       ORDER BY received_at DESC, sent_at DESC | 
					
						
							|  |  |  |       LIMIT 1; | 
					
						
							|  |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-04-08 12:16:10 -07:00
										 |  |  |   ).get({ | 
					
						
							|  |  |  |     conversationId, | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     ourUuid, | 
					
						
							| 
									
										
										
										
											2021-04-08 12:16:10 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     return undefined; | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   return jsonToObject(row.json); | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-08-16 09:56:27 -07:00
										 |  |  | function getLastConversationPreview({ | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  |   conversationId, | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   ourUuid, | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  | }: { | 
					
						
							|  |  |  |   conversationId: string; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   ourUuid: UUIDStringType; | 
					
						
							| 
									
										
										
										
											2021-08-16 09:56:27 -07:00
										 |  |  | }): MessageType | undefined { | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const row = prepare( | 
					
						
							| 
									
										
										
										
											2021-04-08 12:16:10 -07:00
										 |  |  |     db, | 
					
						
							|  |  |  |     `
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       SELECT json FROM messages | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       WHERE | 
					
						
							|  |  |  |         conversationId = $conversationId AND | 
					
						
							| 
									
										
										
										
											2021-09-20 12:57:59 -05:00
										 |  |  |         ( | 
					
						
							|  |  |  |           expiresAt IS NULL OR | 
					
						
							|  |  |  |           (expiresAt > $now) | 
					
						
							|  |  |  |         ) AND | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |         ( | 
					
						
							|  |  |  |           type IS NULL | 
					
						
							|  |  |  |           OR | 
					
						
							|  |  |  |           type NOT IN ( | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |             'change-number-notification', | 
					
						
							| 
									
										
										
										
											2021-06-01 13:45:43 -07:00
										 |  |  |             'group-v1-migration', | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |             'message-history-unsynced', | 
					
						
							|  |  |  |             'profile-change', | 
					
						
							|  |  |  |             'story', | 
					
						
							| 
									
										
										
										
											2021-08-05 16:34:49 -07:00
										 |  |  |             'universal-timer-notification', | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |             'verified-change' | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |           ) | 
					
						
							|  |  |  |         ) AND NOT | 
					
						
							|  |  |  |         ( | 
					
						
							| 
									
										
										
										
											2021-11-18 15:15:38 -08:00
										 |  |  |           type IS 'group-v2-change' AND | 
					
						
							|  |  |  |           json_extract(json, '$.groupV2Change.from') IS NOT $ourUuid AND | 
					
						
							|  |  |  |           json_extract(json, '$.groupV2Change.details.length') IS 1 AND | 
					
						
							|  |  |  |           json_extract(json, '$.groupV2Change.details[0].type') IS 'member-remove' AND | 
					
						
							|  |  |  |           json_extract(json, '$.groupV2Change.details[0].uuid') IS NOT $ourUuid | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       ORDER BY received_at DESC, sent_at DESC | 
					
						
							|  |  |  |       LIMIT 1; | 
					
						
							|  |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-04-08 12:16:10 -07:00
										 |  |  |   ).get({ | 
					
						
							|  |  |  |     conversationId, | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     ourUuid, | 
					
						
							| 
									
										
										
										
											2021-09-20 12:57:59 -05:00
										 |  |  |     now: Date.now(), | 
					
						
							| 
									
										
										
										
											2021-04-08 12:16:10 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     return undefined; | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   return jsonToObject(row.json); | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-08-16 09:56:27 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | async function getLastConversationMessages({ | 
					
						
							|  |  |  |   conversationId, | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   ourUuid, | 
					
						
							| 
									
										
										
										
											2021-08-16 09:56:27 -07:00
										 |  |  | }: { | 
					
						
							|  |  |  |   conversationId: string; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   ourUuid: UUIDStringType; | 
					
						
							| 
									
										
										
										
											2021-12-10 14:51:54 -08:00
										 |  |  | }): Promise<LastConversationMessagesType> { | 
					
						
							| 
									
										
										
										
											2021-08-16 09:56:27 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return db.transaction(() => { | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       activity: getLastConversationActivity({ | 
					
						
							|  |  |  |         conversationId, | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |         ourUuid, | 
					
						
							| 
									
										
										
										
											2021-08-16 09:56:27 -07:00
										 |  |  |       }), | 
					
						
							|  |  |  |       preview: getLastConversationPreview({ | 
					
						
							|  |  |  |         conversationId, | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |         ourUuid, | 
					
						
							| 
									
										
										
										
											2021-08-16 09:56:27 -07:00
										 |  |  |       }), | 
					
						
							|  |  |  |       hasUserInitiatedMessages: hasUserInitiatedMessages(conversationId), | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   })(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | function getOldestUnreadMessageForConversation( | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   conversationId: string, | 
					
						
							|  |  |  |   storyId?: UUIDStringType | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): MessageMetricsType | undefined { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const row = db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT * FROM messages WHERE | 
					
						
							|  |  |  |         conversationId = $conversationId AND | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |         readStatus = ${ReadStatus.Unread} AND | 
					
						
							|  |  |  |         type IS NOT 'story' AND | 
					
						
							|  |  |  |         storyId IS $storyId | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       ORDER BY received_at ASC, sent_at ASC | 
					
						
							|  |  |  |       LIMIT 1; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .get({ | 
					
						
							|  |  |  |       conversationId, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       storyId: storyId || null, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     return undefined; | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  | async function getTotalUnreadForConversation( | 
					
						
							|  |  |  |   conversationId: string, | 
					
						
							|  |  |  |   storyId?: UUIDStringType | 
					
						
							|  |  |  | ): Promise<number> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const row = db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT count(id) | 
					
						
							|  |  |  |       FROM messages | 
					
						
							|  |  |  |       WHERE | 
					
						
							|  |  |  |         conversationId = $conversationId AND | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |         readStatus = ${ReadStatus.Unread} AND | 
					
						
							|  |  |  |         type IS NOT 'story' AND | 
					
						
							|  |  |  |         storyId IS $storyId; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .get({ | 
					
						
							|  |  |  |       conversationId, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       storyId: storyId || null, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							|  |  |  |     throw new Error('getTotalUnreadForConversation: Unable to get count'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row['count(id)']; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getMessageMetricsForConversation( | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   conversationId: string, | 
					
						
							|  |  |  |   storyId?: UUIDStringType | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<ConversationMetricsType> { | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   const oldest = getOldestMessageForConversation(conversationId, storyId); | 
					
						
							|  |  |  |   const newest = getNewestMessageForConversation(conversationId, storyId); | 
					
						
							|  |  |  |   const oldestUnread = getOldestUnreadMessageForConversation( | 
					
						
							|  |  |  |     conversationId, | 
					
						
							|  |  |  |     storyId | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   const totalUnread = await getTotalUnreadForConversation( | 
					
						
							|  |  |  |     conversationId, | 
					
						
							|  |  |  |     storyId | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     oldest: oldest ? pick(oldest, ['received_at', 'sent_at', 'id']) : undefined, | 
					
						
							|  |  |  |     newest: newest ? pick(newest, ['received_at', 'sent_at', 'id']) : undefined, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     oldestUnread: oldestUnread | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       ? pick(oldestUnread, ['received_at', 'sent_at', 'id']) | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       : undefined, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     totalUnread, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-07 14:43:19 -06:00
										 |  |  | async function hasGroupCallHistoryMessage( | 
					
						
							|  |  |  |   conversationId: string, | 
					
						
							|  |  |  |   eraId: string | 
					
						
							|  |  |  | ): Promise<boolean> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const row: { 'count(*)': number } | undefined = db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT count(*) FROM messages | 
					
						
							|  |  |  |       WHERE conversationId = $conversationId | 
					
						
							|  |  |  |       AND type = 'call-history' | 
					
						
							|  |  |  |       AND json_extract(json, '$.callHistoryDetails.callMode') = 'Group' | 
					
						
							|  |  |  |       AND json_extract(json, '$.callHistoryDetails.eraId') = $eraId | 
					
						
							|  |  |  |       LIMIT 1; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .get({ | 
					
						
							|  |  |  |       conversationId, | 
					
						
							|  |  |  |       eraId, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-12-07 14:43:19 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   if (row) { | 
					
						
							|  |  |  |     return Boolean(row['count(*)']); | 
					
						
							| 
									
										
										
										
											2020-12-07 14:43:19 -06:00
										 |  |  |   } | 
					
						
							|  |  |  |   return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-10 11:28:49 -07:00
										 |  |  | async function migrateConversationMessages( | 
					
						
							|  |  |  |   obsoleteId: string, | 
					
						
							|  |  |  |   currentId: string | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-07-10 11:28:49 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     UPDATE messages SET | 
					
						
							| 
									
										
										
										
											2020-07-10 11:28:49 -07:00
										 |  |  |       conversationId = $currentId, | 
					
						
							|  |  |  |       json = json_set(json, '$.conversationId', $currentId) | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     WHERE conversationId = $obsoleteId; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     obsoleteId, | 
					
						
							|  |  |  |     currentId, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-07-10 11:28:49 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getMessagesBySentAt( | 
					
						
							|  |  |  |   sentAt: number | 
					
						
							|  |  |  | ): Promise<Array<MessageType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const rows: JSONRows = db | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       SELECT json FROM messages | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       WHERE sent_at = $sent_at | 
					
						
							|  |  |  |       ORDER BY received_at DESC, sent_at DESC; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ | 
					
						
							|  |  |  |       sent_at: sentAt, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   return rows.map(row => jsonToObject(row.json)); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getExpiredMessages(): Promise<Array<MessageType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const now = Date.now(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const rows: JSONRows = db | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       SELECT json FROM messages WHERE | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |         expiresAt IS NOT NULL AND | 
					
						
							|  |  |  |         expiresAt <= $now | 
					
						
							|  |  |  |       ORDER BY expiresAt ASC; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |     .all({ now }); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   return rows.map(row => jsonToObject(row.json)); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 14:12:04 -05:00
										 |  |  | async function getMessagesUnexpectedlyMissingExpirationStartTimestamp(): Promise< | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |   Array<MessageType> | 
					
						
							|  |  |  | > { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const rows: JSONRows = db | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     .prepare<EmptyQuery>( | 
					
						
							|  |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       SELECT json FROM messages | 
					
						
							|  |  |  |       INDEXED BY messages_unexpectedly_missing_expiration_start_timestamp | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       WHERE | 
					
						
							|  |  |  |         expireTimer > 0 AND | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |         expirationStartTimestamp IS NULL AND | 
					
						
							| 
									
										
										
										
											2021-06-18 14:12:04 -05:00
										 |  |  |         ( | 
					
						
							|  |  |  |           type IS 'outgoing' OR | 
					
						
							|  |  |  |           (type IS 'incoming' AND ( | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |             readStatus = ${ReadStatus.Read} OR | 
					
						
							|  |  |  |             readStatus = ${ReadStatus.Viewed} OR | 
					
						
							|  |  |  |             readStatus IS NULL | 
					
						
							| 
									
										
										
										
											2021-06-18 14:12:04 -05:00
										 |  |  |           )) | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all(); | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   return rows.map(row => jsonToObject(row.json)); | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  | async function getSoonestMessageExpiry(): Promise<undefined | number> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |   // Note: we use `pluck` to only get the first column.
 | 
					
						
							|  |  |  |   const result: null | number = db | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     .prepare<EmptyQuery>( | 
					
						
							|  |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |       SELECT MIN(expiresAt) | 
					
						
							|  |  |  |       FROM messages; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |     .pluck(true) | 
					
						
							|  |  |  |     .get(); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-16 17:20:17 -05:00
										 |  |  |   return result || undefined; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-19 11:17:51 -05:00
										 |  |  | async function getNextTapToViewMessageTimestampToAgeOut(): Promise< | 
					
						
							|  |  |  |   undefined | number | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | > { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-05-19 11:17:51 -05:00
										 |  |  |   const row = db | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     .prepare<EmptyQuery>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT json FROM messages | 
					
						
							|  |  |  |       WHERE | 
					
						
							|  |  |  |         isViewOnce = 1 | 
					
						
							|  |  |  |         AND (isErased IS NULL OR isErased != 1) | 
					
						
							|  |  |  |       ORDER BY received_at ASC, sent_at ASC | 
					
						
							|  |  |  |       LIMIT 1; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-05-19 11:17:51 -05:00
										 |  |  |     .get(); | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-19 11:17:51 -05:00
										 |  |  |   if (!row) { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     return undefined; | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   const data = jsonToObject<MessageType>(row.json); | 
					
						
							| 
									
										
										
										
											2021-05-19 11:17:51 -05:00
										 |  |  |   const result = data.received_at_ms || data.received_at; | 
					
						
							|  |  |  |   return isNormalNumber(result) ? result : undefined; | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getTapToViewMessagesNeedingErase(): Promise<Array<MessageType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |   const THIRTY_DAYS_AGO = Date.now() - 30 * 24 * 60 * 60 * 1000; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const rows: JSONRows = db | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       SELECT json | 
					
						
							|  |  |  |       FROM messages | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       WHERE | 
					
						
							|  |  |  |         isViewOnce = 1 | 
					
						
							|  |  |  |         AND (isErased IS NULL OR isErased != 1) | 
					
						
							|  |  |  |         AND received_at <= $THIRTY_DAYS_AGO | 
					
						
							|  |  |  |       ORDER BY received_at ASC, sent_at ASC; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ | 
					
						
							|  |  |  |       THIRTY_DAYS_AGO, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   return rows.map(row => jsonToObject(row.json)); | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-17 16:11:24 -07:00
										 |  |  | const MAX_UNPROCESSED_ATTEMPTS = 3; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 11:03:42 -07:00
										 |  |  | function saveUnprocessedSync(data: UnprocessedType): string { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-05-17 11:03:42 -07:00
										 |  |  |   const { | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     timestamp, | 
					
						
							|  |  |  |     version, | 
					
						
							|  |  |  |     attempts, | 
					
						
							|  |  |  |     envelope, | 
					
						
							|  |  |  |     source, | 
					
						
							|  |  |  |     sourceUuid, | 
					
						
							|  |  |  |     sourceDevice, | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  |     serverGuid, | 
					
						
							| 
									
										
										
										
											2021-05-17 11:03:42 -07:00
										 |  |  |     serverTimestamp, | 
					
						
							|  |  |  |     decrypted, | 
					
						
							|  |  |  |   } = data; | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |   if (!id) { | 
					
						
							| 
									
										
										
										
											2021-05-27 11:45:45 -04:00
										 |  |  |     throw new Error('saveUnprocessedSync: id was falsey'); | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-17 16:11:24 -07:00
										 |  |  |   if (attempts >= MAX_UNPROCESSED_ATTEMPTS) { | 
					
						
							|  |  |  |     removeUnprocessedSync(id); | 
					
						
							|  |  |  |     return id; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-17 11:03:42 -07:00
										 |  |  |   prepare( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     INSERT OR REPLACE INTO unprocessed ( | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       id, | 
					
						
							|  |  |  |       timestamp, | 
					
						
							|  |  |  |       version, | 
					
						
							|  |  |  |       attempts, | 
					
						
							|  |  |  |       envelope, | 
					
						
							| 
									
										
										
										
											2021-05-17 11:03:42 -07:00
										 |  |  |       source, | 
					
						
							|  |  |  |       sourceUuid, | 
					
						
							|  |  |  |       sourceDevice, | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  |       serverGuid, | 
					
						
							| 
									
										
										
										
											2021-05-17 11:03:42 -07:00
										 |  |  |       serverTimestamp, | 
					
						
							|  |  |  |       decrypted | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $id, | 
					
						
							|  |  |  |       $timestamp, | 
					
						
							|  |  |  |       $version, | 
					
						
							|  |  |  |       $attempts, | 
					
						
							|  |  |  |       $envelope, | 
					
						
							|  |  |  |       $source, | 
					
						
							|  |  |  |       $sourceUuid, | 
					
						
							|  |  |  |       $sourceDevice, | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  |       $serverGuid, | 
					
						
							| 
									
										
										
										
											2021-05-17 11:03:42 -07:00
										 |  |  |       $serverTimestamp, | 
					
						
							|  |  |  |       $decrypted | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     timestamp, | 
					
						
							|  |  |  |     version, | 
					
						
							|  |  |  |     attempts, | 
					
						
							| 
									
										
										
										
											2021-05-17 11:03:42 -07:00
										 |  |  |     envelope: envelope || null, | 
					
						
							|  |  |  |     source: source || null, | 
					
						
							|  |  |  |     sourceUuid: sourceUuid || null, | 
					
						
							|  |  |  |     sourceDevice: sourceDevice || null, | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  |     serverGuid: serverGuid || null, | 
					
						
							| 
									
										
										
										
											2021-05-17 11:03:42 -07:00
										 |  |  |     serverTimestamp: serverTimestamp || null, | 
					
						
							|  |  |  |     decrypted: decrypted || null, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | function updateUnprocessedWithDataSync( | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   id: string, | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  |   data: UnprocessedUpdateType | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | ): void { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  |   const { | 
					
						
							|  |  |  |     source, | 
					
						
							|  |  |  |     sourceUuid, | 
					
						
							|  |  |  |     sourceDevice, | 
					
						
							|  |  |  |     serverGuid, | 
					
						
							|  |  |  |     serverTimestamp, | 
					
						
							|  |  |  |     decrypted, | 
					
						
							|  |  |  |   } = data; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 16:13:21 -07:00
										 |  |  |   prepare( | 
					
						
							|  |  |  |     db, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     `
 | 
					
						
							|  |  |  |     UPDATE unprocessed SET | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |       source = $source, | 
					
						
							| 
									
										
										
										
											2021-02-04 11:43:10 -08:00
										 |  |  |       sourceUuid = $sourceUuid, | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |       sourceDevice = $sourceDevice, | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  |       serverGuid = $serverGuid, | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |       serverTimestamp = $serverTimestamp, | 
					
						
							|  |  |  |       decrypted = $decrypted | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     WHERE id = $id; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     source: source || null, | 
					
						
							|  |  |  |     sourceUuid: sourceUuid || null, | 
					
						
							|  |  |  |     sourceDevice: sourceDevice || null, | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  |     serverGuid: serverGuid || null, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     serverTimestamp: serverTimestamp || null, | 
					
						
							|  |  |  |     decrypted: decrypted || null, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | async function updateUnprocessedWithData( | 
					
						
							|  |  |  |   id: string, | 
					
						
							|  |  |  |   data: UnprocessedUpdateType | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							|  |  |  |   return updateUnprocessedWithDataSync(id, data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateUnprocessedsWithData( | 
					
						
							| 
									
										
										
										
											2021-04-16 16:13:13 -07:00
										 |  |  |   arrayOfUnprocessed: Array<{ id: string; data: UnprocessedUpdateType }> | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.transaction(() => { | 
					
						
							|  |  |  |     for (const { id, data } of arrayOfUnprocessed) { | 
					
						
							| 
									
										
										
										
											2021-05-14 10:52:47 -07:00
										 |  |  |       assertSync(updateUnprocessedWithDataSync(id, data)); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   })(); | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getUnprocessedById( | 
					
						
							|  |  |  |   id: string | 
					
						
							|  |  |  | ): Promise<UnprocessedType | undefined> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const row = db | 
					
						
							|  |  |  |     .prepare<Query>('SELECT * FROM unprocessed WHERE id = $id;') | 
					
						
							|  |  |  |     .get({ | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return row; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getUnprocessedCount(): Promise<number> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return getCountFromTable(getInstance(), 'unprocessed'); | 
					
						
							| 
									
										
										
										
											2018-09-28 15:51:26 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getAllUnprocessed(): Promise<Array<UnprocessedType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows = db | 
					
						
							|  |  |  |     .prepare<EmptyQuery>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT * | 
					
						
							|  |  |  |       FROM unprocessed | 
					
						
							|  |  |  |       ORDER BY timestamp ASC; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |   return rows; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-22 11:44:51 -07:00
										 |  |  | function removeUnprocessedsSync(ids: Array<string>): void { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-22 11:44:51 -07:00
										 |  |  |   db.prepare<ArrayQuery>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     DELETE FROM unprocessed | 
					
						
							|  |  |  |     WHERE id IN ( ${ids.map(() => '?').join(', ')} ); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run(ids); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-17 16:11:24 -07:00
										 |  |  | function removeUnprocessedSync(id: string | Array<string>): void { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-06-22 11:44:51 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   if (!Array.isArray(id)) { | 
					
						
							| 
									
										
										
										
											2021-04-06 11:15:17 -07:00
										 |  |  |     prepare(db, 'DELETE FROM unprocessed WHERE id = $id;').run({ id }); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!id.length) { | 
					
						
							| 
									
										
										
										
											2021-09-17 16:11:24 -07:00
										 |  |  |     throw new Error('removeUnprocessedSync: No ids to delete!'); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   assertSync(batchMultiVarQuery(db, id, removeUnprocessedsSync)); | 
					
						
							| 
									
										
										
										
											2021-09-17 16:11:24 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function removeUnprocessed(id: string | Array<string>): Promise<void> { | 
					
						
							|  |  |  |   removeUnprocessedSync(id); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function removeAllUnprocessed(): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<EmptyQuery>('DELETE FROM unprocessed;').run(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | // Attachment Downloads
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | const ATTACHMENT_DOWNLOADS_TABLE = 'attachment_downloads'; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getNextAttachmentDownloadJobs( | 
					
						
							|  |  |  |   limit?: number, | 
					
						
							|  |  |  |   options: { timestamp?: number } = {} | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<Array<AttachmentDownloadJobType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const timestamp = | 
					
						
							|  |  |  |     options && options.timestamp ? options.timestamp : Date.now(); | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows: JSONRows = db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT json | 
					
						
							|  |  |  |       FROM attachment_downloads | 
					
						
							| 
									
										
										
										
											2021-06-01 10:13:10 -07:00
										 |  |  |       WHERE pending = 0 AND timestamp <= $timestamp | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       ORDER BY timestamp DESC | 
					
						
							|  |  |  |       LIMIT $limit; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ | 
					
						
							|  |  |  |       limit: limit || 3, | 
					
						
							|  |  |  |       timestamp, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   return rows.map(row => jsonToObject(row.json)); | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function saveAttachmentDownloadJob( | 
					
						
							|  |  |  |   job: AttachmentDownloadJobType | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  |   const { id, pending, timestamp } = job; | 
					
						
							|  |  |  |   if (!id) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'saveAttachmentDownloadJob: Provided job did not have a truthy id' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     INSERT OR REPLACE INTO attachment_downloads ( | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  |       id, | 
					
						
							|  |  |  |       pending, | 
					
						
							|  |  |  |       timestamp, | 
					
						
							|  |  |  |       json | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $id, | 
					
						
							|  |  |  |       $pending, | 
					
						
							|  |  |  |       $timestamp, | 
					
						
							|  |  |  |       $json | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     ) | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     pending, | 
					
						
							|  |  |  |     timestamp, | 
					
						
							|  |  |  |     json: objectToJSON(job), | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function setAttachmentDownloadJobPending( | 
					
						
							|  |  |  |   id: string, | 
					
						
							|  |  |  |   pending: boolean | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     UPDATE attachment_downloads | 
					
						
							|  |  |  |     SET pending = $pending | 
					
						
							|  |  |  |     WHERE id = $id; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     pending: pending ? 1 : 0, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function resetAttachmentDownloadPending(): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<EmptyQuery>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     UPDATE attachment_downloads | 
					
						
							|  |  |  |     SET pending = 0 | 
					
						
							|  |  |  |     WHERE pending != 0; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run(); | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-09 19:38:11 -07:00
										 |  |  | async function removeAttachmentDownloadJob(id: string): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return removeById(getInstance(), ATTACHMENT_DOWNLOADS_TABLE, id); | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  | async function removeAllAttachmentDownloadJobs(): Promise<void> { | 
					
						
							|  |  |  |   return removeAllFromTable(getInstance(), ATTACHMENT_DOWNLOADS_TABLE); | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | // Stickers
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function createOrUpdateStickerPack(pack: StickerPackType): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const { | 
					
						
							|  |  |  |     attemptedStatus, | 
					
						
							|  |  |  |     author, | 
					
						
							|  |  |  |     coverStickerId, | 
					
						
							|  |  |  |     createdAt, | 
					
						
							|  |  |  |     downloadAttempts, | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     installedAt, | 
					
						
							|  |  |  |     key, | 
					
						
							|  |  |  |     lastUsed, | 
					
						
							|  |  |  |     status, | 
					
						
							|  |  |  |     stickerCount, | 
					
						
							|  |  |  |     title, | 
					
						
							|  |  |  |   } = pack; | 
					
						
							|  |  |  |   if (!id) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'createOrUpdateStickerPack: Provided data did not have a truthy id' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows = db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT id | 
					
						
							|  |  |  |       FROM sticker_packs | 
					
						
							|  |  |  |       WHERE id = $id; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ id }); | 
					
						
							| 
									
										
										
										
											2019-05-23 18:27:42 -07:00
										 |  |  |   const payload = { | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  |     attemptedStatus: attemptedStatus ?? null, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     author, | 
					
						
							|  |  |  |     coverStickerId, | 
					
						
							|  |  |  |     createdAt: createdAt || Date.now(), | 
					
						
							|  |  |  |     downloadAttempts: downloadAttempts || 1, | 
					
						
							|  |  |  |     id, | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  |     installedAt: installedAt ?? null, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     key, | 
					
						
							|  |  |  |     lastUsed: lastUsed || null, | 
					
						
							|  |  |  |     status, | 
					
						
							|  |  |  |     stickerCount, | 
					
						
							|  |  |  |     title, | 
					
						
							| 
									
										
										
										
											2019-05-23 18:27:42 -07:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (rows && rows.length) { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     db.prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       UPDATE sticker_packs SET | 
					
						
							| 
									
										
										
										
											2019-05-23 18:27:42 -07:00
										 |  |  |         attemptedStatus = $attemptedStatus, | 
					
						
							|  |  |  |         author = $author, | 
					
						
							|  |  |  |         coverStickerId = $coverStickerId, | 
					
						
							|  |  |  |         createdAt = $createdAt, | 
					
						
							|  |  |  |         downloadAttempts = $downloadAttempts, | 
					
						
							|  |  |  |         installedAt = $installedAt, | 
					
						
							|  |  |  |         key = $key, | 
					
						
							|  |  |  |         lastUsed = $lastUsed, | 
					
						
							|  |  |  |         status = $status, | 
					
						
							|  |  |  |         stickerCount = $stickerCount, | 
					
						
							|  |  |  |         title = $title | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       WHERE id = $id; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ).run(payload); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 18:27:42 -07:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     INSERT INTO sticker_packs ( | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |       attemptedStatus, | 
					
						
							|  |  |  |       author, | 
					
						
							|  |  |  |       coverStickerId, | 
					
						
							|  |  |  |       createdAt, | 
					
						
							|  |  |  |       downloadAttempts, | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |       installedAt, | 
					
						
							|  |  |  |       key, | 
					
						
							|  |  |  |       lastUsed, | 
					
						
							|  |  |  |       status, | 
					
						
							|  |  |  |       stickerCount, | 
					
						
							|  |  |  |       title | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $attemptedStatus, | 
					
						
							|  |  |  |       $author, | 
					
						
							|  |  |  |       $coverStickerId, | 
					
						
							|  |  |  |       $createdAt, | 
					
						
							|  |  |  |       $downloadAttempts, | 
					
						
							|  |  |  |       $id, | 
					
						
							|  |  |  |       $installedAt, | 
					
						
							|  |  |  |       $key, | 
					
						
							|  |  |  |       $lastUsed, | 
					
						
							|  |  |  |       $status, | 
					
						
							|  |  |  |       $stickerCount, | 
					
						
							|  |  |  |       $title | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     ) | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run(payload); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateStickerPackStatus( | 
					
						
							|  |  |  |   id: string, | 
					
						
							|  |  |  |   status: StickerPackStatusType, | 
					
						
							|  |  |  |   options?: { timestamp: number } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const timestamp = options ? options.timestamp || Date.now() : Date.now(); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const installedAt = status === 'installed' ? timestamp : null; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     UPDATE sticker_packs | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |     SET status = $status, installedAt = $installedAt | 
					
						
							|  |  |  |     WHERE id = $id; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     status, | 
					
						
							|  |  |  |     installedAt, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-01-27 14:39:45 -08:00
										 |  |  | async function clearAllErrorStickerPackAttempts(): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<EmptyQuery>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     UPDATE sticker_packs | 
					
						
							|  |  |  |     SET downloadAttempts = 0 | 
					
						
							|  |  |  |     WHERE status = 'error'; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run(); | 
					
						
							| 
									
										
										
										
											2021-01-27 14:39:45 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function createOrUpdateSticker(sticker: StickerType): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-11-11 16:43:05 -06:00
										 |  |  |   const { emoji, height, id, isCoverOnly, lastUsed, packId, path, width } = | 
					
						
							|  |  |  |     sticker; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   if (!isNumber(id)) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'createOrUpdateSticker: Provided data did not have a numeric id' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!packId) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'createOrUpdateSticker: Provided data did not have a truthy id' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     INSERT OR REPLACE INTO stickers ( | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |       emoji, | 
					
						
							|  |  |  |       height, | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |       isCoverOnly, | 
					
						
							|  |  |  |       lastUsed, | 
					
						
							|  |  |  |       packId, | 
					
						
							|  |  |  |       path, | 
					
						
							|  |  |  |       width | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $emoji, | 
					
						
							|  |  |  |       $height, | 
					
						
							|  |  |  |       $id, | 
					
						
							|  |  |  |       $isCoverOnly, | 
					
						
							|  |  |  |       $lastUsed, | 
					
						
							|  |  |  |       $packId, | 
					
						
							|  |  |  |       $path, | 
					
						
							|  |  |  |       $width | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     ) | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							| 
									
										
										
										
											2021-07-09 12:36:10 -07:00
										 |  |  |     emoji: emoji ?? null, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     height, | 
					
						
							|  |  |  |     id, | 
					
						
							| 
									
										
										
										
											2021-04-07 13:00:22 -07:00
										 |  |  |     isCoverOnly: isCoverOnly ? 1 : 0, | 
					
						
							|  |  |  |     lastUsed: lastUsed || null, | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     packId, | 
					
						
							|  |  |  |     path, | 
					
						
							|  |  |  |     width, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateStickerLastUsed( | 
					
						
							|  |  |  |   packId: string, | 
					
						
							|  |  |  |   stickerId: number, | 
					
						
							|  |  |  |   lastUsed: number | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     UPDATE stickers | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |     SET lastUsed = $lastUsed | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     WHERE id = $id AND packId = $packId; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     id: stickerId, | 
					
						
							|  |  |  |     packId, | 
					
						
							|  |  |  |     lastUsed, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     UPDATE sticker_packs | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |     SET lastUsed = $lastUsed | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     WHERE id = $id; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     id: packId, | 
					
						
							|  |  |  |     lastUsed, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function addStickerPackReference( | 
					
						
							|  |  |  |   messageId: string, | 
					
						
							|  |  |  |   packId: string | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   if (!messageId) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'addStickerPackReference: Provided data did not have a truthy messageId' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!packId) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'addStickerPackReference: Provided data did not have a truthy packId' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     INSERT OR REPLACE INTO sticker_references ( | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |       messageId, | 
					
						
							|  |  |  |       packId | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $messageId, | 
					
						
							|  |  |  |       $packId | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     ) | 
					
						
							|  |  |  |     `
 | 
					
						
							| 
									
										
										
										
											2021-04-08 17:50:25 -07:00
										 |  |  |   ).run({ | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     messageId, | 
					
						
							|  |  |  |     packId, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function deleteStickerPackReference( | 
					
						
							|  |  |  |   messageId: string, | 
					
						
							|  |  |  |   packId: string | 
					
						
							| 
									
										
										
										
											2021-07-29 11:59:26 -07:00
										 |  |  | ): Promise<ReadonlyArray<string> | undefined> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   if (!messageId) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'addStickerPackReference: Provided data did not have a truthy messageId' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!packId) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'addStickerPackReference: Provided data did not have a truthy packId' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   return db | 
					
						
							|  |  |  |     .transaction(() => { | 
					
						
							|  |  |  |       // We use an immediate transaction here to immediately acquire an exclusive lock,
 | 
					
						
							|  |  |  |       //   which would normally only happen when we did our first write.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // We need this to ensure that our five queries are all atomic, with no
 | 
					
						
							|  |  |  |       // other changes happening while we do it:
 | 
					
						
							|  |  |  |       // 1. Delete our target messageId/packId references
 | 
					
						
							|  |  |  |       // 2. Check the number of references still pointing at packId
 | 
					
						
							|  |  |  |       // 3. If that number is zero, get pack from sticker_packs database
 | 
					
						
							|  |  |  |       // 4. If it's not installed, then grab all of its sticker paths
 | 
					
						
							|  |  |  |       // 5. If it's not installed, then sticker pack (which cascades to all
 | 
					
						
							|  |  |  |       //    stickers and references)
 | 
					
						
							|  |  |  |       db.prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         DELETE FROM sticker_references | 
					
						
							|  |  |  |         WHERE messageId = $messageId AND packId = $packId; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ).run({ | 
					
						
							|  |  |  |         messageId, | 
					
						
							|  |  |  |         packId, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       const countRow = db | 
					
						
							|  |  |  |         .prepare<Query>( | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |           SELECT count(*) FROM sticker_references | 
					
						
							|  |  |  |           WHERE packId = $packId; | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         .get({ packId }); | 
					
						
							|  |  |  |       if (!countRow) { | 
					
						
							|  |  |  |         throw new Error( | 
					
						
							|  |  |  |           'deleteStickerPackReference: Unable to get count of references' | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const count = countRow['count(*)']; | 
					
						
							|  |  |  |       if (count > 0) { | 
					
						
							| 
									
										
										
										
											2021-07-29 11:59:26 -07:00
										 |  |  |         return undefined; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       const packRow: { status: StickerPackStatusType } = db | 
					
						
							|  |  |  |         .prepare<Query>( | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |           SELECT status FROM sticker_packs | 
					
						
							|  |  |  |           WHERE id = $packId; | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         .get({ packId }); | 
					
						
							|  |  |  |       if (!packRow) { | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |         logger.warn('deleteStickerPackReference: did not find referenced pack'); | 
					
						
							| 
									
										
										
										
											2021-07-29 11:59:26 -07:00
										 |  |  |         return undefined; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       } | 
					
						
							|  |  |  |       const { status } = packRow; | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       if (status === 'installed') { | 
					
						
							| 
									
										
										
										
											2021-07-29 11:59:26 -07:00
										 |  |  |         return undefined; | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       const stickerPathRows: Array<{ path: string }> = db | 
					
						
							|  |  |  |         .prepare<Query>( | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |           SELECT path FROM stickers | 
					
						
							|  |  |  |           WHERE packId = $packId; | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         .all({ | 
					
						
							|  |  |  |           packId, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       db.prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         DELETE FROM sticker_packs | 
					
						
							|  |  |  |         WHERE id = $packId; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ).run({ | 
					
						
							|  |  |  |         packId, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       return (stickerPathRows || []).map(row => row.path); | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |     .immediate(); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-03 13:10:32 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function deleteStickerPack(packId: string): Promise<Array<string>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   if (!packId) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'deleteStickerPack: Provided data did not have a truthy packId' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   return db | 
					
						
							|  |  |  |     .transaction(() => { | 
					
						
							|  |  |  |       // We use an immediate transaction here to immediately acquire an exclusive lock,
 | 
					
						
							|  |  |  |       //   which would normally only happen when we did our first write.
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       // We need this to ensure that our two queries are atomic, with no other changes
 | 
					
						
							|  |  |  |       //   happening while we do it:
 | 
					
						
							|  |  |  |       // 1. Grab all of target pack's sticker paths
 | 
					
						
							|  |  |  |       // 2. Delete sticker pack (which cascades to all stickers and references)
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       const stickerPathRows: Array<{ path: string }> = db | 
					
						
							|  |  |  |         .prepare<Query>( | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |           SELECT path FROM stickers | 
					
						
							|  |  |  |           WHERE packId = $packId; | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         .all({ | 
					
						
							|  |  |  |           packId, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       db.prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         DELETE FROM sticker_packs | 
					
						
							|  |  |  |         WHERE id = $packId; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ).run({ packId }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return (stickerPathRows || []).map(row => row.path); | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |     .immediate(); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-03 13:10:32 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getStickerCount(): Promise<number> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   return getCountFromTable(getInstance(), 'stickers'); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getAllStickerPacks(): Promise<Array<StickerPackType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows = db | 
					
						
							|  |  |  |     .prepare<EmptyQuery>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT * FROM sticker_packs | 
					
						
							|  |  |  |       ORDER BY installedAt DESC, createdAt DESC | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all(); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return rows || []; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getAllStickers(): Promise<Array<StickerType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows = db | 
					
						
							|  |  |  |     .prepare<EmptyQuery>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT * FROM stickers | 
					
						
							|  |  |  |       ORDER BY packId ASC, id ASC | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all(); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-07 13:00:22 -07:00
										 |  |  |   return (rows || []).map(row => rowToSticker(row)); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getRecentStickers({ limit }: { limit?: number } = {}): Promise< | 
					
						
							|  |  |  |   Array<StickerType> | 
					
						
							|  |  |  | > { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   // Note: we avoid 'IS NOT NULL' here because it does seem to bypass our index
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows = db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT stickers.* FROM stickers | 
					
						
							|  |  |  |       JOIN sticker_packs on stickers.packId = sticker_packs.id | 
					
						
							|  |  |  |       WHERE stickers.lastUsed > 0 AND sticker_packs.status = 'installed' | 
					
						
							|  |  |  |       ORDER BY stickers.lastUsed DESC | 
					
						
							|  |  |  |       LIMIT $limit | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ | 
					
						
							|  |  |  |       limit: limit || 24, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-07 13:00:22 -07:00
										 |  |  |   return (rows || []).map(row => rowToSticker(row)); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  | // Emojis
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateEmojiUsage( | 
					
						
							|  |  |  |   shortName: string, | 
					
						
							|  |  |  |   timeUsed: number = Date.now() | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.transaction(() => { | 
					
						
							|  |  |  |     const rows = db | 
					
						
							|  |  |  |       .prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         SELECT * FROM emojis | 
					
						
							|  |  |  |         WHERE shortName = $shortName; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |       .get({ | 
					
						
							|  |  |  |         shortName, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (rows) { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       db.prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         UPDATE emojis | 
					
						
							|  |  |  |         SET lastUsage = $timeUsed | 
					
						
							|  |  |  |         WHERE shortName = $shortName; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ).run({ shortName, timeUsed }); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       db.prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         INSERT INTO emojis(shortName, lastUsage) | 
					
						
							|  |  |  |         VALUES ($shortName, $timeUsed); | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ).run({ shortName, timeUsed }); | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   })(); | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function getRecentEmojis(limit = 32): Promise<Array<EmojiType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   const rows = db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT * | 
					
						
							|  |  |  |       FROM emojis | 
					
						
							|  |  |  |       ORDER BY lastUsage DESC | 
					
						
							|  |  |  |       LIMIT $limit; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ limit }); | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return rows || []; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-02 18:01:13 -05:00
										 |  |  | async function getAllBadges(): Promise<Array<BadgeType>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const [badgeRows, badgeImageFileRows] = db.transaction(() => [ | 
					
						
							|  |  |  |     db.prepare<EmptyQuery>('SELECT * FROM badges').all(), | 
					
						
							|  |  |  |     db.prepare<EmptyQuery>('SELECT * FROM badgeImageFiles').all(), | 
					
						
							|  |  |  |   ])(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const badgeImagesByBadge = new Map< | 
					
						
							|  |  |  |     string, | 
					
						
							|  |  |  |     Array<undefined | BadgeImageType> | 
					
						
							|  |  |  |   >(); | 
					
						
							|  |  |  |   for (const badgeImageFileRow of badgeImageFileRows) { | 
					
						
							|  |  |  |     const { badgeId, order, localPath, url, theme } = badgeImageFileRow; | 
					
						
							|  |  |  |     const badgeImages = badgeImagesByBadge.get(badgeId) || []; | 
					
						
							|  |  |  |     badgeImages[order] = { | 
					
						
							|  |  |  |       ...(badgeImages[order] || {}), | 
					
						
							|  |  |  |       [parseBadgeImageTheme(theme)]: { | 
					
						
							|  |  |  |         localPath: dropNull(localPath), | 
					
						
							|  |  |  |         url, | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     badgeImagesByBadge.set(badgeId, badgeImages); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return badgeRows.map(badgeRow => ({ | 
					
						
							|  |  |  |     id: badgeRow.id, | 
					
						
							|  |  |  |     category: parseBadgeCategory(badgeRow.category), | 
					
						
							|  |  |  |     name: badgeRow.name, | 
					
						
							|  |  |  |     descriptionTemplate: badgeRow.descriptionTemplate, | 
					
						
							|  |  |  |     images: (badgeImagesByBadge.get(badgeRow.id) || []).filter(isNotNil), | 
					
						
							|  |  |  |   })); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This should match the logic in the badges Redux reducer.
 | 
					
						
							|  |  |  | async function updateOrCreateBadges( | 
					
						
							|  |  |  |   badges: ReadonlyArray<BadgeType> | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const insertBadge = prepare<Query>( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     INSERT OR REPLACE INTO badges ( | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |       category, | 
					
						
							|  |  |  |       name, | 
					
						
							|  |  |  |       descriptionTemplate | 
					
						
							|  |  |  |     ) VALUES ( | 
					
						
							|  |  |  |       $id, | 
					
						
							|  |  |  |       $category, | 
					
						
							|  |  |  |       $name, | 
					
						
							|  |  |  |       $descriptionTemplate | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   const getImageFilesForBadge = prepare<Query>( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     'SELECT url, localPath FROM badgeImageFiles WHERE badgeId = $badgeId' | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   const insertBadgeImageFile = prepare<Query>( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     INSERT INTO badgeImageFiles ( | 
					
						
							|  |  |  |       badgeId, | 
					
						
							|  |  |  |       'order', | 
					
						
							|  |  |  |       url, | 
					
						
							|  |  |  |       localPath, | 
					
						
							|  |  |  |       theme | 
					
						
							|  |  |  |     ) VALUES ( | 
					
						
							|  |  |  |       $badgeId, | 
					
						
							|  |  |  |       $order, | 
					
						
							|  |  |  |       $url, | 
					
						
							|  |  |  |       $localPath, | 
					
						
							|  |  |  |       $theme | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   db.transaction(() => { | 
					
						
							|  |  |  |     badges.forEach(badge => { | 
					
						
							|  |  |  |       const { id: badgeId } = badge; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const oldLocalPaths = new Map<string, string>(); | 
					
						
							|  |  |  |       for (const { url, localPath } of getImageFilesForBadge.all({ badgeId })) { | 
					
						
							|  |  |  |         if (localPath) { | 
					
						
							|  |  |  |           oldLocalPaths.set(url, localPath); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       insertBadge.run({ | 
					
						
							|  |  |  |         id: badgeId, | 
					
						
							|  |  |  |         category: badge.category, | 
					
						
							|  |  |  |         name: badge.name, | 
					
						
							|  |  |  |         descriptionTemplate: badge.descriptionTemplate, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       for (const [order, image] of badge.images.entries()) { | 
					
						
							|  |  |  |         for (const [theme, imageFile] of Object.entries(image)) { | 
					
						
							|  |  |  |           insertBadgeImageFile.run({ | 
					
						
							|  |  |  |             badgeId, | 
					
						
							|  |  |  |             localPath: | 
					
						
							|  |  |  |               imageFile.localPath || oldLocalPaths.get(imageFile.url) || null, | 
					
						
							|  |  |  |             order, | 
					
						
							|  |  |  |             theme, | 
					
						
							|  |  |  |             url: imageFile.url, | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   })(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function badgeImageFileDownloaded( | 
					
						
							|  |  |  |   url: string, | 
					
						
							|  |  |  |   localPath: string | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   prepare<Query>( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     'UPDATE badgeImageFiles SET localPath = $localPath WHERE url = $url' | 
					
						
							|  |  |  |   ).run({ url, localPath }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function getAllBadgeImageFileLocalPaths(): Promise<Set<string>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const localPaths = db | 
					
						
							|  |  |  |     .prepare<EmptyQuery>( | 
					
						
							|  |  |  |       'SELECT localPath FROM badgeImageFiles WHERE localPath IS NOT NULL' | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .pluck() | 
					
						
							|  |  |  |     .all(); | 
					
						
							|  |  |  |   return new Set(localPaths); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  | type StoryDistributionForDatabase = Readonly< | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     senderKeyInfoJson: string | null; | 
					
						
							|  |  |  |   } & Omit<StoryDistributionType, 'senderKeyInfo'> | 
					
						
							|  |  |  | >; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function hydrateStoryDistribution( | 
					
						
							|  |  |  |   fromDatabase: StoryDistributionForDatabase | 
					
						
							|  |  |  | ): StoryDistributionType { | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     ...omit(fromDatabase, 'senderKeyInfoJson'), | 
					
						
							|  |  |  |     senderKeyInfo: fromDatabase.senderKeyInfoJson | 
					
						
							|  |  |  |       ? JSON.parse(fromDatabase.senderKeyInfoJson) | 
					
						
							|  |  |  |       : undefined, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | function freezeStoryDistribution( | 
					
						
							|  |  |  |   story: StoryDistributionType | 
					
						
							|  |  |  | ): StoryDistributionForDatabase { | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     ...omit(story, 'senderKeyInfo'), | 
					
						
							|  |  |  |     senderKeyInfoJson: story.senderKeyInfo | 
					
						
							|  |  |  |       ? JSON.stringify(story.senderKeyInfo) | 
					
						
							|  |  |  |       : null, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function _getAllStoryDistributions(): Promise< | 
					
						
							|  |  |  |   Array<StoryDistributionType> | 
					
						
							|  |  |  | > { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const storyDistributions = db | 
					
						
							|  |  |  |     .prepare<EmptyQuery>('SELECT * FROM storyDistributions;') | 
					
						
							|  |  |  |     .all(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return storyDistributions.map(hydrateStoryDistribution); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function _getAllStoryDistributionMembers(): Promise< | 
					
						
							|  |  |  |   Array<StoryDistributionMemberType> | 
					
						
							|  |  |  | > { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   return db | 
					
						
							|  |  |  |     .prepare<EmptyQuery>('SELECT * FROM storyDistributionMembers;') | 
					
						
							|  |  |  |     .all(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function _deleteAllStoryDistributions(): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   db.prepare<EmptyQuery>('DELETE FROM storyDistributions;').run(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function createNewStoryDistribution( | 
					
						
							| 
									
										
										
										
											2021-12-09 18:15:59 -08:00
										 |  |  |   distribution: StoryDistributionWithMembersType | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  | ): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   db.transaction(() => { | 
					
						
							| 
									
										
										
										
											2021-12-09 18:15:59 -08:00
										 |  |  |     const payload = freezeStoryDistribution(distribution); | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     prepare( | 
					
						
							|  |  |  |       db, | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       INSERT INTO storyDistributions( | 
					
						
							|  |  |  |         id, | 
					
						
							|  |  |  |         name, | 
					
						
							|  |  |  |         avatarUrlPath, | 
					
						
							|  |  |  |         avatarKey, | 
					
						
							|  |  |  |         senderKeyInfoJson | 
					
						
							|  |  |  |       ) VALUES ( | 
					
						
							|  |  |  |         $id, | 
					
						
							|  |  |  |         $name, | 
					
						
							|  |  |  |         $avatarUrlPath, | 
					
						
							|  |  |  |         $avatarKey, | 
					
						
							|  |  |  |         $senderKeyInfoJson | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ).run(payload); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-09 18:15:59 -08:00
										 |  |  |     const { id: listId, members } = distribution; | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const memberInsertStatement = prepare( | 
					
						
							|  |  |  |       db, | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       INSERT OR REPLACE INTO storyDistributionMembers ( | 
					
						
							|  |  |  |         listId, | 
					
						
							|  |  |  |         uuid | 
					
						
							|  |  |  |       ) VALUES ( | 
					
						
							|  |  |  |         $listId, | 
					
						
							|  |  |  |         $uuid | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const uuid of members) { | 
					
						
							|  |  |  |       memberInsertStatement.run({ | 
					
						
							|  |  |  |         listId, | 
					
						
							|  |  |  |         uuid, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   })(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function getAllStoryDistributionsWithMembers(): Promise< | 
					
						
							|  |  |  |   Array<StoryDistributionWithMembersType> | 
					
						
							|  |  |  | > { | 
					
						
							|  |  |  |   const allDistributions = await _getAllStoryDistributions(); | 
					
						
							|  |  |  |   const allMembers = await _getAllStoryDistributionMembers(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const byListId = groupBy(allMembers, member => member.listId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return allDistributions.map(list => ({ | 
					
						
							|  |  |  |     ...list, | 
					
						
							|  |  |  |     members: (byListId[list.id] || []).map(member => member.uuid), | 
					
						
							|  |  |  |   })); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-12-09 18:15:59 -08:00
										 |  |  | async function modifyStoryDistribution( | 
					
						
							|  |  |  |   distribution: StoryDistributionType | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							|  |  |  |   const payload = freezeStoryDistribution(distribution); | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   prepare( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     UPDATE storyDistributions | 
					
						
							|  |  |  |     SET | 
					
						
							|  |  |  |       name = $name, | 
					
						
							|  |  |  |       avatarUrlPath = $avatarUrlPath, | 
					
						
							|  |  |  |       avatarKey = $avatarKey, | 
					
						
							|  |  |  |       senderKeyInfoJson = $senderKeyInfoJson | 
					
						
							|  |  |  |     WHERE id = $id | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run(payload); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  | async function modifyStoryDistributionMembers( | 
					
						
							|  |  |  |   listId: string, | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     toAdd, | 
					
						
							|  |  |  |     toRemove, | 
					
						
							|  |  |  |   }: { toAdd: Array<UUIDStringType>; toRemove: Array<UUIDStringType> } | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   db.transaction(() => { | 
					
						
							|  |  |  |     const memberInsertStatement = prepare( | 
					
						
							|  |  |  |       db, | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       INSERT OR REPLACE INTO storyDistributionMembers ( | 
					
						
							|  |  |  |         listId, | 
					
						
							|  |  |  |         uuid | 
					
						
							|  |  |  |       ) VALUES ( | 
					
						
							|  |  |  |         $listId, | 
					
						
							|  |  |  |         $uuid | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const uuid of toAdd) { | 
					
						
							|  |  |  |       memberInsertStatement.run({ | 
					
						
							|  |  |  |         listId, | 
					
						
							|  |  |  |         uuid, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     batchMultiVarQuery(db, toRemove, (uuids: Array<UUIDStringType>) => { | 
					
						
							|  |  |  |       db.prepare<ArrayQuery>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         DELETE FROM storyDistributionMembers | 
					
						
							|  |  |  |         WHERE listId = ? AND uuid IN ( ${uuids.map(() => '?').join(', ')} ); | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ).run([listId, ...uuids]); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   })(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function deleteStoryDistribution(id: UUIDStringType): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   db.prepare<Query>('DELETE FROM storyDistributions WHERE id = $id;').run({ | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function _getAllStoryReads(): Promise<Array<StoryReadType>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   return db.prepare<EmptyQuery>('SELECT * FROM storyReads;').all(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function _deleteAllStoryReads(): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   db.prepare<EmptyQuery>('DELETE FROM storyReads;').run(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function addNewStoryRead(read: StoryReadType): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   prepare( | 
					
						
							|  |  |  |     db, | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     INSERT OR REPLACE INTO storyReads( | 
					
						
							|  |  |  |       authorId, | 
					
						
							|  |  |  |       conversationId, | 
					
						
							|  |  |  |       storyId, | 
					
						
							|  |  |  |       storyReadDate | 
					
						
							|  |  |  |     ) VALUES ( | 
					
						
							|  |  |  |       $authorId, | 
					
						
							|  |  |  |       $conversationId, | 
					
						
							|  |  |  |       $storyId, | 
					
						
							|  |  |  |       $storyReadDate | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run(read); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function getLastStoryReadsForAuthor({ | 
					
						
							|  |  |  |   authorId, | 
					
						
							|  |  |  |   conversationId, | 
					
						
							|  |  |  |   limit: initialLimit, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   authorId: UUIDStringType; | 
					
						
							|  |  |  |   conversationId?: UUIDStringType; | 
					
						
							|  |  |  |   limit?: number; | 
					
						
							|  |  |  | }): Promise<Array<StoryReadType>> { | 
					
						
							|  |  |  |   const limit = initialLimit || 5; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   return db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT * FROM storyReads | 
					
						
							|  |  |  |       WHERE | 
					
						
							|  |  |  |         authorId = $authorId AND | 
					
						
							|  |  |  |         ($conversationId IS NULL OR conversationId = $conversationId) | 
					
						
							|  |  |  |       ORDER BY storyReadDate DESC | 
					
						
							|  |  |  |       LIMIT $limit; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ | 
					
						
							|  |  |  |       authorId, | 
					
						
							|  |  |  |       conversationId: conversationId || null, | 
					
						
							|  |  |  |       limit, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | // All data in database
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function removeAll(): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-07-31 19:29:51 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.transaction(() => { | 
					
						
							|  |  |  |     db.exec(`
 | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       DELETE FROM attachment_downloads; | 
					
						
							| 
									
										
										
										
											2021-11-02 18:01:13 -05:00
										 |  |  |       DELETE FROM badgeImageFiles; | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       DELETE FROM badges; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       DELETE FROM conversations; | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       DELETE FROM emojis; | 
					
						
							|  |  |  |       DELETE FROM groupCallRings; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       DELETE FROM identityKeys; | 
					
						
							|  |  |  |       DELETE FROM items; | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       DELETE FROM jobs; | 
					
						
							|  |  |  |       DELETE FROM jobs; | 
					
						
							|  |  |  |       DELETE FROM messages_fts; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       DELETE FROM messages; | 
					
						
							|  |  |  |       DELETE FROM preKeys; | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       DELETE FROM reactions; | 
					
						
							| 
									
										
										
										
											2021-05-13 18:18:43 -07:00
										 |  |  |       DELETE FROM senderKeys; | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       DELETE FROM sendLogMessageIds; | 
					
						
							|  |  |  |       DELETE FROM sendLogPayloads; | 
					
						
							|  |  |  |       DELETE FROM sendLogRecipients; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       DELETE FROM sessions; | 
					
						
							|  |  |  |       DELETE FROM signedPreKeys; | 
					
						
							|  |  |  |       DELETE FROM sticker_packs; | 
					
						
							|  |  |  |       DELETE FROM sticker_references; | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       DELETE FROM stickers; | 
					
						
							|  |  |  |       DELETE FROM storyDistributionMembers; | 
					
						
							|  |  |  |       DELETE FROM storyDistributions; | 
					
						
							|  |  |  |       DELETE FROM storyReads; | 
					
						
							|  |  |  |       DELETE FROM unprocessed; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     `);
 | 
					
						
							|  |  |  |   })(); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Anything that isn't user-visible data
 | 
					
						
							| 
									
										
										
										
											2021-08-30 14:39:57 -07:00
										 |  |  | async function removeAllConfiguration( | 
					
						
							|  |  |  |   mode = RemoveAllConfiguration.Full | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   db.transaction(() => { | 
					
						
							| 
									
										
										
										
											2021-05-27 13:47:39 -07:00
										 |  |  |     db.exec( | 
					
						
							| 
									
										
										
										
											2021-05-25 15:40:04 -07:00
										 |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       DELETE FROM identityKeys; | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       DELETE FROM jobs; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       DELETE FROM preKeys; | 
					
						
							| 
									
										
										
										
											2021-05-13 18:18:43 -07:00
										 |  |  |       DELETE FROM senderKeys; | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       DELETE FROM sendLogMessageIds; | 
					
						
							|  |  |  |       DELETE FROM sendLogPayloads; | 
					
						
							|  |  |  |       DELETE FROM sendLogRecipients; | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       DELETE FROM sessions; | 
					
						
							|  |  |  |       DELETE FROM signedPreKeys; | 
					
						
							|  |  |  |       DELETE FROM unprocessed; | 
					
						
							| 
									
										
										
										
											2021-08-30 14:39:57 -07:00
										 |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-05-27 13:47:39 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2021-08-30 14:39:57 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (mode === RemoveAllConfiguration.Full) { | 
					
						
							|  |  |  |       db.exec( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         DELETE FROM items; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } else if (mode === RemoveAllConfiguration.Soft) { | 
					
						
							|  |  |  |       const itemIds: ReadonlyArray<string> = db | 
					
						
							|  |  |  |         .prepare<EmptyQuery>('SELECT id FROM items') | 
					
						
							|  |  |  |         .pluck(true) | 
					
						
							|  |  |  |         .all(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const allowedSet = new Set<string>(STORAGE_UI_KEYS); | 
					
						
							|  |  |  |       for (const id of itemIds) { | 
					
						
							|  |  |  |         if (!allowedSet.has(id)) { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |           removeById(db, 'items', id); | 
					
						
							| 
									
										
										
										
											2021-08-30 14:39:57 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       throw missingCaseError(mode); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-11 13:11:31 -07:00
										 |  |  |     db.exec( | 
					
						
							|  |  |  |       "UPDATE conversations SET json = json_remove(json, '$.senderKeyInfo');" | 
					
						
							| 
									
										
										
										
											2021-05-27 13:47:39 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   })(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getMessagesNeedingUpgrade( | 
					
						
							|  |  |  |   limit: number, | 
					
						
							|  |  |  |   { maxVersion }: { maxVersion: number } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<Array<MessageType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const rows: JSONRows = db | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       SELECT json | 
					
						
							|  |  |  |       FROM messages | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       WHERE schemaVersion IS NULL OR schemaVersion < $maxVersion | 
					
						
							|  |  |  |       LIMIT $limit; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ | 
					
						
							|  |  |  |       maxVersion, | 
					
						
							|  |  |  |       limit, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   return rows.map(row => jsonToObject(row.json)); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function getMessagesWithVisualMediaAttachments( | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   conversationId: string, | 
					
						
							|  |  |  |   { limit }: { limit: number } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<Array<MessageType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const rows: JSONRows = db | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       SELECT json FROM messages WHERE | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |         type IS NOT 'story' AND | 
					
						
							|  |  |  |         storyId IS NULL AND | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |         conversationId = $conversationId AND | 
					
						
							|  |  |  |         hasVisualMediaAttachments = 1 | 
					
						
							|  |  |  |       ORDER BY received_at DESC, sent_at DESC | 
					
						
							|  |  |  |       LIMIT $limit; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ | 
					
						
							|  |  |  |       conversationId, | 
					
						
							|  |  |  |       limit, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   return rows.map(row => jsonToObject(row.json)); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getMessagesWithFileAttachments( | 
					
						
							|  |  |  |   conversationId: string, | 
					
						
							|  |  |  |   { limit }: { limit: number } | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Promise<Array<MessageType>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   const rows = db | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |       SELECT json FROM messages WHERE | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |         type IS NOT 'story' AND | 
					
						
							|  |  |  |         storyId IS NULL AND | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |         conversationId = $conversationId AND | 
					
						
							|  |  |  |         hasFileAttachments = 1 | 
					
						
							|  |  |  |       ORDER BY received_at DESC, sent_at DESC | 
					
						
							|  |  |  |       LIMIT $limit; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ | 
					
						
							|  |  |  |       conversationId, | 
					
						
							|  |  |  |       limit, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:51:45 -07:00
										 |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-27 16:17:05 -04:00
										 |  |  | async function getMessageServerGuidsForSpam( | 
					
						
							|  |  |  |   conversationId: string | 
					
						
							|  |  |  | ): Promise<Array<string>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // The server's maximum is 3, which is why you see `LIMIT 3` in this query. Note that we
 | 
					
						
							|  |  |  |   //   use `pluck` here to only get the first column!
 | 
					
						
							|  |  |  |   return db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT serverGuid | 
					
						
							|  |  |  |       FROM messages | 
					
						
							|  |  |  |       WHERE conversationId = $conversationId | 
					
						
							|  |  |  |       AND type = 'incoming' | 
					
						
							|  |  |  |       AND serverGuid IS NOT NULL | 
					
						
							|  |  |  |       ORDER BY received_at DESC, sent_at DESC | 
					
						
							|  |  |  |       LIMIT 3; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .pluck(true) | 
					
						
							|  |  |  |     .all({ conversationId }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | function getExternalFilesForMessage(message: MessageType): Array<string> { | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const { attachments, contact, quote, preview, sticker } = message; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const files: Array<string> = []; | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   forEach(attachments, attachment => { | 
					
						
							|  |  |  |     const { path: file, thumbnail, screenshot } = attachment; | 
					
						
							|  |  |  |     if (file) { | 
					
						
							|  |  |  |       files.push(file); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (thumbnail && thumbnail.path) { | 
					
						
							|  |  |  |       files.push(thumbnail.path); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (screenshot && screenshot.path) { | 
					
						
							|  |  |  |       files.push(screenshot.path); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (quote && quote.attachments && quote.attachments.length) { | 
					
						
							|  |  |  |     forEach(quote.attachments, attachment => { | 
					
						
							|  |  |  |       const { thumbnail } = attachment; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (thumbnail && thumbnail.path) { | 
					
						
							|  |  |  |         files.push(thumbnail.path); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (contact && contact.length) { | 
					
						
							|  |  |  |     forEach(contact, item => { | 
					
						
							|  |  |  |       const { avatar } = item; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (avatar && avatar.avatar && avatar.avatar.path) { | 
					
						
							|  |  |  |         files.push(avatar.avatar.path); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 19:03:56 -08:00
										 |  |  |   if (preview && preview.length) { | 
					
						
							|  |  |  |     forEach(preview, item => { | 
					
						
							|  |  |  |       const { image } = item; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (image && image.path) { | 
					
						
							|  |  |  |         files.push(image.path); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   if (sticker && sticker.data && sticker.data.path) { | 
					
						
							|  |  |  |     files.push(sticker.data.path); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (sticker.data.thumbnail && sticker.data.thumbnail.path) { | 
					
						
							|  |  |  |       files.push(sticker.data.thumbnail.path); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |   return files; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  | function getExternalFilesForConversation( | 
					
						
							|  |  |  |   conversation: Pick<ConversationType, 'avatar' | 'profileAvatar'> | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Array<string> { | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   const { avatar, profileAvatar } = conversation; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const files: Array<string> = []; | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (avatar && avatar.path) { | 
					
						
							|  |  |  |     files.push(avatar.path); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (profileAvatar && profileAvatar.path) { | 
					
						
							|  |  |  |     files.push(profileAvatar.path); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return files; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  | function getExternalDraftFilesForConversation( | 
					
						
							|  |  |  |   conversation: Pick<ConversationType, 'draftAttachments'> | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | ): Array<string> { | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |   const draftAttachments = conversation.draftAttachments || []; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const files: Array<string> = []; | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   forEach(draftAttachments, attachment => { | 
					
						
							| 
									
										
										
										
											2021-08-30 14:32:56 -07:00
										 |  |  |     if (attachment.pending) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |     const { path: file, screenshotPath } = attachment; | 
					
						
							|  |  |  |     if (file) { | 
					
						
							|  |  |  |       files.push(file); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (screenshotPath) { | 
					
						
							|  |  |  |       files.push(screenshotPath); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return files; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function removeKnownAttachments( | 
					
						
							|  |  |  |   allAttachments: Array<string> | 
					
						
							|  |  |  | ): Promise<Array<string>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const lookup: Dictionary<boolean> = fromPairs( | 
					
						
							|  |  |  |     map(allAttachments, file => [file, true]) | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2021-10-05 09:36:07 -07:00
										 |  |  |   const chunkSize = 500; | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   const total = getMessageCountSync(); | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |   logger.info( | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |     `removeKnownAttachments: About to iterate through ${total} messages` | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let count = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   for (const message of new TableIterator<MessageType>(db, 'messages')) { | 
					
						
							|  |  |  |     const externalFiles = getExternalFilesForMessage(message); | 
					
						
							|  |  |  |     forEach(externalFiles, file => { | 
					
						
							|  |  |  |       delete lookup[file]; | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     count += 1; | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |   logger.info(`removeKnownAttachments: Done processing ${count} messages`); | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   let complete = false; | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   count = 0; | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   let id = ''; | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-14 08:50:45 -07:00
										 |  |  |   const conversationTotal = await getConversationCount(); | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |   logger.info( | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     `removeKnownAttachments: About to iterate through ${conversationTotal} conversations` | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-05 09:36:07 -07:00
										 |  |  |   const fetchConversations = db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |       SELECT json FROM conversations | 
					
						
							|  |  |  |       WHERE id > $id | 
					
						
							|  |  |  |       ORDER BY id ASC | 
					
						
							|  |  |  |       LIMIT $chunkSize; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   while (!complete) { | 
					
						
							| 
									
										
										
										
											2021-10-05 09:36:07 -07:00
										 |  |  |     const rows = fetchConversations.all({ | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |       chunkSize, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     const conversations: Array<ConversationType> = map(rows, row => | 
					
						
							|  |  |  |       jsonToObject(row.json) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     conversations.forEach(conversation => { | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |       const externalFiles = getExternalFilesForConversation(conversation); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       externalFiles.forEach(file => { | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |         delete lookup[file]; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     const lastMessage: ConversationType | undefined = last(conversations); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     if (lastMessage) { | 
					
						
							|  |  |  |       ({ id } = lastMessage); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     complete = conversations.length < chunkSize; | 
					
						
							|  |  |  |     count += conversations.length; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |   logger.info(`removeKnownAttachments: Done processing ${count} conversations`); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |   return Object.keys(lookup); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function removeKnownStickers( | 
					
						
							|  |  |  |   allStickers: Array<string> | 
					
						
							|  |  |  | ): Promise<Array<string>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const lookup: Dictionary<boolean> = fromPairs( | 
					
						
							|  |  |  |     map(allStickers, file => [file, true]) | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const chunkSize = 50; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-07 09:12:26 -07:00
										 |  |  |   const total = await getStickerCount(); | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |   logger.info( | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |     `removeKnownStickers: About to iterate through ${total} stickers` | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let count = 0; | 
					
						
							|  |  |  |   let complete = false; | 
					
						
							|  |  |  |   let rowid = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while (!complete) { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     const rows: Array<{ rowid: number; path: string }> = db | 
					
						
							|  |  |  |       .prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         SELECT rowid, path FROM stickers | 
					
						
							|  |  |  |         WHERE rowid > $rowid | 
					
						
							|  |  |  |         ORDER BY rowid ASC | 
					
						
							|  |  |  |         LIMIT $chunkSize; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |       .all({ | 
					
						
							|  |  |  |         rowid, | 
					
						
							|  |  |  |         chunkSize, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     const files: Array<string> = rows.map(row => row.path); | 
					
						
							|  |  |  |     files.forEach(file => { | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |       delete lookup[file]; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     const lastSticker = last(rows); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |     if (lastSticker) { | 
					
						
							|  |  |  |       ({ rowid } = lastSticker); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     complete = rows.length < chunkSize; | 
					
						
							|  |  |  |     count += rows.length; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |   logger.info(`removeKnownStickers: Done processing ${count} stickers`); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return Object.keys(lookup); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  | async function removeKnownDraftAttachments( | 
					
						
							|  |  |  |   allStickers: Array<string> | 
					
						
							|  |  |  | ): Promise<Array<string>> { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const lookup: Dictionary<boolean> = fromPairs( | 
					
						
							|  |  |  |     map(allStickers, file => [file, true]) | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |   const chunkSize = 50; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-14 08:50:45 -07:00
										 |  |  |   const total = await getConversationCount(); | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |   logger.info( | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |     `removeKnownDraftAttachments: About to iterate through ${total} conversations` | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let complete = false; | 
					
						
							|  |  |  |   let count = 0; | 
					
						
							|  |  |  |   // Though conversations.id is a string, this ensures that, when coerced, this
 | 
					
						
							|  |  |  |   //   value is still a string but it's smaller than every other string.
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |   let id: number | string = 0; | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   while (!complete) { | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     const rows: JSONRows = db | 
					
						
							|  |  |  |       .prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         SELECT json FROM conversations | 
					
						
							|  |  |  |         WHERE id > $id | 
					
						
							|  |  |  |         ORDER BY id ASC | 
					
						
							|  |  |  |         LIMIT $chunkSize; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |       .all({ | 
					
						
							|  |  |  |         id, | 
					
						
							|  |  |  |         chunkSize, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     const conversations: Array<ConversationType> = rows.map(row => | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |       jsonToObject(row.json) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |     conversations.forEach(conversation => { | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |       const externalFiles = getExternalDraftFilesForConversation(conversation); | 
					
						
							| 
									
										
										
										
											2021-04-05 15:18:19 -07:00
										 |  |  |       externalFiles.forEach(file => { | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |         delete lookup[file]; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     const lastMessage: ConversationType | undefined = last(conversations); | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |     if (lastMessage) { | 
					
						
							|  |  |  |       ({ id } = lastMessage); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     complete = conversations.length < chunkSize; | 
					
						
							|  |  |  |     count += conversations.length; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-16 14:54:06 -07:00
										 |  |  |   logger.info( | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |     `removeKnownDraftAttachments: Done processing ${count} conversations` | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return Object.keys(lookup); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-29 18:02:27 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | async function getJobsInQueue(queueType: string): Promise<Array<StoredJob>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return db | 
					
						
							|  |  |  |     .prepare<Query>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |       SELECT id, timestamp, data | 
					
						
							|  |  |  |       FROM jobs | 
					
						
							|  |  |  |       WHERE queueType = $queueType | 
					
						
							|  |  |  |       ORDER BY timestamp; | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .all({ queueType }) | 
					
						
							|  |  |  |     .map(row => ({ | 
					
						
							|  |  |  |       id: row.id, | 
					
						
							|  |  |  |       queueType, | 
					
						
							|  |  |  |       timestamp: row.timestamp, | 
					
						
							|  |  |  |       data: isNotNil(row.data) ? JSON.parse(row.data) : undefined, | 
					
						
							|  |  |  |     })); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  | function insertJobSync(db: Database, job: Readonly<StoredJob>): void { | 
					
						
							| 
									
										
										
										
											2021-04-29 18:02:27 -05:00
										 |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |       INSERT INTO jobs | 
					
						
							|  |  |  |       (id, queueType, timestamp, data) | 
					
						
							|  |  |  |       VALUES | 
					
						
							|  |  |  |       ($id, $queueType, $timestamp, $data); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     id: job.id, | 
					
						
							|  |  |  |     queueType: job.queueType, | 
					
						
							|  |  |  |     timestamp: job.timestamp, | 
					
						
							|  |  |  |     data: isNotNil(job.data) ? JSON.stringify(job.data) : null, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-31 15:58:39 -05:00
										 |  |  | async function insertJob(job: Readonly<StoredJob>): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   return insertJobSync(db, job); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 18:02:27 -05:00
										 |  |  | async function deleteJob(id: string): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   db.prepare<Query>('DELETE FROM jobs WHERE id = $id').run({ id }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-28 12:15:17 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-20 11:06:15 -05:00
										 |  |  | async function processGroupCallRingRequest( | 
					
						
							|  |  |  |   ringId: bigint | 
					
						
							|  |  |  | ): Promise<ProcessGroupCallRingRequestResult> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return db.transaction(() => { | 
					
						
							|  |  |  |     let result: ProcessGroupCallRingRequestResult; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const wasRingPreviouslyCanceled = Boolean( | 
					
						
							|  |  |  |       db | 
					
						
							|  |  |  |         .prepare<Query>( | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |           SELECT 1 FROM groupCallRings | 
					
						
							|  |  |  |           WHERE ringId = $ringId AND isActive = 0 | 
					
						
							|  |  |  |           LIMIT 1; | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         .pluck(true) | 
					
						
							|  |  |  |         .get({ ringId }) | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (wasRingPreviouslyCanceled) { | 
					
						
							|  |  |  |       result = ProcessGroupCallRingRequestResult.RingWasPreviouslyCanceled; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       const isThereAnotherActiveRing = Boolean( | 
					
						
							|  |  |  |         db | 
					
						
							|  |  |  |           .prepare<EmptyQuery>( | 
					
						
							|  |  |  |             `
 | 
					
						
							|  |  |  |             SELECT 1 FROM groupCallRings | 
					
						
							|  |  |  |             WHERE isActive = 1 | 
					
						
							|  |  |  |             LIMIT 1; | 
					
						
							|  |  |  |             `
 | 
					
						
							|  |  |  |           ) | 
					
						
							|  |  |  |           .pluck(true) | 
					
						
							|  |  |  |           .get() | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       if (isThereAnotherActiveRing) { | 
					
						
							|  |  |  |         result = ProcessGroupCallRingRequestResult.ThereIsAnotherActiveRing; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         result = ProcessGroupCallRingRequestResult.ShouldRing; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       db.prepare<Query>( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         INSERT OR IGNORE INTO groupCallRings (ringId, isActive, createdAt) | 
					
						
							|  |  |  |         VALUES ($ringId, 1, $createdAt); | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return result; | 
					
						
							|  |  |  |   })(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function processGroupCallRingCancelation(ringId: bigint): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     INSERT INTO groupCallRings (ringId, isActive, createdAt) | 
					
						
							|  |  |  |     VALUES ($ringId, 0, $createdAt) | 
					
						
							|  |  |  |     ON CONFLICT (ringId) DO | 
					
						
							|  |  |  |     UPDATE SET isActive = 0; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ ringId, createdAt: Date.now() }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This age, in milliseconds, should be longer than any group call ring duration. Beyond
 | 
					
						
							|  |  |  | //   that, it doesn't really matter what the value is.
 | 
					
						
							| 
									
										
										
										
											2021-08-26 09:10:58 -05:00
										 |  |  | const MAX_GROUP_CALL_RING_AGE = 30 * durations.MINUTE; | 
					
						
							| 
									
										
										
										
											2021-08-20 11:06:15 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | async function cleanExpiredGroupCallRings(): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     DELETE FROM groupCallRings | 
					
						
							|  |  |  |     WHERE createdAt < $expiredRingTime; | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     expiredRingTime: Date.now() - MAX_GROUP_CALL_RING_AGE, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 11:45:22 -07:00
										 |  |  | async function getMaxMessageCounter(): Promise<number | undefined> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return db | 
					
						
							|  |  |  |     .prepare<EmptyQuery>( | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     SELECT MAX(counter) | 
					
						
							|  |  |  |     FROM | 
					
						
							|  |  |  |       ( | 
					
						
							|  |  |  |         SELECT MAX(received_at) AS counter FROM messages | 
					
						
							|  |  |  |         UNION | 
					
						
							|  |  |  |         SELECT MAX(timestamp) AS counter FROM unprocessed | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     .pluck() | 
					
						
							|  |  |  |     .get(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-30 11:43:16 -05:00
										 |  |  | async function getStatisticsForLogging(): Promise<Record<string, string>> { | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-07-30 11:43:16 -05:00
										 |  |  |   const counts = await pProps({ | 
					
						
							|  |  |  |     messageCount: getMessageCount(), | 
					
						
							|  |  |  |     conversationCount: getConversationCount(), | 
					
						
							| 
									
										
										
										
											2021-10-26 15:59:08 -07:00
										 |  |  |     sessionCount: getCountFromTable(db, 'sessions'), | 
					
						
							|  |  |  |     senderKeyCount: getCountFromTable(db, 'senderKeys'), | 
					
						
							| 
									
										
										
										
											2021-07-30 11:43:16 -05:00
										 |  |  |   }); | 
					
						
							|  |  |  |   return mapValues(counts, formatCountForLogging); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-28 12:15:17 -04:00
										 |  |  | async function updateAllConversationColors( | 
					
						
							|  |  |  |   conversationColor?: ConversationColorType, | 
					
						
							|  |  |  |   customColorData?: { | 
					
						
							|  |  |  |     id: string; | 
					
						
							|  |  |  |     value: CustomColorType; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   db.prepare<Query>( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     UPDATE conversations | 
					
						
							|  |  |  |     SET json = JSON_PATCH(json, $patch); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   ).run({ | 
					
						
							|  |  |  |     patch: JSON.stringify({ | 
					
						
							|  |  |  |       conversationColor: conversationColor || null, | 
					
						
							|  |  |  |       customColor: customColorData?.value || null, | 
					
						
							|  |  |  |       customColorId: customColorData?.id || null, | 
					
						
							|  |  |  |     }), | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } |