diff --git a/Dockerfile b/Dockerfile index a7091e356..4249f84ba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -88,7 +88,8 @@ WORKDIR /app COPY package*.json ./ # Install production dependencies only -RUN npm ci --omit=dev && npm cache clean --force +COPY scripts/patch-wwebjs-lid-last-received-key.js ./scripts/patch-wwebjs-lid-last-received-key.js +RUN npm ci --omit=dev && node scripts/patch-wwebjs-lid-last-received-key.js && npm cache clean --force # amd64: download Chrome for Testing via Puppeteer and symlink it. # arm64: use Debian's chromium installed above (CfT has no linux-arm64 build). @@ -114,7 +115,7 @@ COPY --from=builder /app/dashboard/dist ./dashboard/dist # Create data directories with correct ownership RUN mkdir -p ./data/sessions ./data/media && \ - chown -R openwa:openwa /app + chown -R openwa:openwa ./data # The non-root openwa user has no home of its own (`useradd -r`, no -m). Chromium resolves the home # dir from the passwd entry via glib's getpwuid() — it IGNORES $HOME — so it tries to read/write diff --git a/scripts/patch-wwebjs-lid-last-received-key.js b/scripts/patch-wwebjs-lid-last-received-key.js new file mode 100644 index 000000000..8ba7b0a22 --- /dev/null +++ b/scripts/patch-wwebjs-lid-last-received-key.js @@ -0,0 +1,57 @@ +const fs = require('fs'); +const path = require('path'); + +const DEFAULT_TARGET = path.join( + __dirname, + '..', + 'node_modules', + 'whatsapp-web.js', + 'src', + 'util', + 'Injected', + 'Utils.js', +); + +const OLD_BLOCK = ` const lastMessage = chat.lastReceivedKey + ? window + .require('WAWebCollections') + .Msg.get(chat.lastReceivedKey._serialized) || + ( + await window + .require('WAWebCollections') + .Msg.getMessagesById([ + chat.lastReceivedKey._serialized, + ]) + )?.messages?.[0] + : null;`; + +const NEW_BLOCK = ` const lastReceivedKey = chat.lastReceivedKey?._serialized || chat.lastReceivedKey?.toString?.(); + const lastMessage = lastReceivedKey + ? window + .require('WAWebCollections') + .Msg.get(lastReceivedKey) || + ( + await window + .require('WAWebCollections') + .Msg.getMessagesById([ + lastReceivedKey, + ]) + )?.messages?.[0] + : null;`; + +function applyPatch(target = DEFAULT_TARGET) { + const source = fs.readFileSync(target, 'utf8'); + if (source.includes(NEW_BLOCK)) return 'already-patched'; + if (!source.includes(OLD_BLOCK)) { + throw new Error(`Unsupported whatsapp-web.js Utils.js shape: ${target}`); + } + fs.writeFileSync(target, source.replace(OLD_BLOCK, NEW_BLOCK)); + return 'patched'; +} + +if (require.main === module) { + const result = applyPatch(process.argv[2] || DEFAULT_TARGET); + console.log(`patch-wwebjs-lid-last-received-key: ${result}`); +} + +module.exports = { applyPatch }; diff --git a/src/engine/adapters/wwebjs-patch.spec.ts b/src/engine/adapters/wwebjs-patch.spec.ts new file mode 100644 index 000000000..2b291dd70 --- /dev/null +++ b/src/engine/adapters/wwebjs-patch.spec.ts @@ -0,0 +1,34 @@ +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; + +describe('patch-wwebjs-lid-last-received-key', () => { + it('adds a string fallback for WhatsApp MsgKey objects without _serialized', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { applyPatch } = require('../../../scripts/patch-wwebjs-lid-last-received-key'); + const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'wwebjs-patch-')); + const target = path.join(dir, 'Utils.js'); + fs.writeFileSync( + target, + ` const lastMessage = chat.lastReceivedKey + ? window + .require('WAWebCollections') + .Msg.get(chat.lastReceivedKey._serialized) || + ( + await window + .require('WAWebCollections') + .Msg.getMessagesById([ + chat.lastReceivedKey._serialized, + ]) + )?.messages?.[0] + : null;`, + ); + + expect(applyPatch(target)).toBe('patched'); + const patched = fs.readFileSync(target, 'utf8'); + + expect(patched).toContain('chat.lastReceivedKey?._serialized || chat.lastReceivedKey?.toString?.()'); + expect(patched).toContain('Msg.get(lastReceivedKey)'); + expect(patched).toContain('Msg.getMessagesById([\n lastReceivedKey,'); + }); +});