levix-bot 4.0.3 → 4.1.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/package.json +1 -1
- package/public/dashboard/assets/index-DngAcp-o.js +431 -0
- package/public/dashboard/assets/index-LvfJ8ybs.css +2 -0
- package/public/dashboard/index.html +2 -2
- package/scheduler.cjs +19 -0
- package/src/commands/gemini.cjs +18 -21
- package/src/commands/group/antilink.cjs +3 -1
- package/src/commands/group/media.cjs +3 -1
- package/src/commands/help.cjs +12 -6
- package/src/commands/mod.cjs +3 -1
- package/src/commands/poll.cjs +36 -20
- package/src/commands/stt.cjs +1 -0
- package/src/commands/tts.cjs +36 -29
- package/src/config/baileys.config.js +5 -1
- package/src/config/settings.cjs +10 -2
- package/src/core/connection.js +21 -2
- package/src/core/socket.js +31 -7
- package/src/db/store.cjs +24 -0
- package/src/handlers/command.handler.js +5 -3
- package/src/middleware/antispam.middleware.js +4 -1
- package/src/middleware/blacklist.middleware.js +4 -1
- package/src/middleware/permissions.middleware.js +38 -51
- package/src/routes/dashboard.api.esm.js +91 -10
- package/src/services/aiAgent.cjs +7 -2
- package/src/services/aiRouter.cjs +9 -2
- package/src/services/aiToolAuth.cjs +114 -0
- package/src/services/aiTools.cjs +490 -55
- package/src/services/modelRegistry.cjs +20 -5
- package/src/utils/groupMetadataCache.cjs +83 -0
- package/src/utils/memory.cjs +35 -20
- package/src/utils/permissionLevel.cjs +55 -0
- package/src/utils/sendBotMessage.esm.js +14 -0
- package/src/utils/storage-hub.cjs +21 -0
- package/public/dashboard/assets/index-BnJbz8iW.css +0 -2
- package/public/dashboard/assets/index-Dn8ZyzsW.js +0 -426
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// Who the AI agent is acting for, and whether that person may do what a tool
|
|
2
|
+
// is about to do.
|
|
3
|
+
//
|
|
4
|
+
// The agent reads other people's messages, quoted text and web pages, and any
|
|
5
|
+
// of them can ask it to call a tool. So authorization never comes from the
|
|
6
|
+
// model. The caller is resolved once from the Baileys message that started the
|
|
7
|
+
// run, with the command dispatcher's own resolveSender(), and frozen. Only a
|
|
8
|
+
// caller minted here counts: a lookalike object — built from tool arguments,
|
|
9
|
+
// spread from a context, typed into a test — is treated as nobody.
|
|
10
|
+
//
|
|
11
|
+
// A tool with a side effect declares `access`: the command it stands in for,
|
|
12
|
+
// and an extra role when that command checks one internally (global memory,
|
|
13
|
+
// the owner role). The check is that command's live permission level through
|
|
14
|
+
// the same evaluatePermissionLevel() the dispatcher runs, so a dashboard change
|
|
15
|
+
// to `!schedule` moves create_reminder with it.
|
|
16
|
+
|
|
17
|
+
const runtimeConfig = require("../config/runtime-config.cjs");
|
|
18
|
+
const { evaluatePermissionLevel } = require("../utils/permissionLevel.cjs");
|
|
19
|
+
|
|
20
|
+
/** Every caller this module has resolved from a real message. */
|
|
21
|
+
const minted = new WeakSet();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Roles some commands demand on top of their permission level, named the way
|
|
25
|
+
* those commands name them.
|
|
26
|
+
*/
|
|
27
|
+
const ROLES = Object.freeze({
|
|
28
|
+
// Owner only — `!perm` for the owner role.
|
|
29
|
+
owner: (caller) => caller.isOwner,
|
|
30
|
+
// Owner or bot admin, never a mere group admin — `!memory` for global scope.
|
|
31
|
+
botPrivileged: (caller) => caller.isOwner || caller.isBotAdmin,
|
|
32
|
+
// The above, or an admin of this WhatsApp group — `!memory` deleting chat memory.
|
|
33
|
+
chatModerator: (caller) => caller.isOwner || caller.isBotAdmin || caller.isGroupAdmin,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const ROLE_ERRORS = Object.freeze({
|
|
37
|
+
owner: "only the bot owner can do this",
|
|
38
|
+
botPrivileged: "only the bot owner or a bot admin can do this",
|
|
39
|
+
chatModerator: "only the bot owner, a bot admin, or an admin of this group can do this",
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the person behind `msg` — the one message that started this agent
|
|
44
|
+
* run — into a frozen caller the tools can trust.
|
|
45
|
+
*
|
|
46
|
+
* @param {object} options
|
|
47
|
+
* @param {object} options.msg - the Baileys message that invoked the AI
|
|
48
|
+
* @param {object} options.sock - Baileys socket (fromMe resolves to its account)
|
|
49
|
+
* @param {object} [options.groupMetadata] - for group-admin detection
|
|
50
|
+
* @param {string} [options.text] - what the sender typed (role tools bind targets to it)
|
|
51
|
+
*/
|
|
52
|
+
async function resolveCaller({ msg, sock, groupMetadata = null, text = "" }) {
|
|
53
|
+
if (!msg?.key?.remoteJid) throw new Error("resolveCaller needs the incoming message");
|
|
54
|
+
const { resolveSender } = await import("../middleware/permissions.middleware.js");
|
|
55
|
+
const sender = resolveSender(msg, groupMetadata, sock);
|
|
56
|
+
const contextInfo = msg.message?.extendedTextMessage?.contextInfo;
|
|
57
|
+
|
|
58
|
+
const caller = Object.freeze({
|
|
59
|
+
chatId: msg.key.remoteJid,
|
|
60
|
+
chatName: groupMetadata?.subject || null,
|
|
61
|
+
isGroup: sender.isGroup,
|
|
62
|
+
senderId: sender.senderId,
|
|
63
|
+
senderName: msg.pushName || null,
|
|
64
|
+
candidates: Object.freeze([...sender.candidates]),
|
|
65
|
+
isOwner: sender.isOwner,
|
|
66
|
+
isBotAdmin: sender.isBotAdmin,
|
|
67
|
+
isGroupAdmin: sender.isGroupAdmin,
|
|
68
|
+
isSenderAdmin: sender.isSenderAdmin,
|
|
69
|
+
// What the sender pointed at in their own message. A quoted message's
|
|
70
|
+
// author is only a *target* here — never who the agent acts for.
|
|
71
|
+
mentionedJids: Object.freeze([...(contextInfo?.mentionedJid || [])]),
|
|
72
|
+
quotedParticipant: contextInfo?.participant || null,
|
|
73
|
+
text: String(text || ""),
|
|
74
|
+
});
|
|
75
|
+
minted.add(caller);
|
|
76
|
+
return caller;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** True only for a caller resolveCaller() produced. */
|
|
80
|
+
function isTrustedCaller(caller) {
|
|
81
|
+
return Boolean(caller) && typeof caller === "object" && minted.has(caller);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* May `caller` use a tool whose access rule is `access`?
|
|
86
|
+
*
|
|
87
|
+
* @param {null|{command: string, role?: keyof ROLES}} access - null = open tool
|
|
88
|
+
* @param {object} caller
|
|
89
|
+
* @returns {{allowed: true} | {allowed: false, error: string}}
|
|
90
|
+
*/
|
|
91
|
+
function authorize(access, caller) {
|
|
92
|
+
if (!access) return { allowed: true };
|
|
93
|
+
if (!isTrustedCaller(caller)) {
|
|
94
|
+
return { allowed: false, error: "this action needs a verified sender, and there is none" };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const level = runtimeConfig.getPermission(access.command);
|
|
98
|
+
const { hasPermission } = evaluatePermissionLevel(level, caller);
|
|
99
|
+
if (!hasPermission) {
|
|
100
|
+
return {
|
|
101
|
+
allowed: false,
|
|
102
|
+
error: `not allowed: this does what !${access.command} does, and that is ${level} here`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (access.role) {
|
|
107
|
+
const check = ROLES[access.role];
|
|
108
|
+
if (!check) return { allowed: false, error: `unknown role requirement: ${access.role}` };
|
|
109
|
+
if (!check(caller)) return { allowed: false, error: ROLE_ERRORS[access.role] };
|
|
110
|
+
}
|
|
111
|
+
return { allowed: true };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = { resolveCaller, isTrustedCaller, authorize, ROLES };
|