waha / src /core /engines /noweb /useMultiFileAuthState.ts
NitinBot002's picture
Upload 384 files
4327358 verified
import {
AuthenticationCreds,
AuthenticationState,
proto,
} from '@adiwajshing/baileys';
import { BufferJSON, initAuthCreds } from '@adiwajshing/baileys/lib/Utils';
import { mkdir, readFile, stat, unlink } from 'fs/promises';
import { join } from 'path';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const writeFileAtomic = require('write-file-atomic');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const AsyncLock = require('async-lock');
// We need to lock files due to the fact that we are using async functions to read and write files
// https://github.com/WhiskeySockets/Baileys/issues/794
// https://github.com/nodejs/node/issues/26338
// Default pending is 1000, set it to infinity
// https://github.com/rogierschouten/async-lock/issues/63
const fileLock = new AsyncLock({
timeout: 5_000,
maxPending: Infinity,
maxExecutionTime: 30_000,
});
/**
* 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>;
close: () => Promise<void>;
}> => {
const writeData = (data: any, file: string) => {
const filePath = join(folder, fixFileName(file));
return fileLock.acquire(filePath, () =>
writeFileAtomic(
join(filePath),
JSON.stringify(data, BufferJSON.replacer),
),
);
};
const readData = async (file: string) => {
try {
const filePath = join(folder, fixFileName(file));
const data = await fileLock.acquire(filePath, () =>
readFile(filePath, { encoding: 'utf-8' }),
);
return JSON.parse(data, BufferJSON.reviver);
} catch (error) {
return null;
}
};
const removeData = async (file: string) => {
try {
const filePath = join(folder, fixFileName(file));
await fileLock.acquire(filePath, () => unlink(filePath));
} catch {}
};
const folderInfo = await stat(folder).catch(() => {
return null;
});
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 fixFileName = (file?: string) =>
file?.replace(/\//g, '__')?.replace(/:/g, '-') || '';
const creds: AuthenticationCreds =
(await readData('creds.json')) || initAuthCreds();
return {
state: {
creds,
keys: {
get: async (type, ids) => {
const data = {};
await Promise.all(
ids.map(async (id) => {
let value = await readData(`${type}-${id}.json`);
if (type === 'app-state-sync-key' && value) {
value = proto.Message.AppStateSyncKeyData.fromObject(value);
}
data[id] = value;
}),
);
return data;
},
set: async (data) => {
const tasks: Promise<void>[] = [];
for (const category in data) {
for (const id in data[category]) {
const value = data[category][id];
const file = `${category}-${id}.json`;
tasks.push(value ? writeData(value, file) : removeData(file));
}
}
await Promise.all(tasks);
},
},
},
saveCreds: () => {
return writeData(creds, 'creds.json');
},
close: async () => {
return;
},
};
};