Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Signal/libsignal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ export function makeLibSignalRepository(
}, `delete-${jids.length}-sessions`)
},

close() {
migratedSessionCache.clear()
lidMapping.close()
},

async migrateSession(
fromJid: string,
toJid: string
Expand Down
7 changes: 7 additions & 0 deletions src/Signal/lid-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,4 +206,11 @@ export class LIDMappingStore {
this.logger.trace(`Found reverse mapping: ${lid} → ${pnJid}`)
return pnJid
}

/**
* Close the cache and release resources
*/
close(): void {
this.mappingCache.clear()
}
}
30 changes: 25 additions & 5 deletions src/Socket/chats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,17 @@
getMessage
} = config
const sock = makeSocket(config)
const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError } = sock
const {
ev,
ws,
authState,
generateMessageTag,
sendNode,
query,
signalRepository,
onUnexpectedError,
registerSocketEndHandler
} = sock

let privacySettings: { [_: string]: string } | undefined

Expand Down Expand Up @@ -95,10 +105,6 @@
useClones: false
}) as CacheStore)

if (!config.placeholderResendCache) {
config.placeholderResendCache = placeholderResendCache
}

/** helper function to fetch the given app state sync key */
const getAppStateSyncKey = async (keyId: string) => {
const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId])
Expand Down Expand Up @@ -575,7 +581,7 @@
// collection is done with sync
collectionsToHandle.delete(name)
}
} catch (error: any) {

Check warning on line 584 in src/Socket/chats.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
// if retry attempts overshoot
// or key not found
const isIrrecoverableError =
Expand Down Expand Up @@ -1186,6 +1192,20 @@
}, 20_000)
})

registerSocketEndHandler(() => {
if (awaitingSyncTimeout) {
clearTimeout(awaitingSyncTimeout)
awaitingSyncTimeout = undefined
}

if (!config.placeholderResendCache && placeholderResendCache.close) {
placeholderResendCache.close()
}

syncState = SyncState.Connecting
privacySettings = undefined
})

return {
...sock,
createCallLink,
Expand Down
20 changes: 19 additions & 1 deletion src/Socket/messages-recv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@
sendReceipt,
uploadPreKeys,
sendPeerDataOperationMessage,
messageRetryManager
messageRetryManager,
registerSocketEndHandler
} = sock

/** this mutex ensures that each retryRequest will wait for the previous one to finish */
Expand Down Expand Up @@ -185,7 +186,7 @@
return
}

let data: any

Check warning on line 189 in src/Socket/messages-recv.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
try {
data = JSON.parse(mexNode.content.toString())
} catch (error) {
Expand Down Expand Up @@ -281,7 +282,7 @@
case 'update':
const settingsNode = getBinaryNodeChild(child, 'settings')
if (settingsNode) {
const update: Record<string, any> = {}

Check warning on line 285 in src/Socket/messages-recv.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
const nameNode = getBinaryNodeChild(settingsNode, 'name')
if (nameNode?.content) update.name = nameNode.content.toString()

Expand Down Expand Up @@ -1575,6 +1576,23 @@
}
})

registerSocketEndHandler(() => {
if (!config.msgRetryCounterCache && msgRetryCache.close) {
msgRetryCache.close()
}

if (!config.callOfferCache && callOfferCache.close) {
callOfferCache.close()
}

if (!config.placeholderResendCache && placeholderResendCache.close) {
placeholderResendCache.close()
}

identityAssertDebounce.close()
sendActiveReceipts = false
})

return {
...sock,
sendMessageAck,
Expand Down
18 changes: 17 additions & 1 deletion src/Socket/messages-send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@
fetchPrivacySettings,
sendNode,
groupMetadata,
groupToggleEphemeral
groupToggleEphemeral,
registerSocketEndHandler
} = sock

const userDevicesCache =
Expand Down Expand Up @@ -508,8 +509,8 @@
const meLid = authState.creds.me?.lid
const meLidUser = meLid ? jidDecode(meLid)?.user : null

const encryptionPromises = (patchedMessages as any).map(

Check warning on line 512 in src/Socket/messages-send.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
async ({ recipientJid: jid, message: patchedMessage }: any) => {

Check warning on line 513 in src/Socket/messages-send.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
if (!jid) return null
let msgToEncrypt = patchedMessage
if (dsmMessage) {
Expand Down Expand Up @@ -1054,6 +1055,21 @@

const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update')

registerSocketEndHandler(() => {
if (!config.userDevicesCache && userDevicesCache.close) {
userDevicesCache.close()
}

if (peerSessionsCache.close) {
peerSessionsCache.close()
}

mediaConn = undefined as any

Check warning on line 1067 in src/Socket/messages-send.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
if (messageRetryManager) {
messageRetryManager.clear()
}
})

return {
...sock,
getPrivacyTokens,
Expand Down Expand Up @@ -1098,7 +1114,7 @@
content.url = getUrlFromDirectPath(content.directPath!)

logger.debug({ directPath: media.directPath, key: result.key }, 'media update successful')
} catch (err: any) {

Check warning on line 1117 in src/Socket/messages-send.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
error = err
}
}
Expand Down
18 changes: 18 additions & 0 deletions src/Socket/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@

const msgId = node.attrs.id

const result = await promiseTimeout<any>(timeoutMs, async (resolve, reject) => {

Check warning on line 195 in src/Socket/socket.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
const result = waitForMessage(msgId, timeoutMs).catch(reject)
sendNode(node)
.then(async () => resolve(await result))
Expand Down Expand Up @@ -364,6 +364,8 @@
let qrTimer: NodeJS.Timeout
let closed = false

const socketEndHandlers: Array<(error: Error | undefined) => void> = []

/** log & process any unexpected errors */
const onUnexpectedError = (err: Error | Boom, msg: string) => {
logger.error({ err }, `unexpected error in '${msg}'`)
Expand Down Expand Up @@ -621,12 +623,22 @@
ws.removeAllListeners('open')
ws.removeAllListeners('message')

signalRepository.close?.()

if (!ws.isClosed && !ws.isClosing) {
try {
ws.close()
} catch {}
}

for (const handler of socketEndHandlers) {
try {
handler(error)
} catch (err) {
logger.error({ err }, 'error in socket end handler')
}
}

ev.emit('connection.update', {
connection: 'close',
lastDisconnect: {
Expand All @@ -635,6 +647,7 @@
}
})
ev.removeAllListeners('connection.update')
ev.destroy()
}

const waitForSocketOpen = async () => {
Expand Down Expand Up @@ -826,7 +839,7 @@
ws.on('open', async () => {
try {
await validateConnection()
} catch (err: any) {

Check warning on line 842 in src/Socket/socket.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
logger.error({ err }, 'error in validating connection')
end(err)
}
Expand Down Expand Up @@ -894,7 +907,7 @@
ev.emit('connection.update', { isNewLogin: true, qr: undefined })

await sendNode(reply)
} catch (error: any) {

Check warning on line 910 in src/Socket/socket.ts

View workflow job for this annotation

GitHub Actions / check-lint

Unexpected any. Specify a different type
logger.info({ trace: error.stack }, 'error in pairing')
end(error)
}
Expand Down Expand Up @@ -1029,6 +1042,10 @@
Object.assign(creds, update)
})

const registerSocketEndHandler = (handler: (error: Error | undefined) => void) => {
socketEndHandlers.push(handler)
}

return {
type: 'md' as 'md',
ws,
Expand All @@ -1046,6 +1063,7 @@
sendNode,
logout,
end,
registerSocketEndHandler,
onUnexpectedError,
uploadPreKeys,
uploadPreKeysToServerIfRequired,
Expand Down
1 change: 1 addition & 0 deletions src/Types/Signal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,5 @@ export type SignalRepository = {
// Optimized repository with pre-loaded LID mapping store
export interface SignalRepositoryWithLIDStore extends SignalRepository {
lidMapping: LIDMappingStore
close?: () => void
}
1 change: 1 addition & 0 deletions src/Types/Socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export type CacheStore = {
del(key: string): void | Promise<void> | number | boolean
/** flush all data */
flushAll(): void | Promise<void>
close?: () => void
}

export type PossiblyExtendedCacheStore = CacheStore & {
Expand Down
21 changes: 20 additions & 1 deletion src/Utils/event-buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ type BaileysBufferableEventEmitter = BaileysEventEmitter & {
flush(): boolean
/** is there an ongoing buffer */
isBuffering(): boolean
/** destroy the event buffer, clearing all resources */
destroy(): void
}

/**
Expand Down Expand Up @@ -225,7 +227,24 @@ export const makeEventBuffer = (logger: ILogger): BaileysBufferableEventEmitter
},
on: (...args) => ev.on(...args),
off: (...args) => ev.off(...args),
removeAllListeners: (...args) => ev.removeAllListeners(...args)
removeAllListeners: (...args) => ev.removeAllListeners(...args),
destroy() {
// Clear buffer timeout
if (bufferTimeout) {
clearTimeout(bufferTimeout)
bufferTimeout = null
}

// Clear history cache
historyCache.clear()
// Reset buffer data
data = makeBufferData()
isBuffering = false
bufferCount = 0
// Remove all listeners
ev.removeAllListeners()
logger.debug('Event buffer destroyed')
}
}
}

Expand Down
19 changes: 19 additions & 0 deletions src/Utils/message-retry-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,25 @@ export class MessageRetryManager {
}
}

clear(): void {
this.recentMessagesMap.clear()
this.messageKeyIndex.clear()
this.sessionRecreateHistory.clear()
this.retryCounters.clear()
for (const messageId of Object.keys(this.pendingPhoneRequests)) {
this.cancelPendingPhoneRequest(messageId)
}

this.statistics = {
totalRetries: 0,
successfulRetries: 0,
failedRetries: 0,
mediaRetries: 0,
sessionRecreations: 0,
phoneRequests: 0
}
}

private keyToString(key: RecentMessageKey): string {
return `${key.to}${MESSAGE_KEY_SEPARATOR}${key.id}`
}
Expand Down