mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
feat: add "multi file auth state" implementation
1. add multi file auth state since it's far more efficient than single state 2. deprecate single file auth state (please don't use it anymore)
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
import { Boom } from '@hapi/boom'
|
||||
import { randomBytes } from 'crypto'
|
||||
import type { Logger } from 'pino'
|
||||
import { proto } from '../../WAProto'
|
||||
import type { AuthenticationCreds, AuthenticationState, SignalDataSet, SignalDataTypeMap, SignalKeyStore, SignalKeyStoreWithTransaction, TransactionCapabilityOptions } from '../Types'
|
||||
import type { AuthenticationCreds, SignalDataSet, SignalDataTypeMap, SignalKeyStore, SignalKeyStoreWithTransaction, TransactionCapabilityOptions } from '../Types'
|
||||
import { Curve, signedKeyPair } from './crypto'
|
||||
import { BufferJSON, delay, generateRegistrationId } from './generics'
|
||||
import { delay, generateRegistrationId } from './generics'
|
||||
|
||||
/**
|
||||
* Adds DB like transaction capability (https://en.wikipedia.org/wiki/Database_transaction) to the SignalKeyStore,
|
||||
@@ -126,79 +125,4 @@ export const initAuthCreds = (): AuthenticationCreds => {
|
||||
unarchiveChats: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 */
|
||||
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
|
||||
const { readFileSync, writeFileSync, existsSync } = require('fs')
|
||||
let creds: AuthenticationCreds
|
||||
let keys: any = { }
|
||||
|
||||
// save the authentication state to a file
|
||||
const saveState = () => {
|
||||
logger && logger.trace('saving auth state')
|
||||
writeFileSync(
|
||||
filename,
|
||||
// BufferJSON replacer utility saves buffers nicely
|
||||
JSON.stringify({ creds, keys }, BufferJSON.replacer, 2)
|
||||
)
|
||||
}
|
||||
|
||||
if(existsSync(filename)) {
|
||||
const result = JSON.parse(
|
||||
readFileSync(filename, { encoding: 'utf-8' }),
|
||||
BufferJSON.reviver
|
||||
)
|
||||
creds = result.creds
|
||||
keys = result.keys
|
||||
} else {
|
||||
creds = initAuthCreds()
|
||||
keys = { }
|
||||
}
|
||||
|
||||
return {
|
||||
state: {
|
||||
creds,
|
||||
keys: {
|
||||
get: (type, ids) => {
|
||||
const key = KEY_MAP[type]
|
||||
return ids.reduce(
|
||||
(dict, id) => {
|
||||
let value = keys[key]?.[id]
|
||||
if(value) {
|
||||
if(type === 'app-state-sync-key') {
|
||||
value = proto.AppStateSyncKeyData.fromObject(value)
|
||||
}
|
||||
|
||||
dict[id] = value
|
||||
}
|
||||
|
||||
return dict
|
||||
}, { }
|
||||
)
|
||||
},
|
||||
set: (data) => {
|
||||
for(const _key in data) {
|
||||
const key = KEY_MAP[_key as keyof SignalDataTypeMap]
|
||||
keys[key] = keys[key] || { }
|
||||
Object.assign(keys[key], data[_key])
|
||||
}
|
||||
|
||||
saveState()
|
||||
}
|
||||
}
|
||||
},
|
||||
saveState
|
||||
}
|
||||
}
|
||||
@@ -11,4 +11,6 @@ export * from './chat-utils'
|
||||
export * from './lt-hash'
|
||||
export * from './auth-utils'
|
||||
export * from './legacy-msgs'
|
||||
export * from './baileys-event-stream'
|
||||
export * from './baileys-event-stream'
|
||||
export * from './use-single-file-auth-state'
|
||||
export * from './use-multi-file-auth-state'
|
||||
78
src/Utils/use-multi-file-auth-state.ts
Normal file
78
src/Utils/use-multi-file-auth-state.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { mkdir, readFile, stat, writeFile } from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import { proto } from '../../WAProto'
|
||||
import { AuthenticationCreds, AuthenticationState, SignalDataTypeMap } from '../Types'
|
||||
import { initAuthCreds } from './auth-utils'
|
||||
import { BufferJSON } from './generics'
|
||||
|
||||
/**
|
||||
* stores the full authentication state in a single folder.
|
||||
* Far more efficient than singlefileauthstate
|
||||
*
|
||||
* Again, I wouldn't endorse this for any production level use other than perhaps a bot.
|
||||
* Would recommend writing an auth state for use with a proper SQL or No-SQL DB
|
||||
* */
|
||||
export const useMultiFileAuthState = async(folder: string): Promise<{ state: AuthenticationState, saveCreds: () => Promise<void> }> => {
|
||||
|
||||
const writeData = (data: any, file: string) => {
|
||||
return writeFile(join(folder, file), JSON.stringify(data, BufferJSON.replacer))
|
||||
}
|
||||
|
||||
const readData = async(file: string) => {
|
||||
try {
|
||||
const data = await readFile(join(folder, file), { encoding: 'utf-8' })
|
||||
return JSON.parse(data, BufferJSON.reviver)
|
||||
} catch(error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const folderInfo = await stat(folder).catch(() => { })
|
||||
if(folderInfo) {
|
||||
if(!folderInfo.isDirectory()) {
|
||||
throw new Error(`found something that is not a directory at ${folder}, either delete it or specify a different location`)
|
||||
}
|
||||
} else {
|
||||
await mkdir(folder, { recursive: true })
|
||||
}
|
||||
|
||||
const creds: AuthenticationCreds = await readData('creds.json') || initAuthCreds()
|
||||
|
||||
return {
|
||||
state: {
|
||||
creds,
|
||||
keys: {
|
||||
get: async(type, ids) => {
|
||||
const data: { [_: string]: SignalDataTypeMap[typeof type] } = { }
|
||||
await Promise.all(
|
||||
ids.map(
|
||||
async id => {
|
||||
let value = await readData(`${type}-${id}.json`)
|
||||
if(type === 'app-state-sync-key') {
|
||||
value = proto.AppStateSyncKeyData.fromObject(data)
|
||||
}
|
||||
|
||||
data[id] = value
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
return data
|
||||
},
|
||||
set: async(data) => {
|
||||
const tasks: Promise<void>[] = []
|
||||
for(const category in data) {
|
||||
for(const id in data[category]) {
|
||||
tasks.push(writeData(data[category][id], `${category}-${id}.json`))
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(tasks)
|
||||
}
|
||||
}
|
||||
},
|
||||
saveCreds: () => {
|
||||
return writeData(creds, 'creds.json')
|
||||
}
|
||||
}
|
||||
}
|
||||
85
src/Utils/use-single-file-auth-state.ts
Normal file
85
src/Utils/use-single-file-auth-state.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import type { Logger } from 'pino'
|
||||
import { proto } from '../../WAProto'
|
||||
import type { AuthenticationCreds, AuthenticationState, SignalDataTypeMap } from '../Types'
|
||||
import { initAuthCreds } from './auth-utils'
|
||||
import { BufferJSON } from './generics'
|
||||
|
||||
// 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'
|
||||
}
|
||||
/**
|
||||
* @deprecated use multi file auth state instead please
|
||||
* stores the full authentication state in a single JSON file
|
||||
*
|
||||
* DO NOT USE IN A PROD ENVIRONMENT, only meant to serve as an example
|
||||
* */
|
||||
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
|
||||
const { readFileSync, writeFileSync, existsSync } = require('fs')
|
||||
let creds: AuthenticationCreds
|
||||
let keys: any = { }
|
||||
|
||||
// save the authentication state to a file
|
||||
const saveState = () => {
|
||||
logger && logger.trace('saving auth state')
|
||||
writeFileSync(
|
||||
filename,
|
||||
// BufferJSON replacer utility saves buffers nicely
|
||||
JSON.stringify({ creds, keys }, BufferJSON.replacer, 2)
|
||||
)
|
||||
}
|
||||
|
||||
if(existsSync(filename)) {
|
||||
const result = JSON.parse(
|
||||
readFileSync(filename, { encoding: 'utf-8' }),
|
||||
BufferJSON.reviver
|
||||
)
|
||||
creds = result.creds
|
||||
keys = result.keys
|
||||
} else {
|
||||
creds = initAuthCreds()
|
||||
keys = { }
|
||||
}
|
||||
|
||||
return {
|
||||
state: {
|
||||
creds,
|
||||
keys: {
|
||||
get: (type, ids) => {
|
||||
const key = KEY_MAP[type]
|
||||
return ids.reduce(
|
||||
(dict, id) => {
|
||||
let value = keys[key]?.[id]
|
||||
if(value) {
|
||||
if(type === 'app-state-sync-key') {
|
||||
value = proto.AppStateSyncKeyData.fromObject(value)
|
||||
}
|
||||
|
||||
dict[id] = value
|
||||
}
|
||||
|
||||
return dict
|
||||
}, { }
|
||||
)
|
||||
},
|
||||
set: (data) => {
|
||||
for(const _key in data) {
|
||||
const key = KEY_MAP[_key as keyof SignalDataTypeMap]
|
||||
keys[key] = keys[key] || { }
|
||||
Object.assign(keys[key], data[_key])
|
||||
}
|
||||
|
||||
saveState()
|
||||
}
|
||||
}
|
||||
},
|
||||
saveState
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user