@manybot/manybot 5.7.0 → 5.9.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.
Files changed (81) hide show
  1. package/README.md +28 -3
  2. package/dist/client/banner.js +10 -0
  3. package/dist/client/banner.test.js +31 -0
  4. package/dist/client/store.js +91 -6
  5. package/dist/client/store.test.js +170 -0
  6. package/dist/config.js +28 -44
  7. package/dist/config.test.js +26 -0
  8. package/dist/download/queue.js +13 -4
  9. package/dist/drivers/baileys/adapter.js +133 -15
  10. package/dist/drivers/baileys/api/contacts.integration.test.js +261 -0
  11. package/dist/drivers/baileys/api/groupMeta.test.js +235 -0
  12. package/dist/drivers/baileys/api/index.js +384 -62
  13. package/dist/drivers/baileys/index.js +92 -36
  14. package/dist/drivers/baileys/loginPrompt.js +0 -2
  15. package/dist/drivers/baileys/messageHandler.js +344 -4
  16. package/dist/drivers/baileys/messageHandler.test.js +445 -0
  17. package/dist/drivers/baileysAdapter.test.js +378 -0
  18. package/dist/drivers/jid.js +26 -0
  19. package/dist/drivers/jid.test.js +74 -0
  20. package/dist/drivers/types.js +5 -5
  21. package/dist/i18n/index.js +20 -24
  22. package/dist/kernel/activeDriverSend.js +21 -0
  23. package/dist/kernel/activeDriverSend.test.js +89 -0
  24. package/dist/kernel/alerts.js +3 -9
  25. package/dist/kernel/chatOverrides.js +46 -0
  26. package/dist/kernel/chatOverrides.test.js +59 -0
  27. package/dist/kernel/chatSession.js +65 -0
  28. package/dist/kernel/chatSession.test.js +46 -0
  29. package/dist/kernel/commandAccess.js +66 -0
  30. package/dist/kernel/commandAccess.test.js +74 -0
  31. package/dist/kernel/commandDeprecation.js +170 -0
  32. package/dist/kernel/commandDeprecation.test.js +114 -0
  33. package/dist/kernel/commandMenu.js +357 -0
  34. package/dist/kernel/commandMenu.test.js +363 -0
  35. package/dist/kernel/commandPermissions.js +171 -0
  36. package/dist/kernel/commandPermissions.test.js +227 -0
  37. package/dist/kernel/commandRegistry.js +583 -0
  38. package/dist/kernel/commandRegistry.test.js +158 -0
  39. package/dist/kernel/commandsConfig.js +949 -0
  40. package/dist/kernel/commandsConfig.test.js +482 -0
  41. package/dist/kernel/contactAutoSave.js +6 -6
  42. package/dist/kernel/contactAutoSave.test.js +87 -0
  43. package/dist/kernel/coreCommands.js +62 -0
  44. package/dist/kernel/driverManager.js +10 -6
  45. package/dist/kernel/driverManager.test.js +90 -0
  46. package/dist/kernel/integrationMode.js +88 -0
  47. package/dist/kernel/integrationMode.test.js +95 -0
  48. package/dist/kernel/loadIntegrationPlugin.test.js +67 -0
  49. package/dist/kernel/pluginApi.test.js +600 -0
  50. package/dist/kernel/pluginGuard.js +18 -13
  51. package/dist/kernel/pluginGuard.test.js +39 -0
  52. package/dist/kernel/pluginLoader.js +169 -11
  53. package/dist/kernel/pluginLoader.test.js +190 -0
  54. package/dist/kernel/runCommand.js +284 -0
  55. package/dist/kernel/runCommand.test.js +497 -0
  56. package/dist/kernel/sendFallbackGuard.js +19 -48
  57. package/dist/kernel/sendFallbackGuard.test.js +80 -0
  58. package/dist/kernel/sendGuard.js +38 -42
  59. package/dist/kernel/sendGuard.test.js +102 -0
  60. package/dist/kernel/settingsDb.js +19 -5
  61. package/dist/kernel/statusServer.js +9 -2
  62. package/dist/kernel/statusServer.test.js +70 -0
  63. package/dist/kernel/testConfig.js +192 -0
  64. package/dist/kernel/testConfig.test.js +181 -0
  65. package/dist/kernel/updateCheck.js +33 -10
  66. package/dist/locales/en.json +77 -13
  67. package/dist/locales/es.json +77 -13
  68. package/dist/locales/pt.json +77 -13
  69. package/dist/logger/logger.js +23 -3
  70. package/dist/logger/logger.test.js +45 -0
  71. package/dist/main.js +5 -76
  72. package/dist/plugins/__manybot_integration__/index.js +184 -0
  73. package/dist/plugins/__manybot_integration__/index.test.js +218 -0
  74. package/dist/utils/phoneNumber.js +83 -0
  75. package/dist/utils/phoneNumber.test.js +53 -0
  76. package/package.json +76 -18
  77. package/dist/drivers/whatsmeow/client.js +0 -252
  78. package/dist/drivers/whatsmeow/index.js +0 -79
  79. package/dist/drivers/whatsmeow/installer.js +0 -86
  80. package/dist/drivers/whatsmeow/supervisor.js +0 -328
  81. package/dist/drivers/whatsmeow/whatsmeow.proto +0 -64
@@ -0,0 +1,227 @@
1
+ import test, { describe } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { checkPermission, clearCooldowns, matchId, matchesAny } from "./commandPermissions.js";
4
+ import { resolvePermissions } from "./commandRegistry.js";
5
+ function senderOf(pn) {
6
+ return { lid: null, pn };
7
+ }
8
+ function createMockContext(overrides) {
9
+ return {
10
+ isGroup: false,
11
+ chatId: "123456789@c.us",
12
+ sender: senderOf("5511999999999@c.us"),
13
+ isSenderAdmin: async () => false,
14
+ isBotAdmin: async () => false,
15
+ ...overrides,
16
+ };
17
+ }
18
+ function createMockEntry(specPerms, specMsgs, pluginPerms, defaultsPerms, defaultsMsgs) {
19
+ const permissions = resolvePermissions(specPerms, specMsgs, pluginPerms, defaultsPerms, defaultsMsgs);
20
+ return {
21
+ id: "test::command",
22
+ cmd: "test",
23
+ aliases: [],
24
+ desc: "Test command",
25
+ category: null,
26
+ group: null,
27
+ manual: null,
28
+ source: "plugin",
29
+ pluginName: "testPlugin",
30
+ function: null,
31
+ functions: [],
32
+ loading: null,
33
+ handler: async () => "ok",
34
+ text: null,
35
+ permissions,
36
+ arguments: [],
37
+ subcommands: {},
38
+ categoryHiddenInScope: null,
39
+ hiddenOutsideScope: null,
40
+ };
41
+ }
42
+ describe("commandPermissions", () => {
43
+ test("matching utilities (matchId, matchesAny)", () => {
44
+ assert.equal(matchId("5511999999999@c.us", "5511999999999"), true);
45
+ assert.equal(matchId("5511999999999@s.whatsapp.net", "5511999999999@c.us"), true);
46
+ assert.equal(matchId("5511999999999", "5511888888888"), false);
47
+ assert.equal(matchesAny("5511999999999@c.us", ["5511888888888", "5511999999999"]), true);
48
+ assert.equal(matchesAny("5511777777777@c.us", ["5511888888888", "5511999999999"]), false);
49
+ });
50
+ test("permission resolution hierarchy (yaml > plugin > defaults)", () => {
51
+ const entry = createMockEntry({ admin: true, cooldownSeconds: 10 }, { senderNotAdmin: "Admin only!" }, { admin: false, botAdmin: true }, { cooldownSeconds: 60 }, { senderNotAdmin: "Default admin msg", botNotAdmin: "Default bot admin msg" });
52
+ assert.equal(entry.permissions.admin, true); // yaml overrides plugin
53
+ assert.equal(entry.permissions.botAdmin, true); // from plugin
54
+ assert.equal(entry.permissions.cooldownSeconds, 10); // yaml overrides global default
55
+ assert.equal(entry.permissions.messages.senderNotAdmin, "Admin only!"); // yaml msg overrides default
56
+ assert.equal(entry.permissions.messages.botNotAdmin, "Default bot admin msg"); // fallback to global default msg
57
+ });
58
+ test("owner permission check", async () => {
59
+ const ownerEntry = createMockEntry({ owner: true }, { ownerOnly: "Only owner allowed" });
60
+ const nonOwnerCtx = createMockContext({ sender: senderOf("5511888888888@c.us") });
61
+ // When OWNER_NUMBER is not set or sender does not match
62
+ const res = await checkPermission(ownerEntry, nonOwnerCtx);
63
+ assert.equal(res.allowed, false);
64
+ if (!res.allowed)
65
+ assert.equal(res.message, "Only owner allowed");
66
+ });
67
+ test("scope permission check", async () => {
68
+ const groupEntry = createMockEntry({ scope: "group" }, { wrongScope: "Group only" });
69
+ const dmEntry = createMockEntry({ scope: "dm" }, { wrongScope: "DM only" });
70
+ const dmCtx = createMockContext({ isGroup: false });
71
+ const groupCtx = createMockContext({ isGroup: true, chatId: "12036301234567890@g.us" });
72
+ const res1 = await checkPermission(groupEntry, dmCtx);
73
+ assert.equal(res1.allowed, false);
74
+ if (!res1.allowed)
75
+ assert.equal(res1.message, "Group only");
76
+ const res2 = await checkPermission(groupEntry, groupCtx);
77
+ assert.equal(res2.allowed, true);
78
+ const res3 = await checkPermission(dmEntry, groupCtx);
79
+ assert.equal(res3.allowed, false);
80
+ if (!res3.allowed)
81
+ assert.equal(res3.message, "DM only");
82
+ const res4 = await checkPermission(dmEntry, dmCtx);
83
+ assert.equal(res4.allowed, true);
84
+ });
85
+ test("dono permission check (specific-owner JID, independent of OWNER_NUMBER)", async () => {
86
+ const donoEntry = createMockEntry({ dono: "5511999999999@c.us" }, { donoOnly: "Only the dono can use this" });
87
+ const nonDonoCtx = createMockContext({ sender: senderOf("5511888888888@c.us") });
88
+ const res1 = await checkPermission(donoEntry, nonDonoCtx);
89
+ assert.equal(res1.allowed, false);
90
+ if (!res1.allowed)
91
+ assert.equal(res1.message, "Only the dono can use this");
92
+ const donoCtx = createMockContext({ sender: senderOf("5511999999999@c.us") });
93
+ const res2 = await checkPermission(donoEntry, donoCtx);
94
+ assert.equal(res2.allowed, true);
95
+ });
96
+ test("dono check accepts either LID or PN form of the sender", async () => {
97
+ const donoEntry = createMockEntry({ dono: "5511999999999" });
98
+ const lidOnlyCtx = createMockContext({ sender: { lid: "5511999999999@lid", pn: null } });
99
+ assert.equal((await checkPermission(donoEntry, lidOnlyCtx)).allowed, true);
100
+ });
101
+ test("allowedChats check (closed list of chats the command may run in)", async () => {
102
+ const entry = createMockEntry({ allowedChats: ["120363111111111111@g.us"] }, { allowedChats: "This command doesn't run here" });
103
+ const allowedCtx = createMockContext({ isGroup: true, chatId: "120363111111111111@g.us" });
104
+ const disallowedCtx = createMockContext({ isGroup: true, chatId: "120363999999999999@g.us" });
105
+ const resAllowed = await checkPermission(entry, allowedCtx);
106
+ assert.equal(resAllowed.allowed, true);
107
+ const resDisallowed = await checkPermission(entry, disallowedCtx);
108
+ assert.equal(resDisallowed.allowed, false);
109
+ if (!resDisallowed.allowed)
110
+ assert.equal(resDisallowed.message, "This command doesn't run here");
111
+ });
112
+ test("allowedChats check is skipped entirely when the list is empty/unset", async () => {
113
+ const entry = createMockEntry({ allowedChats: [] });
114
+ const anyChatCtx = createMockContext({ isGroup: true, chatId: "120363000000000000@g.us" });
115
+ assert.equal((await checkPermission(entry, anyChatCtx)).allowed, true);
116
+ });
117
+ test("blacklist check", async () => {
118
+ const entry = createMockEntry({
119
+ blacklist: {
120
+ groups: ["120363999999999999@g.us"],
121
+ users: ["5511888888888@c.us"],
122
+ },
123
+ });
124
+ const blacklistedUserCtx = createMockContext({ sender: senderOf("5511888888888@c.us") });
125
+ const blacklistedGroupCtx = createMockContext({ isGroup: true, chatId: "120363999999999999@g.us" });
126
+ const allowedCtx = createMockContext({ sender: senderOf("5511999999999@c.us") });
127
+ assert.equal((await checkPermission(entry, blacklistedUserCtx)).allowed, false);
128
+ assert.equal((await checkPermission(entry, blacklistedGroupCtx)).allowed, false);
129
+ assert.equal((await checkPermission(entry, allowedCtx)).allowed, true);
130
+ });
131
+ test("whitelist check", async () => {
132
+ const entry = createMockEntry({
133
+ whitelist: {
134
+ groups: ["120363111111111111@g.us"],
135
+ users: ["5511999999999@c.us"],
136
+ },
137
+ });
138
+ const whitelistedUserCtx = createMockContext({ sender: senderOf("5511999999999@c.us") });
139
+ const unwhitelistedUserCtx = createMockContext({ sender: senderOf("5511888888888@c.us") });
140
+ assert.equal((await checkPermission(entry, whitelistedUserCtx)).allowed, true);
141
+ assert.equal((await checkPermission(entry, unwhitelistedUserCtx)).allowed, false);
142
+ });
143
+ test("admin and botAdmin checks", async () => {
144
+ const adminEntry = createMockEntry({ admin: true }, { senderNotAdmin: "Need admin", wrongScope: "Group required" });
145
+ const botAdminEntry = createMockEntry({ botAdmin: true }, { botNotAdmin: "Need bot admin", wrongScope: "Group required" });
146
+ const nonAdminGroupCtx = createMockContext({
147
+ isGroup: true,
148
+ isSenderAdmin: async () => false,
149
+ isBotAdmin: async () => false,
150
+ });
151
+ const adminGroupCtx = createMockContext({
152
+ isGroup: true,
153
+ isSenderAdmin: async () => true,
154
+ isBotAdmin: async () => true,
155
+ });
156
+ const resAdmin = await checkPermission(adminEntry, nonAdminGroupCtx);
157
+ assert.equal(resAdmin.allowed, false);
158
+ if (!resAdmin.allowed)
159
+ assert.equal(resAdmin.message, "Need admin");
160
+ const resBotAdmin = await checkPermission(botAdminEntry, nonAdminGroupCtx);
161
+ assert.equal(resBotAdmin.allowed, false);
162
+ if (!resBotAdmin.allowed)
163
+ assert.equal(resBotAdmin.message, "Need bot admin");
164
+ assert.equal((await checkPermission(adminEntry, adminGroupCtx)).allowed, true);
165
+ // Outside a group there's no admin concept at all — this must be
166
+ // reported as wrongScope, not as "you/the bot aren't admin", since
167
+ // isSenderAdmin()/isBotAdmin() are never even called here.
168
+ const dmCtx = createMockContext({ isGroup: false });
169
+ const resAdminDm = await checkPermission(adminEntry, dmCtx);
170
+ assert.equal(resAdminDm.allowed, false);
171
+ if (!resAdminDm.allowed)
172
+ assert.equal(resAdminDm.message, "Group required");
173
+ const resBotAdminDm = await checkPermission(botAdminEntry, dmCtx);
174
+ assert.equal(resBotAdminDm.allowed, false);
175
+ if (!resBotAdminDm.allowed)
176
+ assert.equal(resBotAdminDm.message, "Group required");
177
+ assert.equal((await checkPermission(botAdminEntry, adminGroupCtx)).allowed, true);
178
+ });
179
+ test("cooldown check and state consumption", async () => {
180
+ clearCooldowns();
181
+ const entry = createMockEntry({ cooldownSeconds: 30 }, { cooldown: "Wait {{seconds}}s" });
182
+ const ctx = createMockContext({ sender: senderOf("5511999999999@c.us") });
183
+ // First invocation passes and sets cooldown
184
+ const res1 = await checkPermission(entry, ctx);
185
+ assert.equal(res1.allowed, true);
186
+ // Immediate second invocation fails
187
+ const res2 = await checkPermission(entry, ctx);
188
+ assert.equal(res2.allowed, false);
189
+ if (!res2.allowed) {
190
+ assert.match(res2.message ?? "", /Wait 30s/);
191
+ }
192
+ // Resetting cooldown allows next call
193
+ clearCooldowns();
194
+ const res3 = await checkPermission(entry, ctx);
195
+ assert.equal(res3.allowed, true);
196
+ });
197
+ test("evaluation order: dono is checked before allowedChats/blacklist", async () => {
198
+ const entry = createMockEntry({
199
+ dono: "5511999999999@c.us",
200
+ allowedChats: ["120363111111111111@g.us"],
201
+ blacklist: { groups: [], users: ["5511888888888@c.us"] },
202
+ }, { donoOnly: "dono message", allowedChats: "chats message", blacklist: "blacklist message" });
203
+ // Sender fails dono AND would also fail allowedChats/blacklist — dono's
204
+ // message must win since it's evaluated first.
205
+ const ctx = createMockContext({
206
+ sender: senderOf("5511888888888@c.us"),
207
+ isGroup: true,
208
+ chatId: "120363999999999999@g.us",
209
+ });
210
+ const res = await checkPermission(entry, ctx);
211
+ assert.equal(res.allowed, false);
212
+ if (!res.allowed)
213
+ assert.equal(res.message, "dono message");
214
+ });
215
+ test("cooldown is NOT consumed when an earlier check fails", async () => {
216
+ clearCooldowns();
217
+ const entry = createMockEntry({ scope: "group", cooldownSeconds: 30 }, { wrongScope: "Group only" });
218
+ const dmCtx = createMockContext({ isGroup: false });
219
+ // Scope check fails
220
+ const res1 = await checkPermission(entry, dmCtx);
221
+ assert.equal(res1.allowed, false);
222
+ // Now test in group context — first group call should pass because cooldown was never recorded
223
+ const groupCtx = createMockContext({ isGroup: true, chatId: "120363111@g.us" });
224
+ const res2 = await checkPermission(entry, groupCtx);
225
+ assert.equal(res2.allowed, true);
226
+ });
227
+ });