mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
Slint fixes applied
This commit is contained in:
@@ -23,7 +23,7 @@ setInterval(() => {
|
|||||||
}, 10_000)
|
}, 10_000)
|
||||||
|
|
||||||
// start a connection
|
// start a connection
|
||||||
const startSock = async () => {
|
const startSock = async() => {
|
||||||
const { state, saveCreds } = await useMultiFileAuthState('baileys_auth_info')
|
const { state, saveCreds } = await useMultiFileAuthState('baileys_auth_info')
|
||||||
// fetch latest version of WA Web
|
// fetch latest version of WA Web
|
||||||
const { version, isLatest } = await fetchLatestBaileysVersion()
|
const { version, isLatest } = await fetchLatestBaileysVersion()
|
||||||
@@ -49,7 +49,7 @@ const startSock = async () => {
|
|||||||
|
|
||||||
store?.bind(sock.ev)
|
store?.bind(sock.ev)
|
||||||
|
|
||||||
const sendMessageWTyping = async (msg: AnyMessageContent, jid: string) => {
|
const sendMessageWTyping = async(msg: AnyMessageContent, jid: string) => {
|
||||||
await sock.presenceSubscribe(jid)
|
await sock.presenceSubscribe(jid)
|
||||||
await delay(500)
|
await delay(500)
|
||||||
|
|
||||||
@@ -65,15 +65,15 @@ const startSock = async () => {
|
|||||||
// efficiently in a batch
|
// efficiently in a batch
|
||||||
sock.ev.process(
|
sock.ev.process(
|
||||||
// events is a map for event name => event data
|
// events is a map for event name => event data
|
||||||
async (events) => {
|
async(events) => {
|
||||||
// something about the connection changed
|
// something about the connection changed
|
||||||
// maybe it closed, or we received all offline message or connection opened
|
// maybe it closed, or we received all offline message or connection opened
|
||||||
if (events['connection.update']) {
|
if(events['connection.update']) {
|
||||||
const update = events['connection.update']
|
const update = events['connection.update']
|
||||||
const { connection, lastDisconnect } = update
|
const { connection, lastDisconnect } = update
|
||||||
if (connection === 'close') {
|
if(connection === 'close') {
|
||||||
// reconnect if not logged out
|
// reconnect if not logged out
|
||||||
if ((lastDisconnect?.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut) {
|
if((lastDisconnect?.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut) {
|
||||||
startSock()
|
startSock()
|
||||||
} else {
|
} else {
|
||||||
console.log('Connection closed. You are logged out.')
|
console.log('Connection closed. You are logged out.')
|
||||||
@@ -84,37 +84,37 @@ const startSock = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// credentials updated -- save them
|
// credentials updated -- save them
|
||||||
if (events['creds.update']) {
|
if(events['creds.update']) {
|
||||||
await saveCreds()
|
await saveCreds()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events['labels.association']) {
|
if(events['labels.association']) {
|
||||||
console.log(events['labels.association'])
|
console.log(events['labels.association'])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (events['labels.edit']) {
|
if(events['labels.edit']) {
|
||||||
console.log(events['labels.edit'])
|
console.log(events['labels.edit'])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events.call) {
|
if(events.call) {
|
||||||
console.log('recv call event', events.call)
|
console.log('recv call event', events.call)
|
||||||
}
|
}
|
||||||
|
|
||||||
// history received
|
// history received
|
||||||
if (events['messaging-history.set']) {
|
if(events['messaging-history.set']) {
|
||||||
const { chats, contacts, messages, isLatest } = events['messaging-history.set']
|
const { chats, contacts, messages, isLatest } = events['messaging-history.set']
|
||||||
console.log(`recv ${chats.length} chats, ${contacts.length} contacts, ${messages.length} msgs (is latest: ${isLatest})`)
|
console.log(`recv ${chats.length} chats, ${contacts.length} contacts, ${messages.length} msgs (is latest: ${isLatest})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// received a new message
|
// received a new message
|
||||||
if (events['messages.upsert']) {
|
if(events['messages.upsert']) {
|
||||||
const upsert = events['messages.upsert']
|
const upsert = events['messages.upsert']
|
||||||
console.log('recv messages ', JSON.stringify(upsert, undefined, 2))
|
console.log('recv messages ', JSON.stringify(upsert, undefined, 2))
|
||||||
|
|
||||||
if (upsert.type === 'notify') {
|
if(upsert.type === 'notify') {
|
||||||
for (const msg of upsert.messages) {
|
for(const msg of upsert.messages) {
|
||||||
if (!msg.key.fromMe && doReplies) {
|
if(!msg.key.fromMe && doReplies) {
|
||||||
console.log('replying to', msg.key.remoteJid)
|
console.log('replying to', msg.key.remoteJid)
|
||||||
await sock!.readMessages([msg.key])
|
await sock!.readMessages([msg.key])
|
||||||
await sendMessageWTyping({ text: 'Hello there!' }, msg.key.remoteJid!)
|
await sendMessageWTyping({ text: 'Hello there!' }, msg.key.remoteJid!)
|
||||||
@@ -124,15 +124,15 @@ const startSock = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// messages updated like status delivered, message deleted etc.
|
// messages updated like status delivered, message deleted etc.
|
||||||
if (events['messages.update']) {
|
if(events['messages.update']) {
|
||||||
console.log(
|
console.log(
|
||||||
JSON.stringify(events['messages.update'], undefined, 2)
|
JSON.stringify(events['messages.update'], undefined, 2)
|
||||||
)
|
)
|
||||||
|
|
||||||
for (const { key, update } of events['messages.update']) {
|
for(const { key, update } of events['messages.update']) {
|
||||||
if (update.pollUpdates) {
|
if(update.pollUpdates) {
|
||||||
const pollCreation = await getMessage(key)
|
const pollCreation = await getMessage(key)
|
||||||
if (pollCreation) {
|
if(pollCreation) {
|
||||||
console.log(
|
console.log(
|
||||||
'got poll update, aggregation: ',
|
'got poll update, aggregation: ',
|
||||||
getAggregateVotesInPollMessage({
|
getAggregateVotesInPollMessage({
|
||||||
@@ -145,25 +145,25 @@ const startSock = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events['message-receipt.update']) {
|
if(events['message-receipt.update']) {
|
||||||
console.log(events['message-receipt.update'])
|
console.log(events['message-receipt.update'])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events['messages.reaction']) {
|
if(events['messages.reaction']) {
|
||||||
console.log(events['messages.reaction'])
|
console.log(events['messages.reaction'])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events['presence.update']) {
|
if(events['presence.update']) {
|
||||||
console.log(events['presence.update'])
|
console.log(events['presence.update'])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events['chats.update']) {
|
if(events['chats.update']) {
|
||||||
console.log(events['chats.update'])
|
console.log(events['chats.update'])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events['contacts.update']) {
|
if(events['contacts.update']) {
|
||||||
for (const contact of events['contacts.update']) {
|
for(const contact of events['contacts.update']) {
|
||||||
if (typeof contact.imgUrl !== 'undefined') {
|
if(typeof contact.imgUrl !== 'undefined') {
|
||||||
const newUrl = contact.imgUrl === null
|
const newUrl = contact.imgUrl === null
|
||||||
? null
|
? null
|
||||||
: await sock!.profilePictureUrl(contact.id!).catch(() => null)
|
: await sock!.profilePictureUrl(contact.id!).catch(() => null)
|
||||||
@@ -174,7 +174,7 @@ const startSock = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events['chats.delete']) {
|
if(events['chats.delete']) {
|
||||||
console.log('chats deleted ', events['chats.delete'])
|
console.log('chats deleted ', events['chats.delete'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,7 @@ const startSock = async () => {
|
|||||||
return sock
|
return sock
|
||||||
|
|
||||||
async function getMessage(key: WAMessageKey): Promise<WAMessageContent | undefined> {
|
async function getMessage(key: WAMessageKey): Promise<WAMessageContent | undefined> {
|
||||||
if (store) {
|
if(store) {
|
||||||
const msg = await store.loadMessage(key.remoteJid!, key.id!)
|
const msg = await store.loadMessage(key.remoteJid!, key.id!)
|
||||||
return msg?.message || undefined
|
return msg?.message || undefined
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,13 +37,13 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
const processingMutex = makeMutex()
|
const processingMutex = makeMutex()
|
||||||
|
|
||||||
/** helper function to fetch the given app state sync key */
|
/** helper function to fetch the given app state sync key */
|
||||||
const getAppStateSyncKey = async (keyId: string) => {
|
const getAppStateSyncKey = async(keyId: string) => {
|
||||||
const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId])
|
const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId])
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchPrivacySettings = async (force: boolean = false) => {
|
const fetchPrivacySettings = async(force: boolean = false) => {
|
||||||
if (!privacySettings || force) {
|
if(!privacySettings || force) {
|
||||||
const { content } = await query({
|
const { content } = await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -62,7 +62,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** helper function to run a privacy IQ query */
|
/** helper function to run a privacy IQ query */
|
||||||
const privacyQuery = async (name: string, value: string) => {
|
const privacyQuery = async(name: string, value: string) => {
|
||||||
await query({
|
await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -83,31 +83,31 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateLastSeenPrivacy = async (value: WAPrivacyValue) => {
|
const updateLastSeenPrivacy = async(value: WAPrivacyValue) => {
|
||||||
await privacyQuery('last', value)
|
await privacyQuery('last', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateOnlinePrivacy = async (value: WAPrivacyOnlineValue) => {
|
const updateOnlinePrivacy = async(value: WAPrivacyOnlineValue) => {
|
||||||
await privacyQuery('online', value)
|
await privacyQuery('online', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateProfilePicturePrivacy = async (value: WAPrivacyValue) => {
|
const updateProfilePicturePrivacy = async(value: WAPrivacyValue) => {
|
||||||
await privacyQuery('profile', value)
|
await privacyQuery('profile', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateStatusPrivacy = async (value: WAPrivacyValue) => {
|
const updateStatusPrivacy = async(value: WAPrivacyValue) => {
|
||||||
await privacyQuery('status', value)
|
await privacyQuery('status', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateReadReceiptsPrivacy = async (value: WAReadReceiptsValue) => {
|
const updateReadReceiptsPrivacy = async(value: WAReadReceiptsValue) => {
|
||||||
await privacyQuery('readreceipts', value)
|
await privacyQuery('readreceipts', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateGroupsAddPrivacy = async (value: WAPrivacyValue) => {
|
const updateGroupsAddPrivacy = async(value: WAPrivacyValue) => {
|
||||||
await privacyQuery('groupadd', value)
|
await privacyQuery('groupadd', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateDefaultDisappearingMode = async (duration: number) => {
|
const updateDefaultDisappearingMode = async(duration: number) => {
|
||||||
await query({
|
await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -125,7 +125,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** helper function to run a generic IQ query */
|
/** helper function to run a generic IQ query */
|
||||||
const interactiveQuery = async (userNodes: BinaryNode[], queryNode: BinaryNode) => {
|
const interactiveQuery = async(userNodes: BinaryNode[], queryNode: BinaryNode) => {
|
||||||
const result = await query({
|
const result = await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -166,7 +166,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
return users
|
return users
|
||||||
}
|
}
|
||||||
|
|
||||||
const onWhatsApp = async (...jids: string[]) => {
|
const onWhatsApp = async(...jids: string[]) => {
|
||||||
const results = await interactiveQuery(
|
const results = await interactiveQuery(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@@ -190,12 +190,12 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
}).filter(item => item.exists)
|
}).filter(item => item.exists)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchStatus = async (jid: string) => {
|
const fetchStatus = async(jid: string) => {
|
||||||
const [result] = await interactiveQuery(
|
const [result] = await interactiveQuery(
|
||||||
[{ tag: 'user', attrs: { jid } }],
|
[{ tag: 'user', attrs: { jid } }],
|
||||||
{ tag: 'status', attrs: {} }
|
{ tag: 'status', attrs: {} }
|
||||||
)
|
)
|
||||||
if (result) {
|
if(result) {
|
||||||
const status = getBinaryNodeChild(result, 'status')
|
const status = getBinaryNodeChild(result, 'status')
|
||||||
return {
|
return {
|
||||||
status: status?.content!.toString(),
|
status: status?.content!.toString(),
|
||||||
@@ -205,7 +205,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** update the profile picture for yourself or a group */
|
/** update the profile picture for yourself or a group */
|
||||||
const updateProfilePicture = async (jid: string, content: WAMediaUpload) => {
|
const updateProfilePicture = async(jid: string, content: WAMediaUpload) => {
|
||||||
const { img } = await generateProfilePicture(content)
|
const { img } = await generateProfilePicture(content)
|
||||||
await query({
|
await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
@@ -225,7 +225,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** remove the profile picture for yourself or a group */
|
/** remove the profile picture for yourself or a group */
|
||||||
const removeProfilePicture = async (jid: string) => {
|
const removeProfilePicture = async(jid: string) => {
|
||||||
await query({
|
await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -237,7 +237,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** update the profile status for yourself */
|
/** update the profile status for yourself */
|
||||||
const updateProfileStatus = async (status: string) => {
|
const updateProfileStatus = async(status: string) => {
|
||||||
await query({
|
await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -255,11 +255,11 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateProfileName = async (name: string) => {
|
const updateProfileName = async(name: string) => {
|
||||||
await chatModify({ pushNameSetting: name }, '')
|
await chatModify({ pushNameSetting: name }, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchBlocklist = async () => {
|
const fetchBlocklist = async() => {
|
||||||
const result = await query({
|
const result = await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -274,7 +274,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
.map(n => n.attrs.jid)
|
.map(n => n.attrs.jid)
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateBlockStatus = async (jid: string, action: 'block' | 'unblock') => {
|
const updateBlockStatus = async(jid: string, action: 'block' | 'unblock') => {
|
||||||
await query({
|
await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -294,7 +294,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getBusinessProfile = async (jid: string): Promise<WABusinessProfile | void> => {
|
const getBusinessProfile = async(jid: string): Promise<WABusinessProfile | void> => {
|
||||||
const results = await query({
|
const results = await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -314,7 +314,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
|
|
||||||
const profileNode = getBinaryNodeChild(results, 'business_profile')
|
const profileNode = getBinaryNodeChild(results, 'business_profile')
|
||||||
const profiles = getBinaryNodeChild(profileNode, 'profile')
|
const profiles = getBinaryNodeChild(profileNode, 'profile')
|
||||||
if (profiles) {
|
if(profiles) {
|
||||||
const address = getBinaryNodeChild(profiles, 'address')
|
const address = getBinaryNodeChild(profiles, 'address')
|
||||||
const description = getBinaryNodeChild(profiles, 'description')
|
const description = getBinaryNodeChild(profiles, 'description')
|
||||||
const website = getBinaryNodeChild(profiles, 'website')
|
const website = getBinaryNodeChild(profiles, 'website')
|
||||||
@@ -340,7 +340,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateAccountSyncTimestamp = async (fromTimestamp: number | string) => {
|
const updateAccountSyncTimestamp = async(fromTimestamp: number | string) => {
|
||||||
logger.info({ fromTimestamp }, 'requesting account sync')
|
logger.info({ fromTimestamp }, 'requesting account sync')
|
||||||
await sendNode({
|
await sendNode({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
@@ -376,30 +376,30 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const resyncAppState = ev.createBufferedFunction(async (collections: readonly WAPatchName[], isInitialSync: boolean) => {
|
const resyncAppState = ev.createBufferedFunction(async(collections: readonly WAPatchName[], isInitialSync: boolean) => {
|
||||||
// we use this to determine which events to fire
|
// we use this to determine which events to fire
|
||||||
// otherwise when we resync from scratch -- all notifications will fire
|
// otherwise when we resync from scratch -- all notifications will fire
|
||||||
const initialVersionMap: { [T in WAPatchName]?: number } = {}
|
const initialVersionMap: { [T in WAPatchName]?: number } = {}
|
||||||
const globalMutationMap: ChatMutationMap = {}
|
const globalMutationMap: ChatMutationMap = {}
|
||||||
|
|
||||||
await authState.keys.transaction(
|
await authState.keys.transaction(
|
||||||
async () => {
|
async() => {
|
||||||
const collectionsToHandle = new Set<string>(collections)
|
const collectionsToHandle = new Set<string>(collections)
|
||||||
// in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
|
// in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
|
||||||
const attemptsMap: { [T in WAPatchName]?: number } = {}
|
const attemptsMap: { [T in WAPatchName]?: number } = {}
|
||||||
// keep executing till all collections are done
|
// keep executing till all collections are done
|
||||||
// sometimes a single patch request will not return all the patches (God knows why)
|
// sometimes a single patch request will not return all the patches (God knows why)
|
||||||
// so we fetch till they're all done (this is determined by the "has_more_patches" flag)
|
// so we fetch till they're all done (this is determined by the "has_more_patches" flag)
|
||||||
while (collectionsToHandle.size) {
|
while(collectionsToHandle.size) {
|
||||||
const states = {} as { [T in WAPatchName]: LTHashState }
|
const states = {} as { [T in WAPatchName]: LTHashState }
|
||||||
const nodes: BinaryNode[] = []
|
const nodes: BinaryNode[] = []
|
||||||
|
|
||||||
for (const name of collectionsToHandle) {
|
for(const name of collectionsToHandle) {
|
||||||
const result = await authState.keys.get('app-state-sync-version', [name])
|
const result = await authState.keys.get('app-state-sync-version', [name])
|
||||||
let state = result[name]
|
let state = result[name]
|
||||||
|
|
||||||
if (state) {
|
if(state) {
|
||||||
if (typeof initialVersionMap[name] === 'undefined') {
|
if(typeof initialVersionMap[name] === 'undefined') {
|
||||||
initialVersionMap[name] = state.version
|
initialVersionMap[name] = state.version
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -439,11 +439,11 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
|
|
||||||
// extract from binary node
|
// extract from binary node
|
||||||
const decoded = await extractSyncdPatches(result, config?.options)
|
const decoded = await extractSyncdPatches(result, config?.options)
|
||||||
for (const key in decoded) {
|
for(const key in decoded) {
|
||||||
const name = key as WAPatchName
|
const name = key as WAPatchName
|
||||||
const { patches, hasMorePatches, snapshot } = decoded[name]
|
const { patches, hasMorePatches, snapshot } = decoded[name]
|
||||||
try {
|
try {
|
||||||
if (snapshot) {
|
if(snapshot) {
|
||||||
const { state: newState, mutationMap } = await decodeSyncdSnapshot(
|
const { state: newState, mutationMap } = await decodeSyncdSnapshot(
|
||||||
name,
|
name,
|
||||||
snapshot,
|
snapshot,
|
||||||
@@ -460,7 +460,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only process if there are syncd patches
|
// only process if there are syncd patches
|
||||||
if (patches.length) {
|
if(patches.length) {
|
||||||
const { state: newState, mutationMap } = await decodePatches(
|
const { state: newState, mutationMap } = await decodePatches(
|
||||||
name,
|
name,
|
||||||
patches,
|
patches,
|
||||||
@@ -480,12 +480,12 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
Object.assign(globalMutationMap, mutationMap)
|
Object.assign(globalMutationMap, mutationMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasMorePatches) {
|
if(hasMorePatches) {
|
||||||
logger.info(`${name} has more patches...`)
|
logger.info(`${name} has more patches...`)
|
||||||
} else { // collection is done with sync
|
} else { // collection is done with sync
|
||||||
collectionsToHandle.delete(name)
|
collectionsToHandle.delete(name)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch(error) {
|
||||||
// if retry attempts overshoot
|
// if retry attempts overshoot
|
||||||
// or key not found
|
// or key not found
|
||||||
const isIrrecoverableError = attemptsMap[name]! >= MAX_SYNC_ATTEMPTS
|
const isIrrecoverableError = attemptsMap[name]! >= MAX_SYNC_ATTEMPTS
|
||||||
@@ -499,7 +499,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
// increment number of retries
|
// increment number of retries
|
||||||
attemptsMap[name] = (attemptsMap[name] || 0) + 1
|
attemptsMap[name] = (attemptsMap[name] || 0) + 1
|
||||||
|
|
||||||
if (isIrrecoverableError) {
|
if(isIrrecoverableError) {
|
||||||
// stop retrying
|
// stop retrying
|
||||||
collectionsToHandle.delete(name)
|
collectionsToHandle.delete(name)
|
||||||
}
|
}
|
||||||
@@ -510,7 +510,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const { onMutation } = newAppStateChunkHandler(isInitialSync)
|
const { onMutation } = newAppStateChunkHandler(isInitialSync)
|
||||||
for (const key in globalMutationMap) {
|
for(const key in globalMutationMap) {
|
||||||
onMutation(globalMutationMap[key])
|
onMutation(globalMutationMap[key])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -520,7 +520,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
* type = "preview" for a low res picture
|
* type = "preview" for a low res picture
|
||||||
* type = "image for the high res picture"
|
* type = "image for the high res picture"
|
||||||
*/
|
*/
|
||||||
const profilePictureUrl = async (jid: string, type: 'preview' | 'image' = 'preview', timeoutMs?: number) => {
|
const profilePictureUrl = async(jid: string, type: 'preview' | 'image' = 'preview', timeoutMs?: number) => {
|
||||||
jid = jidNormalizedUser(jid)
|
jid = jidNormalizedUser(jid)
|
||||||
const result = await query({
|
const result = await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
@@ -537,10 +537,10 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
return child?.attrs?.url
|
return child?.attrs?.url
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendPresenceUpdate = async (type: WAPresence, toJid?: string) => {
|
const sendPresenceUpdate = async(type: WAPresence, toJid?: string) => {
|
||||||
const me = authState.creds.me!
|
const me = authState.creds.me!
|
||||||
if (type === 'available' || type === 'unavailable') {
|
if(type === 'available' || type === 'unavailable') {
|
||||||
if (!me!.name) {
|
if(!me!.name) {
|
||||||
logger.warn('no name present, ignoring presence update request...')
|
logger.warn('no name present, ignoring presence update request...')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -600,23 +600,23 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
const jid = attrs.from
|
const jid = attrs.from
|
||||||
const participant = attrs.participant || attrs.from
|
const participant = attrs.participant || attrs.from
|
||||||
|
|
||||||
if (shouldIgnoreJid(jid)) {
|
if(shouldIgnoreJid(jid)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tag === 'presence') {
|
if(tag === 'presence') {
|
||||||
presence = {
|
presence = {
|
||||||
lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available',
|
lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available',
|
||||||
lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined
|
lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined
|
||||||
}
|
}
|
||||||
} else if (Array.isArray(content)) {
|
} else if(Array.isArray(content)) {
|
||||||
const [firstChild] = content
|
const [firstChild] = content
|
||||||
let type = firstChild.tag as WAPresence
|
let type = firstChild.tag as WAPresence
|
||||||
if (type === 'paused') {
|
if(type === 'paused') {
|
||||||
type = 'available'
|
type = 'available'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstChild.attrs?.media === 'audio') {
|
if(firstChild.attrs?.media === 'audio') {
|
||||||
type = 'recording'
|
type = 'recording'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -625,15 +625,15 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
logger.error({ tag, attrs, content }, 'recv invalid presence node')
|
logger.error({ tag, attrs, content }, 'recv invalid presence node')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (presence) {
|
if(presence) {
|
||||||
ev.emit('presence.update', { id: jid, presences: { [participant]: presence } })
|
ev.emit('presence.update', { id: jid, presences: { [participant]: presence } })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const appPatch = async (patchCreate: WAPatchCreate) => {
|
const appPatch = async(patchCreate: WAPatchCreate) => {
|
||||||
const name = patchCreate.type
|
const name = patchCreate.type
|
||||||
const myAppStateKeyId = authState.creds.myAppStateKeyId
|
const myAppStateKeyId = authState.creds.myAppStateKeyId
|
||||||
if (!myAppStateKeyId) {
|
if(!myAppStateKeyId) {
|
||||||
throw new Boom('App state key not present!', { statusCode: 400 })
|
throw new Boom('App state key not present!', { statusCode: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,9 +641,9 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
let encodeResult: { patch: proto.ISyncdPatch, state: LTHashState }
|
let encodeResult: { patch: proto.ISyncdPatch, state: LTHashState }
|
||||||
|
|
||||||
await processingMutex.mutex(
|
await processingMutex.mutex(
|
||||||
async () => {
|
async() => {
|
||||||
await authState.keys.transaction(
|
await authState.keys.transaction(
|
||||||
async () => {
|
async() => {
|
||||||
logger.debug({ patch: patchCreate }, 'applying app patch')
|
logger.debug({ patch: patchCreate }, 'applying app patch')
|
||||||
|
|
||||||
await resyncAppState([name], false)
|
await resyncAppState([name], false)
|
||||||
@@ -698,7 +698,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (config.emitOwnEvents) {
|
if(config.emitOwnEvents) {
|
||||||
const { onMutation } = newAppStateChunkHandler(false)
|
const { onMutation } = newAppStateChunkHandler(false)
|
||||||
const { mutationMap } = await decodePatches(
|
const { mutationMap } = await decodePatches(
|
||||||
name,
|
name,
|
||||||
@@ -709,14 +709,14 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
undefined,
|
undefined,
|
||||||
logger,
|
logger,
|
||||||
)
|
)
|
||||||
for (const key in mutationMap) {
|
for(const key in mutationMap) {
|
||||||
onMutation(mutationMap[key])
|
onMutation(mutationMap[key])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** sending abt props may fix QR scan fail if server expects */
|
/** sending abt props may fix QR scan fail if server expects */
|
||||||
const fetchAbt = async () => {
|
const fetchAbt = async() => {
|
||||||
const abtNode = await query({
|
const abtNode = await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -732,7 +732,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
const propsNode = getBinaryNodeChild(abtNode, 'props')
|
const propsNode = getBinaryNodeChild(abtNode, 'props')
|
||||||
|
|
||||||
let props: { [_: string]: string } = {}
|
let props: { [_: string]: string } = {}
|
||||||
if (propsNode) {
|
if(propsNode) {
|
||||||
props = reduceBinaryNodeToDictionary(propsNode, 'prop')
|
props = reduceBinaryNodeToDictionary(propsNode, 'prop')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -742,7 +742,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** sending non-abt props may fix QR scan fail if server expects */
|
/** sending non-abt props may fix QR scan fail if server expects */
|
||||||
const fetchProps = async () => {
|
const fetchProps = async() => {
|
||||||
const resultNode = await query({
|
const resultNode = await query({
|
||||||
tag: 'iq',
|
tag: 'iq',
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -758,7 +758,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
const propsNode = getBinaryNodeChild(resultNode, 'props')
|
const propsNode = getBinaryNodeChild(resultNode, 'props')
|
||||||
|
|
||||||
let props: { [_: string]: string } = {}
|
let props: { [_: string]: string } = {}
|
||||||
if (propsNode) {
|
if(propsNode) {
|
||||||
props = reduceBinaryNodeToDictionary(propsNode, 'prop')
|
props = reduceBinaryNodeToDictionary(propsNode, 'prop')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -827,7 +827,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
* queries need to be fired on connection open
|
* queries need to be fired on connection open
|
||||||
* help ensure parity with WA Web
|
* help ensure parity with WA Web
|
||||||
* */
|
* */
|
||||||
const executeInitQueries = async () => {
|
const executeInitQueries = async() => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
fetchAbt(),
|
fetchAbt(),
|
||||||
fetchProps(),
|
fetchProps(),
|
||||||
@@ -836,19 +836,19 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
const upsertMessage = ev.createBufferedFunction(async (msg: WAMessage, type: MessageUpsertType) => {
|
const upsertMessage = ev.createBufferedFunction(async(msg: WAMessage, type: MessageUpsertType) => {
|
||||||
ev.emit('messages.upsert', { messages: [msg], type })
|
ev.emit('messages.upsert', { messages: [msg], type })
|
||||||
|
|
||||||
if (!!msg.pushName) {
|
if(!!msg.pushName) {
|
||||||
let jid = msg.key.fromMe ? authState.creds.me!.id : (msg.key.participant || msg.key.remoteJid)
|
let jid = msg.key.fromMe ? authState.creds.me!.id : (msg.key.participant || msg.key.remoteJid)
|
||||||
jid = jidNormalizedUser(jid!)
|
jid = jidNormalizedUser(jid!)
|
||||||
|
|
||||||
if (!msg.key.fromMe) {
|
if(!msg.key.fromMe) {
|
||||||
ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName! }])
|
ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName! }])
|
||||||
}
|
}
|
||||||
|
|
||||||
// update our pushname too
|
// update our pushname too
|
||||||
if (msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) {
|
if(msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) {
|
||||||
ev.emit('creds.update', { me: { ...authState.creds.me!, name: msg.pushName! } })
|
ev.emit('creds.update', { me: { ...authState.creds.me!, name: msg.pushName! } })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -861,14 +861,14 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
)
|
)
|
||||||
: false
|
: false
|
||||||
|
|
||||||
if (historyMsg && !authState.creds.myAppStateKeyId) {
|
if(historyMsg && !authState.creds.myAppStateKeyId) {
|
||||||
logger.warn('skipping app state sync, as myAppStateKeyId is not set')
|
logger.warn('skipping app state sync, as myAppStateKeyId is not set')
|
||||||
pendingAppStateSync = true
|
pendingAppStateSync = true
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
(async () => {
|
(async() => {
|
||||||
if (
|
if(
|
||||||
historyMsg
|
historyMsg
|
||||||
&& authState.creds.myAppStateKeyId
|
&& authState.creds.myAppStateKeyId
|
||||||
) {
|
) {
|
||||||
@@ -890,7 +890,7 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
)
|
)
|
||||||
])
|
])
|
||||||
|
|
||||||
if (
|
if(
|
||||||
msg.message?.protocolMessage?.appStateSyncKeyShare
|
msg.message?.protocolMessage?.appStateSyncKeyShare
|
||||||
&& pendingAppStateSync
|
&& pendingAppStateSync
|
||||||
) {
|
) {
|
||||||
@@ -899,14 +899,14 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function doAppStateSync() {
|
async function doAppStateSync() {
|
||||||
if (!authState.creds.accountSyncCounter) {
|
if(!authState.creds.accountSyncCounter) {
|
||||||
logger.info('doing initial app state sync')
|
logger.info('doing initial app state sync')
|
||||||
await resyncAppState(ALL_WA_PATCH_NAMES, true)
|
await resyncAppState(ALL_WA_PATCH_NAMES, true)
|
||||||
|
|
||||||
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1
|
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1
|
||||||
ev.emit('creds.update', { accountSyncCounter })
|
ev.emit('creds.update', { accountSyncCounter })
|
||||||
|
|
||||||
if (needToFlushWithAppStateSync) {
|
if(needToFlushWithAppStateSync) {
|
||||||
logger.debug('flushing with app state sync')
|
logger.debug('flushing with app state sync')
|
||||||
ev.flush()
|
ev.flush()
|
||||||
}
|
}
|
||||||
@@ -917,31 +917,31 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
ws.on('CB:presence', handlePresenceUpdate)
|
ws.on('CB:presence', handlePresenceUpdate)
|
||||||
ws.on('CB:chatstate', handlePresenceUpdate)
|
ws.on('CB:chatstate', handlePresenceUpdate)
|
||||||
|
|
||||||
ws.on('CB:ib,,dirty', async (node: BinaryNode) => {
|
ws.on('CB:ib,,dirty', async(node: BinaryNode) => {
|
||||||
const { attrs } = getBinaryNodeChild(node, 'dirty')!
|
const { attrs } = getBinaryNodeChild(node, 'dirty')!
|
||||||
const type = attrs.type
|
const type = attrs.type
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'account_sync':
|
case 'account_sync':
|
||||||
if (attrs.timestamp) {
|
if(attrs.timestamp) {
|
||||||
let { lastAccountSyncTimestamp } = authState.creds
|
let { lastAccountSyncTimestamp } = authState.creds
|
||||||
if (lastAccountSyncTimestamp) {
|
if(lastAccountSyncTimestamp) {
|
||||||
await updateAccountSyncTimestamp(lastAccountSyncTimestamp)
|
await updateAccountSyncTimestamp(lastAccountSyncTimestamp)
|
||||||
}
|
|
||||||
|
|
||||||
lastAccountSyncTimestamp = +attrs.timestamp
|
|
||||||
ev.emit('creds.update', { lastAccountSyncTimestamp })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
lastAccountSyncTimestamp = +attrs.timestamp
|
||||||
default:
|
ev.emit('creds.update', { lastAccountSyncTimestamp })
|
||||||
logger.info({ node }, 'received unknown sync')
|
}
|
||||||
break
|
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
logger.info({ node }, 'received unknown sync')
|
||||||
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
|
ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
|
||||||
if (connection === 'open') {
|
if(connection === 'open') {
|
||||||
if (fireInitQueries) {
|
if(fireInitQueries) {
|
||||||
executeInitQueries()
|
executeInitQueries()
|
||||||
.catch(
|
.catch(
|
||||||
error => onUnexpectedError(error, 'init queries')
|
error => onUnexpectedError(error, 'init queries')
|
||||||
@@ -954,11 +954,11 @@ export const makeChatsSocket = (config: SocketConfig) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (receivedPendingNotifications) {
|
if(receivedPendingNotifications) {
|
||||||
// if we don't have the app state key
|
// if we don't have the app state key
|
||||||
// we keep buffering events until we finally have
|
// we keep buffering events until we finally have
|
||||||
// the key and can sync the messages
|
// the key and can sync the messages
|
||||||
if (!authState.creds?.myAppStateKeyId) {
|
if(!authState.creds?.myAppStateKeyId) {
|
||||||
ev.buffer()
|
ev.buffer()
|
||||||
needToFlushWithAppStateSync = true
|
needToFlushWithAppStateSync = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export default (
|
|||||||
const labelAssociations = new KeyedDB<LabelAssociation, string>(labelAssociationKey, labelAssociationKey.key)
|
const labelAssociations = new KeyedDB<LabelAssociation, string>(labelAssociationKey, labelAssociationKey.key)
|
||||||
|
|
||||||
const assertMessageList = (jid: string) => {
|
const assertMessageList = (jid: string) => {
|
||||||
if (!messages[jid]) {
|
if(!messages[jid]) {
|
||||||
messages[jid] = makeMessagesDictionary()
|
messages[jid] = makeMessagesDictionary()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ export default (
|
|||||||
|
|
||||||
const contactsUpsert = (newContacts: Contact[]) => {
|
const contactsUpsert = (newContacts: Contact[]) => {
|
||||||
const oldContacts = new Set(Object.keys(contacts))
|
const oldContacts = new Set(Object.keys(contacts))
|
||||||
for (const contact of newContacts) {
|
for(const contact of newContacts) {
|
||||||
oldContacts.delete(contact.id)
|
oldContacts.delete(contact.id)
|
||||||
contacts[contact.id] = Object.assign(
|
contacts[contact.id] = Object.assign(
|
||||||
contacts[contact.id] || {},
|
contacts[contact.id] || {},
|
||||||
@@ -112,7 +112,7 @@ export default (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const labelsUpsert = (newLabels: Label[]) => {
|
const labelsUpsert = (newLabels: Label[]) => {
|
||||||
for (const label of newLabels) {
|
for(const label of newLabels) {
|
||||||
labels.upsertById(label.id, label)
|
labels.upsertById(label.id, label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,10 +134,10 @@ export default (
|
|||||||
messages: newMessages,
|
messages: newMessages,
|
||||||
isLatest
|
isLatest
|
||||||
}) => {
|
}) => {
|
||||||
if (isLatest) {
|
if(isLatest) {
|
||||||
chats.clear()
|
chats.clear()
|
||||||
|
|
||||||
for (const id in messages) {
|
for(const id in messages) {
|
||||||
delete messages[id]
|
delete messages[id]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,13 +146,13 @@ export default (
|
|||||||
logger.debug({ chatsAdded }, 'synced chats')
|
logger.debug({ chatsAdded }, 'synced chats')
|
||||||
|
|
||||||
const oldContacts = contactsUpsert(newContacts)
|
const oldContacts = contactsUpsert(newContacts)
|
||||||
for (const jid of oldContacts) {
|
for(const jid of oldContacts) {
|
||||||
delete contacts[jid]
|
delete contacts[jid]
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug({ deletedContacts: oldContacts.size, newContacts }, 'synced contacts')
|
logger.debug({ deletedContacts: oldContacts.size, newContacts }, 'synced contacts')
|
||||||
|
|
||||||
for (const msg of newMessages) {
|
for(const msg of newMessages) {
|
||||||
const jid = msg.key.remoteJid!
|
const jid = msg.key.remoteJid!
|
||||||
const list = assertMessageList(jid)
|
const list = assertMessageList(jid)
|
||||||
list.upsert(msg, 'prepend')
|
list.upsert(msg, 'prepend')
|
||||||
@@ -162,8 +162,8 @@ export default (
|
|||||||
})
|
})
|
||||||
|
|
||||||
ev.on('contacts.update', updates => {
|
ev.on('contacts.update', updates => {
|
||||||
for (const update of updates) {
|
for(const update of updates) {
|
||||||
if (contacts[update.id!]) {
|
if(contacts[update.id!]) {
|
||||||
Object.assign(contacts[update.id!], update)
|
Object.assign(contacts[update.id!], update)
|
||||||
} else {
|
} else {
|
||||||
logger.debug({ update }, 'got update for non-existant contact')
|
logger.debug({ update }, 'got update for non-existant contact')
|
||||||
@@ -174,28 +174,28 @@ export default (
|
|||||||
chats.upsert(...newChats)
|
chats.upsert(...newChats)
|
||||||
})
|
})
|
||||||
ev.on('chats.update', updates => {
|
ev.on('chats.update', updates => {
|
||||||
for (let update of updates) {
|
for(let update of updates) {
|
||||||
const result = chats.update(update.id!, chat => {
|
const result = chats.update(update.id!, chat => {
|
||||||
if (update.unreadCount! > 0) {
|
if(update.unreadCount! > 0) {
|
||||||
update = { ...update }
|
update = { ...update }
|
||||||
update.unreadCount = (chat.unreadCount || 0) + update.unreadCount!
|
update.unreadCount = (chat.unreadCount || 0) + update.unreadCount!
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.assign(chat, update)
|
Object.assign(chat, update)
|
||||||
})
|
})
|
||||||
if (!result) {
|
if(!result) {
|
||||||
logger.debug({ update }, 'got update for non-existant chat')
|
logger.debug({ update }, 'got update for non-existant chat')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ev.on('labels.edit', (label: Label) => {
|
ev.on('labels.edit', (label: Label) => {
|
||||||
if (label.deleted) {
|
if(label.deleted) {
|
||||||
return labels.deleteById(label.id)
|
return labels.deleteById(label.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WhatsApp can store only up to 20 labels
|
// WhatsApp can store only up to 20 labels
|
||||||
if (labels.count() < 20) {
|
if(labels.count() < 20) {
|
||||||
return labels.upsertById(label.id, label)
|
return labels.upsertById(label.id, label)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,14 +204,14 @@ export default (
|
|||||||
|
|
||||||
ev.on('labels.association', ({ type, association }) => {
|
ev.on('labels.association', ({ type, association }) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'add':
|
case 'add':
|
||||||
labelAssociations.upsert(association)
|
labelAssociations.upsert(association)
|
||||||
break
|
break
|
||||||
case 'remove':
|
case 'remove':
|
||||||
labelAssociations.delete(association)
|
labelAssociations.delete(association)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
console.error(`unknown operation type [${type}]`)
|
console.error(`unknown operation type [${type}]`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -220,52 +220,52 @@ export default (
|
|||||||
Object.assign(presences[id], update)
|
Object.assign(presences[id], update)
|
||||||
})
|
})
|
||||||
ev.on('chats.delete', deletions => {
|
ev.on('chats.delete', deletions => {
|
||||||
for (const item of deletions) {
|
for(const item of deletions) {
|
||||||
chats.deleteById(item)
|
chats.deleteById(item)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
ev.on('messages.upsert', ({ messages: newMessages, type }) => {
|
ev.on('messages.upsert', ({ messages: newMessages, type }) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'append':
|
case 'append':
|
||||||
case 'notify':
|
case 'notify':
|
||||||
for (const msg of newMessages) {
|
for(const msg of newMessages) {
|
||||||
const jid = jidNormalizedUser(msg.key.remoteJid!)
|
const jid = jidNormalizedUser(msg.key.remoteJid!)
|
||||||
const list = assertMessageList(jid)
|
const list = assertMessageList(jid)
|
||||||
list.upsert(msg, 'append')
|
list.upsert(msg, 'append')
|
||||||
|
|
||||||
if (type === 'notify') {
|
if(type === 'notify') {
|
||||||
if (!chats.get(jid)) {
|
if(!chats.get(jid)) {
|
||||||
ev.emit('chats.upsert', [
|
ev.emit('chats.upsert', [
|
||||||
{
|
{
|
||||||
id: jid,
|
id: jid,
|
||||||
conversationTimestamp: toNumber(msg.messageTimestamp),
|
conversationTimestamp: toNumber(msg.messageTimestamp),
|
||||||
unreadCount: 1
|
unreadCount: 1
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
ev.on('messages.update', updates => {
|
ev.on('messages.update', updates => {
|
||||||
for (const { update, key } of updates) {
|
for(const { update, key } of updates) {
|
||||||
const list = assertMessageList(key.remoteJid!)
|
const list = assertMessageList(key.remoteJid!)
|
||||||
const result = list.updateAssign(key.id!, update)
|
const result = list.updateAssign(key.id!, update)
|
||||||
if (!result) {
|
if(!result) {
|
||||||
logger.debug({ update }, 'got update for non-existent message')
|
logger.debug({ update }, 'got update for non-existent message')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
ev.on('messages.delete', item => {
|
ev.on('messages.delete', item => {
|
||||||
if ('all' in item) {
|
if('all' in item) {
|
||||||
const list = messages[item.jid]
|
const list = messages[item.jid]
|
||||||
list?.clear()
|
list?.clear()
|
||||||
} else {
|
} else {
|
||||||
const jid = item.keys[0].remoteJid!
|
const jid = item.keys[0].remoteJid!
|
||||||
const list = messages[jid]
|
const list = messages[jid]
|
||||||
if (list) {
|
if(list) {
|
||||||
const idSet = new Set(item.keys.map(k => k.id))
|
const idSet = new Set(item.keys.map(k => k.id))
|
||||||
list.filter(m => !idSet.has(m.key.id))
|
list.filter(m => !idSet.has(m.key.id))
|
||||||
}
|
}
|
||||||
@@ -273,9 +273,9 @@ export default (
|
|||||||
})
|
})
|
||||||
|
|
||||||
ev.on('groups.update', updates => {
|
ev.on('groups.update', updates => {
|
||||||
for (const update of updates) {
|
for(const update of updates) {
|
||||||
const id = update.id!
|
const id = update.id!
|
||||||
if (groupMetadata[id]) {
|
if(groupMetadata[id]) {
|
||||||
Object.assign(groupMetadata[id], update)
|
Object.assign(groupMetadata[id], update)
|
||||||
} else {
|
} else {
|
||||||
logger.debug({ update }, 'got update for non-existant group metadata')
|
logger.debug({ update }, 'got update for non-existant group metadata')
|
||||||
@@ -285,42 +285,42 @@ export default (
|
|||||||
|
|
||||||
ev.on('group-participants.update', ({ id, participants, action }) => {
|
ev.on('group-participants.update', ({ id, participants, action }) => {
|
||||||
const metadata = groupMetadata[id]
|
const metadata = groupMetadata[id]
|
||||||
if (metadata) {
|
if(metadata) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'add':
|
case 'add':
|
||||||
metadata.participants.push(...participants.map(id => ({ id, isAdmin: false, isSuperAdmin: false })))
|
metadata.participants.push(...participants.map(id => ({ id, isAdmin: false, isSuperAdmin: false })))
|
||||||
break
|
break
|
||||||
case 'demote':
|
case 'demote':
|
||||||
case 'promote':
|
case 'promote':
|
||||||
for (const participant of metadata.participants) {
|
for(const participant of metadata.participants) {
|
||||||
if (participants.includes(participant.id)) {
|
if(participants.includes(participant.id)) {
|
||||||
participant.isAdmin = action === 'promote'
|
participant.isAdmin = action === 'promote'
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
case 'remove':
|
case 'remove':
|
||||||
metadata.participants = metadata.participants.filter(p => !participants.includes(p.id))
|
metadata.participants = metadata.participants.filter(p => !participants.includes(p.id))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ev.on('message-receipt.update', updates => {
|
ev.on('message-receipt.update', updates => {
|
||||||
for (const { key, receipt } of updates) {
|
for(const { key, receipt } of updates) {
|
||||||
const obj = messages[key.remoteJid!]
|
const obj = messages[key.remoteJid!]
|
||||||
const msg = obj?.get(key.id!)
|
const msg = obj?.get(key.id!)
|
||||||
if (msg) {
|
if(msg) {
|
||||||
updateMessageWithReceipt(msg, receipt)
|
updateMessageWithReceipt(msg, receipt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ev.on('messages.reaction', (reactions) => {
|
ev.on('messages.reaction', (reactions) => {
|
||||||
for (const { key, reaction } of reactions) {
|
for(const { key, reaction } of reactions) {
|
||||||
const obj = messages[key.remoteJid!]
|
const obj = messages[key.remoteJid!]
|
||||||
const msg = obj?.get(key.id!)
|
const msg = obj?.get(key.id!)
|
||||||
if (msg) {
|
if(msg) {
|
||||||
updateMessageWithReaction(msg, reaction)
|
updateMessageWithReaction(msg, reaction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,20 +335,14 @@ export default (
|
|||||||
labelAssociations
|
labelAssociations
|
||||||
})
|
})
|
||||||
|
|
||||||
const fromJSON = (json: {
|
const fromJSON = (json: {chats: Chat[], contacts: { [id: string]: Contact }, messages: { [id: string]: WAMessage[] }, labels: { [labelId: string]: Label }, labelAssociations: LabelAssociation[]}) => {
|
||||||
chats: Chat[],
|
|
||||||
contacts: { [id: string]: Contact },
|
|
||||||
messages: { [id: string]: WAMessage[] },
|
|
||||||
labels: { [labelId: string]: Label },
|
|
||||||
labelAssociations: LabelAssociation[]
|
|
||||||
}) => {
|
|
||||||
chats.upsert(...json.chats)
|
chats.upsert(...json.chats)
|
||||||
labelAssociations.upsert(...json.labelAssociations || [])
|
labelAssociations.upsert(...json.labelAssociations || [])
|
||||||
contactsUpsert(Object.values(json.contacts))
|
contactsUpsert(Object.values(json.contacts))
|
||||||
labelsUpsert(Object.values(json.labels || {}))
|
labelsUpsert(Object.values(json.labels || {}))
|
||||||
for (const jid in json.messages) {
|
for(const jid in json.messages) {
|
||||||
const list = assertMessageList(jid)
|
const list = assertMessageList(jid)
|
||||||
for (const msg of json.messages[jid]) {
|
for(const msg of json.messages[jid]) {
|
||||||
list.upsert(proto.WebMessageInfo.fromObject(msg), 'append')
|
list.upsert(proto.WebMessageInfo.fromObject(msg), 'append')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -366,15 +360,15 @@ export default (
|
|||||||
labelAssociations,
|
labelAssociations,
|
||||||
bind,
|
bind,
|
||||||
/** loads messages from the store, if not found -- uses the legacy connection */
|
/** loads messages from the store, if not found -- uses the legacy connection */
|
||||||
loadMessages: async (jid: string, count: number, cursor: WAMessageCursor) => {
|
loadMessages: async(jid: string, count: number, cursor: WAMessageCursor) => {
|
||||||
const list = assertMessageList(jid)
|
const list = assertMessageList(jid)
|
||||||
const mode = !cursor || 'before' in cursor ? 'before' : 'after'
|
const mode = !cursor || 'before' in cursor ? 'before' : 'after'
|
||||||
const cursorKey = !!cursor ? ('before' in cursor ? cursor.before : cursor.after) : undefined
|
const cursorKey = !!cursor ? ('before' in cursor ? cursor.before : cursor.after) : undefined
|
||||||
const cursorValue = cursorKey ? list.get(cursorKey.id!) : undefined
|
const cursorValue = cursorKey ? list.get(cursorKey.id!) : undefined
|
||||||
|
|
||||||
let messages: WAMessage[]
|
let messages: WAMessage[]
|
||||||
if (list && mode === 'before' && (!cursorKey || cursorValue)) {
|
if(list && mode === 'before' && (!cursorKey || cursorValue)) {
|
||||||
if (cursorValue) {
|
if(cursorValue) {
|
||||||
const msgIdx = list.array.findIndex(m => m.key.id === cursorKey?.id)
|
const msgIdx = list.array.findIndex(m => m.key.id === cursorKey?.id)
|
||||||
messages = list.array.slice(0, msgIdx)
|
messages = list.array.slice(0, msgIdx)
|
||||||
} else {
|
} else {
|
||||||
@@ -382,7 +376,7 @@ export default (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const diff = count - messages.length
|
const diff = count - messages.length
|
||||||
if (diff < 0) {
|
if(diff < 0) {
|
||||||
messages = messages.slice(-count) // get the last X messages
|
messages = messages.slice(-count) // get the last X messages
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -423,27 +417,27 @@ export default (
|
|||||||
return associations.map(({ labelId }) => labelId)
|
return associations.map(({ labelId }) => labelId)
|
||||||
|
|
||||||
},
|
},
|
||||||
loadMessage: async (jid: string, id: string) => messages[jid]?.get(id),
|
loadMessage: async(jid: string, id: string) => messages[jid]?.get(id),
|
||||||
mostRecentMessage: async (jid: string) => {
|
mostRecentMessage: async(jid: string) => {
|
||||||
const message: WAMessage | undefined = messages[jid]?.array.slice(-1)[0]
|
const message: WAMessage | undefined = messages[jid]?.array.slice(-1)[0]
|
||||||
return message
|
return message
|
||||||
},
|
},
|
||||||
fetchImageUrl: async (jid: string, sock: WASocket | undefined) => {
|
fetchImageUrl: async(jid: string, sock: WASocket | undefined) => {
|
||||||
const contact = contacts[jid]
|
const contact = contacts[jid]
|
||||||
if (!contact) {
|
if(!contact) {
|
||||||
return sock?.profilePictureUrl(jid)
|
return sock?.profilePictureUrl(jid)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof contact.imgUrl === 'undefined') {
|
if(typeof contact.imgUrl === 'undefined') {
|
||||||
contact.imgUrl = await sock?.profilePictureUrl(jid)
|
contact.imgUrl = await sock?.profilePictureUrl(jid)
|
||||||
}
|
}
|
||||||
|
|
||||||
return contact.imgUrl
|
return contact.imgUrl
|
||||||
},
|
},
|
||||||
fetchGroupMetadata: async (jid: string, sock: WASocket | undefined) => {
|
fetchGroupMetadata: async(jid: string, sock: WASocket | undefined) => {
|
||||||
if (!groupMetadata[jid]) {
|
if(!groupMetadata[jid]) {
|
||||||
const metadata = await sock?.groupMetadata(jid)
|
const metadata = await sock?.groupMetadata(jid)
|
||||||
if (metadata) {
|
if(metadata) {
|
||||||
groupMetadata[jid] = metadata
|
groupMetadata[jid] = metadata
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -460,7 +454,7 @@ export default (
|
|||||||
|
|
||||||
// return groupMetadata[jid]
|
// return groupMetadata[jid]
|
||||||
// },
|
// },
|
||||||
fetchMessageReceipts: async ({ remoteJid, id }: WAMessageKey) => {
|
fetchMessageReceipts: async({ remoteJid, id }: WAMessageKey) => {
|
||||||
const list = messages[remoteJid!]
|
const list = messages[remoteJid!]
|
||||||
const msg = list?.get(id!)
|
const msg = list?.get(id!)
|
||||||
return msg?.userReceipt
|
return msg?.userReceipt
|
||||||
@@ -475,7 +469,7 @@ export default (
|
|||||||
readFromFile: (path: string) => {
|
readFromFile: (path: string) => {
|
||||||
// require fs here so that in case "fs" is not available -- the app does not crash
|
// require fs here so that in case "fs" is not available -- the app does not crash
|
||||||
const { readFileSync, existsSync } = require('fs')
|
const { readFileSync, existsSync } = require('fs')
|
||||||
if (existsSync(path)) {
|
if(existsSync(path)) {
|
||||||
logger.debug({ path }, 'reading from file')
|
logger.debug({ path }, 'reading from file')
|
||||||
const jsonStr = readFileSync(path, { encoding: 'utf-8' })
|
const jsonStr = readFileSync(path, { encoding: 'utf-8' })
|
||||||
const json = JSON.parse(jsonStr)
|
const json = JSON.parse(jsonStr)
|
||||||
|
|||||||
@@ -1,31 +1,32 @@
|
|||||||
export class ObjectRepository<T extends object> {
|
export class ObjectRepository<T extends object> {
|
||||||
readonly entityMap: Map<string, T>
|
readonly entityMap: Map<string, T>
|
||||||
|
|
||||||
constructor(entities: Record<string, T> = {}) {
|
constructor(entities: Record<string, T> = {}) {
|
||||||
this.entityMap = new Map(Object.entries(entities))
|
this.entityMap = new Map(Object.entries(entities))
|
||||||
}
|
}
|
||||||
|
|
||||||
findById(id: string) {
|
findById(id: string) {
|
||||||
return this.entityMap.get(id)
|
return this.entityMap.get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll() {
|
findAll() {
|
||||||
return Array.from(this.entityMap.values())
|
return Array.from(this.entityMap.values())
|
||||||
}
|
}
|
||||||
|
|
||||||
upsertById(id: string, entity: T) {
|
upsertById(id: string, entity: T) {
|
||||||
return this.entityMap.set(id, { ...entity })
|
return this.entityMap.set(id, { ...entity })
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteById(id: string) {
|
deleteById(id: string) {
|
||||||
return this.entityMap.delete(id)
|
return this.entityMap.delete(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
count() {
|
count() {
|
||||||
return this.entityMap.size
|
return this.entityMap.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return this.findAll()
|
||||||
|
}
|
||||||
|
|
||||||
toJSON() {
|
|
||||||
return this.findAll()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -15,13 +15,7 @@ export type WAReadReceiptsValue = 'all' | 'none'
|
|||||||
/** set of statuses visible to other people; see updatePresence() in WhatsAppWeb.Send */
|
/** set of statuses visible to other people; see updatePresence() in WhatsAppWeb.Send */
|
||||||
export type WAPresence = 'unavailable' | 'available' | 'composing' | 'recording' | 'paused'
|
export type WAPresence = 'unavailable' | 'available' | 'composing' | 'recording' | 'paused'
|
||||||
|
|
||||||
export const ALL_WA_PATCH_NAMES = [
|
export const ALL_WA_PATCH_NAMES = ['critical_block', 'critical_unblock_low', 'regular_high', 'regular_low', 'regular'] as const
|
||||||
'critical_block',
|
|
||||||
'critical_unblock_low',
|
|
||||||
'regular_high',
|
|
||||||
'regular_low',
|
|
||||||
'regular'
|
|
||||||
] as const
|
|
||||||
|
|
||||||
export type WAPatchName = typeof ALL_WA_PATCH_NAMES[number]
|
export type WAPatchName = typeof ALL_WA_PATCH_NAMES[number]
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
/** Association type */
|
/** Association type */
|
||||||
export enum LabelAssociationType {
|
export enum LabelAssociationType {
|
||||||
Chat = 'label_jid',
|
Chat = 'label_jid',
|
||||||
Message = 'label_message'
|
Message = 'label_message'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LabelAssociationTypes = `${LabelAssociationType}`
|
export type LabelAssociationTypes = `${LabelAssociationType}`
|
||||||
|
|
||||||
/** Association for chat */
|
/** Association for chat */
|
||||||
export interface ChatLabelAssociation {
|
export interface ChatLabelAssociation {
|
||||||
type: LabelAssociationType.Chat
|
type: LabelAssociationType.Chat
|
||||||
chatId: string
|
chatId: string
|
||||||
labelId: string
|
labelId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Association for message */
|
/** Association for message */
|
||||||
export interface MessageLabelAssociation {
|
export interface MessageLabelAssociation {
|
||||||
type: LabelAssociationType.Message
|
type: LabelAssociationType.Message
|
||||||
chatId: string
|
chatId: string
|
||||||
messageId: string
|
messageId: string
|
||||||
labelId: string
|
labelId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LabelAssociation = ChatLabelAssociation | MessageLabelAssociation
|
export type LabelAssociation = ChatLabelAssociation | MessageLabelAssociation
|
||||||
|
|
||||||
/** Body for add/remove chat label association action */
|
/** Body for add/remove chat label association action */
|
||||||
export interface ChatLabelAssociationActionBody {
|
export interface ChatLabelAssociationActionBody {
|
||||||
labelId: string
|
labelId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** body for add/remove message label association action */
|
/** body for add/remove message label association action */
|
||||||
export interface MessageLabelAssociationActionBody {
|
export interface MessageLabelAssociationActionBody {
|
||||||
labelId: string
|
labelId: string
|
||||||
messageId: string
|
messageId: string
|
||||||
}
|
}
|
||||||
@@ -29,12 +29,12 @@ const generateMac = (operation: proto.SyncdMutation.SyncdOperation, data: Buffer
|
|||||||
const getKeyData = () => {
|
const getKeyData = () => {
|
||||||
let r: number
|
let r: number
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case proto.SyncdMutation.SyncdOperation.SET:
|
case proto.SyncdMutation.SyncdOperation.SET:
|
||||||
r = 0x01
|
r = 0x01
|
||||||
break
|
break
|
||||||
case proto.SyncdMutation.SyncdOperation.REMOVE:
|
case proto.SyncdMutation.SyncdOperation.REMOVE:
|
||||||
r = 0x02
|
r = 0x02
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
const buff = Buffer.from([r])
|
const buff = Buffer.from([r])
|
||||||
@@ -69,8 +69,8 @@ const makeLtHashGenerator = ({ indexValueMap, hash }: Pick<LTHashState, 'hash' |
|
|||||||
mix: ({ indexMac, valueMac, operation }: Mac) => {
|
mix: ({ indexMac, valueMac, operation }: Mac) => {
|
||||||
const indexMacBase64 = Buffer.from(indexMac).toString('base64')
|
const indexMacBase64 = Buffer.from(indexMac).toString('base64')
|
||||||
const prevOp = indexValueMap[indexMacBase64]
|
const prevOp = indexValueMap[indexMacBase64]
|
||||||
if (operation === proto.SyncdMutation.SyncdOperation.REMOVE) {
|
if(operation === proto.SyncdMutation.SyncdOperation.REMOVE) {
|
||||||
if (!prevOp) {
|
if(!prevOp) {
|
||||||
throw new Boom('tried remove, but no previous op', { data: { indexMac, valueMac } })
|
throw new Boom('tried remove, but no previous op', { data: { indexMac, valueMac } })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ const makeLtHashGenerator = ({ indexValueMap, hash }: Pick<LTHashState, 'hash' |
|
|||||||
indexValueMap[indexMacBase64] = { valueMac }
|
indexValueMap[indexMacBase64] = { valueMac }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prevOp) {
|
if(prevOp) {
|
||||||
subBuffs.push(new Uint8Array(prevOp.valueMac).buffer)
|
subBuffs.push(new Uint8Array(prevOp.valueMac).buffer)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -120,14 +120,14 @@ const generatePatchMac = (snapshotMac: Uint8Array, valueMacs: Uint8Array[], vers
|
|||||||
|
|
||||||
export const newLTHashState = (): LTHashState => ({ version: 0, hash: Buffer.alloc(128), indexValueMap: {} })
|
export const newLTHashState = (): LTHashState => ({ version: 0, hash: Buffer.alloc(128), indexValueMap: {} })
|
||||||
|
|
||||||
export const encodeSyncdPatch = async (
|
export const encodeSyncdPatch = async(
|
||||||
{ type, index, syncAction, apiVersion, operation }: WAPatchCreate,
|
{ type, index, syncAction, apiVersion, operation }: WAPatchCreate,
|
||||||
myAppStateKeyId: string,
|
myAppStateKeyId: string,
|
||||||
state: LTHashState,
|
state: LTHashState,
|
||||||
getAppStateSyncKey: FetchAppStateSyncKey
|
getAppStateSyncKey: FetchAppStateSyncKey
|
||||||
) => {
|
) => {
|
||||||
const key = !!myAppStateKeyId ? await getAppStateSyncKey(myAppStateKeyId) : undefined
|
const key = !!myAppStateKeyId ? await getAppStateSyncKey(myAppStateKeyId) : undefined
|
||||||
if (!key) {
|
if(!key) {
|
||||||
throw new Boom(`myAppStateKey ("${myAppStateKeyId}") not present`, { statusCode: 404 })
|
throw new Boom(`myAppStateKey ("${myAppStateKeyId}") not present`, { statusCode: 404 })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ export const encodeSyncdPatch = async (
|
|||||||
return { patch, state }
|
return { patch, state }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const decodeSyncdMutations = async (
|
export const decodeSyncdMutations = async(
|
||||||
msgMutations: (proto.ISyncdMutation | proto.ISyncdRecord)[],
|
msgMutations: (proto.ISyncdMutation | proto.ISyncdRecord)[],
|
||||||
initialState: LTHashState,
|
initialState: LTHashState,
|
||||||
getAppStateSyncKey: FetchAppStateSyncKey,
|
getAppStateSyncKey: FetchAppStateSyncKey,
|
||||||
@@ -196,7 +196,7 @@ export const decodeSyncdMutations = async (
|
|||||||
// indexKey used to HMAC sign record.index.blob
|
// indexKey used to HMAC sign record.index.blob
|
||||||
// valueEncryptionKey used to AES-256-CBC encrypt record.value.blob[0:-32]
|
// valueEncryptionKey used to AES-256-CBC encrypt record.value.blob[0:-32]
|
||||||
// the remaining record.value.blob[0:-32] is the mac, it the HMAC sign of key.keyId + decoded proto data + length of bytes in keyId
|
// the remaining record.value.blob[0:-32] is the mac, it the HMAC sign of key.keyId + decoded proto data + length of bytes in keyId
|
||||||
for (const msgMutation of msgMutations!) {
|
for(const msgMutation of msgMutations!) {
|
||||||
// if it's a syncdmutation, get the operation property
|
// if it's a syncdmutation, get the operation property
|
||||||
// otherwise, if it's only a record -- it'll be a SET mutation
|
// otherwise, if it's only a record -- it'll be a SET mutation
|
||||||
const operation = 'operation' in msgMutation ? msgMutation.operation : proto.SyncdMutation.SyncdOperation.SET
|
const operation = 'operation' in msgMutation ? msgMutation.operation : proto.SyncdMutation.SyncdOperation.SET
|
||||||
@@ -206,9 +206,9 @@ export const decodeSyncdMutations = async (
|
|||||||
const content = Buffer.from(record.value!.blob!)
|
const content = Buffer.from(record.value!.blob!)
|
||||||
const encContent = content.slice(0, -32)
|
const encContent = content.slice(0, -32)
|
||||||
const ogValueMac = content.slice(-32)
|
const ogValueMac = content.slice(-32)
|
||||||
if (validateMacs) {
|
if(validateMacs) {
|
||||||
const contentHmac = generateMac(operation!, encContent, record.keyId!.id!, key.valueMacKey)
|
const contentHmac = generateMac(operation!, encContent, record.keyId!.id!, key.valueMacKey)
|
||||||
if (Buffer.compare(contentHmac, ogValueMac) !== 0) {
|
if(Buffer.compare(contentHmac, ogValueMac) !== 0) {
|
||||||
throw new Boom('HMAC content verification failed')
|
throw new Boom('HMAC content verification failed')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,9 +216,9 @@ export const decodeSyncdMutations = async (
|
|||||||
const result = aesDecrypt(encContent, key.valueEncryptionKey)
|
const result = aesDecrypt(encContent, key.valueEncryptionKey)
|
||||||
const syncAction = proto.SyncActionData.decode(result)
|
const syncAction = proto.SyncActionData.decode(result)
|
||||||
|
|
||||||
if (validateMacs) {
|
if(validateMacs) {
|
||||||
const hmac = hmacSign(syncAction.index, key.indexKey)
|
const hmac = hmacSign(syncAction.index, key.indexKey)
|
||||||
if (Buffer.compare(hmac, record.index!.blob!) !== 0) {
|
if(Buffer.compare(hmac, record.index!.blob!) !== 0) {
|
||||||
throw new Boom('HMAC index verification failed')
|
throw new Boom('HMAC index verification failed')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,7 +238,7 @@ export const decodeSyncdMutations = async (
|
|||||||
async function getKey(keyId: Uint8Array) {
|
async function getKey(keyId: Uint8Array) {
|
||||||
const base64Key = Buffer.from(keyId!).toString('base64')
|
const base64Key = Buffer.from(keyId!).toString('base64')
|
||||||
const keyEnc = await getAppStateSyncKey(base64Key)
|
const keyEnc = await getAppStateSyncKey(base64Key)
|
||||||
if (!keyEnc) {
|
if(!keyEnc) {
|
||||||
throw new Boom(`failed to find key "${base64Key}" to decode mutation`, { statusCode: 404, data: { msgMutations } })
|
throw new Boom(`failed to find key "${base64Key}" to decode mutation`, { statusCode: 404, data: { msgMutations } })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@ export const decodeSyncdMutations = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const decodeSyncdPatch = async (
|
export const decodeSyncdPatch = async(
|
||||||
msg: proto.ISyncdPatch,
|
msg: proto.ISyncdPatch,
|
||||||
name: WAPatchName,
|
name: WAPatchName,
|
||||||
initialState: LTHashState,
|
initialState: LTHashState,
|
||||||
@@ -254,10 +254,10 @@ export const decodeSyncdPatch = async (
|
|||||||
onMutation: (mutation: ChatMutation) => void,
|
onMutation: (mutation: ChatMutation) => void,
|
||||||
validateMacs: boolean
|
validateMacs: boolean
|
||||||
) => {
|
) => {
|
||||||
if (validateMacs) {
|
if(validateMacs) {
|
||||||
const base64Key = Buffer.from(msg.keyId!.id!).toString('base64')
|
const base64Key = Buffer.from(msg.keyId!.id!).toString('base64')
|
||||||
const mainKeyObj = await getAppStateSyncKey(base64Key)
|
const mainKeyObj = await getAppStateSyncKey(base64Key)
|
||||||
if (!mainKeyObj) {
|
if(!mainKeyObj) {
|
||||||
throw new Boom(`failed to find key "${base64Key}" to decode patch`, { statusCode: 404, data: { msg } })
|
throw new Boom(`failed to find key "${base64Key}" to decode patch`, { statusCode: 404, data: { msg } })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +265,7 @@ export const decodeSyncdPatch = async (
|
|||||||
const mutationmacs = msg.mutations!.map(mutation => mutation.record!.value!.blob!.slice(-32))
|
const mutationmacs = msg.mutations!.map(mutation => mutation.record!.value!.blob!.slice(-32))
|
||||||
|
|
||||||
const patchMac = generatePatchMac(msg.snapshotMac!, mutationmacs, toNumber(msg.version!.version!), name, mainKey.patchMacKey)
|
const patchMac = generatePatchMac(msg.snapshotMac!, mutationmacs, toNumber(msg.version!.version!), name, mainKey.patchMacKey)
|
||||||
if (Buffer.compare(patchMac, msg.patchMac!) !== 0) {
|
if(Buffer.compare(patchMac, msg.patchMac!) !== 0) {
|
||||||
throw new Boom('Invalid patch mac')
|
throw new Boom('Invalid patch mac')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,7 +274,7 @@ export const decodeSyncdPatch = async (
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export const extractSyncdPatches = async (
|
export const extractSyncdPatches = async(
|
||||||
result: BinaryNode,
|
result: BinaryNode,
|
||||||
options: AxiosRequestConfig<any>
|
options: AxiosRequestConfig<any>
|
||||||
) => {
|
) => {
|
||||||
@@ -296,8 +296,8 @@ export const extractSyncdPatches = async (
|
|||||||
const hasMorePatches = collectionNode.attrs.has_more_patches === 'true'
|
const hasMorePatches = collectionNode.attrs.has_more_patches === 'true'
|
||||||
|
|
||||||
let snapshot: proto.ISyncdSnapshot | undefined = undefined
|
let snapshot: proto.ISyncdSnapshot | undefined = undefined
|
||||||
if (snapshotNode && !!snapshotNode.content) {
|
if(snapshotNode && !!snapshotNode.content) {
|
||||||
if (!Buffer.isBuffer(snapshotNode)) {
|
if(!Buffer.isBuffer(snapshotNode)) {
|
||||||
snapshotNode.content = Buffer.from(Object.values(snapshotNode.content))
|
snapshotNode.content = Buffer.from(Object.values(snapshotNode.content))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,14 +308,14 @@ export const extractSyncdPatches = async (
|
|||||||
snapshot = proto.SyncdSnapshot.decode(data)
|
snapshot = proto.SyncdSnapshot.decode(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let { content } of patches) {
|
for(let { content } of patches) {
|
||||||
if (content) {
|
if(content) {
|
||||||
if (!Buffer.isBuffer(content)) {
|
if(!Buffer.isBuffer(content)) {
|
||||||
content = Buffer.from(Object.values(content))
|
content = Buffer.from(Object.values(content))
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncd = proto.SyncdPatch.decode(content! as Uint8Array)
|
const syncd = proto.SyncdPatch.decode(content! as Uint8Array)
|
||||||
if (!syncd.version) {
|
if(!syncd.version) {
|
||||||
syncd.version = { version: +collectionNode.attrs.version + 1 }
|
syncd.version = { version: +collectionNode.attrs.version + 1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,7 +332,7 @@ export const extractSyncdPatches = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const downloadExternalBlob = async (
|
export const downloadExternalBlob = async(
|
||||||
blob: proto.IExternalBlobReference,
|
blob: proto.IExternalBlobReference,
|
||||||
options: AxiosRequestConfig<any>
|
options: AxiosRequestConfig<any>
|
||||||
) => {
|
) => {
|
||||||
@@ -345,7 +345,7 @@ export const downloadExternalBlob = async (
|
|||||||
return Buffer.concat(bufferArray)
|
return Buffer.concat(bufferArray)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const downloadExternalPatch = async (
|
export const downloadExternalPatch = async(
|
||||||
blob: proto.IExternalBlobReference,
|
blob: proto.IExternalBlobReference,
|
||||||
options: AxiosRequestConfig<any>
|
options: AxiosRequestConfig<any>
|
||||||
) => {
|
) => {
|
||||||
@@ -354,7 +354,7 @@ export const downloadExternalPatch = async (
|
|||||||
return syncData
|
return syncData
|
||||||
}
|
}
|
||||||
|
|
||||||
export const decodeSyncdSnapshot = async (
|
export const decodeSyncdSnapshot = async(
|
||||||
name: WAPatchName,
|
name: WAPatchName,
|
||||||
snapshot: proto.ISyncdSnapshot,
|
snapshot: proto.ISyncdSnapshot,
|
||||||
getAppStateSyncKey: FetchAppStateSyncKey,
|
getAppStateSyncKey: FetchAppStateSyncKey,
|
||||||
@@ -383,16 +383,16 @@ export const decodeSyncdSnapshot = async (
|
|||||||
newState.hash = hash
|
newState.hash = hash
|
||||||
newState.indexValueMap = indexValueMap
|
newState.indexValueMap = indexValueMap
|
||||||
|
|
||||||
if (validateMacs) {
|
if(validateMacs) {
|
||||||
const base64Key = Buffer.from(snapshot.keyId!.id!).toString('base64')
|
const base64Key = Buffer.from(snapshot.keyId!.id!).toString('base64')
|
||||||
const keyEnc = await getAppStateSyncKey(base64Key)
|
const keyEnc = await getAppStateSyncKey(base64Key)
|
||||||
if (!keyEnc) {
|
if(!keyEnc) {
|
||||||
throw new Boom(`failed to find key "${base64Key}" to decode mutation`)
|
throw new Boom(`failed to find key "${base64Key}" to decode mutation`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = mutationKeys(keyEnc.keyData!)
|
const result = mutationKeys(keyEnc.keyData!)
|
||||||
const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey)
|
const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey)
|
||||||
if (Buffer.compare(snapshot.mac!, computedSnapshotMac) !== 0) {
|
if(Buffer.compare(snapshot.mac!, computedSnapshotMac) !== 0) {
|
||||||
throw new Boom(`failed to verify LTHash at ${newState.version} of ${name} from snapshot`)
|
throw new Boom(`failed to verify LTHash at ${newState.version} of ${name} from snapshot`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -403,7 +403,7 @@ export const decodeSyncdSnapshot = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const decodePatches = async (
|
export const decodePatches = async(
|
||||||
name: WAPatchName,
|
name: WAPatchName,
|
||||||
syncds: proto.ISyncdPatch[],
|
syncds: proto.ISyncdPatch[],
|
||||||
initial: LTHashState,
|
initial: LTHashState,
|
||||||
@@ -420,10 +420,10 @@ export const decodePatches = async (
|
|||||||
|
|
||||||
const mutationMap: ChatMutationMap = {}
|
const mutationMap: ChatMutationMap = {}
|
||||||
|
|
||||||
for (let i = 0; i < syncds.length; i++) {
|
for(let i = 0; i < syncds.length; i++) {
|
||||||
const syncd = syncds[i]
|
const syncd = syncds[i]
|
||||||
const { version, keyId, snapshotMac } = syncd
|
const { version, keyId, snapshotMac } = syncd
|
||||||
if (syncd.externalMutations) {
|
if(syncd.externalMutations) {
|
||||||
logger?.trace({ name, version }, 'downloading external patch')
|
logger?.trace({ name, version }, 'downloading external patch')
|
||||||
const ref = await downloadExternalPatch(syncd.externalMutations, options)
|
const ref = await downloadExternalPatch(syncd.externalMutations, options)
|
||||||
logger?.debug({ name, version, mutations: ref.mutations.length }, 'downloaded external patch')
|
logger?.debug({ name, version, mutations: ref.mutations.length }, 'downloaded external patch')
|
||||||
@@ -452,16 +452,16 @@ export const decodePatches = async (
|
|||||||
newState.hash = decodeResult.hash
|
newState.hash = decodeResult.hash
|
||||||
newState.indexValueMap = decodeResult.indexValueMap
|
newState.indexValueMap = decodeResult.indexValueMap
|
||||||
|
|
||||||
if (validateMacs) {
|
if(validateMacs) {
|
||||||
const base64Key = Buffer.from(keyId!.id!).toString('base64')
|
const base64Key = Buffer.from(keyId!.id!).toString('base64')
|
||||||
const keyEnc = await getAppStateSyncKey(base64Key)
|
const keyEnc = await getAppStateSyncKey(base64Key)
|
||||||
if (!keyEnc) {
|
if(!keyEnc) {
|
||||||
throw new Boom(`failed to find key "${base64Key}" to decode mutation`)
|
throw new Boom(`failed to find key "${base64Key}" to decode mutation`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = mutationKeys(keyEnc.keyData!)
|
const result = mutationKeys(keyEnc.keyData!)
|
||||||
const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey)
|
const computedSnapshotMac = generateSnapshotMac(newState.hash, newState.version, name, result.snapshotMacKey)
|
||||||
if (Buffer.compare(snapshotMac!, computedSnapshotMac) !== 0) {
|
if(Buffer.compare(snapshotMac!, computedSnapshotMac) !== 0) {
|
||||||
throw new Boom(`failed to verify LTHash at ${newState.version} of ${name}`)
|
throw new Boom(`failed to verify LTHash at ${newState.version} of ${name}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -480,25 +480,25 @@ export const chatModificationToAppPatch = (
|
|||||||
const OP = proto.SyncdMutation.SyncdOperation
|
const OP = proto.SyncdMutation.SyncdOperation
|
||||||
const getMessageRange = (lastMessages: LastMessageList) => {
|
const getMessageRange = (lastMessages: LastMessageList) => {
|
||||||
let messageRange: proto.SyncActionValue.ISyncActionMessageRange
|
let messageRange: proto.SyncActionValue.ISyncActionMessageRange
|
||||||
if (Array.isArray(lastMessages)) {
|
if(Array.isArray(lastMessages)) {
|
||||||
const lastMsg = lastMessages[lastMessages.length - 1]
|
const lastMsg = lastMessages[lastMessages.length - 1]
|
||||||
messageRange = {
|
messageRange = {
|
||||||
lastMessageTimestamp: lastMsg?.messageTimestamp,
|
lastMessageTimestamp: lastMsg?.messageTimestamp,
|
||||||
messages: lastMessages?.length ? lastMessages.map(
|
messages: lastMessages?.length ? lastMessages.map(
|
||||||
m => {
|
m => {
|
||||||
if (!m.key?.id || !m.key?.remoteJid) {
|
if(!m.key?.id || !m.key?.remoteJid) {
|
||||||
throw new Boom('Incomplete key', { statusCode: 400, data: m })
|
throw new Boom('Incomplete key', { statusCode: 400, data: m })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isJidGroup(m.key.remoteJid) && !m.key.fromMe && !m.key.participant) {
|
if(isJidGroup(m.key.remoteJid) && !m.key.fromMe && !m.key.participant) {
|
||||||
throw new Boom('Expected not from me message to have participant', { statusCode: 400, data: m })
|
throw new Boom('Expected not from me message to have participant', { statusCode: 400, data: m })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m.messageTimestamp || !toNumber(m.messageTimestamp)) {
|
if(!m.messageTimestamp || !toNumber(m.messageTimestamp)) {
|
||||||
throw new Boom('Missing timestamp in last message list', { statusCode: 400, data: m })
|
throw new Boom('Missing timestamp in last message list', { statusCode: 400, data: m })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m.key.participant) {
|
if(m.key.participant) {
|
||||||
m.key.participant = jidNormalizedUser(m.key.participant)
|
m.key.participant = jidNormalizedUser(m.key.participant)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,7 +514,7 @@ export const chatModificationToAppPatch = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
let patch: WAPatchCreate
|
let patch: WAPatchCreate
|
||||||
if ('mute' in mod) {
|
if('mute' in mod) {
|
||||||
patch = {
|
patch = {
|
||||||
syncAction: {
|
syncAction: {
|
||||||
muteAction: {
|
muteAction: {
|
||||||
@@ -527,7 +527,7 @@ export const chatModificationToAppPatch = (
|
|||||||
apiVersion: 2,
|
apiVersion: 2,
|
||||||
operation: OP.SET
|
operation: OP.SET
|
||||||
}
|
}
|
||||||
} else if ('archive' in mod) {
|
} else if('archive' in mod) {
|
||||||
patch = {
|
patch = {
|
||||||
syncAction: {
|
syncAction: {
|
||||||
archiveChatAction: {
|
archiveChatAction: {
|
||||||
@@ -540,7 +540,7 @@ export const chatModificationToAppPatch = (
|
|||||||
apiVersion: 3,
|
apiVersion: 3,
|
||||||
operation: OP.SET
|
operation: OP.SET
|
||||||
}
|
}
|
||||||
} else if ('markRead' in mod) {
|
} else if('markRead' in mod) {
|
||||||
patch = {
|
patch = {
|
||||||
syncAction: {
|
syncAction: {
|
||||||
markChatAsReadAction: {
|
markChatAsReadAction: {
|
||||||
@@ -553,8 +553,8 @@ export const chatModificationToAppPatch = (
|
|||||||
apiVersion: 3,
|
apiVersion: 3,
|
||||||
operation: OP.SET
|
operation: OP.SET
|
||||||
}
|
}
|
||||||
} else if ('clear' in mod) {
|
} else if('clear' in mod) {
|
||||||
if (mod.clear === 'all') {
|
if(mod.clear === 'all') {
|
||||||
throw new Boom('not supported')
|
throw new Boom('not supported')
|
||||||
} else {
|
} else {
|
||||||
const key = mod.clear.messages[0]
|
const key = mod.clear.messages[0]
|
||||||
@@ -571,7 +571,7 @@ export const chatModificationToAppPatch = (
|
|||||||
operation: OP.SET
|
operation: OP.SET
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ('pin' in mod) {
|
} else if('pin' in mod) {
|
||||||
patch = {
|
patch = {
|
||||||
syncAction: {
|
syncAction: {
|
||||||
pinAction: {
|
pinAction: {
|
||||||
@@ -583,7 +583,7 @@ export const chatModificationToAppPatch = (
|
|||||||
apiVersion: 5,
|
apiVersion: 5,
|
||||||
operation: OP.SET
|
operation: OP.SET
|
||||||
}
|
}
|
||||||
} else if ('delete' in mod) {
|
} else if('delete' in mod) {
|
||||||
patch = {
|
patch = {
|
||||||
syncAction: {
|
syncAction: {
|
||||||
deleteChatAction: {
|
deleteChatAction: {
|
||||||
@@ -595,7 +595,7 @@ export const chatModificationToAppPatch = (
|
|||||||
apiVersion: 6,
|
apiVersion: 6,
|
||||||
operation: OP.SET
|
operation: OP.SET
|
||||||
}
|
}
|
||||||
} else if ('pushNameSetting' in mod) {
|
} else if('pushNameSetting' in mod) {
|
||||||
patch = {
|
patch = {
|
||||||
syncAction: {
|
syncAction: {
|
||||||
pushNameSetting: {
|
pushNameSetting: {
|
||||||
@@ -607,7 +607,7 @@ export const chatModificationToAppPatch = (
|
|||||||
apiVersion: 1,
|
apiVersion: 1,
|
||||||
operation: OP.SET,
|
operation: OP.SET,
|
||||||
}
|
}
|
||||||
} else if ('addChatLabel' in mod) {
|
} else if('addChatLabel' in mod) {
|
||||||
patch = {
|
patch = {
|
||||||
syncAction: {
|
syncAction: {
|
||||||
labelAssociationAction: {
|
labelAssociationAction: {
|
||||||
@@ -619,7 +619,7 @@ export const chatModificationToAppPatch = (
|
|||||||
apiVersion: 3,
|
apiVersion: 3,
|
||||||
operation: OP.SET,
|
operation: OP.SET,
|
||||||
}
|
}
|
||||||
} else if ('removeChatLabel' in mod) {
|
} else if('removeChatLabel' in mod) {
|
||||||
patch = {
|
patch = {
|
||||||
syncAction: {
|
syncAction: {
|
||||||
labelAssociationAction: {
|
labelAssociationAction: {
|
||||||
@@ -631,7 +631,7 @@ export const chatModificationToAppPatch = (
|
|||||||
apiVersion: 3,
|
apiVersion: 3,
|
||||||
operation: OP.SET,
|
operation: OP.SET,
|
||||||
}
|
}
|
||||||
} else if ('addMessageLabel' in mod) {
|
} else if('addMessageLabel' in mod) {
|
||||||
patch = {
|
patch = {
|
||||||
syncAction: {
|
syncAction: {
|
||||||
labelAssociationAction: {
|
labelAssociationAction: {
|
||||||
@@ -650,7 +650,7 @@ export const chatModificationToAppPatch = (
|
|||||||
apiVersion: 3,
|
apiVersion: 3,
|
||||||
operation: OP.SET,
|
operation: OP.SET,
|
||||||
}
|
}
|
||||||
} else if ('removeMessageLabel' in mod) {
|
} else if('removeMessageLabel' in mod) {
|
||||||
patch = {
|
patch = {
|
||||||
syncAction: {
|
syncAction: {
|
||||||
labelAssociationAction: {
|
labelAssociationAction: {
|
||||||
@@ -695,7 +695,7 @@ export const processSyncAction = (
|
|||||||
index: [type, id, msgId, fromMe]
|
index: [type, id, msgId, fromMe]
|
||||||
} = syncAction
|
} = syncAction
|
||||||
|
|
||||||
if (action?.muteAction) {
|
if(action?.muteAction) {
|
||||||
ev.emit(
|
ev.emit(
|
||||||
'chats.update',
|
'chats.update',
|
||||||
[
|
[
|
||||||
@@ -708,7 +708,7 @@ export const processSyncAction = (
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
} else if (action?.archiveChatAction || type === 'archive' || type === 'unarchive') {
|
} else if(action?.archiveChatAction || type === 'archive' || type === 'unarchive') {
|
||||||
// okay so we've to do some annoying computation here
|
// okay so we've to do some annoying computation here
|
||||||
// when we're initially syncing the app state
|
// when we're initially syncing the app state
|
||||||
// there are a few cases we need to handle
|
// there are a few cases we need to handle
|
||||||
@@ -737,7 +737,7 @@ export const processSyncAction = (
|
|||||||
archived: isArchived,
|
archived: isArchived,
|
||||||
conditional: getChatUpdateConditional(id, msgRange)
|
conditional: getChatUpdateConditional(id, msgRange)
|
||||||
}])
|
}])
|
||||||
} else if (action?.markChatAsReadAction) {
|
} else if(action?.markChatAsReadAction) {
|
||||||
const markReadAction = action.markChatAsReadAction
|
const markReadAction = action.markChatAsReadAction
|
||||||
// basically we don't need to fire an "read" update if the chat is being marked as read
|
// basically we don't need to fire an "read" update if the chat is being marked as read
|
||||||
// because the chat is read by default
|
// because the chat is read by default
|
||||||
@@ -749,7 +749,7 @@ export const processSyncAction = (
|
|||||||
unreadCount: isNullUpdate ? null : !!markReadAction?.read ? 0 : -1,
|
unreadCount: isNullUpdate ? null : !!markReadAction?.read ? 0 : -1,
|
||||||
conditional: getChatUpdateConditional(id, markReadAction?.messageRange)
|
conditional: getChatUpdateConditional(id, markReadAction?.messageRange)
|
||||||
}])
|
}])
|
||||||
} else if (action?.deleteMessageForMeAction || type === 'deleteMessageForMe') {
|
} else if(action?.deleteMessageForMeAction || type === 'deleteMessageForMe') {
|
||||||
ev.emit('messages.delete', {
|
ev.emit('messages.delete', {
|
||||||
keys: [
|
keys: [
|
||||||
{
|
{
|
||||||
@@ -759,30 +759,30 @@ export const processSyncAction = (
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
} else if (action?.contactAction) {
|
} else if(action?.contactAction) {
|
||||||
ev.emit('contacts.upsert', [{ id, name: action.contactAction!.fullName! }])
|
ev.emit('contacts.upsert', [{ id, name: action.contactAction!.fullName! }])
|
||||||
} else if (action?.pushNameSetting) {
|
} else if(action?.pushNameSetting) {
|
||||||
const name = action?.pushNameSetting?.name
|
const name = action?.pushNameSetting?.name
|
||||||
if (name && me?.name !== name) {
|
if(name && me?.name !== name) {
|
||||||
ev.emit('creds.update', { me: { ...me, name } })
|
ev.emit('creds.update', { me: { ...me, name } })
|
||||||
}
|
}
|
||||||
} else if (action?.pinAction) {
|
} else if(action?.pinAction) {
|
||||||
ev.emit('chats.update', [{
|
ev.emit('chats.update', [{
|
||||||
id,
|
id,
|
||||||
pinned: action.pinAction?.pinned ? toNumber(action.timestamp!) : null,
|
pinned: action.pinAction?.pinned ? toNumber(action.timestamp!) : null,
|
||||||
conditional: getChatUpdateConditional(id, undefined)
|
conditional: getChatUpdateConditional(id, undefined)
|
||||||
}])
|
}])
|
||||||
} else if (action?.unarchiveChatsSetting) {
|
} else if(action?.unarchiveChatsSetting) {
|
||||||
const unarchiveChats = !!action.unarchiveChatsSetting.unarchiveChats
|
const unarchiveChats = !!action.unarchiveChatsSetting.unarchiveChats
|
||||||
ev.emit('creds.update', { accountSettings: { unarchiveChats } })
|
ev.emit('creds.update', { accountSettings: { unarchiveChats } })
|
||||||
|
|
||||||
logger?.info(`archive setting updated => '${action.unarchiveChatsSetting.unarchiveChats}'`)
|
logger?.info(`archive setting updated => '${action.unarchiveChatsSetting.unarchiveChats}'`)
|
||||||
if (accountSettings) {
|
if(accountSettings) {
|
||||||
accountSettings.unarchiveChats = unarchiveChats
|
accountSettings.unarchiveChats = unarchiveChats
|
||||||
}
|
}
|
||||||
} else if (action?.starAction || type === 'star') {
|
} else if(action?.starAction || type === 'star') {
|
||||||
let starred = action?.starAction?.starred
|
let starred = action?.starAction?.starred
|
||||||
if (typeof starred !== 'boolean') {
|
if(typeof starred !== 'boolean') {
|
||||||
starred = syncAction.index[syncAction.index.length - 1] === '1'
|
starred = syncAction.index[syncAction.index.length - 1] === '1'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -792,11 +792,11 @@ export const processSyncAction = (
|
|||||||
update: { starred }
|
update: { starred }
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
} else if (action?.deleteChatAction || type === 'deleteChat') {
|
} else if(action?.deleteChatAction || type === 'deleteChat') {
|
||||||
if (!isInitialSync) {
|
if(!isInitialSync) {
|
||||||
ev.emit('chats.delete', [id])
|
ev.emit('chats.delete', [id])
|
||||||
}
|
}
|
||||||
} else if (action?.labelEditAction) {
|
} else if(action?.labelEditAction) {
|
||||||
const { name, color, deleted, predefinedId } = action.labelEditAction!
|
const { name, color, deleted, predefinedId } = action.labelEditAction!
|
||||||
|
|
||||||
ev.emit('labels.edit', {
|
ev.emit('labels.edit', {
|
||||||
@@ -806,7 +806,7 @@ export const processSyncAction = (
|
|||||||
deleted: deleted!,
|
deleted: deleted!,
|
||||||
predefinedId: predefinedId ? String(predefinedId) : undefined
|
predefinedId: predefinedId ? String(predefinedId) : undefined
|
||||||
})
|
})
|
||||||
} else if (action?.labelAssociationAction) {
|
} else if(action?.labelAssociationAction) {
|
||||||
ev.emit('labels.association', {
|
ev.emit('labels.association', {
|
||||||
type: action.labelAssociationAction.labeled
|
type: action.labelAssociationAction.labeled
|
||||||
? 'add'
|
? 'add'
|
||||||
@@ -832,7 +832,7 @@ export const processSyncAction = (
|
|||||||
return isInitialSync
|
return isInitialSync
|
||||||
? (data) => {
|
? (data) => {
|
||||||
const chat = data.historySets.chats[id] || data.chatUpserts[id]
|
const chat = data.historySets.chats[id] || data.chatUpserts[id]
|
||||||
if (chat) {
|
if(chat) {
|
||||||
return msgRange ? isValidPatchBasedOnMessageRange(chat, msgRange) : true
|
return msgRange ? isValidPatchBasedOnMessageRange(chat, msgRange) : true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user