@moxxy/plugin-channel-whatsapp 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/auth-state.d.ts +66 -0
- package/dist/auth-state.d.ts.map +1 -0
- package/dist/auth-state.js +99 -0
- package/dist/auth-state.js.map +1 -0
- package/dist/baileys-socket.d.ts +10 -0
- package/dist/baileys-socket.d.ts.map +1 -0
- package/dist/baileys-socket.js +93 -0
- package/dist/baileys-socket.js.map +1 -0
- package/dist/channel/turn-runner.d.ts +58 -0
- package/dist/channel/turn-runner.d.ts.map +1 -0
- package/dist/channel/turn-runner.js +130 -0
- package/dist/channel/turn-runner.js.map +1 -0
- package/dist/channel/voice-handler.d.ts +27 -0
- package/dist/channel/voice-handler.d.ts.map +1 -0
- package/dist/channel/voice-handler.js +55 -0
- package/dist/channel/voice-handler.js.map +1 -0
- package/dist/channel.d.ts +105 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/channel.js +459 -0
- package/dist/channel.js.map +1 -0
- package/dist/consent-prompt.d.ts +9 -0
- package/dist/consent-prompt.d.ts.map +1 -0
- package/dist/consent-prompt.js +28 -0
- package/dist/consent-prompt.js.map +1 -0
- package/dist/consent.d.ts +22 -0
- package/dist/consent.d.ts.map +1 -0
- package/dist/consent.js +40 -0
- package/dist/consent.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +211 -0
- package/dist/index.js.map +1 -0
- package/dist/keys.d.ts +39 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +86 -0
- package/dist/keys.js.map +1 -0
- package/dist/message-gate.d.ts +50 -0
- package/dist/message-gate.d.ts.map +1 -0
- package/dist/message-gate.js +150 -0
- package/dist/message-gate.js.map +1 -0
- package/dist/pair-flow.d.ts +12 -0
- package/dist/pair-flow.d.ts.map +1 -0
- package/dist/pair-flow.js +120 -0
- package/dist/pair-flow.js.map +1 -0
- package/dist/permission.d.ts +29 -0
- package/dist/permission.d.ts.map +1 -0
- package/dist/permission.js +78 -0
- package/dist/permission.js.map +1 -0
- package/dist/setup-wizard.d.ts +13 -0
- package/dist/setup-wizard.d.ts.map +1 -0
- package/dist/setup-wizard.js +127 -0
- package/dist/setup-wizard.js.map +1 -0
- package/dist/socket.d.ts +72 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +22 -0
- package/dist/socket.js.map +1 -0
- package/package.json +99 -0
- package/src/auth-state.test.ts +103 -0
- package/src/auth-state.ts +161 -0
- package/src/baileys-socket.ts +154 -0
- package/src/channel/turn-runner.test.ts +31 -0
- package/src/channel/turn-runner.ts +156 -0
- package/src/channel/voice-handler.ts +83 -0
- package/src/channel.test.ts +344 -0
- package/src/channel.ts +571 -0
- package/src/consent-prompt.ts +28 -0
- package/src/consent.test.ts +56 -0
- package/src/consent.ts +48 -0
- package/src/index.ts +284 -0
- package/src/keys.test.ts +59 -0
- package/src/keys.ts +78 -0
- package/src/message-gate.test.ts +121 -0
- package/src/message-gate.ts +184 -0
- package/src/pair-flow.ts +133 -0
- package/src/permission.test.ts +94 -0
- package/src/permission.ts +114 -0
- package/src/setup-wizard.ts +162 -0
- package/src/socket.test.ts +17 -0
- package/src/socket.ts +89 -0
- package/src/subcommands.test.ts +198 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** The read/write vault slice the consent gate needs. */
|
|
2
|
+
export interface ConsentVault {
|
|
3
|
+
get(name: string): Promise<string | null>;
|
|
4
|
+
set(name: string, value: string, tags?: ReadonlyArray<string>): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The non-negotiable warning shown as the FIRST step of every setup path.
|
|
8
|
+
* Baileys speaks the WhatsApp Web protocol without WhatsApp's blessing;
|
|
9
|
+
* users must opt in to that risk explicitly before anything else happens.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CONSENT_WARNING: string;
|
|
12
|
+
/** One-line refusal used by every path that hits the gate un-acknowledged. */
|
|
13
|
+
export declare const CONSENT_REQUIRED_MESSAGE: string;
|
|
14
|
+
/**
|
|
15
|
+
* Whether the operator has acknowledged the ToS/ban risk. Env override first
|
|
16
|
+
* (headless/dedicated runners), then the vault receipt written by the wizard or
|
|
17
|
+
* the desktop config panel. Vault errors (locked/unavailable) count as "no".
|
|
18
|
+
*/
|
|
19
|
+
export declare function hasConsent(vault: ConsentVault | undefined): Promise<boolean>;
|
|
20
|
+
/** Persist the wizard's typed acknowledgment as a dated receipt. */
|
|
21
|
+
export declare function recordConsent(vault: ConsentVault): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=consent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../src/consent.ts"],"names":[],"mappings":"AAEA,yDAAyD;AACzD,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,QAQkC,CAAC;AAE/D,8EAA8E;AAC9E,eAAO,MAAM,wBAAwB,QAGgD,CAAC;AAEtF;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAQlF;AAED,oEAAoE;AACpE,wBAAsB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE"}
|
package/dist/consent.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { WHATSAPP_CONSENT_ENV, WHATSAPP_CONSENT_KEY, isConsentValue } from './keys.js';
|
|
2
|
+
/**
|
|
3
|
+
* The non-negotiable warning shown as the FIRST step of every setup path.
|
|
4
|
+
* Baileys speaks the WhatsApp Web protocol without WhatsApp's blessing;
|
|
5
|
+
* users must opt in to that risk explicitly before anything else happens.
|
|
6
|
+
*/
|
|
7
|
+
export const CONSENT_WARNING = 'This channel uses Baileys, an UNOFFICIAL WhatsApp Web client.\n' +
|
|
8
|
+
'\n' +
|
|
9
|
+
' - Automating an account this way violates WhatsApp\'s Terms of Service.\n' +
|
|
10
|
+
' - WhatsApp actively detects unofficial clients and CAN PERMANENTLY BAN\n' +
|
|
11
|
+
' the phone number — without warning and without appeal.\n' +
|
|
12
|
+
' - Strongly consider linking a SECONDARY number, not your personal one.\n' +
|
|
13
|
+
'\n' +
|
|
14
|
+
'moxxy cannot make this safe; it can only make it explicit.';
|
|
15
|
+
/** One-line refusal used by every path that hits the gate un-acknowledged. */
|
|
16
|
+
export const CONSENT_REQUIRED_MESSAGE = 'WhatsApp channel not acknowledged: it relies on an unofficial API that violates ' +
|
|
17
|
+
"WhatsApp's ToS and can get the number banned. Run `moxxy whatsapp setup` and type " +
|
|
18
|
+
`'yes' to accept that risk (or set ${WHATSAPP_CONSENT_ENV}=yes for headless runs).`;
|
|
19
|
+
/**
|
|
20
|
+
* Whether the operator has acknowledged the ToS/ban risk. Env override first
|
|
21
|
+
* (headless/dedicated runners), then the vault receipt written by the wizard or
|
|
22
|
+
* the desktop config panel. Vault errors (locked/unavailable) count as "no".
|
|
23
|
+
*/
|
|
24
|
+
export async function hasConsent(vault) {
|
|
25
|
+
if (isConsentValue(process.env[WHATSAPP_CONSENT_ENV]))
|
|
26
|
+
return true;
|
|
27
|
+
if (!vault)
|
|
28
|
+
return false;
|
|
29
|
+
try {
|
|
30
|
+
return isConsentValue(await vault.get(WHATSAPP_CONSENT_KEY));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Persist the wizard's typed acknowledgment as a dated receipt. */
|
|
37
|
+
export async function recordConsent(vault) {
|
|
38
|
+
await vault.set(WHATSAPP_CONSENT_KEY, `acknowledged@${new Date().toISOString()}`, ['whatsapp']);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=consent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.js","sourceRoot":"","sources":["../src/consent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAQvF;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAC1B,iEAAiE;IACjE,IAAI;IACJ,6EAA6E;IAC7E,4EAA4E;IAC5E,8DAA8D;IAC9D,4EAA4E;IAC5E,IAAI;IACJ,4DAA4D,CAAC;AAE/D,8EAA8E;AAC9E,MAAM,CAAC,MAAM,wBAAwB,GACnC,kFAAkF;IAClF,oFAAoF;IACpF,qCAAqC,oBAAoB,0BAA0B,CAAC;AAEtF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAA+B;IAC9D,IAAI,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACnE,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,CAAC;QACH,OAAO,cAAc,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAmB;IACrD,MAAM,KAAK,CAAC,GAAG,CAAC,oBAAoB,EAAE,gBAAgB,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;AAClG,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Plugin } from '@moxxy/sdk';
|
|
2
|
+
import type { VaultStore } from '@moxxy/plugin-vault';
|
|
3
|
+
export { WhatsAppChannel, type WhatsAppChannelOptions, type WhatsAppStartOpts } from './channel.js';
|
|
4
|
+
export { createWhatsAppPermissionController, parsePermissionReply, formatPermissionPrompt, type WhatsAppPermissionController, } from './permission.js';
|
|
5
|
+
export { createFileAuthStorage, createWhatsAppAuthState, hasStoredCreds, sanitizeAuthKey, type WhatsAppAuthStorage, type BaileysAuthBridge, } from './auth-state.js';
|
|
6
|
+
export { gateInboundMessage, MAX_AUDIO_BYTES, MAX_TEXT_CHARS, type GateVerdict, } from './message-gate.js';
|
|
7
|
+
export { runWhatsAppTurn, splitWhatsAppText, WHATSAPP_MAX_MESSAGE_CHARS, } from './channel/turn-runner.js';
|
|
8
|
+
export { CONSENT_WARNING, CONSENT_REQUIRED_MESSAGE, hasConsent, recordConsent } from './consent.js';
|
|
9
|
+
export { WHATSAPP_CONSENT_KEY, WHATSAPP_OWNER_JID_KEY, WHATSAPP_ALLOWED_JIDS_KEY, WHATSAPP_CONSENT_ENV, WHATSAPP_AUTH_DIR, normalizeJid, parseAllowedJids, isConsentValue, } from './keys.js';
|
|
10
|
+
export { disconnectStatusCode, WA_DISCONNECT, type WhatsAppSocket, type WhatsAppSocketFactory, } from './socket.js';
|
|
11
|
+
export interface BuildWhatsAppPluginOptions {
|
|
12
|
+
/** Host-injected encrypted secret store (available immediately). */
|
|
13
|
+
readonly vault: VaultStore;
|
|
14
|
+
}
|
|
15
|
+
/** Build the WhatsApp channel plugin with a host-injected vault (mirrors the
|
|
16
|
+
* Telegram/Slack plugins' dual-export pattern). */
|
|
17
|
+
export declare function buildWhatsAppPlugin(opts: BuildWhatsAppPluginOptions): Plugin;
|
|
18
|
+
/**
|
|
19
|
+
* Discovery-loadable default export: resolves the vault from the inter-plugin
|
|
20
|
+
* service registry in `onInit` (the vault plugin publishes `'vault'`). Requires
|
|
21
|
+
* `@moxxy/plugin-vault` to load first (declared in `package.json`
|
|
22
|
+
* `moxxy.requirements`).
|
|
23
|
+
*/
|
|
24
|
+
export declare const whatsappPlugin: Plugin;
|
|
25
|
+
export default whatsappPlugin;
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAE3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AActD,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACpG,OAAO,EACL,kCAAkC,EAClC,oBAAoB,EACpB,sBAAsB,EACtB,KAAK,4BAA4B,GAClC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,cAAc,EACd,eAAe,EACf,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,0BAA0B,GAC3B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACpG,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,yBAAyB,EACzB,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,0BAA0B;IACzC,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED;oDACoD;AACpD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,0BAA0B,GAAG,MAAM,CAE5E;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,MAgBzB,CAAC;AAEL,eAAe,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { defineChannel, definePlugin } from '@moxxy/sdk';
|
|
2
|
+
import { moxxyPath } from '@moxxy/sdk/server';
|
|
3
|
+
import { createFileAuthStorage, hasStoredCreds } from './auth-state.js';
|
|
4
|
+
import { WhatsAppChannel } from './channel.js';
|
|
5
|
+
import { CONSENT_REQUIRED_MESSAGE, hasConsent } from './consent.js';
|
|
6
|
+
import { WHATSAPP_ALLOWED_JIDS_KEY, WHATSAPP_AUTH_DIR, WHATSAPP_CONSENT_KEY, WHATSAPP_OWNER_JID_KEY, parseAllowedJids, } from './keys.js';
|
|
7
|
+
import { runWhatsAppWizard, unpairLocal } from './setup-wizard.js';
|
|
8
|
+
import { runWhatsAppPairFlow } from './pair-flow.js';
|
|
9
|
+
export { WhatsAppChannel } from './channel.js';
|
|
10
|
+
export { createWhatsAppPermissionController, parsePermissionReply, formatPermissionPrompt, } from './permission.js';
|
|
11
|
+
export { createFileAuthStorage, createWhatsAppAuthState, hasStoredCreds, sanitizeAuthKey, } from './auth-state.js';
|
|
12
|
+
export { gateInboundMessage, MAX_AUDIO_BYTES, MAX_TEXT_CHARS, } from './message-gate.js';
|
|
13
|
+
export { runWhatsAppTurn, splitWhatsAppText, WHATSAPP_MAX_MESSAGE_CHARS, } from './channel/turn-runner.js';
|
|
14
|
+
export { CONSENT_WARNING, CONSENT_REQUIRED_MESSAGE, hasConsent, recordConsent } from './consent.js';
|
|
15
|
+
export { WHATSAPP_CONSENT_KEY, WHATSAPP_OWNER_JID_KEY, WHATSAPP_ALLOWED_JIDS_KEY, WHATSAPP_CONSENT_ENV, WHATSAPP_AUTH_DIR, normalizeJid, parseAllowedJids, isConsentValue, } from './keys.js';
|
|
16
|
+
export { disconnectStatusCode, WA_DISCONNECT, } from './socket.js';
|
|
17
|
+
/** Build the WhatsApp channel plugin with a host-injected vault (mirrors the
|
|
18
|
+
* Telegram/Slack plugins' dual-export pattern). */
|
|
19
|
+
export function buildWhatsAppPlugin(opts) {
|
|
20
|
+
return makeWhatsAppPlugin(() => opts.vault);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Discovery-loadable default export: resolves the vault from the inter-plugin
|
|
24
|
+
* service registry in `onInit` (the vault plugin publishes `'vault'`). Requires
|
|
25
|
+
* `@moxxy/plugin-vault` to load first (declared in `package.json`
|
|
26
|
+
* `moxxy.requirements`).
|
|
27
|
+
*/
|
|
28
|
+
export const whatsappPlugin = (() => {
|
|
29
|
+
let resolved = null;
|
|
30
|
+
const getVault = () => {
|
|
31
|
+
if (!resolved) {
|
|
32
|
+
throw new Error('@moxxy/plugin-channel-whatsapp: the "vault" service is unavailable — @moxxy/plugin-vault must load first');
|
|
33
|
+
}
|
|
34
|
+
return resolved;
|
|
35
|
+
};
|
|
36
|
+
const hooks = {
|
|
37
|
+
onInit: (ctx) => {
|
|
38
|
+
resolved = ctx.services.require('vault');
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
return makeWhatsAppPlugin(getVault, hooks);
|
|
42
|
+
})();
|
|
43
|
+
export default whatsappPlugin;
|
|
44
|
+
function readAllowedJids(options) {
|
|
45
|
+
const raw = options?.['allowedJids'];
|
|
46
|
+
if (Array.isArray(raw))
|
|
47
|
+
return raw.map((x) => String(x));
|
|
48
|
+
if (typeof raw === 'string' && raw.trim().length > 0)
|
|
49
|
+
return parseAllowedJids(raw);
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
function makeWhatsAppPlugin(getVault, hooks) {
|
|
53
|
+
return definePlugin({
|
|
54
|
+
name: '@moxxy/plugin-channel-whatsapp',
|
|
55
|
+
version: '0.0.0',
|
|
56
|
+
...(hooks ? { hooks } : {}),
|
|
57
|
+
channels: [
|
|
58
|
+
defineChannel({
|
|
59
|
+
name: 'whatsapp',
|
|
60
|
+
description: 'WhatsApp channel via Baileys — UNOFFICIAL API (violates WhatsApp ToS; the number ' +
|
|
61
|
+
'can be banned — use a secondary number). QR device-link pairing, JID allow-list.',
|
|
62
|
+
// Like Slack/Telegram: its own dedicated, isolated runner (separate
|
|
63
|
+
// socket + sticky session) so the bot keeps a persistent history apart
|
|
64
|
+
// from the user's desktop/TUI work. Baileys is an outbound WebSocket —
|
|
65
|
+
// no tunnel, no tunnelProvider registration.
|
|
66
|
+
dedicatedRunner: true,
|
|
67
|
+
sessionSource: 'whatsapp',
|
|
68
|
+
config: {
|
|
69
|
+
fields: [
|
|
70
|
+
{
|
|
71
|
+
name: 'tosAcknowledged',
|
|
72
|
+
label: "Risk acknowledgment — type 'yes'",
|
|
73
|
+
vaultKey: WHATSAPP_CONSENT_KEY,
|
|
74
|
+
required: true,
|
|
75
|
+
help: 'Unofficial WhatsApp API: violates the ToS and can get the number banned. ' +
|
|
76
|
+
"Typing anything other than 'yes' keeps the channel disarmed.",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'allowedJids',
|
|
80
|
+
label: 'Extra allowed JIDs (comma-separated)',
|
|
81
|
+
vaultKey: WHATSAPP_ALLOWED_JIDS_KEY,
|
|
82
|
+
required: false,
|
|
83
|
+
placeholder: '15551234567@s.whatsapp.net',
|
|
84
|
+
help: 'Your own Note-to-Self chat is always allowed once linked.',
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
hasRequestUrl: false,
|
|
88
|
+
runHint: 'On your phone: WhatsApp -> Settings -> Linked devices -> Link a device, then scan the QR.',
|
|
89
|
+
connect: {
|
|
90
|
+
kind: 'qr',
|
|
91
|
+
title: 'Link WhatsApp',
|
|
92
|
+
hint: 'Scan with the phone that owns the account: WhatsApp -> Settings -> Linked devices ' +
|
|
93
|
+
'-> Link a device. The QR refreshes periodically until scanned.',
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
create: (deps) => {
|
|
97
|
+
const options = deps.options;
|
|
98
|
+
const allowedJids = readAllowedJids(options);
|
|
99
|
+
const editFrameMs = options?.['editFrameMs'];
|
|
100
|
+
const channelOpts = {
|
|
101
|
+
vault: getVault(),
|
|
102
|
+
...(allowedJids ? { allowedJids } : {}),
|
|
103
|
+
...(typeof editFrameMs === 'number' ? { editFrameMs } : {}),
|
|
104
|
+
logger: deps.logger,
|
|
105
|
+
};
|
|
106
|
+
return new WhatsAppChannel(channelOpts);
|
|
107
|
+
},
|
|
108
|
+
isAvailable: async () => {
|
|
109
|
+
// Cheap probes only: one vault read + one stat-shaped file read.
|
|
110
|
+
let consent = false;
|
|
111
|
+
try {
|
|
112
|
+
consent = await hasConsent(getVault());
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// Vault unavailable in a probe/listing context; the env override
|
|
116
|
+
// inside hasConsent(undefined) still counts.
|
|
117
|
+
consent = await hasConsent(undefined);
|
|
118
|
+
}
|
|
119
|
+
if (!consent)
|
|
120
|
+
return { ok: false, reason: CONSENT_REQUIRED_MESSAGE };
|
|
121
|
+
const linked = await hasStoredCreds(createFileAuthStorage(moxxyPath(WHATSAPP_AUTH_DIR)));
|
|
122
|
+
if (!linked) {
|
|
123
|
+
return {
|
|
124
|
+
ok: false,
|
|
125
|
+
reason: 'No WhatsApp account linked (or the phone logged this device out). Run ' +
|
|
126
|
+
'`moxxy channels whatsapp pair` to scan the QR.',
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return { ok: true };
|
|
130
|
+
},
|
|
131
|
+
interactiveCommand: 'setup',
|
|
132
|
+
subcommands: {
|
|
133
|
+
setup: {
|
|
134
|
+
description: 'Interactive setup: acknowledge the unofficial-API risk (required first step), ' +
|
|
135
|
+
'link via QR, manage the allow-list, then start. Shown by default for ' +
|
|
136
|
+
'`moxxy whatsapp` on a TTY.',
|
|
137
|
+
run: async (ctx) => {
|
|
138
|
+
if (process.stdin.isTTY !== true) {
|
|
139
|
+
// Headless: the consent gate still holds — start refuses without
|
|
140
|
+
// an acknowledgment (vault receipt or MOXXY_WHATSAPP_TOS_ACK).
|
|
141
|
+
const vault = ctx.deps.vault;
|
|
142
|
+
if (!(await hasConsent(vault))) {
|
|
143
|
+
process.stderr.write(`${CONSENT_REQUIRED_MESSAGE}\n`);
|
|
144
|
+
return 1;
|
|
145
|
+
}
|
|
146
|
+
return ctx.startChannel();
|
|
147
|
+
}
|
|
148
|
+
return runWhatsAppWizard(ctx);
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
pair: {
|
|
152
|
+
description: 'Link a WhatsApp account: renders the rotating QR in the terminal; scan it from ' +
|
|
153
|
+
'WhatsApp -> Settings -> Linked devices. Requires the typed risk acknowledgment.',
|
|
154
|
+
run: async (ctx) => {
|
|
155
|
+
if (process.stdin.isTTY !== true) {
|
|
156
|
+
process.stderr.write('Pairing needs a TTY to show the QR. Run `moxxy channels whatsapp pair` on a ' +
|
|
157
|
+
'workstation, or pair from the desktop Channels panel.\n');
|
|
158
|
+
return 1;
|
|
159
|
+
}
|
|
160
|
+
return runWhatsAppPairFlow(ctx);
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
status: {
|
|
164
|
+
description: 'Report consent/link/allow-list state as JSON (with re-pair guidance after a logout).',
|
|
165
|
+
run: async (ctx) => {
|
|
166
|
+
const vault = ctx.deps.vault;
|
|
167
|
+
if (!vault) {
|
|
168
|
+
process.stderr.write('vault unavailable\n');
|
|
169
|
+
return 1;
|
|
170
|
+
}
|
|
171
|
+
const consentAcknowledged = await hasConsent(vault);
|
|
172
|
+
const linked = await hasStoredCreds(createFileAuthStorage(moxxyPath(WHATSAPP_AUTH_DIR)));
|
|
173
|
+
const ownerJid = await vault.get(WHATSAPP_OWNER_JID_KEY);
|
|
174
|
+
const allowedJids = parseAllowedJids(await vault.get(WHATSAPP_ALLOWED_JIDS_KEY));
|
|
175
|
+
const state = !consentAcknowledged
|
|
176
|
+
? 'needs-consent'
|
|
177
|
+
: !linked
|
|
178
|
+
? 'needs-pairing'
|
|
179
|
+
: 'ready';
|
|
180
|
+
const guidance = !consentAcknowledged
|
|
181
|
+
? 'Run `moxxy whatsapp setup` and type yes to acknowledge the unofficial-API risk.'
|
|
182
|
+
: !linked
|
|
183
|
+
? ownerJid
|
|
184
|
+
? 'The phone logged this device out (or credentials were cleared) — run `moxxy channels whatsapp pair` to re-link.'
|
|
185
|
+
: 'Run `moxxy channels whatsapp pair` to link an account.'
|
|
186
|
+
: null;
|
|
187
|
+
process.stdout.write(JSON.stringify({ state, consentAcknowledged, linked, ownerJid, allowedJids, guidance }, null, 2) + '\n');
|
|
188
|
+
return 0;
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
unpair: {
|
|
192
|
+
description: 'Forget the local WhatsApp credentials (also remove the device on the phone under Linked devices).',
|
|
193
|
+
run: async (ctx) => {
|
|
194
|
+
const vault = ctx.deps.vault;
|
|
195
|
+
if (!vault) {
|
|
196
|
+
process.stderr.write('vault unavailable\n');
|
|
197
|
+
return 1;
|
|
198
|
+
}
|
|
199
|
+
const removed = await unpairLocal(vault);
|
|
200
|
+
process.stdout.write(removed
|
|
201
|
+
? 'unpaired — also remove the device on your phone: WhatsApp -> Settings -> Linked devices\n'
|
|
202
|
+
: 'no linked account was stored\n');
|
|
203
|
+
return 0;
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
}),
|
|
208
|
+
],
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/keys.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vault keys + pure JID helpers shared by the channel, its subcommands, and the
|
|
3
|
+
* interactive setup wizard. Kept in their own module so the wizard / pair-flow
|
|
4
|
+
* helpers can import them without pulling in the plugin's full index (or, worse,
|
|
5
|
+
* Baileys — which stays behind a lazy import in `baileys-socket.ts`).
|
|
6
|
+
*/
|
|
7
|
+
/** Vault key holding the user's typed acknowledgment of the ToS/ban risk. */
|
|
8
|
+
export declare const WHATSAPP_CONSENT_KEY = "whatsapp_tos_acknowledged";
|
|
9
|
+
/** Vault key holding the linked account's own (normalized) JID. */
|
|
10
|
+
export declare const WHATSAPP_OWNER_JID_KEY = "whatsapp_owner_jid";
|
|
11
|
+
/** Vault key holding extra allow-listed JIDs (JSON array or comma-separated). */
|
|
12
|
+
export declare const WHATSAPP_ALLOWED_JIDS_KEY = "whatsapp_allowed_jids";
|
|
13
|
+
/** Env override for the consent gate (headless/dedicated runners). */
|
|
14
|
+
export declare const WHATSAPP_CONSENT_ENV = "MOXXY_WHATSAPP_TOS_ACK";
|
|
15
|
+
/** Directory (under the moxxy home) holding the rotating Baileys auth state. */
|
|
16
|
+
export declare const WHATSAPP_AUTH_DIR = "whatsapp-auth";
|
|
17
|
+
/**
|
|
18
|
+
* Normalize a WhatsApp JID for identity comparison: the user part of a
|
|
19
|
+
* multi-device JID carries `:device` (and historically `_agent`) suffixes that
|
|
20
|
+
* vary per login (`1234567890:12@s.whatsapp.net`), so equality checks must
|
|
21
|
+
* compare the bare `user@server` form. Returns null for values that don't look
|
|
22
|
+
* like a JID at all, so callers treat garbage as "no identity" instead of
|
|
23
|
+
* silently allow-listing it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function normalizeJid(raw: string | null | undefined): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Parse a stored/configured allow-list. Accepts a JSON string array or a
|
|
28
|
+
* comma/whitespace-separated string; every entry is normalized and non-JIDs are
|
|
29
|
+
* dropped (never silently allow-listed). Returns a de-duplicated array.
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseAllowedJids(raw: string | null | undefined): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Whether a stored consent value counts as an actual acknowledgment. The setup
|
|
34
|
+
* wizard stores `acknowledged@<ISO date>`; the desktop panel / `moxxy init`
|
|
35
|
+
* declarative field stores whatever the user typed — only an affirmative "yes"
|
|
36
|
+
* counts, so typing "no" into the form does NOT arm the channel.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isConsentValue(raw: string | null | undefined): boolean;
|
|
39
|
+
//# sourceMappingURL=keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,8BAA8B,CAAC;AAChE,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAC3D,iFAAiF;AACjF,eAAO,MAAM,yBAAyB,0BAA0B,CAAC;AACjE,sEAAsE;AACtE,eAAO,MAAM,oBAAoB,2BAA2B,CAAC;AAC7D,gFAAgF;AAChF,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAEjD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAY1E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAoBzE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAItE"}
|
package/dist/keys.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vault keys + pure JID helpers shared by the channel, its subcommands, and the
|
|
3
|
+
* interactive setup wizard. Kept in their own module so the wizard / pair-flow
|
|
4
|
+
* helpers can import them without pulling in the plugin's full index (or, worse,
|
|
5
|
+
* Baileys — which stays behind a lazy import in `baileys-socket.ts`).
|
|
6
|
+
*/
|
|
7
|
+
/** Vault key holding the user's typed acknowledgment of the ToS/ban risk. */
|
|
8
|
+
export const WHATSAPP_CONSENT_KEY = 'whatsapp_tos_acknowledged';
|
|
9
|
+
/** Vault key holding the linked account's own (normalized) JID. */
|
|
10
|
+
export const WHATSAPP_OWNER_JID_KEY = 'whatsapp_owner_jid';
|
|
11
|
+
/** Vault key holding extra allow-listed JIDs (JSON array or comma-separated). */
|
|
12
|
+
export const WHATSAPP_ALLOWED_JIDS_KEY = 'whatsapp_allowed_jids';
|
|
13
|
+
/** Env override for the consent gate (headless/dedicated runners). */
|
|
14
|
+
export const WHATSAPP_CONSENT_ENV = 'MOXXY_WHATSAPP_TOS_ACK';
|
|
15
|
+
/** Directory (under the moxxy home) holding the rotating Baileys auth state. */
|
|
16
|
+
export const WHATSAPP_AUTH_DIR = 'whatsapp-auth';
|
|
17
|
+
/**
|
|
18
|
+
* Normalize a WhatsApp JID for identity comparison: the user part of a
|
|
19
|
+
* multi-device JID carries `:device` (and historically `_agent`) suffixes that
|
|
20
|
+
* vary per login (`1234567890:12@s.whatsapp.net`), so equality checks must
|
|
21
|
+
* compare the bare `user@server` form. Returns null for values that don't look
|
|
22
|
+
* like a JID at all, so callers treat garbage as "no identity" instead of
|
|
23
|
+
* silently allow-listing it.
|
|
24
|
+
*/
|
|
25
|
+
export function normalizeJid(raw) {
|
|
26
|
+
if (typeof raw !== 'string')
|
|
27
|
+
return null;
|
|
28
|
+
const trimmed = raw.trim();
|
|
29
|
+
if (trimmed.length === 0 || trimmed.length > 256)
|
|
30
|
+
return null;
|
|
31
|
+
const at = trimmed.indexOf('@');
|
|
32
|
+
if (at <= 0 || at !== trimmed.lastIndexOf('@'))
|
|
33
|
+
return null;
|
|
34
|
+
const server = trimmed.slice(at + 1).toLowerCase();
|
|
35
|
+
if (!/^[a-z0-9.-]+$/.test(server))
|
|
36
|
+
return null;
|
|
37
|
+
// Strip the device (`:NN`) and legacy agent (`_N`) suffixes off the user part.
|
|
38
|
+
const user = trimmed.slice(0, at).split(':')[0].split('_')[0];
|
|
39
|
+
if (user.length === 0)
|
|
40
|
+
return null;
|
|
41
|
+
return `${user}@${server}`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Parse a stored/configured allow-list. Accepts a JSON string array or a
|
|
45
|
+
* comma/whitespace-separated string; every entry is normalized and non-JIDs are
|
|
46
|
+
* dropped (never silently allow-listed). Returns a de-duplicated array.
|
|
47
|
+
*/
|
|
48
|
+
export function parseAllowedJids(raw) {
|
|
49
|
+
if (raw == null)
|
|
50
|
+
return [];
|
|
51
|
+
let entries = [];
|
|
52
|
+
const trimmed = raw.trim();
|
|
53
|
+
if (trimmed.startsWith('[')) {
|
|
54
|
+
try {
|
|
55
|
+
const parsed = JSON.parse(trimmed);
|
|
56
|
+
if (Array.isArray(parsed))
|
|
57
|
+
entries = parsed;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
entries = trimmed.split(/[,\s]+/);
|
|
65
|
+
}
|
|
66
|
+
const out = new Set();
|
|
67
|
+
for (const entry of entries) {
|
|
68
|
+
const jid = normalizeJid(typeof entry === 'string' ? entry : null);
|
|
69
|
+
if (jid)
|
|
70
|
+
out.add(jid);
|
|
71
|
+
}
|
|
72
|
+
return [...out];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Whether a stored consent value counts as an actual acknowledgment. The setup
|
|
76
|
+
* wizard stores `acknowledged@<ISO date>`; the desktop panel / `moxxy init`
|
|
77
|
+
* declarative field stores whatever the user typed — only an affirmative "yes"
|
|
78
|
+
* counts, so typing "no" into the form does NOT arm the channel.
|
|
79
|
+
*/
|
|
80
|
+
export function isConsentValue(raw) {
|
|
81
|
+
if (typeof raw !== 'string')
|
|
82
|
+
return false;
|
|
83
|
+
const v = raw.trim().toLowerCase();
|
|
84
|
+
return v === 'yes' || v.startsWith('acknowledged@');
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=keys.js.map
|
package/dist/keys.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { WaInboundMessage } from './socket.js';
|
|
2
|
+
/**
|
|
3
|
+
* The single inbound gate every session-reaching path goes through (AGENTS.md:
|
|
4
|
+
* gate EVERY session-reaching path behind pairing; A8: zod-validate inbound
|
|
5
|
+
* before it touches the session). Pure — the channel feeds it the message plus
|
|
6
|
+
* its identity/allow-list state and branches on the verdict.
|
|
7
|
+
*
|
|
8
|
+
* Order of checks (each is load-bearing):
|
|
9
|
+
* 1. only `notify` upserts — history syncs / appends are not fresh input.
|
|
10
|
+
* 2. shape-validate + size-cap the consumed fields (zod).
|
|
11
|
+
* 3. drop own echoes: Baileys receives this client's OWN outbound sends back
|
|
12
|
+
* via `messages.upsert`; without the sent-id check the bot replies to
|
|
13
|
+
* itself in a loop.
|
|
14
|
+
* 4. drop `fromMe` messages outside the owner's self-chat ("Note to Self"):
|
|
15
|
+
* those are the owner talking to OTHER people from their phone — the bot
|
|
16
|
+
* must never treat someone's private conversation as a prompt.
|
|
17
|
+
* 5. allow-list by normalized JID: the owner's self-chat is allowed by
|
|
18
|
+
* default (seeded by the channel); everything else must be explicitly
|
|
19
|
+
* allow-listed. Unauthorized senders are dropped SILENTLY — replying would
|
|
20
|
+
* both leak the bot's existence and look like spam (ban risk).
|
|
21
|
+
*/
|
|
22
|
+
/** Cap on inbound prompt text (WhatsApp itself allows ~65k). */
|
|
23
|
+
export declare const MAX_TEXT_CHARS = 16000;
|
|
24
|
+
/** Cap on a voice note / audio file we will download + buffer for transcription. */
|
|
25
|
+
export declare const MAX_AUDIO_BYTES: number;
|
|
26
|
+
export type GateVerdict = {
|
|
27
|
+
readonly ok: false;
|
|
28
|
+
readonly reason: string;
|
|
29
|
+
} | {
|
|
30
|
+
readonly ok: true;
|
|
31
|
+
readonly kind: 'text';
|
|
32
|
+
readonly jid: string;
|
|
33
|
+
readonly text: string;
|
|
34
|
+
} | {
|
|
35
|
+
readonly ok: true;
|
|
36
|
+
readonly kind: 'audio';
|
|
37
|
+
readonly jid: string;
|
|
38
|
+
readonly mimeType: string;
|
|
39
|
+
readonly declaredBytes: number | null;
|
|
40
|
+
};
|
|
41
|
+
export interface GateState {
|
|
42
|
+
/** The linked account's own normalized JID (null until the socket opens). */
|
|
43
|
+
readonly ownJid: string | null;
|
|
44
|
+
/** Normalized JIDs allowed to drive the session (includes ownJid). */
|
|
45
|
+
readonly allowedJids: ReadonlySet<string>;
|
|
46
|
+
/** Message ids of THIS client's own recent sends (echo/loop protection). */
|
|
47
|
+
readonly isOwnSend: (messageId: string) => boolean;
|
|
48
|
+
}
|
|
49
|
+
export declare function gateInboundMessage(state: GateState, upsertType: string, raw: WaInboundMessage): GateVerdict;
|
|
50
|
+
//# sourceMappingURL=message-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-gate.d.ts","sourceRoot":"","sources":["../src/message-gate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,gEAAgE;AAChE,eAAO,MAAM,cAAc,QAAS,CAAC;AACrC,oFAAoF;AACpF,eAAO,MAAM,eAAe,QAAmB,CAAC;AAehD,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzF;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC,CAAC;AAEN,MAAM,WAAW,SAAS;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1C,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;CACpD;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,EAChB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,gBAAgB,GACpB,WAAW,CAmDb"}
|