@rubytech/create-maxy 1.0.459 → 1.0.461
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/package.json +1 -1
- package/payload/maxy/server.js +358 -264
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +21 -4
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +7 -7
- package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +1 -1
- package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +1 -1
package/package.json
CHANGED
package/payload/maxy/server.js
CHANGED
|
@@ -23641,7 +23641,8 @@ var WhatsAppAccountSchema = external_exports.object({
|
|
|
23641
23641
|
dmPolicy: DmPolicySchema.optional().describe("Who can message your public agent via WhatsApp DM. 'Open' lets anyone message. 'Allowlist' restricts to approved phone numbers. 'Disabled' blocks all public messages."),
|
|
23642
23642
|
groupPolicy: GroupPolicySchema.optional().describe("Whether your agent responds in WhatsApp group chats. 'Open' enables group responses (subject to per-group activation). 'Allowlist' restricts to approved senders. 'Disabled' ignores all group messages."),
|
|
23643
23643
|
selfChatMode: external_exports.boolean().optional().describe("Enable if the bot uses the owner's personal WhatsApp number (same phone for admin and bot). Changes how self-messages are routed."),
|
|
23644
|
-
|
|
23644
|
+
adminPhones: external_exports.array(external_exports.string()).optional().describe("Phone numbers (E.164) that route to the admin agent. Managed via add-admin-phone / remove-admin-phone."),
|
|
23645
|
+
allowFrom: external_exports.array(external_exports.string()).optional().describe("Phone numbers allowed to message the public agent when DM policy is 'allowlist'. Add '*' for open access. Does not affect admin routing \u2014 use adminPhones for that."),
|
|
23645
23646
|
groupAllowFrom: external_exports.array(external_exports.string()).optional().describe("Phone numbers allowed to trigger the agent in groups when group policy is 'allowlist'."),
|
|
23646
23647
|
sendReadReceipts: external_exports.boolean().optional().default(true).describe("Whether to send read receipts (blue ticks) when messages are received. Disabling may feel less responsive but preserves privacy."),
|
|
23647
23648
|
groups: external_exports.record(
|
|
@@ -23671,7 +23672,8 @@ var WhatsAppConfigSchema = external_exports.object({
|
|
|
23671
23672
|
accounts: external_exports.record(external_exports.string(), WhatsAppAccountSchema.optional()).optional().describe("Per-account config keyed by account ID."),
|
|
23672
23673
|
dmPolicy: DmPolicySchema.optional().default("disabled").describe("Who can message your public agent via WhatsApp DM. 'Open' lets anyone message. 'Allowlist' restricts to approved phone numbers. 'Disabled' blocks all public messages."),
|
|
23673
23674
|
groupPolicy: GroupPolicySchema.optional().default("disabled").describe("Whether your agent responds in WhatsApp group chats. 'Open' enables group responses (subject to per-group activation). 'Allowlist' restricts to approved senders. 'Disabled' ignores all group messages."),
|
|
23674
|
-
|
|
23675
|
+
adminPhones: external_exports.array(external_exports.string()).optional().describe("Phone numbers (E.164) that route to the admin agent. Managed via add-admin-phone / remove-admin-phone."),
|
|
23676
|
+
allowFrom: external_exports.array(external_exports.string()).optional().describe("Phone numbers allowed to message the public agent when DM policy is 'allowlist'. Add '*' for open access. Does not affect admin routing \u2014 use adminPhones for that."),
|
|
23675
23677
|
groupAllowFrom: external_exports.array(external_exports.string()).optional().describe("Phone numbers allowed to trigger the agent in groups when group policy is 'allowlist'."),
|
|
23676
23678
|
sendReadReceipts: external_exports.boolean().optional().default(true).describe("Whether to send read receipts (blue ticks) when messages are received. Disabling may feel less responsive but preserves privacy."),
|
|
23677
23679
|
mediaMaxMb: external_exports.number().int().positive().optional().default(50).describe("Maximum file size in MB for media the agent will download and process."),
|
|
@@ -23689,6 +23691,282 @@ var WhatsAppConfigSchema = external_exports.object({
|
|
|
23689
23691
|
});
|
|
23690
23692
|
});
|
|
23691
23693
|
|
|
23694
|
+
// app/lib/whatsapp/config-persist.ts
|
|
23695
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync10 } from "fs";
|
|
23696
|
+
import { resolve as resolve9, join as join5 } from "path";
|
|
23697
|
+
var TAG4 = "[whatsapp:config]";
|
|
23698
|
+
function configPath(accountDir) {
|
|
23699
|
+
return resolve9(accountDir, "account.json");
|
|
23700
|
+
}
|
|
23701
|
+
function readConfig(accountDir) {
|
|
23702
|
+
const path2 = configPath(accountDir);
|
|
23703
|
+
if (!existsSync10(path2)) throw new Error(`account.json not found at ${path2}`);
|
|
23704
|
+
return JSON.parse(readFileSync10(path2, "utf-8"));
|
|
23705
|
+
}
|
|
23706
|
+
function writeConfig(accountDir, config2) {
|
|
23707
|
+
const path2 = configPath(accountDir);
|
|
23708
|
+
writeFileSync6(path2, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
23709
|
+
}
|
|
23710
|
+
function reloadManagerConfig(accountDir) {
|
|
23711
|
+
try {
|
|
23712
|
+
const config2 = readConfig(accountDir);
|
|
23713
|
+
reloadConfig(config2);
|
|
23714
|
+
console.error(`${TAG4} reloaded manager config`);
|
|
23715
|
+
} catch (err) {
|
|
23716
|
+
console.error(`${TAG4} manager config reload failed: ${String(err)}`);
|
|
23717
|
+
}
|
|
23718
|
+
}
|
|
23719
|
+
var E164_PATTERN = /^\+\d{7,15}$/;
|
|
23720
|
+
function migrateAdminPhones(wa) {
|
|
23721
|
+
if (wa.adminPhones !== void 0) return 0;
|
|
23722
|
+
if (!Array.isArray(wa.allowFrom)) return 0;
|
|
23723
|
+
const allowFrom = wa.allowFrom;
|
|
23724
|
+
const phones = [];
|
|
23725
|
+
const remaining = [];
|
|
23726
|
+
for (const entry of allowFrom) {
|
|
23727
|
+
if (E164_PATTERN.test(entry)) {
|
|
23728
|
+
phones.push(entry);
|
|
23729
|
+
} else {
|
|
23730
|
+
remaining.push(entry);
|
|
23731
|
+
}
|
|
23732
|
+
}
|
|
23733
|
+
if (phones.length === 0) return 0;
|
|
23734
|
+
wa.adminPhones = phones;
|
|
23735
|
+
wa.allowFrom = remaining;
|
|
23736
|
+
console.error(`${TAG4} migrated ${phones.length} phone(s) from allowFrom to adminPhones`);
|
|
23737
|
+
return phones.length;
|
|
23738
|
+
}
|
|
23739
|
+
function persistAfterPairing(accountDir, accountId, selfPhone) {
|
|
23740
|
+
try {
|
|
23741
|
+
const config2 = readConfig(accountDir);
|
|
23742
|
+
if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
|
|
23743
|
+
config2.whatsapp = {};
|
|
23744
|
+
}
|
|
23745
|
+
const wa = config2.whatsapp;
|
|
23746
|
+
if (!wa.accounts || typeof wa.accounts !== "object") {
|
|
23747
|
+
wa.accounts = {};
|
|
23748
|
+
}
|
|
23749
|
+
const accounts = wa.accounts;
|
|
23750
|
+
if (!accounts[accountId] || typeof accounts[accountId] !== "object") {
|
|
23751
|
+
accounts[accountId] = { name: "Main" };
|
|
23752
|
+
}
|
|
23753
|
+
if (selfPhone) {
|
|
23754
|
+
const normalized = selfPhone.startsWith("+") ? selfPhone : `+${selfPhone}`;
|
|
23755
|
+
if (!Array.isArray(wa.adminPhones)) {
|
|
23756
|
+
wa.adminPhones = [];
|
|
23757
|
+
}
|
|
23758
|
+
const adminPhones = wa.adminPhones;
|
|
23759
|
+
if (!adminPhones.includes(normalized)) {
|
|
23760
|
+
adminPhones.push(normalized);
|
|
23761
|
+
console.error(`${TAG4} added selfPhone=${normalized} to adminPhones`);
|
|
23762
|
+
}
|
|
23763
|
+
} else {
|
|
23764
|
+
console.error(`${TAG4} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
|
|
23765
|
+
}
|
|
23766
|
+
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
23767
|
+
if (!parsed.success) {
|
|
23768
|
+
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
23769
|
+
console.error(`${TAG4} validation failed after pairing: ${msg}`);
|
|
23770
|
+
return { ok: false, error: `Validation failed: ${msg}` };
|
|
23771
|
+
}
|
|
23772
|
+
config2.whatsapp = parsed.data;
|
|
23773
|
+
writeConfig(accountDir, config2);
|
|
23774
|
+
console.error(`${TAG4} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
|
|
23775
|
+
reloadManagerConfig(accountDir);
|
|
23776
|
+
return { ok: true };
|
|
23777
|
+
} catch (err) {
|
|
23778
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
23779
|
+
console.error(`${TAG4} persist failed account=${accountId}: ${msg}`);
|
|
23780
|
+
return { ok: false, error: msg };
|
|
23781
|
+
}
|
|
23782
|
+
}
|
|
23783
|
+
function addAdminPhone(accountDir, phone) {
|
|
23784
|
+
const normalized = phone.trim();
|
|
23785
|
+
if (!E164_PATTERN.test(normalized)) {
|
|
23786
|
+
return { ok: false, error: `Invalid phone format "${normalized}". Expected E.164 (e.g. +441234567890).` };
|
|
23787
|
+
}
|
|
23788
|
+
try {
|
|
23789
|
+
const config2 = readConfig(accountDir);
|
|
23790
|
+
if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
|
|
23791
|
+
config2.whatsapp = {};
|
|
23792
|
+
}
|
|
23793
|
+
const wa = config2.whatsapp;
|
|
23794
|
+
migrateAdminPhones(wa);
|
|
23795
|
+
if (!Array.isArray(wa.adminPhones)) {
|
|
23796
|
+
wa.adminPhones = [];
|
|
23797
|
+
}
|
|
23798
|
+
const adminPhones = wa.adminPhones;
|
|
23799
|
+
if (adminPhones.includes(normalized)) {
|
|
23800
|
+
return { ok: true, message: `Phone ${normalized} is already in the admin list.` };
|
|
23801
|
+
}
|
|
23802
|
+
adminPhones.push(normalized);
|
|
23803
|
+
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
23804
|
+
if (!parsed.success) {
|
|
23805
|
+
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
23806
|
+
return { ok: false, error: `Validation failed: ${msg}` };
|
|
23807
|
+
}
|
|
23808
|
+
config2.whatsapp = parsed.data;
|
|
23809
|
+
writeConfig(accountDir, config2);
|
|
23810
|
+
console.error(`${TAG4} added admin phone=${normalized}`);
|
|
23811
|
+
reloadManagerConfig(accountDir);
|
|
23812
|
+
return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
|
|
23813
|
+
} catch (err) {
|
|
23814
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
23815
|
+
console.error(`${TAG4} addAdminPhone failed: ${msg}`);
|
|
23816
|
+
return { ok: false, error: msg };
|
|
23817
|
+
}
|
|
23818
|
+
}
|
|
23819
|
+
function removeAdminPhone(accountDir, phone) {
|
|
23820
|
+
const normalized = phone.trim();
|
|
23821
|
+
try {
|
|
23822
|
+
const config2 = readConfig(accountDir);
|
|
23823
|
+
if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
|
|
23824
|
+
return { ok: true, message: `No WhatsApp config exists \u2014 nothing to remove.` };
|
|
23825
|
+
}
|
|
23826
|
+
const wa = config2.whatsapp;
|
|
23827
|
+
migrateAdminPhones(wa);
|
|
23828
|
+
if (!Array.isArray(wa.adminPhones)) {
|
|
23829
|
+
return { ok: true, message: `No admin phones configured \u2014 nothing to remove.` };
|
|
23830
|
+
}
|
|
23831
|
+
const adminPhones = wa.adminPhones;
|
|
23832
|
+
const idx = adminPhones.indexOf(normalized);
|
|
23833
|
+
if (idx === -1) {
|
|
23834
|
+
return { ok: true, message: `Phone ${normalized} is not in the admin list.` };
|
|
23835
|
+
}
|
|
23836
|
+
adminPhones.splice(idx, 1);
|
|
23837
|
+
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
23838
|
+
if (!parsed.success) {
|
|
23839
|
+
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
23840
|
+
return { ok: false, error: `Validation failed: ${msg}` };
|
|
23841
|
+
}
|
|
23842
|
+
config2.whatsapp = parsed.data;
|
|
23843
|
+
writeConfig(accountDir, config2);
|
|
23844
|
+
console.error(`${TAG4} removed admin phone=${normalized}`);
|
|
23845
|
+
reloadManagerConfig(accountDir);
|
|
23846
|
+
return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
|
|
23847
|
+
} catch (err) {
|
|
23848
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
23849
|
+
console.error(`${TAG4} removeAdminPhone failed: ${msg}`);
|
|
23850
|
+
return { ok: false, error: msg };
|
|
23851
|
+
}
|
|
23852
|
+
}
|
|
23853
|
+
function readAdminPhones(accountDir) {
|
|
23854
|
+
try {
|
|
23855
|
+
const config2 = readConfig(accountDir);
|
|
23856
|
+
const wa = config2.whatsapp;
|
|
23857
|
+
if (!wa) return [];
|
|
23858
|
+
const migrated = migrateAdminPhones(wa);
|
|
23859
|
+
if (migrated > 0) {
|
|
23860
|
+
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
23861
|
+
if (parsed.success) {
|
|
23862
|
+
config2.whatsapp = parsed.data;
|
|
23863
|
+
writeConfig(accountDir, config2);
|
|
23864
|
+
reloadManagerConfig(accountDir);
|
|
23865
|
+
}
|
|
23866
|
+
}
|
|
23867
|
+
if (!Array.isArray(wa.adminPhones)) return [];
|
|
23868
|
+
return wa.adminPhones.filter((p) => typeof p === "string");
|
|
23869
|
+
} catch {
|
|
23870
|
+
return [];
|
|
23871
|
+
}
|
|
23872
|
+
}
|
|
23873
|
+
function setPublicAgent(accountDir, slug) {
|
|
23874
|
+
const trimmed = slug.trim();
|
|
23875
|
+
if (!trimmed) {
|
|
23876
|
+
return { ok: false, error: "Agent slug cannot be empty." };
|
|
23877
|
+
}
|
|
23878
|
+
const agentConfigPath = join5(accountDir, "agents", trimmed, "config.json");
|
|
23879
|
+
if (!existsSync10(agentConfigPath)) {
|
|
23880
|
+
return { ok: false, error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
|
|
23881
|
+
}
|
|
23882
|
+
try {
|
|
23883
|
+
const config2 = readConfig(accountDir);
|
|
23884
|
+
if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
|
|
23885
|
+
config2.whatsapp = {};
|
|
23886
|
+
}
|
|
23887
|
+
const wa = config2.whatsapp;
|
|
23888
|
+
wa.publicAgent = trimmed;
|
|
23889
|
+
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
23890
|
+
if (!parsed.success) {
|
|
23891
|
+
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
23892
|
+
return { ok: false, error: `Validation failed: ${msg}` };
|
|
23893
|
+
}
|
|
23894
|
+
config2.whatsapp = parsed.data;
|
|
23895
|
+
writeConfig(accountDir, config2);
|
|
23896
|
+
console.error(`${TAG4} publicAgent set to ${trimmed}`);
|
|
23897
|
+
reloadManagerConfig(accountDir);
|
|
23898
|
+
return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
|
|
23899
|
+
} catch (err) {
|
|
23900
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
23901
|
+
console.error(`${TAG4} setPublicAgent failed: ${msg}`);
|
|
23902
|
+
return { ok: false, error: msg };
|
|
23903
|
+
}
|
|
23904
|
+
}
|
|
23905
|
+
function getPublicAgent(accountDir) {
|
|
23906
|
+
try {
|
|
23907
|
+
const config2 = readConfig(accountDir);
|
|
23908
|
+
const wa = config2.whatsapp;
|
|
23909
|
+
if (!wa) return null;
|
|
23910
|
+
const slug = wa.publicAgent;
|
|
23911
|
+
if (typeof slug === "string" && slug.trim()) {
|
|
23912
|
+
return slug.trim();
|
|
23913
|
+
}
|
|
23914
|
+
return null;
|
|
23915
|
+
} catch {
|
|
23916
|
+
return null;
|
|
23917
|
+
}
|
|
23918
|
+
}
|
|
23919
|
+
function updateConfig(accountDir, fields) {
|
|
23920
|
+
const fieldNames = Object.keys(fields);
|
|
23921
|
+
if (fieldNames.length === 0) {
|
|
23922
|
+
return { ok: false, error: "No fields provided to update." };
|
|
23923
|
+
}
|
|
23924
|
+
try {
|
|
23925
|
+
const config2 = readConfig(accountDir);
|
|
23926
|
+
if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
|
|
23927
|
+
config2.whatsapp = {};
|
|
23928
|
+
}
|
|
23929
|
+
const wa = config2.whatsapp;
|
|
23930
|
+
migrateAdminPhones(wa);
|
|
23931
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
23932
|
+
wa[key] = value;
|
|
23933
|
+
}
|
|
23934
|
+
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
23935
|
+
if (!parsed.success) {
|
|
23936
|
+
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
23937
|
+
console.error(`${TAG4} update validation failed: ${msg}`);
|
|
23938
|
+
return { ok: false, error: `Validation failed: ${msg}` };
|
|
23939
|
+
}
|
|
23940
|
+
config2.whatsapp = parsed.data;
|
|
23941
|
+
writeConfig(accountDir, config2);
|
|
23942
|
+
console.error(`${TAG4} updated fields=[${fieldNames.join(",")}]`);
|
|
23943
|
+
reloadManagerConfig(accountDir);
|
|
23944
|
+
return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
|
|
23945
|
+
} catch (err) {
|
|
23946
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
23947
|
+
console.error(`${TAG4} updateConfig failed: ${msg}`);
|
|
23948
|
+
return { ok: false, error: msg };
|
|
23949
|
+
}
|
|
23950
|
+
}
|
|
23951
|
+
function getConfig(accountDir) {
|
|
23952
|
+
try {
|
|
23953
|
+
const config2 = readConfig(accountDir);
|
|
23954
|
+
const wa = config2.whatsapp;
|
|
23955
|
+
if (!wa) return null;
|
|
23956
|
+
const migrated = migrateAdminPhones(wa);
|
|
23957
|
+
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
23958
|
+
if (!parsed.success) return wa;
|
|
23959
|
+
if (migrated > 0) {
|
|
23960
|
+
config2.whatsapp = parsed.data;
|
|
23961
|
+
writeConfig(accountDir, config2);
|
|
23962
|
+
reloadManagerConfig(accountDir);
|
|
23963
|
+
}
|
|
23964
|
+
return parsed.data;
|
|
23965
|
+
} catch {
|
|
23966
|
+
return null;
|
|
23967
|
+
}
|
|
23968
|
+
}
|
|
23969
|
+
|
|
23692
23970
|
// app/lib/whatsapp/normalize.ts
|
|
23693
23971
|
var WHATSAPP_USER_JID_RE = /^(\d+)(?::\d+)?@s\.whatsapp\.net$/i;
|
|
23694
23972
|
var WHATSAPP_LID_RE = /^(\d+)@lid$/i;
|
|
@@ -23852,18 +24130,16 @@ function resolveAccountConfig(config2, accountConfig) {
|
|
|
23852
24130
|
return {
|
|
23853
24131
|
dmPolicy: accountConfig?.dmPolicy ?? config2.dmPolicy ?? "disabled",
|
|
23854
24132
|
groupPolicy: accountConfig?.groupPolicy ?? config2.groupPolicy ?? "disabled",
|
|
24133
|
+
adminPhones: accountConfig?.adminPhones ?? config2.adminPhones ?? [],
|
|
23855
24134
|
allowFrom: accountConfig?.allowFrom ?? config2.allowFrom ?? [],
|
|
23856
24135
|
groupAllowFrom: accountConfig?.groupAllowFrom ?? config2.groupAllowFrom ?? [],
|
|
23857
24136
|
selfChatMode: accountConfig?.selfChatMode ?? false
|
|
23858
24137
|
};
|
|
23859
24138
|
}
|
|
23860
|
-
function isAdminPhone(phone,
|
|
24139
|
+
function isAdminPhone(phone, adminPhones) {
|
|
23861
24140
|
const normalized = normalizeE164(phone);
|
|
23862
24141
|
if (!normalized) return false;
|
|
23863
|
-
return
|
|
23864
|
-
if (entry === "*") return false;
|
|
23865
|
-
return phonesMatch(entry, normalized);
|
|
23866
|
-
});
|
|
24142
|
+
return adminPhones.some((entry) => phonesMatch(entry, normalized));
|
|
23867
24143
|
}
|
|
23868
24144
|
function checkDmAccess(params) {
|
|
23869
24145
|
const { senderPhone, selfPhone } = params;
|
|
@@ -23871,7 +24147,7 @@ function checkDmAccess(params) {
|
|
|
23871
24147
|
if (phonesMatch(senderPhone, selfPhone)) {
|
|
23872
24148
|
return { allowed: true, reason: "self-phone", agentType: "admin" };
|
|
23873
24149
|
}
|
|
23874
|
-
if (isAdminPhone(senderPhone, cfg.
|
|
24150
|
+
if (isAdminPhone(senderPhone, cfg.adminPhones)) {
|
|
23875
24151
|
return { allowed: true, reason: "admin-binding", agentType: "admin" };
|
|
23876
24152
|
}
|
|
23877
24153
|
switch (cfg.dmPolicy) {
|
|
@@ -23912,10 +24188,7 @@ function checkGroupAccess(params) {
|
|
|
23912
24188
|
return { allowed: true, reason: "group-policy-open", agentType: "public" };
|
|
23913
24189
|
}
|
|
23914
24190
|
case "allowlist": {
|
|
23915
|
-
const inList = cfg.groupAllowFrom.some((entry) => phonesMatch(entry, senderPhone)) || cfg.
|
|
23916
|
-
if (entry === "*") return false;
|
|
23917
|
-
return phonesMatch(entry, senderPhone);
|
|
23918
|
-
});
|
|
24191
|
+
const inList = cfg.groupAllowFrom.some((entry) => phonesMatch(entry, senderPhone)) || cfg.adminPhones.some((entry) => phonesMatch(entry, senderPhone));
|
|
23919
24192
|
if (!inList) {
|
|
23920
24193
|
return { allowed: false, reason: "not-in-group-allowlist", agentType: "public" };
|
|
23921
24194
|
}
|
|
@@ -23979,7 +24252,7 @@ function isDuplicateInbound(key) {
|
|
|
23979
24252
|
}
|
|
23980
24253
|
|
|
23981
24254
|
// app/lib/whatsapp/outbound/send.ts
|
|
23982
|
-
var
|
|
24255
|
+
var TAG5 = "[whatsapp:outbound]";
|
|
23983
24256
|
async function sendTextMessage(sock, to, text, opts) {
|
|
23984
24257
|
try {
|
|
23985
24258
|
const jid = to.includes("@") ? to : toWhatsappJid(to);
|
|
@@ -23991,11 +24264,11 @@ async function sendTextMessage(sock, to, text, opts) {
|
|
|
23991
24264
|
const messageId = result?.key?.id;
|
|
23992
24265
|
if (messageId) {
|
|
23993
24266
|
trackAgentSentMessage(messageId);
|
|
23994
|
-
console.error(`${
|
|
24267
|
+
console.error(`${TAG5} sent text to=${jid} id=${messageId}`);
|
|
23995
24268
|
}
|
|
23996
24269
|
return { success: true, messageId: messageId ?? void 0 };
|
|
23997
24270
|
} catch (err) {
|
|
23998
|
-
console.error(`${
|
|
24271
|
+
console.error(`${TAG5} send failed to=${to}: ${String(err)}`);
|
|
23999
24272
|
return { success: false, error: String(err) };
|
|
24000
24273
|
}
|
|
24001
24274
|
}
|
|
@@ -24015,13 +24288,13 @@ async function sendReadReceipt(sock, chatJid, messageIds, participant) {
|
|
|
24015
24288
|
// app/lib/whatsapp/inbound/media.ts
|
|
24016
24289
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
24017
24290
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
24018
|
-
import { join as
|
|
24291
|
+
import { join as join6 } from "path";
|
|
24019
24292
|
import {
|
|
24020
24293
|
downloadMediaMessage,
|
|
24021
24294
|
downloadContentFromMessage,
|
|
24022
24295
|
normalizeMessageContent as normalizeMessageContent2
|
|
24023
24296
|
} from "@whiskeysockets/baileys";
|
|
24024
|
-
var
|
|
24297
|
+
var TAG6 = "[whatsapp:media]";
|
|
24025
24298
|
var MEDIA_DIR = "/tmp/maxy-media";
|
|
24026
24299
|
function mimeToExt(mimetype) {
|
|
24027
24300
|
const map2 = {
|
|
@@ -24077,47 +24350,47 @@ async function downloadInboundMedia(msg, sock, opts) {
|
|
|
24077
24350
|
}
|
|
24078
24351
|
);
|
|
24079
24352
|
if (!buffer || buffer.length === 0) {
|
|
24080
|
-
console.error(`${
|
|
24353
|
+
console.error(`${TAG6} primary download returned empty, trying direct fallback`);
|
|
24081
24354
|
const downloadable = getDownloadableContent(content);
|
|
24082
24355
|
if (downloadable) {
|
|
24083
24356
|
try {
|
|
24084
24357
|
const stream = await downloadContentFromMessage(downloadable.downloadable, downloadable.mediaType);
|
|
24085
24358
|
buffer = await streamToBuffer(stream);
|
|
24086
24359
|
} catch (fallbackErr) {
|
|
24087
|
-
console.error(`${
|
|
24360
|
+
console.error(`${TAG6} direct download fallback failed: ${String(fallbackErr)}`);
|
|
24088
24361
|
}
|
|
24089
24362
|
}
|
|
24090
24363
|
}
|
|
24091
24364
|
if (!buffer || buffer.length === 0) {
|
|
24092
|
-
console.error(`${
|
|
24365
|
+
console.error(`${TAG6} download failed: empty buffer for ${mimetype ?? "unknown"}`);
|
|
24093
24366
|
return void 0;
|
|
24094
24367
|
}
|
|
24095
24368
|
if (buffer.length > maxBytes) {
|
|
24096
24369
|
const sizeMB = (buffer.length / (1024 * 1024)).toFixed(1);
|
|
24097
24370
|
const limitMB = (maxBytes / (1024 * 1024)).toFixed(0);
|
|
24098
|
-
console.error(`${
|
|
24371
|
+
console.error(`${TAG6} media too large type=${mimetype ?? "unknown"} size=${sizeMB}MB limit=${limitMB}MB`);
|
|
24099
24372
|
return void 0;
|
|
24100
24373
|
}
|
|
24101
24374
|
await mkdir2(MEDIA_DIR, { recursive: true });
|
|
24102
24375
|
const ext = mimeToExt(mimetype ?? "application/octet-stream");
|
|
24103
24376
|
const filename = `${randomUUID6()}.${ext}`;
|
|
24104
|
-
const filePath =
|
|
24377
|
+
const filePath = join6(MEDIA_DIR, filename);
|
|
24105
24378
|
await writeFile2(filePath, buffer);
|
|
24106
24379
|
const sizeKB = (buffer.length / 1024).toFixed(0);
|
|
24107
|
-
console.error(`${
|
|
24380
|
+
console.error(`${TAG6} media downloaded type=${mimetype ?? "unknown"} size=${sizeKB}KB path=${filePath}`);
|
|
24108
24381
|
return {
|
|
24109
24382
|
path: filePath,
|
|
24110
24383
|
mimetype: mimetype ?? "application/octet-stream",
|
|
24111
24384
|
size: buffer.length
|
|
24112
24385
|
};
|
|
24113
24386
|
} catch (err) {
|
|
24114
|
-
console.error(`${
|
|
24387
|
+
console.error(`${TAG6} media download failed type=${mimetype ?? "unknown"} error=${String(err)}`);
|
|
24115
24388
|
return void 0;
|
|
24116
24389
|
}
|
|
24117
24390
|
}
|
|
24118
24391
|
|
|
24119
24392
|
// app/lib/whatsapp/inbound/debounce.ts
|
|
24120
|
-
var
|
|
24393
|
+
var TAG7 = "[whatsapp:debounce]";
|
|
24121
24394
|
function createInboundDebouncer(opts) {
|
|
24122
24395
|
const { debounceMs, buildKey, onFlush, onError } = opts;
|
|
24123
24396
|
const pending = /* @__PURE__ */ new Map();
|
|
@@ -24129,7 +24402,7 @@ function createInboundDebouncer(opts) {
|
|
|
24129
24402
|
pending.delete(key);
|
|
24130
24403
|
const batchSize = batch.entries.length;
|
|
24131
24404
|
try {
|
|
24132
|
-
console.error(`${
|
|
24405
|
+
console.error(`${TAG7} debounce flush key=${key} batchSize=${batchSize}`);
|
|
24133
24406
|
const result = onFlush(batch.entries);
|
|
24134
24407
|
if (result && typeof result.catch === "function") {
|
|
24135
24408
|
result.catch(onError);
|
|
@@ -24187,7 +24460,7 @@ function createInboundDebouncer(opts) {
|
|
|
24187
24460
|
}
|
|
24188
24461
|
|
|
24189
24462
|
// app/lib/whatsapp/manager.ts
|
|
24190
|
-
var
|
|
24463
|
+
var TAG8 = "[whatsapp:manager]";
|
|
24191
24464
|
var MAX_RECONNECT_ATTEMPTS = 10;
|
|
24192
24465
|
var connections = /* @__PURE__ */ new Map();
|
|
24193
24466
|
var configDir = null;
|
|
@@ -24196,7 +24469,7 @@ var onInboundMessage = null;
|
|
|
24196
24469
|
var initialized = false;
|
|
24197
24470
|
async function init(opts) {
|
|
24198
24471
|
if (initialized) {
|
|
24199
|
-
console.error(`${
|
|
24472
|
+
console.error(`${TAG8} already initialized`);
|
|
24200
24473
|
return;
|
|
24201
24474
|
}
|
|
24202
24475
|
configDir = opts.configDir;
|
|
@@ -24204,20 +24477,20 @@ async function init(opts) {
|
|
|
24204
24477
|
loadConfig(opts.accountConfig);
|
|
24205
24478
|
const accountIds = listCredentialAccountIds(configDir);
|
|
24206
24479
|
if (accountIds.length === 0) {
|
|
24207
|
-
console.error(`${
|
|
24480
|
+
console.error(`${TAG8} init: no stored WhatsApp credentials found`);
|
|
24208
24481
|
initialized = true;
|
|
24209
24482
|
return;
|
|
24210
24483
|
}
|
|
24211
|
-
console.error(`${
|
|
24484
|
+
console.error(`${TAG8} init: found ${accountIds.length} credentialed account(s): ${accountIds.join(", ")}`);
|
|
24212
24485
|
initialized = true;
|
|
24213
24486
|
for (const accountId of accountIds) {
|
|
24214
24487
|
const accountCfg = whatsAppConfig.accounts?.[accountId];
|
|
24215
24488
|
if (accountCfg?.enabled === false) {
|
|
24216
|
-
console.error(`${
|
|
24489
|
+
console.error(`${TAG8} skipping disabled account=${accountId}`);
|
|
24217
24490
|
continue;
|
|
24218
24491
|
}
|
|
24219
24492
|
startConnection(accountId).catch((err) => {
|
|
24220
|
-
console.error(`${
|
|
24493
|
+
console.error(`${TAG8} failed to auto-start account=${accountId}: ${formatError(err)}`);
|
|
24221
24494
|
});
|
|
24222
24495
|
}
|
|
24223
24496
|
}
|
|
@@ -24226,7 +24499,7 @@ async function startConnection(accountId) {
|
|
|
24226
24499
|
const authDir = resolveAuthDir(configDir, accountId);
|
|
24227
24500
|
const hasAuth = await authExists(authDir);
|
|
24228
24501
|
if (!hasAuth) {
|
|
24229
|
-
console.error(`${
|
|
24502
|
+
console.error(`${TAG8} no credentials for account=${accountId}`);
|
|
24230
24503
|
return;
|
|
24231
24504
|
}
|
|
24232
24505
|
await stopConnection(accountId);
|
|
@@ -24261,11 +24534,11 @@ async function stopConnection(accountId) {
|
|
|
24261
24534
|
conn.sock.ev.removeAllListeners("creds.update");
|
|
24262
24535
|
conn.sock.ws?.close?.();
|
|
24263
24536
|
} catch (err) {
|
|
24264
|
-
console.warn(`${
|
|
24537
|
+
console.warn(`${TAG8} socket cleanup error during stop account=${accountId}: ${String(err)}`);
|
|
24265
24538
|
}
|
|
24266
24539
|
}
|
|
24267
24540
|
connections.delete(accountId);
|
|
24268
|
-
console.error(`${
|
|
24541
|
+
console.error(`${TAG8} stopped account=${accountId}`);
|
|
24269
24542
|
}
|
|
24270
24543
|
function getStatus() {
|
|
24271
24544
|
return Array.from(connections.values()).map((conn) => ({
|
|
@@ -24304,13 +24577,13 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
24304
24577
|
connections.set(accountId, conn);
|
|
24305
24578
|
try {
|
|
24306
24579
|
await sock.sendPresenceUpdate("available");
|
|
24307
|
-
console.error(`${
|
|
24580
|
+
console.error(`${TAG8} presence set to available account=${accountId}`);
|
|
24308
24581
|
} catch (err) {
|
|
24309
|
-
console.error(`${
|
|
24582
|
+
console.error(`${TAG8} presence update failed account=${accountId}: ${String(err)}`);
|
|
24310
24583
|
}
|
|
24311
24584
|
monitorInbound(conn);
|
|
24312
24585
|
watchForDisconnect(conn);
|
|
24313
|
-
console.error(`${
|
|
24586
|
+
console.error(`${TAG8} registered login socket for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
24314
24587
|
}
|
|
24315
24588
|
function reloadConfig(accountConfig) {
|
|
24316
24589
|
loadConfig(accountConfig);
|
|
@@ -24320,22 +24593,24 @@ async function shutdown() {
|
|
|
24320
24593
|
const ids = Array.from(connections.keys());
|
|
24321
24594
|
await Promise.all(ids.map((id) => stopConnection(id)));
|
|
24322
24595
|
initialized = false;
|
|
24323
|
-
console.error(`${
|
|
24596
|
+
console.error(`${TAG8} shutdown complete`);
|
|
24324
24597
|
}
|
|
24325
24598
|
function loadConfig(accountConfig) {
|
|
24326
24599
|
try {
|
|
24327
24600
|
const raw2 = accountConfig?.whatsapp;
|
|
24328
24601
|
if (raw2 && typeof raw2 === "object") {
|
|
24329
|
-
const
|
|
24602
|
+
const wa = raw2;
|
|
24603
|
+
migrateAdminPhones(wa);
|
|
24604
|
+
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
24330
24605
|
if (parsed.success) {
|
|
24331
24606
|
whatsAppConfig = parsed.data;
|
|
24332
24607
|
} else {
|
|
24333
|
-
console.error(`${
|
|
24608
|
+
console.error(`${TAG8} config validation failed: ${parsed.error.message}`);
|
|
24334
24609
|
whatsAppConfig = {};
|
|
24335
24610
|
}
|
|
24336
24611
|
}
|
|
24337
24612
|
} catch (err) {
|
|
24338
|
-
console.error(`${
|
|
24613
|
+
console.error(`${TAG8} config load error: ${String(err)}`);
|
|
24339
24614
|
whatsAppConfig = {};
|
|
24340
24615
|
}
|
|
24341
24616
|
}
|
|
@@ -24343,13 +24618,13 @@ async function connectWithReconnect(conn) {
|
|
|
24343
24618
|
const maxAttempts = MAX_RECONNECT_ATTEMPTS;
|
|
24344
24619
|
while (!conn.abortController.signal.aborted) {
|
|
24345
24620
|
try {
|
|
24346
|
-
console.error(`${
|
|
24621
|
+
console.error(`${TAG8} connecting account=${conn.accountId} attempt=${conn.reconnectAttempts}`);
|
|
24347
24622
|
const sock = await createWaSocket({
|
|
24348
24623
|
authDir: conn.authDir,
|
|
24349
24624
|
silent: true
|
|
24350
24625
|
});
|
|
24351
24626
|
conn.sock = sock;
|
|
24352
|
-
console.error(`${
|
|
24627
|
+
console.error(`${TAG8} socket created account=${conn.accountId} \u2014 waiting for connection`);
|
|
24353
24628
|
await waitForConnection(sock);
|
|
24354
24629
|
const selfId = readSelfId(conn.authDir);
|
|
24355
24630
|
conn.connected = true;
|
|
@@ -24359,12 +24634,12 @@ async function connectWithReconnect(conn) {
|
|
|
24359
24634
|
conn.reconnectAttempts = 0;
|
|
24360
24635
|
conn.lastError = void 0;
|
|
24361
24636
|
conn.lidMapping = sock.signalRepository?.lidMapping ?? null;
|
|
24362
|
-
console.error(`${
|
|
24637
|
+
console.error(`${TAG8} connected account=${conn.accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
24363
24638
|
try {
|
|
24364
24639
|
await sock.sendPresenceUpdate("available");
|
|
24365
|
-
console.error(`${
|
|
24640
|
+
console.error(`${TAG8} presence set to available account=${conn.accountId}`);
|
|
24366
24641
|
} catch (err) {
|
|
24367
|
-
console.error(`${
|
|
24642
|
+
console.error(`${TAG8} presence update failed account=${conn.accountId}: ${String(err)}`);
|
|
24368
24643
|
}
|
|
24369
24644
|
if (conn.debouncer) {
|
|
24370
24645
|
conn.debouncer.destroy();
|
|
@@ -24379,19 +24654,19 @@ async function connectWithReconnect(conn) {
|
|
|
24379
24654
|
conn.sock = null;
|
|
24380
24655
|
const classification = classifyDisconnect(err);
|
|
24381
24656
|
conn.lastError = classification.message;
|
|
24382
|
-
console.error(`${
|
|
24657
|
+
console.error(`${TAG8} disconnect account=${conn.accountId}: ${classification.kind} \u2014 ${classification.message}`);
|
|
24383
24658
|
if (!classification.shouldRetry) {
|
|
24384
|
-
console.error(`${
|
|
24659
|
+
console.error(`${TAG8} terminal disconnect for account=${conn.accountId}, stopping reconnection`);
|
|
24385
24660
|
return;
|
|
24386
24661
|
}
|
|
24387
24662
|
conn.reconnectAttempts++;
|
|
24388
24663
|
if (conn.reconnectAttempts > maxAttempts) {
|
|
24389
|
-
console.error(`${
|
|
24664
|
+
console.error(`${TAG8} max retries exceeded for account=${conn.accountId}`);
|
|
24390
24665
|
conn.lastError = `Max reconnect attempts (${maxAttempts}) exceeded`;
|
|
24391
24666
|
return;
|
|
24392
24667
|
}
|
|
24393
24668
|
const delay = computeBackoff(conn.reconnectAttempts);
|
|
24394
|
-
console.error(`${
|
|
24669
|
+
console.error(`${TAG8} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${conn.reconnectAttempts}/${maxAttempts})`);
|
|
24395
24670
|
await new Promise((resolve17) => {
|
|
24396
24671
|
const timer = setTimeout(resolve17, delay);
|
|
24397
24672
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
@@ -24421,11 +24696,11 @@ function watchForDisconnect(conn) {
|
|
|
24421
24696
|
conn.sock.ev.on("connection.update", (update) => {
|
|
24422
24697
|
if (update.connection === "close") {
|
|
24423
24698
|
if (connections.get(conn.accountId) !== conn) return;
|
|
24424
|
-
console.error(`${
|
|
24699
|
+
console.error(`${TAG8} socket disconnected for account=${conn.accountId}`);
|
|
24425
24700
|
conn.connected = false;
|
|
24426
24701
|
conn.sock = null;
|
|
24427
24702
|
connectWithReconnect(conn).catch((err) => {
|
|
24428
|
-
console.error(`${
|
|
24703
|
+
console.error(`${TAG8} reconnection failed for account=${conn.accountId}: ${formatError(err)}`);
|
|
24429
24704
|
});
|
|
24430
24705
|
}
|
|
24431
24706
|
});
|
|
@@ -24434,7 +24709,7 @@ function monitorInbound(conn) {
|
|
|
24434
24709
|
if (!conn.sock || !onInboundMessage) return;
|
|
24435
24710
|
const sock = conn.sock;
|
|
24436
24711
|
const debounceMs = whatsAppConfig.accounts?.[conn.accountId]?.debounceMs ?? whatsAppConfig.debounceMs ?? 0;
|
|
24437
|
-
console.error(`${
|
|
24712
|
+
console.error(`${TAG8} monitorInbound started account=${conn.accountId} debounceMs=${debounceMs}`);
|
|
24438
24713
|
conn.debouncer = createInboundDebouncer({
|
|
24439
24714
|
debounceMs,
|
|
24440
24715
|
buildKey: (payload) => {
|
|
@@ -24447,7 +24722,7 @@ function monitorInbound(conn) {
|
|
|
24447
24722
|
onInboundMessage(entries[0]);
|
|
24448
24723
|
return;
|
|
24449
24724
|
}
|
|
24450
|
-
console.error(`${
|
|
24725
|
+
console.error(`${TAG8} debounce: combining ${entries.length} messages account=${conn.accountId} from=${entries[0].senderPhone}`);
|
|
24451
24726
|
const last = entries[entries.length - 1];
|
|
24452
24727
|
const mediaEntry = entries.find((e) => e.mediaPath);
|
|
24453
24728
|
const combinedText = entries.map((e) => e.text).filter(Boolean).join("\n");
|
|
@@ -24460,7 +24735,7 @@ function monitorInbound(conn) {
|
|
|
24460
24735
|
});
|
|
24461
24736
|
},
|
|
24462
24737
|
onError: (err) => {
|
|
24463
|
-
console.error(`${
|
|
24738
|
+
console.error(`${TAG8} debounce flush error account=${conn.accountId}: ${String(err)}`);
|
|
24464
24739
|
}
|
|
24465
24740
|
});
|
|
24466
24741
|
sock.ev.on("messages.upsert", async (upsert) => {
|
|
@@ -24469,7 +24744,7 @@ function monitorInbound(conn) {
|
|
|
24469
24744
|
try {
|
|
24470
24745
|
await handleInboundMessage(conn, msg);
|
|
24471
24746
|
} catch (err) {
|
|
24472
|
-
console.error(`${
|
|
24747
|
+
console.error(`${TAG8} inbound handler error account=${conn.accountId}: ${String(err)}`);
|
|
24473
24748
|
}
|
|
24474
24749
|
}
|
|
24475
24750
|
});
|
|
@@ -24479,31 +24754,31 @@ async function handleInboundMessage(conn, msg) {
|
|
|
24479
24754
|
const remoteJid = msg.key.remoteJid;
|
|
24480
24755
|
if (!remoteJid) return;
|
|
24481
24756
|
if (remoteJid === "status@broadcast") {
|
|
24482
|
-
console.error(`${
|
|
24757
|
+
console.error(`${TAG8} drop: status broadcast account=${conn.accountId}`);
|
|
24483
24758
|
return;
|
|
24484
24759
|
}
|
|
24485
24760
|
if (!msg.message) {
|
|
24486
|
-
console.error(`${
|
|
24761
|
+
console.error(`${TAG8} drop: empty message account=${conn.accountId} from=${remoteJid}`);
|
|
24487
24762
|
return;
|
|
24488
24763
|
}
|
|
24489
24764
|
const dedupKey = `${conn.accountId}:${remoteJid}:${msg.key.id}`;
|
|
24490
24765
|
if (isDuplicateInbound(dedupKey)) {
|
|
24491
|
-
console.error(`${
|
|
24766
|
+
console.error(`${TAG8} drop: duplicate account=${conn.accountId} key=${dedupKey}`);
|
|
24492
24767
|
return;
|
|
24493
24768
|
}
|
|
24494
24769
|
if (msg.key.fromMe) {
|
|
24495
24770
|
if (msg.key.id && isAgentSentMessage(msg.key.id)) {
|
|
24496
|
-
console.error(`${
|
|
24771
|
+
console.error(`${TAG8} drop: echo suppression account=${conn.accountId} msgId=${msg.key.id}`);
|
|
24497
24772
|
return;
|
|
24498
24773
|
}
|
|
24499
24774
|
const extracted2 = extractMessage(msg);
|
|
24500
24775
|
if (!extracted2.text) {
|
|
24501
|
-
console.error(`${
|
|
24776
|
+
console.error(`${TAG8} owner reply skipped \u2014 no text content account=${conn.accountId}`);
|
|
24502
24777
|
return;
|
|
24503
24778
|
}
|
|
24504
24779
|
const isGroup2 = isGroupJid(remoteJid);
|
|
24505
24780
|
const senderPhone2 = conn.selfPhone ?? "owner";
|
|
24506
|
-
console.error(`${
|
|
24781
|
+
console.error(`${TAG8} owner reply mirrored to session from=${senderPhone2} account=${conn.accountId}`);
|
|
24507
24782
|
const reply2 = async (text) => {
|
|
24508
24783
|
const currentSock = conn.sock;
|
|
24509
24784
|
if (!currentSock) throw new Error("WhatsApp disconnected \u2014 cannot reply");
|
|
@@ -24524,7 +24799,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
24524
24799
|
}
|
|
24525
24800
|
const extracted = extractMessage(msg);
|
|
24526
24801
|
if (!extracted.text && !extracted.mediaType) {
|
|
24527
|
-
console.error(`${
|
|
24802
|
+
console.error(`${TAG8} drop: no text or media account=${conn.accountId} from=${remoteJid}`);
|
|
24528
24803
|
return;
|
|
24529
24804
|
}
|
|
24530
24805
|
let mediaResult;
|
|
@@ -24534,7 +24809,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
24534
24809
|
maxBytes: maxMb * 1024 * 1024
|
|
24535
24810
|
});
|
|
24536
24811
|
if (!mediaResult) {
|
|
24537
|
-
console.error(`${
|
|
24812
|
+
console.error(`${TAG8} media download returned undefined account=${conn.accountId} type=${extracted.mediaType} from=${remoteJid}`);
|
|
24538
24813
|
}
|
|
24539
24814
|
}
|
|
24540
24815
|
const isGroup = isGroupJid(remoteJid);
|
|
@@ -24562,7 +24837,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
24562
24837
|
});
|
|
24563
24838
|
}
|
|
24564
24839
|
console.error(
|
|
24565
|
-
`${
|
|
24840
|
+
`${TAG8} inbound account=${conn.accountId} from=${senderPhone} group=${isGroup} access=${accessResult.allowed ? "allowed" : "blocked"}(${accessResult.reason}) agent=${accessResult.agentType}` + (extracted.mediaType ? ` media=${extracted.mediaType}` : "") + (mediaResult ? ` mediaPath=${mediaResult.path}` : "") + (extracted.quotedMessage ? ` replyTo=${extracted.quotedMessage.id}` : "")
|
|
24566
24841
|
);
|
|
24567
24842
|
if (!accessResult.allowed) return;
|
|
24568
24843
|
const sendReceipts = whatsAppConfig.accounts?.[conn.accountId]?.sendReadReceipts ?? whatsAppConfig.sendReadReceipts ?? true;
|
|
@@ -24597,201 +24872,6 @@ async function handleInboundMessage(conn, msg) {
|
|
|
24597
24872
|
}
|
|
24598
24873
|
}
|
|
24599
24874
|
|
|
24600
|
-
// app/lib/whatsapp/config-persist.ts
|
|
24601
|
-
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync10 } from "fs";
|
|
24602
|
-
import { resolve as resolve9, join as join6 } from "path";
|
|
24603
|
-
var TAG8 = "[whatsapp:config]";
|
|
24604
|
-
function configPath(accountDir) {
|
|
24605
|
-
return resolve9(accountDir, "account.json");
|
|
24606
|
-
}
|
|
24607
|
-
function readConfig(accountDir) {
|
|
24608
|
-
const path2 = configPath(accountDir);
|
|
24609
|
-
if (!existsSync10(path2)) throw new Error(`account.json not found at ${path2}`);
|
|
24610
|
-
return JSON.parse(readFileSync10(path2, "utf-8"));
|
|
24611
|
-
}
|
|
24612
|
-
function writeConfig(accountDir, config2) {
|
|
24613
|
-
const path2 = configPath(accountDir);
|
|
24614
|
-
writeFileSync6(path2, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
24615
|
-
}
|
|
24616
|
-
function reloadManagerConfig(accountDir) {
|
|
24617
|
-
try {
|
|
24618
|
-
const config2 = readConfig(accountDir);
|
|
24619
|
-
reloadConfig(config2);
|
|
24620
|
-
console.error(`${TAG8} reloaded manager config`);
|
|
24621
|
-
} catch (err) {
|
|
24622
|
-
console.error(`${TAG8} manager config reload failed: ${String(err)}`);
|
|
24623
|
-
}
|
|
24624
|
-
}
|
|
24625
|
-
var E164_PATTERN = /^\+\d{7,15}$/;
|
|
24626
|
-
function persistAfterPairing(accountDir, accountId, selfPhone) {
|
|
24627
|
-
try {
|
|
24628
|
-
const config2 = readConfig(accountDir);
|
|
24629
|
-
if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
|
|
24630
|
-
config2.whatsapp = {};
|
|
24631
|
-
}
|
|
24632
|
-
const wa = config2.whatsapp;
|
|
24633
|
-
if (!wa.accounts || typeof wa.accounts !== "object") {
|
|
24634
|
-
wa.accounts = {};
|
|
24635
|
-
}
|
|
24636
|
-
const accounts = wa.accounts;
|
|
24637
|
-
if (!accounts[accountId] || typeof accounts[accountId] !== "object") {
|
|
24638
|
-
accounts[accountId] = { name: "Main" };
|
|
24639
|
-
}
|
|
24640
|
-
if (selfPhone) {
|
|
24641
|
-
const normalized = selfPhone.startsWith("+") ? selfPhone : `+${selfPhone}`;
|
|
24642
|
-
if (!Array.isArray(wa.allowFrom)) {
|
|
24643
|
-
wa.allowFrom = [];
|
|
24644
|
-
}
|
|
24645
|
-
const allowFrom = wa.allowFrom;
|
|
24646
|
-
if (!allowFrom.includes(normalized)) {
|
|
24647
|
-
allowFrom.push(normalized);
|
|
24648
|
-
console.error(`${TAG8} added selfPhone=${normalized} to allowFrom`);
|
|
24649
|
-
}
|
|
24650
|
-
} else {
|
|
24651
|
-
console.error(`${TAG8} skipping allowFrom \u2014 selfPhone is null account=${accountId}`);
|
|
24652
|
-
}
|
|
24653
|
-
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
24654
|
-
if (!parsed.success) {
|
|
24655
|
-
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
24656
|
-
console.error(`${TAG8} validation failed after pairing: ${msg}`);
|
|
24657
|
-
return { ok: false, error: `Validation failed: ${msg}` };
|
|
24658
|
-
}
|
|
24659
|
-
config2.whatsapp = parsed.data;
|
|
24660
|
-
writeConfig(accountDir, config2);
|
|
24661
|
-
console.error(`${TAG8} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
|
|
24662
|
-
reloadManagerConfig(accountDir);
|
|
24663
|
-
return { ok: true };
|
|
24664
|
-
} catch (err) {
|
|
24665
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
24666
|
-
console.error(`${TAG8} persist failed account=${accountId}: ${msg}`);
|
|
24667
|
-
return { ok: false, error: msg };
|
|
24668
|
-
}
|
|
24669
|
-
}
|
|
24670
|
-
function addAdminPhone(accountDir, phone) {
|
|
24671
|
-
const normalized = phone.trim();
|
|
24672
|
-
if (!E164_PATTERN.test(normalized)) {
|
|
24673
|
-
return { ok: false, error: `Invalid phone format "${normalized}". Expected E.164 (e.g. +441234567890).` };
|
|
24674
|
-
}
|
|
24675
|
-
try {
|
|
24676
|
-
const config2 = readConfig(accountDir);
|
|
24677
|
-
if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
|
|
24678
|
-
config2.whatsapp = {};
|
|
24679
|
-
}
|
|
24680
|
-
const wa = config2.whatsapp;
|
|
24681
|
-
if (!Array.isArray(wa.allowFrom)) {
|
|
24682
|
-
wa.allowFrom = [];
|
|
24683
|
-
}
|
|
24684
|
-
const allowFrom = wa.allowFrom;
|
|
24685
|
-
if (allowFrom.includes(normalized)) {
|
|
24686
|
-
return { ok: true, message: `Phone ${normalized} is already in the admin list.` };
|
|
24687
|
-
}
|
|
24688
|
-
allowFrom.push(normalized);
|
|
24689
|
-
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
24690
|
-
if (!parsed.success) {
|
|
24691
|
-
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
24692
|
-
return { ok: false, error: `Validation failed: ${msg}` };
|
|
24693
|
-
}
|
|
24694
|
-
config2.whatsapp = parsed.data;
|
|
24695
|
-
writeConfig(accountDir, config2);
|
|
24696
|
-
console.error(`${TAG8} added admin phone=${normalized}`);
|
|
24697
|
-
reloadManagerConfig(accountDir);
|
|
24698
|
-
return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
|
|
24699
|
-
} catch (err) {
|
|
24700
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
24701
|
-
console.error(`${TAG8} addAdminPhone failed: ${msg}`);
|
|
24702
|
-
return { ok: false, error: msg };
|
|
24703
|
-
}
|
|
24704
|
-
}
|
|
24705
|
-
function removeAdminPhone(accountDir, phone) {
|
|
24706
|
-
const normalized = phone.trim();
|
|
24707
|
-
try {
|
|
24708
|
-
const config2 = readConfig(accountDir);
|
|
24709
|
-
if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
|
|
24710
|
-
return { ok: true, message: `No WhatsApp config exists \u2014 nothing to remove.` };
|
|
24711
|
-
}
|
|
24712
|
-
const wa = config2.whatsapp;
|
|
24713
|
-
if (!Array.isArray(wa.allowFrom)) {
|
|
24714
|
-
return { ok: true, message: `No admin phones configured \u2014 nothing to remove.` };
|
|
24715
|
-
}
|
|
24716
|
-
const allowFrom = wa.allowFrom;
|
|
24717
|
-
const idx = allowFrom.indexOf(normalized);
|
|
24718
|
-
if (idx === -1) {
|
|
24719
|
-
return { ok: true, message: `Phone ${normalized} is not in the admin list.` };
|
|
24720
|
-
}
|
|
24721
|
-
allowFrom.splice(idx, 1);
|
|
24722
|
-
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
24723
|
-
if (!parsed.success) {
|
|
24724
|
-
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
24725
|
-
return { ok: false, error: `Validation failed: ${msg}` };
|
|
24726
|
-
}
|
|
24727
|
-
config2.whatsapp = parsed.data;
|
|
24728
|
-
writeConfig(accountDir, config2);
|
|
24729
|
-
console.error(`${TAG8} removed admin phone=${normalized}`);
|
|
24730
|
-
reloadManagerConfig(accountDir);
|
|
24731
|
-
return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
|
|
24732
|
-
} catch (err) {
|
|
24733
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
24734
|
-
console.error(`${TAG8} removeAdminPhone failed: ${msg}`);
|
|
24735
|
-
return { ok: false, error: msg };
|
|
24736
|
-
}
|
|
24737
|
-
}
|
|
24738
|
-
function readAdminPhones(accountDir) {
|
|
24739
|
-
try {
|
|
24740
|
-
const config2 = readConfig(accountDir);
|
|
24741
|
-
const wa = config2.whatsapp;
|
|
24742
|
-
if (!wa || !Array.isArray(wa.allowFrom)) return [];
|
|
24743
|
-
return wa.allowFrom.filter((p) => typeof p === "string");
|
|
24744
|
-
} catch {
|
|
24745
|
-
return [];
|
|
24746
|
-
}
|
|
24747
|
-
}
|
|
24748
|
-
function setPublicAgent(accountDir, slug) {
|
|
24749
|
-
const trimmed = slug.trim();
|
|
24750
|
-
if (!trimmed) {
|
|
24751
|
-
return { ok: false, error: "Agent slug cannot be empty." };
|
|
24752
|
-
}
|
|
24753
|
-
const agentConfigPath = join6(accountDir, "agents", trimmed, "config.json");
|
|
24754
|
-
if (!existsSync10(agentConfigPath)) {
|
|
24755
|
-
return { ok: false, error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
|
|
24756
|
-
}
|
|
24757
|
-
try {
|
|
24758
|
-
const config2 = readConfig(accountDir);
|
|
24759
|
-
if (!config2.whatsapp || typeof config2.whatsapp !== "object") {
|
|
24760
|
-
config2.whatsapp = {};
|
|
24761
|
-
}
|
|
24762
|
-
const wa = config2.whatsapp;
|
|
24763
|
-
wa.publicAgent = trimmed;
|
|
24764
|
-
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
24765
|
-
if (!parsed.success) {
|
|
24766
|
-
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
24767
|
-
return { ok: false, error: `Validation failed: ${msg}` };
|
|
24768
|
-
}
|
|
24769
|
-
config2.whatsapp = parsed.data;
|
|
24770
|
-
writeConfig(accountDir, config2);
|
|
24771
|
-
console.error(`${TAG8} publicAgent set to ${trimmed}`);
|
|
24772
|
-
reloadManagerConfig(accountDir);
|
|
24773
|
-
return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
|
|
24774
|
-
} catch (err) {
|
|
24775
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
24776
|
-
console.error(`${TAG8} setPublicAgent failed: ${msg}`);
|
|
24777
|
-
return { ok: false, error: msg };
|
|
24778
|
-
}
|
|
24779
|
-
}
|
|
24780
|
-
function getPublicAgent(accountDir) {
|
|
24781
|
-
try {
|
|
24782
|
-
const config2 = readConfig(accountDir);
|
|
24783
|
-
const wa = config2.whatsapp;
|
|
24784
|
-
if (!wa) return null;
|
|
24785
|
-
const slug = wa.publicAgent;
|
|
24786
|
-
if (typeof slug === "string" && slug.trim()) {
|
|
24787
|
-
return slug.trim();
|
|
24788
|
-
}
|
|
24789
|
-
return null;
|
|
24790
|
-
} catch {
|
|
24791
|
-
return null;
|
|
24792
|
-
}
|
|
24793
|
-
}
|
|
24794
|
-
|
|
24795
24875
|
// app/api/whatsapp/login/wait/route.ts
|
|
24796
24876
|
async function POST10(req) {
|
|
24797
24877
|
try {
|
|
@@ -24977,7 +25057,7 @@ function serializeWhatsAppSchema() {
|
|
|
24977
25057
|
"",
|
|
24978
25058
|
"## Constraints (superRefine)",
|
|
24979
25059
|
"",
|
|
24980
|
-
'- `dmPolicy: "open"` requires `allowFrom` to include `"*"` (both top-level and per-account)',
|
|
25060
|
+
'- `dmPolicy: "open"` requires `allowFrom` to include `"*"` (both top-level and per-account). This is a DM policy constraint \u2014 it does not affect admin phones, which live in the separate `adminPhones` field.',
|
|
24981
25061
|
"- Account-level fields override top-level defaults when present"
|
|
24982
25062
|
].join("\n");
|
|
24983
25063
|
return [topLevel, "", account, constraints].join("\n");
|
|
@@ -24987,7 +25067,7 @@ function serializeWhatsAppSchema() {
|
|
|
24987
25067
|
async function POST14(req) {
|
|
24988
25068
|
try {
|
|
24989
25069
|
const body = await req.json().catch(() => ({}));
|
|
24990
|
-
const { action, phone, slug, accountId } = body;
|
|
25070
|
+
const { action, phone, slug, fields, accountId } = body;
|
|
24991
25071
|
if (!action || typeof action !== "string") {
|
|
24992
25072
|
return Response.json({ ok: false, error: 'Missing required field "action".' }, { status: 400 });
|
|
24993
25073
|
}
|
|
@@ -25056,9 +25136,23 @@ async function POST14(req) {
|
|
|
25056
25136
|
return Response.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
|
|
25057
25137
|
}
|
|
25058
25138
|
}
|
|
25139
|
+
case "update-config": {
|
|
25140
|
+
if (!fields || typeof fields !== "object" || Array.isArray(fields)) {
|
|
25141
|
+
return Response.json({ ok: false, error: 'Missing required field "fields" (object with config field names and values).' }, { status: 400 });
|
|
25142
|
+
}
|
|
25143
|
+
const result = updateConfig(account.accountDir, fields);
|
|
25144
|
+
const fieldNames = Object.keys(fields);
|
|
25145
|
+
console.error(`[whatsapp:api] config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
|
|
25146
|
+
return Response.json(result, { status: result.ok ? 200 : 400 });
|
|
25147
|
+
}
|
|
25148
|
+
case "get-config": {
|
|
25149
|
+
const waConfig = getConfig(account.accountDir);
|
|
25150
|
+
console.error(`[whatsapp:api] config action=get-config`);
|
|
25151
|
+
return Response.json({ ok: true, config: waConfig });
|
|
25152
|
+
}
|
|
25059
25153
|
default:
|
|
25060
25154
|
return Response.json(
|
|
25061
|
-
{ ok: false, error: `Unknown action "${action}". Valid actions: add-admin-phone, remove-admin-phone, list-admin-phones, set-public-agent, get-public-agent, schema, list-groups.` },
|
|
25155
|
+
{ ok: false, error: `Unknown action "${action}". Valid actions: add-admin-phone, remove-admin-phone, list-admin-phones, set-public-agent, get-public-agent, update-config, get-config, schema, list-groups.` },
|
|
25062
25156
|
{ status: 400 }
|
|
25063
25157
|
);
|
|
25064
25158
|
}
|
|
@@ -127,14 +127,25 @@ server.tool("whatsapp-send", "Send a WhatsApp message to a phone number or group
|
|
|
127
127
|
}
|
|
128
128
|
});
|
|
129
129
|
// ─── whatsapp-config ──────────────────────────────────────────────────
|
|
130
|
-
server.tool("whatsapp-config", "Manage WhatsApp configuration. Actions: add-admin-phone (register a phone as admin — messages from it route to admin agent), remove-admin-phone (demote to public routing), list-admin-phones (show current admin phones), set-public-agent (set which agent handles WhatsApp messages from non-admin phones — requires the agent slug), get-public-agent (show the currently configured public agent), schema (return all config field definitions — names, types, defaults, descriptions, constraints), list-groups (return WhatsApp groups the account belongs to — names, JIDs, participant counts). Phone numbers must be E.164 format (e.g. +441234567890).", {
|
|
131
|
-
action: z.enum(["add-admin-phone", "remove-admin-phone", "list-admin-phones", "set-public-agent", "get-public-agent", "schema", "list-groups"]).describe("The config action to perform"),
|
|
130
|
+
server.tool("whatsapp-config", "Manage WhatsApp configuration. Actions: add-admin-phone (register a phone as admin — messages from it route to admin agent), remove-admin-phone (demote to public routing), list-admin-phones (show current admin phones), set-public-agent (set which agent handles WhatsApp messages from non-admin phones — requires the agent slug), get-public-agent (show the currently configured public agent), update-config (change any WhatsApp config field — pass field names and values in the fields parameter), get-config (return the full current WhatsApp config object), schema (return all config field definitions — names, types, defaults, descriptions, constraints), list-groups (return WhatsApp groups the account belongs to — names, JIDs, participant counts). Phone numbers must be E.164 format (e.g. +441234567890).", {
|
|
131
|
+
action: z.enum(["add-admin-phone", "remove-admin-phone", "list-admin-phones", "set-public-agent", "get-public-agent", "update-config", "get-config", "schema", "list-groups"]).describe("The config action to perform"),
|
|
132
132
|
phone: z.string().optional().describe("Phone number in E.164 format (required for add/remove admin phone)"),
|
|
133
133
|
slug: z.string().optional().describe("Agent slug (required for set-public-agent)"),
|
|
134
|
+
fields: z.string().optional().describe('JSON string of config fields to update (required for update-config, e.g. \'{"dmPolicy":"open","allowFrom":["*"]}\')'),
|
|
134
135
|
accountId: z.string().optional().describe('Account ID for list-groups (default: "default")'),
|
|
135
|
-
}, async ({ action, phone, slug, accountId }) => {
|
|
136
|
+
}, async ({ action, phone, slug, fields, accountId }) => {
|
|
136
137
|
try {
|
|
137
|
-
|
|
138
|
+
// Parse fields JSON string for update-config
|
|
139
|
+
let parsedFields;
|
|
140
|
+
if (action === "update-config" && fields) {
|
|
141
|
+
try {
|
|
142
|
+
parsedFields = JSON.parse(fields);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return textResult('Invalid JSON in "fields" parameter. Expected a JSON object, e.g. {"dmPolicy":"open","allowFrom":["*"]}', true);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const result = await callApi("/api/whatsapp/config", "POST", { action, phone, slug, fields: parsedFields, accountId });
|
|
138
149
|
if (!result.ok)
|
|
139
150
|
return textResult(result.error ?? "Config operation failed.", true);
|
|
140
151
|
if (action === "list-admin-phones") {
|
|
@@ -152,6 +163,12 @@ server.tool("whatsapp-config", "Manage WhatsApp configuration. Actions: add-admi
|
|
|
152
163
|
if (action === "schema") {
|
|
153
164
|
return textResult(result.text ?? "Schema unavailable.");
|
|
154
165
|
}
|
|
166
|
+
if (action === "get-config") {
|
|
167
|
+
const waConfig = result.config;
|
|
168
|
+
if (!waConfig)
|
|
169
|
+
return textResult("No WhatsApp config found. Connect WhatsApp first.");
|
|
170
|
+
return textResult(JSON.stringify(waConfig, null, 2));
|
|
171
|
+
}
|
|
155
172
|
if (action === "list-groups") {
|
|
156
173
|
const groups = result.groups ?? [];
|
|
157
174
|
if (groups.length === 0)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -30,7 +30,7 @@ The initial first-run wizard flow stays single-account. Multi-account is managed
|
|
|
30
30
|
```yaml
|
|
31
31
|
channels:
|
|
32
32
|
whatsapp:
|
|
33
|
-
|
|
33
|
+
adminPhones:
|
|
34
34
|
- "<admin-phone>" # E.164 phone the user messages from
|
|
35
35
|
accounts:
|
|
36
36
|
default:
|
|
@@ -42,7 +42,7 @@ Accounts are linked via QR in the Channels UI. Outbound routing (`src/web/outbou
|
|
|
42
42
|
|
|
43
43
|
| Target | Account | Reason |
|
|
44
44
|
|--------|---------|--------|
|
|
45
|
-
| Admin
|
|
45
|
+
| Admin phones | Account with admin binding | Admin's registered number |
|
|
46
46
|
| Self-chat | Account with selfChatMode | Admin talking to themselves |
|
|
47
47
|
| Group messages | Account that is a group member | Whichever is in the group |
|
|
48
48
|
| Customer DMs | Account with open DM policy | Dedicated agent number |
|
|
@@ -88,14 +88,14 @@ The platform enforces this at multiple levels:
|
|
|
88
88
|
| Term | Definition | Code |
|
|
89
89
|
|------|-----------|------|
|
|
90
90
|
| **Self phone** | The WhatsApp account's own phone number (the phone that scanned the QR code). Self-chat only. | `phonesMatch(senderPhone, selfPhone)` in `access-control.ts` |
|
|
91
|
-
| **Admin phones** | Phones
|
|
92
|
-
| **Public/unknown** | Any phone that is not self and not in `
|
|
91
|
+
| **Admin phones** | Phones listed in `adminPhones` in `account.json`. The user's personal phone is auto-registered on QR link; additional phones are added via `whatsapp-config action: "add-admin-phone"`. | `isAdminPhone()` in `access-control.ts` — iterates `adminPhones` |
|
|
92
|
+
| **Public/unknown** | Any phone that is not self and not in `adminPhones`. Subject to DM policy gating. | Everything else in `checkDmAccess()` |
|
|
93
93
|
|
|
94
|
-
**Critical distinction:** The *self phone* is the paired device's number (the WhatsApp account Maxy controls). The *admin phone* is the user's personal phone (the phone they message *from*). These are typically different numbers. If the user's personal phone is not in `
|
|
94
|
+
**Critical distinction:** The *self phone* is the paired device's number (the WhatsApp account Maxy controls). The *admin phone* is the user's personal phone (the phone they message *from*). These are typically different numbers. If the user's personal phone is not in `adminPhones`, their messages route as public — not admin.
|
|
95
95
|
|
|
96
96
|
**`dmPolicy` behaviour (per-account):**
|
|
97
97
|
|
|
98
|
-
| Policy | Self phone | Admin phones (in `
|
|
98
|
+
| Policy | Self phone | Admin phones (in `adminPhones`) | Public/unknown |
|
|
99
99
|
|--------|-----------|-------------------------------|----------------|
|
|
100
100
|
| `"open"` | Allowed | Allowed | Allowed |
|
|
101
101
|
| `"allowlist"` | Allowed | Allowed | Allowed only if in `allowFrom` (wildcard `"*"` counts) |
|
|
@@ -107,7 +107,7 @@ Admin phones always bypass DM policy — the `isAdminPhone()` check runs before
|
|
|
107
107
|
|
|
108
108
|
**Key file:** `access-control.ts` — `checkDmAccess()` and `checkGroupAccess()`.
|
|
109
109
|
|
|
110
|
-
**Config path:** Per-account at `whatsapp.accounts.{accountId}.dmPolicy` in `account.json`. Falls back to `whatsapp.dmPolicy`. Changes are conversational — the admin agent writes to config via
|
|
110
|
+
**Config path:** Per-account at `whatsapp.accounts.{accountId}.dmPolicy` in `account.json`. Falls back to `whatsapp.dmPolicy`. Changes are conversational — the admin agent writes to config via `whatsapp-config action: "update-config"`.
|
|
111
111
|
|
|
112
112
|
For config field definitions (field names, types, valid values), use `whatsapp-config action: schema`.
|
|
113
113
|
|
|
@@ -68,7 +68,7 @@ Admin phone registration and public agent selection persist across relinks — n
|
|
|
68
68
|
- **QR expired** — generate a new one with `whatsapp-login-start`
|
|
69
69
|
- **Phone not scanning** — ensure the phone has internet, WhatsApp is updated, and the user is in Linked Devices
|
|
70
70
|
- **Keeps disconnecting** — check `whatsapp-status` for error details. A 401 means re-link is needed. Repeated transient errors suggest network issues on the device.
|
|
71
|
-
- **Messages routing as public** — the user's personal phone is not in `
|
|
71
|
+
- **Messages routing as public** — the user's personal phone is not in `adminPhones`. Call `whatsapp-config` with `action: "add-admin-phone"` and the user's phone number to register it.
|
|
72
72
|
|
|
73
73
|
## Language
|
|
74
74
|
|
|
@@ -47,7 +47,7 @@ When the user wants to configure per-group settings:
|
|
|
47
47
|
|
|
48
48
|
## Writing changes
|
|
49
49
|
|
|
50
|
-
After the user submits, write changes via `
|
|
50
|
+
After the user submits, write changes via `whatsapp-config action: "update-config"` with the `fields` parameter containing a JSON object of the changed fields. Each field is merged individually — unchanged fields are left untouched. Confirm what changed — name each setting that was modified and its new value.
|
|
51
51
|
|
|
52
52
|
## Language
|
|
53
53
|
|