diff --git a/.gitignore b/.gitignore index 8af3a9f..f730000 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ docker/zammad/auto_install/** .npmrc coverage/ build/ -junit.xml \ No newline at end of file +junit.xml +.next diff --git a/apps/metamigo-api/src/app/lib/whatsapp-key-store.ts b/apps/metamigo-api/src/app/lib/whatsapp-key-store.ts deleted file mode 100644 index c62992e..0000000 --- a/apps/metamigo-api/src/app/lib/whatsapp-key-store.ts +++ /dev/null @@ -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, - }; -}; diff --git a/apps/metamigo-api/src/app/routes/whatsapp/index.ts b/apps/metamigo-api/src/app/routes/whatsapp/index.ts index 9edb747..cf23099 100644 --- a/apps/metamigo-api/src/app/routes/whatsapp/index.ts +++ b/apps/metamigo-api/src/app/routes/whatsapp/index.ts @@ -146,7 +146,7 @@ export const UnverifyBotRoute = Helpers.withDefaults({ path: "/api/whatsapp/bots/{id}/unverify", options: { description: "Unverify bot", - async handler: (request: Hapi.Request, _h: Hapi.ResponseToolkit) { + async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) { const { id } = request.params; const { whatsappService } = request.services(); diff --git a/apps/metamigo-frontend/components/whatsapp/bots/WhatsappBotShow.tsx b/apps/metamigo-frontend/components/whatsapp/bots/WhatsappBotShow.tsx index 455041c..dab988f 100644 --- a/apps/metamigo-frontend/components/whatsapp/bots/WhatsappBotShow.tsx +++ b/apps/metamigo-frontend/components/whatsapp/bots/WhatsappBotShow.tsx @@ -101,8 +101,8 @@ const Sidebar = ({ record }) => { - {receivedMessages.map((receivedMessage) => ( - + {receivedMessages.map((receivedMessage, index) => ( + {receivedMessage.key.remoteJid.replace("@s.whatsapp.net", "")} diff --git a/packages/metamigo-config/package.json b/packages/metamigo-config/package.json index 901f971..a650250 100644 --- a/packages/metamigo-config/package.json +++ b/packages/metamigo-config/package.json @@ -5,7 +5,8 @@ "author": "Abel Luck ", "license": "AGPL-3.0-or-later", "dependencies": { - "@digiresilience/montar": "*" + "@digiresilience/montar": "*", + "@digiresilience/metamigo-common": "*" }, "devDependencies": { "@babel/core": "7.20.12", diff --git a/packages/metamigo-db/src/records/whatsapp/bots.ts b/packages/metamigo-db/src/records/whatsapp/bots.ts index 0133037..c86368f 100644 --- a/packages/metamigo-db/src/records/whatsapp/bots.ts +++ b/packages/metamigo-db/src/records/whatsapp/bots.ts @@ -41,13 +41,14 @@ export class WhatsappBotRecordRepository extends RepositoryBase( ); } - async updateAuthInfo( + async updateVerified( bot: SavedWhatsappBot, - authInfo: string | undefined + verified: boolean ): Promise { return this.db.one( - "UPDATE $1 SET (auth_info, is_verified) = ROW($2, true) WHERE id = $3 RETURNING *", - [this.schemaTable, authInfo, bot.id] + "UPDATE $1 SET (is_verified) = ROW($2) WHERE id = $3 RETURNING *", + [this.schemaTable, verified, bot.id] ); } + }