mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
feat: add retry capability to SignalKeyStore
This commit is contained in:
@@ -45,6 +45,7 @@ export const DEFAULT_CONNECTION_CONFIG: SocketConfig = {
|
|||||||
...BASE_CONNECTION_CONFIG,
|
...BASE_CONNECTION_CONFIG,
|
||||||
downloadHistory: true,
|
downloadHistory: true,
|
||||||
linkPreviewImageThumbnailWidth: 192,
|
linkPreviewImageThumbnailWidth: 192,
|
||||||
|
transactionOpts: { maxCommitRetries: 10, delayBetweenTriesMs: 3000 },
|
||||||
getMessage: async() => undefined
|
getMessage: async() => undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ export const makeSocket = ({
|
|||||||
browser,
|
browser,
|
||||||
auth: initialAuthState,
|
auth: initialAuthState,
|
||||||
printQRInTerminal,
|
printQRInTerminal,
|
||||||
defaultQueryTimeoutMs
|
defaultQueryTimeoutMs,
|
||||||
|
transactionOpts
|
||||||
}: SocketConfig) => {
|
}: SocketConfig) => {
|
||||||
const ws = new WebSocket(waWebSocketUrl, undefined, {
|
const ws = new WebSocket(waWebSocketUrl, undefined, {
|
||||||
origin: DEFAULT_ORIGIN,
|
origin: DEFAULT_ORIGIN,
|
||||||
@@ -51,7 +52,7 @@ export const makeSocket = ({
|
|||||||
|
|
||||||
const { creds } = authState
|
const { creds } = authState
|
||||||
// add transaction capability
|
// add transaction capability
|
||||||
const keys = addTransactionCapability(authState.keys, logger)
|
const keys = addTransactionCapability(authState.keys, logger, transactionOpts)
|
||||||
|
|
||||||
let lastDateRecv: Date
|
let lastDateRecv: Date
|
||||||
let epoch = 1
|
let epoch = 1
|
||||||
|
|||||||
@@ -72,6 +72,11 @@ export type SignalKeyStoreWithTransaction = SignalKeyStore & {
|
|||||||
prefetch<T extends keyof SignalDataTypeMap>(type: T, ids: string[]): Promise<void>
|
prefetch<T extends keyof SignalDataTypeMap>(type: T, ids: string[]): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TransactionCapabilityOptions = {
|
||||||
|
maxCommitRetries: number
|
||||||
|
delayBetweenTriesMs: number
|
||||||
|
}
|
||||||
|
|
||||||
export type SignalAuthState = {
|
export type SignalAuthState = {
|
||||||
creds: SignalCreds
|
creds: SignalCreds
|
||||||
keys: SignalKeyStore
|
keys: SignalKeyStore
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export * from './Call'
|
|||||||
|
|
||||||
import type NodeCache from 'node-cache'
|
import type NodeCache from 'node-cache'
|
||||||
import { proto } from '../../WAProto'
|
import { proto } from '../../WAProto'
|
||||||
import { AuthenticationState } from './Auth'
|
import { AuthenticationState, TransactionCapabilityOptions } from './Auth'
|
||||||
import { CommonSocketConfig } from './Socket'
|
import { CommonSocketConfig } from './Socket'
|
||||||
|
|
||||||
export type MessageRetryMap = { [msgId: string]: number }
|
export type MessageRetryMap = { [msgId: string]: number }
|
||||||
@@ -20,6 +20,8 @@ export type MessageRetryMap = { [msgId: string]: number }
|
|||||||
export type SocketConfig = CommonSocketConfig<AuthenticationState> & {
|
export type SocketConfig = CommonSocketConfig<AuthenticationState> & {
|
||||||
/** By default true, should history messages be downloaded and processed */
|
/** By default true, should history messages be downloaded and processed */
|
||||||
downloadHistory: boolean
|
downloadHistory: boolean
|
||||||
|
/** transaction capability options for SignalKeyStore */
|
||||||
|
transactionOpts: TransactionCapabilityOptions
|
||||||
/** provide a cache to store a user's device list */
|
/** provide a cache to store a user's device list */
|
||||||
userDevicesCache?: NodeCache
|
userDevicesCache?: NodeCache
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,18 +2,9 @@ import { Boom } from '@hapi/boom'
|
|||||||
import { randomBytes } from 'crypto'
|
import { randomBytes } from 'crypto'
|
||||||
import type { Logger } from 'pino'
|
import type { Logger } from 'pino'
|
||||||
import { proto } from '../../WAProto'
|
import { proto } from '../../WAProto'
|
||||||
import type { AuthenticationCreds, AuthenticationState, SignalDataSet, SignalDataTypeMap, SignalKeyStore, SignalKeyStoreWithTransaction } from '../Types'
|
import type { AuthenticationCreds, AuthenticationState, SignalDataSet, SignalDataTypeMap, SignalKeyStore, SignalKeyStoreWithTransaction, TransactionCapabilityOptions } from '../Types'
|
||||||
import { Curve, signedKeyPair } from './crypto'
|
import { Curve, signedKeyPair } from './crypto'
|
||||||
import { BufferJSON, generateRegistrationId } from './generics'
|
import { BufferJSON, delay, generateRegistrationId } from './generics'
|
||||||
|
|
||||||
const KEY_MAP: { [T in keyof SignalDataTypeMap]: string } = {
|
|
||||||
'pre-key': 'preKeys',
|
|
||||||
'session': 'sessions',
|
|
||||||
'sender-key': 'senderKeys',
|
|
||||||
'app-state-sync-key': 'appStateSyncKeys',
|
|
||||||
'app-state-sync-version': 'appStateVersions',
|
|
||||||
'sender-key-memory': 'senderKeyMemory'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds DB like transaction capability (https://en.wikipedia.org/wiki/Database_transaction) to the SignalKeyStore,
|
* Adds DB like transaction capability (https://en.wikipedia.org/wiki/Database_transaction) to the SignalKeyStore,
|
||||||
@@ -22,11 +13,15 @@ const KEY_MAP: { [T in keyof SignalDataTypeMap]: string } = {
|
|||||||
* @param logger logger to log events
|
* @param logger logger to log events
|
||||||
* @returns SignalKeyStore with transaction capability
|
* @returns SignalKeyStore with transaction capability
|
||||||
*/
|
*/
|
||||||
export const addTransactionCapability = (state: SignalKeyStore, logger: Logger): SignalKeyStoreWithTransaction => {
|
export const addTransactionCapability = (state: SignalKeyStore, logger: Logger, { maxCommitRetries, delayBetweenTriesMs }: TransactionCapabilityOptions): SignalKeyStoreWithTransaction => {
|
||||||
let inTransaction = false
|
let inTransaction = false
|
||||||
let transactionCache: SignalDataSet = { }
|
let transactionCache: SignalDataSet = { }
|
||||||
let mutations: SignalDataSet = { }
|
let mutations: SignalDataSet = { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prefetches some data and stores in memory,
|
||||||
|
* useful if these data points will be used together often
|
||||||
|
* */
|
||||||
const prefetch = async(type: keyof SignalDataTypeMap, ids: string[]) => {
|
const prefetch = async(type: keyof SignalDataTypeMap, ids: string[]) => {
|
||||||
if(!inTransaction) {
|
if(!inTransaction) {
|
||||||
throw new Boom('Cannot prefetch without transaction')
|
throw new Boom('Cannot prefetch without transaction')
|
||||||
@@ -90,7 +85,19 @@ export const addTransactionCapability = (state: SignalKeyStore, logger: Logger):
|
|||||||
await work()
|
await work()
|
||||||
if(Object.keys(mutations).length) {
|
if(Object.keys(mutations).length) {
|
||||||
logger.debug('committing transaction')
|
logger.debug('committing transaction')
|
||||||
await state.set(mutations)
|
// retry mechanism to ensure we've some recovery
|
||||||
|
// in case a transaction fails in the first attempt
|
||||||
|
let tries = maxCommitRetries
|
||||||
|
while(tries) {
|
||||||
|
tries -= 1
|
||||||
|
try {
|
||||||
|
await state.set(mutations)
|
||||||
|
break
|
||||||
|
} catch(error) {
|
||||||
|
logger.warn(`failed to commit ${Object.keys(mutations).length} mutations, tries left=${tries}`)
|
||||||
|
await delay(delayBetweenTriesMs)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.debug('no mutations in transaction')
|
logger.debug('no mutations in transaction')
|
||||||
}
|
}
|
||||||
@@ -121,6 +128,16 @@ export const initAuthCreds = (): AuthenticationCreds => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// useless key map only there to maintain backwards compatibility
|
||||||
|
// do not use in your own systems please
|
||||||
|
const KEY_MAP: { [T in keyof SignalDataTypeMap]: string } = {
|
||||||
|
'pre-key': 'preKeys',
|
||||||
|
'session': 'sessions',
|
||||||
|
'sender-key': 'senderKeys',
|
||||||
|
'app-state-sync-key': 'appStateSyncKeys',
|
||||||
|
'app-state-sync-version': 'appStateVersions',
|
||||||
|
'sender-key-memory': 'senderKeyMemory'
|
||||||
|
}
|
||||||
/** stores the full authentication state in a single JSON file */
|
/** stores the full authentication state in a single JSON file */
|
||||||
export const useSingleFileAuthState = (filename: string, logger?: Logger): { state: AuthenticationState, saveState: () => void } => {
|
export const useSingleFileAuthState = (filename: string, logger?: Logger): { state: AuthenticationState, saveState: () => void } => {
|
||||||
// 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
|
||||||
|
|||||||
Reference in New Issue
Block a user