Add missing Metamigo changes

This commit is contained in:
Darren Clarke 2023-03-13 16:09:41 +00:00
parent 27810142b3
commit 161f3fcee2
6 changed files with 12 additions and 200 deletions

1
.gitignore vendored
View file

@ -9,3 +9,4 @@ docker/zammad/auto_install/**
coverage/ coverage/
build/ build/
junit.xml junit.xml
.next

View file

@ -1,191 +0,0 @@
import { Boom } from "@hapi/boom";
import { Server } from "@hapi/hapi";
import { randomBytes } from "node:crypto";
import type { Logger } from "pino";
import {
proto,
BufferJSON,
generateRegistrationId,
Curve,
signedKeyPair,
AuthenticationCreds,
AuthenticationState,
AccountSettings,
SignalDataSet,
SignalDataTypeMap,
SignalKeyStore,
SignalKeyStoreWithTransaction,
} from "@adiwajshing/baileys";
import { SavedWhatsappBot as Bot } from "@digiresilience/metamigo-db";
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",
};
export const addTransactionCapability = (
state: SignalKeyStore,
logger: Logger
): SignalKeyStoreWithTransaction => {
let inTransaction = false;
let transactionCache: SignalDataSet = {};
let mutations: SignalDataSet = {};
const prefetch = async (type: keyof SignalDataTypeMap, ids: string[]) => {
if (!inTransaction) {
throw new Boom("Cannot prefetch without transaction");
}
const dict = transactionCache[type];
const idsRequiringFetch = dict
? ids.filter((item) => !(item in dict))
: ids;
// only fetch if there are any items to fetch
if (idsRequiringFetch.length > 0) {
const result = await state.get(type, idsRequiringFetch);
transactionCache[type] = transactionCache[type] || {};
Object.assign(transactionCache[type], result);
}
};
return {
async get(type, ids) {
if (inTransaction) {
await prefetch(type, ids);
return ids.reduce((dict, id) => {
const value = transactionCache[type]?.[id];
if (value) {
dict[id] = value;
}
return dict;
}, {});
}
return state.get(type, ids);
},
set(data) {
if (inTransaction) {
logger.trace({ types: Object.keys(data) }, "caching in transaction");
for (const key in data) {
transactionCache[key] = transactionCache[key] || {};
Object.assign(transactionCache[key], data[key]);
mutations[key] = mutations[key] || {};
Object.assign(mutations[key], data[key]);
}
} else {
return state.set(data);
}
},
isInTransaction: () => inTransaction,
// @ts-expect-error
prefetch(type, ids) {
logger.trace({ type, ids }, "prefetching");
return prefetch(type, ids);
},
async transaction(work) {
if (inTransaction) {
await work();
} else {
logger.debug("entering transaction");
inTransaction = true;
try {
await work();
if (Object.keys(mutations).length > 0) {
logger.debug("committing transaction");
await state.set(mutations);
} else {
logger.debug("no mutations in transaction");
}
} finally {
inTransaction = false;
transactionCache = {};
mutations = {};
}
}
},
};
};
export const initAuthCreds = (): AuthenticationCreds => {
const identityKey = Curve.generateKeyPair();
return {
noiseKey: Curve.generateKeyPair(),
signedIdentityKey: identityKey,
signedPreKey: signedKeyPair(identityKey, 1),
registrationId: generateRegistrationId(),
advSecretKey: randomBytes(32).toString("base64"),
nextPreKeyId: 1,
firstUnuploadedPreKeyId: 1,
processedHistoryMessages: [],
accountSettings: {
unarchiveChats: false,
},
} as any;
};
export const useDatabaseAuthState = (
bot: Bot,
server: Server
): { state: AuthenticationState; saveState: () => void } => {
const { logger }: any = server;
let creds: AuthenticationCreds;
let keys: any = {};
const saveState = async () => {
logger && logger.trace("saving auth state");
const authInfo = JSON.stringify({ creds, keys }, BufferJSON.replacer, 2);
await server.db().whatsappBots.updateAuthInfo(bot, authInfo);
};
if (bot.authInfo) {
console.log("Auth info exists");
const result = JSON.parse(bot.authInfo, BufferJSON.reviver);
creds = result.creds;
keys = result.keys;
} else {
console.log("Auth info does not exist");
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") {
// @ts-expect-error
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,
};
};

View file

@ -146,7 +146,7 @@ export const UnverifyBotRoute = Helpers.withDefaults({
path: "/api/whatsapp/bots/{id}/unverify", path: "/api/whatsapp/bots/{id}/unverify",
options: { options: {
description: "Unverify bot", description: "Unverify bot",
async handler: (request: Hapi.Request, _h: Hapi.ResponseToolkit) { async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) {
const { id } = request.params; const { id } = request.params;
const { whatsappService } = request.services(); const { whatsappService } = request.services();

View file

@ -101,8 +101,8 @@ const Sidebar = ({ record }) => {
</IconButton> </IconButton>
</Grid> </Grid>
</Grid> </Grid>
{receivedMessages.map((receivedMessage) => ( {receivedMessages.map((receivedMessage, index) => (
<Grid item container direction="column" spacing={1}> <Grid key={index} item container direction="column" spacing={1}>
<Grid item style={{ fontWeight: "bold", color: "#999" }}> <Grid item style={{ fontWeight: "bold", color: "#999" }}>
{receivedMessage.key.remoteJid.replace("@s.whatsapp.net", "")} {receivedMessage.key.remoteJid.replace("@s.whatsapp.net", "")}
</Grid> </Grid>

View file

@ -5,7 +5,8 @@
"author": "Abel Luck <abel@guardianproject.info>", "author": "Abel Luck <abel@guardianproject.info>",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@digiresilience/montar": "*" "@digiresilience/montar": "*",
"@digiresilience/metamigo-common": "*"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.20.12", "@babel/core": "7.20.12",

View file

@ -41,13 +41,14 @@ export class WhatsappBotRecordRepository extends RepositoryBase(
); );
} }
async updateAuthInfo( async updateVerified(
bot: SavedWhatsappBot, bot: SavedWhatsappBot,
authInfo: string | undefined verified: boolean
): Promise<SavedWhatsappBot> { ): Promise<SavedWhatsappBot> {
return this.db.one( return this.db.one(
"UPDATE $1 SET (auth_info, is_verified) = ROW($2, true) WHERE id = $3 RETURNING *", "UPDATE $1 SET (is_verified) = ROW($2) WHERE id = $3 RETURNING *",
[this.schemaTable, authInfo, bot.id] [this.schemaTable, verified, bot.id]
); );
} }
} }