levix-bot 3.3.0-rc.2 → 4.0.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/README.md +13 -9
- package/app.cjs +49 -46
- package/package.json +9 -1
- package/public/dashboard/assets/index-BBBXWzM0.js +391 -0
- package/public/dashboard/assets/index-pYaivHON.css +1 -0
- package/public/dashboard/index.html +19 -0
- package/public/dashboard.css +391 -66
- package/public/dashboard.js +593 -42
- package/scheduler.cjs +9 -14
- package/src/auth/use-database-auth-state.js +2 -4
- package/src/bootstrap/core.js +2 -5
- package/src/bootstrap/panel.js +10 -5
- package/src/cli.js +3 -5
- package/src/commands/autoschedule.cjs +1 -4
- package/src/commands/blacklist.cjs +22 -20
- package/src/commands/block.cjs +2 -4
- package/src/commands/calc.cjs +9 -16
- package/src/commands/digit.cjs +97 -0
- package/src/commands/gemini.cjs +147 -161
- package/src/commands/group/add.cjs +7 -16
- package/src/commands/group/antiSpam.cjs +5 -11
- package/src/commands/group/antilink.cjs +34 -28
- package/src/commands/group/approveall.cjs +3 -11
- package/src/commands/group/clearwarns.cjs +2 -5
- package/src/commands/group/demote.cjs +3 -8
- package/src/commands/group/kick.cjs +12 -15
- package/src/commands/group/media.cjs +17 -24
- package/src/commands/group/members.cjs +1 -2
- package/src/commands/group/promote.cjs +4 -12
- package/src/commands/group/removeall.cjs +2 -5
- package/src/commands/group/setpp.cjs +1 -1
- package/src/commands/group/setrules.cjs +2 -8
- package/src/commands/group/setwarn.cjs +4 -2
- package/src/commands/group/tagadmins.cjs +2 -5
- package/src/commands/group/warn.cjs +26 -24
- package/src/commands/group/warns.cjs +1 -2
- package/src/commands/group/welcome.cjs +13 -17
- package/src/commands/group.cjs +7 -10
- package/src/commands/help.cjs +6 -12
- package/src/commands/lang.cjs +104 -0
- package/src/commands/listschedules.cjs +2 -6
- package/src/commands/loop.cjs +6 -13
- package/src/commands/memory.cjs +19 -32
- package/src/commands/mod.cjs +97 -35
- package/src/commands/perm.cjs +18 -56
- package/src/commands/ping.cjs +1 -5
- package/src/commands/poll.cjs +1 -1
- package/src/commands/rand.cjs +2 -5
- package/src/commands/schedule.cjs +4 -8
- package/src/commands/score.cjs +8 -2
- package/src/commands/setprefix.cjs +2 -2
- package/src/commands/shortlink.cjs +7 -15
- package/src/commands/shutdown.cjs +1 -1
- package/src/commands/sticker.cjs +222 -0
- package/src/commands/stt.cjs +136 -68
- package/src/commands/todo.cjs +9 -15
- package/src/commands/tts.cjs +10 -16
- package/src/commands/unblock.cjs +2 -6
- package/src/commands/weather.cjs +5 -12
- package/src/config/ai-persona.md +5 -3
- package/src/config/available-models.json +1156 -0
- package/src/config/baileys.config.js +2 -1
- package/src/config/brand.esm.js +1 -10
- package/src/config/constants.js +1 -1
- package/src/config/defaults.cjs +66 -66
- package/src/config/lock.cjs +2 -2
- package/src/config/modelCapabilities.cjs +172 -0
- package/src/config/runtime-config.cjs +8 -13
- package/src/config/secrets.cjs +18 -10
- package/src/config/settings.cjs +94 -10
- package/src/config/vault.cjs +107 -0
- package/src/core/connection.js +13 -12
- package/src/core/events.js +41 -23
- package/src/core/proxy.js +7 -7
- package/src/core/session.js +91 -48
- package/src/core/socket.js +12 -15
- package/src/db/db.cjs +14 -6
- package/src/db/store.cjs +221 -87
- package/src/domain/caddy.js +1 -1
- package/src/domain/command.js +40 -19
- package/src/domain/detect.js +1 -1
- package/src/domain/system.js +2 -5
- package/src/handlers/command.handler.js +22 -43
- package/src/handlers/group.handler.js +13 -13
- package/src/handlers/message.handler.js +11 -21
- package/src/index.js +8 -4
- package/src/middleware/antispam.middleware.js +46 -23
- package/src/middleware/blacklist.middleware.js +14 -9
- package/src/middleware/forward-tracking.middleware.js +3 -2
- package/src/middleware/permissions.middleware.js +7 -10
- package/src/panel/login-throttle.cjs +43 -0
- package/src/panel/session-auth.cjs +17 -0
- package/src/routes/dashboard.api.esm.js +394 -72
- package/src/services/aiAgent.cjs +136 -49
- package/src/services/aiProviders.cjs +193 -47
- package/src/services/aiRouter.cjs +137 -0
- package/src/services/aiTools.cjs +305 -128
- package/src/services/llmDiscovery.cjs +481 -0
- package/src/services/modelRegistry.cjs +412 -0
- package/src/utils/datetime.cjs +2 -2
- package/src/utils/lid-helper.esm.js +7 -7
- package/src/utils/logger.cjs +1 -1
- package/src/utils/markdownParser.cjs +159 -0
- package/src/utils/memory.cjs +43 -13
- package/src/utils/messageContent.cjs +40 -0
- package/src/utils/normalizeJid.esm.js +7 -7
- package/src/utils/numerals.cjs +75 -0
- package/src/utils/permissions.esm.js +10 -15
- package/src/utils/providerUrl.cjs +281 -0
- package/src/utils/recurrence.cjs +7 -11
- package/src/utils/sendBotMessage.esm.js +4 -14
- package/src/utils/statusMessage.cjs +5 -13
- package/src/utils/storage.cjs +10 -0
- package/src/utils/storage.esm.js +11 -0
- package/src/utils/textDecode.cjs +4 -9
- package/src/utils/thumbnail.cjs +8 -14
- package/src/utils/typing.esm.js +12 -12
- package/views/dashboard.ejs +134 -106
|
@@ -44,7 +44,7 @@ class StatusMessage {
|
|
|
44
44
|
this.sock,
|
|
45
45
|
this.jid,
|
|
46
46
|
{ text },
|
|
47
|
-
{ replyTo: this.replyTo, quote: this.quote, typing: false, delayMs: 0 }
|
|
47
|
+
{ replyTo: this.replyTo, quote: this.quote, typing: false, delayMs: 0 },
|
|
48
48
|
);
|
|
49
49
|
this.key = sent?.key || null;
|
|
50
50
|
this.lastEditAt = Date.now();
|
|
@@ -71,11 +71,7 @@ class StatusMessage {
|
|
|
71
71
|
// `quoted: null` on purpose: the command handler wraps sock.sendMessage
|
|
72
72
|
// and auto-quotes anything that doesn't set it, and an edit/delete is a
|
|
73
73
|
// protocolMessage — there is nowhere to hang a contextInfo.
|
|
74
|
-
await this.sock.sendMessage(
|
|
75
|
-
this.jid,
|
|
76
|
-
{ text: next, edit: this.key },
|
|
77
|
-
{ quoted: null }
|
|
78
|
-
);
|
|
74
|
+
await this.sock.sendMessage(this.jid, { text: next, edit: this.key }, { quoted: null });
|
|
79
75
|
this.text = next;
|
|
80
76
|
this.lastEditAt = Date.now();
|
|
81
77
|
return true;
|
|
@@ -91,7 +87,7 @@ class StatusMessage {
|
|
|
91
87
|
this.sock,
|
|
92
88
|
this.jid,
|
|
93
89
|
{ text },
|
|
94
|
-
{ replyTo: this.replyTo, quote: this.quote, typing: false, delayMs: 0 }
|
|
90
|
+
{ replyTo: this.replyTo, quote: this.quote, typing: false, delayMs: 0 },
|
|
95
91
|
);
|
|
96
92
|
this.key = sent?.key || null;
|
|
97
93
|
this.text = text;
|
|
@@ -115,7 +111,7 @@ class StatusMessage {
|
|
|
115
111
|
const name = err?.name || "Error";
|
|
116
112
|
const message = err?.message || String(err || "غير معروف");
|
|
117
113
|
return this.finish(
|
|
118
|
-
`❌ *${arabicLead}*\n\n*النوع:* \`${name}\`\n*التفاصيل:* ${message}`.slice(0, 1500)
|
|
114
|
+
`❌ *${arabicLead}*\n\n*النوع:* \`${name}\`\n*التفاصيل:* ${message}`.slice(0, 1500),
|
|
119
115
|
);
|
|
120
116
|
}
|
|
121
117
|
|
|
@@ -124,11 +120,7 @@ class StatusMessage {
|
|
|
124
120
|
this.closed = true;
|
|
125
121
|
if (!this.key) return false;
|
|
126
122
|
try {
|
|
127
|
-
await this.sock.sendMessage(
|
|
128
|
-
this.jid,
|
|
129
|
-
{ delete: this.key },
|
|
130
|
-
{ quoted: null }
|
|
131
|
-
);
|
|
123
|
+
await this.sock.sendMessage(this.jid, { delete: this.key }, { quoted: null });
|
|
132
124
|
this.key = null;
|
|
133
125
|
return true;
|
|
134
126
|
} catch (err) {
|
package/src/utils/storage.cjs
CHANGED
|
@@ -17,6 +17,13 @@ module.exports = {
|
|
|
17
17
|
getGroupSettings: store.getGroupSettings,
|
|
18
18
|
saveGroupSettings: store.saveGroupSettings,
|
|
19
19
|
getAllGroupSettings: store.getAllGroupSettings,
|
|
20
|
+
upsertGroupDirectory: store.upsertGroupDirectory,
|
|
21
|
+
getGroupDirectory: store.getGroupDirectory,
|
|
22
|
+
getAllGroupDirectory: store.getAllGroupDirectory,
|
|
23
|
+
clearWhatsAppDirectory: store.clearWhatsAppDirectory,
|
|
24
|
+
isPairedSession: store.isPairedSession,
|
|
25
|
+
resolveUserPhone: store.resolveUserPhone,
|
|
26
|
+
describePeer: store.describePeer,
|
|
20
27
|
// Warnings
|
|
21
28
|
getUserWarnings: store.getUserWarnings,
|
|
22
29
|
saveUserWarnings: store.saveUserWarnings,
|
|
@@ -77,5 +84,8 @@ module.exports = {
|
|
|
77
84
|
setScheduleStatus: store.setScheduleStatus,
|
|
78
85
|
setScheduleDelivery: store.setScheduleDelivery,
|
|
79
86
|
deleteSchedule: store.deleteSchedule,
|
|
87
|
+
pauseAllSchedules: store.pauseAllSchedules,
|
|
80
88
|
countSchedules: store.countSchedules,
|
|
89
|
+
// AI conversation history
|
|
90
|
+
deleteAllChatHistories: store.deleteAllChatHistories,
|
|
81
91
|
};
|
package/src/utils/storage.esm.js
CHANGED
|
@@ -16,6 +16,13 @@ export const deleteBotSetting = store.deleteBotSetting;
|
|
|
16
16
|
export const getGroupSettings = store.getGroupSettings;
|
|
17
17
|
export const saveGroupSettings = store.saveGroupSettings;
|
|
18
18
|
export const getAllGroupSettings = store.getAllGroupSettings;
|
|
19
|
+
export const upsertGroupDirectory = store.upsertGroupDirectory;
|
|
20
|
+
export const getGroupDirectory = store.getGroupDirectory;
|
|
21
|
+
export const getAllGroupDirectory = store.getAllGroupDirectory;
|
|
22
|
+
export const clearWhatsAppDirectory = store.clearWhatsAppDirectory;
|
|
23
|
+
export const isPairedSession = store.isPairedSession;
|
|
24
|
+
export const resolveUserPhone = store.resolveUserPhone;
|
|
25
|
+
export const describePeer = store.describePeer;
|
|
19
26
|
|
|
20
27
|
// ===== Warnings =====
|
|
21
28
|
export const getUserWarnings = store.getUserWarnings;
|
|
@@ -91,4 +98,8 @@ export const saveSchedule = store.saveSchedule;
|
|
|
91
98
|
export const setScheduleStatus = store.setScheduleStatus;
|
|
92
99
|
export const setScheduleDelivery = store.setScheduleDelivery;
|
|
93
100
|
export const deleteSchedule = store.deleteSchedule;
|
|
101
|
+
export const pauseAllSchedules = store.pauseAllSchedules;
|
|
94
102
|
export const countSchedules = store.countSchedules;
|
|
103
|
+
|
|
104
|
+
// ===== AI conversation history =====
|
|
105
|
+
export const deleteAllChatHistories = store.deleteAllChatHistories;
|
package/src/utils/textDecode.cjs
CHANGED
|
@@ -69,12 +69,8 @@ function decodeHtmlEntities(text) {
|
|
|
69
69
|
function decodeEscapes(text) {
|
|
70
70
|
if (!text || typeof text !== "string" || !text.includes("\\")) return text;
|
|
71
71
|
return text
|
|
72
|
-
.replace(/\\u([0-9a-f]{4})/gi, (match, hex) =>
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
.replace(/\\x([0-9a-f]{2})/gi, (match, hex) =>
|
|
76
|
-
String.fromCharCode(parseInt(hex, 16))
|
|
77
|
-
)
|
|
72
|
+
.replace(/\\u([0-9a-f]{4})/gi, (match, hex) => String.fromCharCode(parseInt(hex, 16)))
|
|
73
|
+
.replace(/\\x([0-9a-f]{2})/gi, (match, hex) => String.fromCharCode(parseInt(hex, 16)))
|
|
78
74
|
.replace(/\\n/g, "\n")
|
|
79
75
|
.replace(/\\t/g, "\t")
|
|
80
76
|
.replace(/\\r/g, "")
|
|
@@ -87,8 +83,7 @@ function decodeEscapes(text) {
|
|
|
87
83
|
// The tell-tale pair that appears when UTF-8 bytes are decoded as latin1: a
|
|
88
84
|
// lead byte (0xC2-0xDF) followed by a continuation byte that landed in the
|
|
89
85
|
// latin1 supplement or the cp1252 punctuation block.
|
|
90
|
-
const MOJIBAKE_HINT =
|
|
91
|
-
/[Â-ß][-¿–—‘-„†-•…‰‹›€™ŒœŠšŸŽžƒˆ˜]/;
|
|
86
|
+
const MOJIBAKE_HINT = /[Â-ß][-¿–—‘-„†-•…‰‹›€™ŒœŠšŸŽžƒˆ˜]/;
|
|
92
87
|
|
|
93
88
|
function textScore(text) {
|
|
94
89
|
let score = 0;
|
|
@@ -179,7 +174,7 @@ function stripHtml(html) {
|
|
|
179
174
|
.replace(/<!--[\s\S]*?-->/g, " ")
|
|
180
175
|
.replace(/<\/(p|div|li|tr|h[1-6]|br)>/gi, "\n")
|
|
181
176
|
.replace(/<br\s*\/?>/gi, "\n")
|
|
182
|
-
.replace(/<[^>]+>/g, " ")
|
|
177
|
+
.replace(/<[^>]+>/g, " "),
|
|
183
178
|
)
|
|
184
179
|
.replace(NOISE_SPACE, " ")
|
|
185
180
|
.replace(/\n{3,}/g, "\n\n")
|
package/src/utils/thumbnail.cjs
CHANGED
|
@@ -105,7 +105,7 @@ function warnMissingFfmpeg(reason) {
|
|
|
105
105
|
{ ffmpeg: ffmpegBin(), reason },
|
|
106
106
|
"[thumbnail] ffmpeg isn't runnable — video previews are off and images " +
|
|
107
107
|
"fall back to raw-JPEG previews. Reinstall `ffmpeg-static`, or point " +
|
|
108
|
-
"Settings -> Media -> ffmpeg path at a working binary."
|
|
108
|
+
"Settings -> Media -> ffmpeg path at a working binary.",
|
|
109
109
|
);
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -216,9 +216,7 @@ function parseProbe(stderr = "") {
|
|
|
216
216
|
hasAudio: /Stream #\d+:\d+[^\n]*?: Audio:/.test(stderr),
|
|
217
217
|
};
|
|
218
218
|
|
|
219
|
-
const dimensions = stderr.match(
|
|
220
|
-
/Stream #\d+:\d+[^\n]*?: Video:[^\n]*?[,\s](\d{2,5})x(\d{2,5})/
|
|
221
|
-
);
|
|
219
|
+
const dimensions = stderr.match(/Stream #\d+:\d+[^\n]*?: Video:[^\n]*?[,\s](\d{2,5})x(\d{2,5})/);
|
|
222
220
|
if (dimensions) {
|
|
223
221
|
info.width = Number(dimensions[1]) || 0;
|
|
224
222
|
info.height = Number(dimensions[2]) || 0;
|
|
@@ -226,10 +224,7 @@ function parseProbe(stderr = "") {
|
|
|
226
224
|
|
|
227
225
|
const duration = stderr.match(/Duration:\s*(\d+):(\d{2}):(\d{2}(?:\.\d+)?)/);
|
|
228
226
|
if (duration) {
|
|
229
|
-
const seconds =
|
|
230
|
-
Number(duration[1]) * 3600 +
|
|
231
|
-
Number(duration[2]) * 60 +
|
|
232
|
-
Number(duration[3]);
|
|
227
|
+
const seconds = Number(duration[1]) * 3600 + Number(duration[2]) * 60 + Number(duration[3]);
|
|
233
228
|
// Round, but never down to 0 — a sub-second clip still has a duration.
|
|
234
229
|
if (Number.isFinite(seconds) && seconds > 0) {
|
|
235
230
|
info.seconds = Math.max(1, Math.round(seconds));
|
|
@@ -270,7 +265,7 @@ async function generateThumbnail(filePath, kind) {
|
|
|
270
265
|
|
|
271
266
|
// For video, seek a second in — frame 0 of a lot of clips is a black fade.
|
|
272
267
|
let result = await runFfmpeg(
|
|
273
|
-
kind === "video" ? ["-y", "-ss", "1", ...encodeArgs] : ["-y", ...encodeArgs]
|
|
268
|
+
kind === "video" ? ["-y", "-ss", "1", ...encodeArgs] : ["-y", ...encodeArgs],
|
|
274
269
|
);
|
|
275
270
|
|
|
276
271
|
if (kind === "video" && (!result.ok || !sizeOf(target))) {
|
|
@@ -322,8 +317,7 @@ function jpegMeta(buffer) {
|
|
|
322
317
|
if (marker === 0xd9 || marker === 0xda) break; // end of header / start of scan
|
|
323
318
|
const length = buffer.readUInt16BE(offset + 2);
|
|
324
319
|
// SOF0..SOF15, minus the DHT/JPG/DAC markers that share the range
|
|
325
|
-
const isFrameHeader =
|
|
326
|
-
marker >= 0xc0 && marker <= 0xcf && ![0xc4, 0xc8, 0xcc].includes(marker);
|
|
320
|
+
const isFrameHeader = marker >= 0xc0 && marker <= 0xcf && ![0xc4, 0xc8, 0xcc].includes(marker);
|
|
327
321
|
if (isFrameHeader) {
|
|
328
322
|
return {
|
|
329
323
|
format: "jpeg",
|
|
@@ -478,7 +472,7 @@ async function resolveSource(media, kind) {
|
|
|
478
472
|
} catch (err) {
|
|
479
473
|
logger.debug(
|
|
480
474
|
{ err: err?.message, url },
|
|
481
|
-
"[thumbnail] remote source could not be fetched for a preview"
|
|
475
|
+
"[thumbnail] remote source could not be fetched for a preview",
|
|
482
476
|
);
|
|
483
477
|
return null;
|
|
484
478
|
}
|
|
@@ -549,7 +543,7 @@ async function withMediaThumbnail(content) {
|
|
|
549
543
|
thumbnail = fs.readFileSync(source.file);
|
|
550
544
|
logger.debug(
|
|
551
545
|
{ bytes: thumbnail.length },
|
|
552
|
-
"[thumbnail] reusing the image itself as its preview"
|
|
546
|
+
"[thumbnail] reusing the image itself as its preview",
|
|
553
547
|
);
|
|
554
548
|
}
|
|
555
549
|
}
|
|
@@ -585,7 +579,7 @@ async function withMediaThumbnail(content) {
|
|
|
585
579
|
} catch (err) {
|
|
586
580
|
logger.debug(
|
|
587
581
|
{ err: err?.message, kind, field },
|
|
588
|
-
"[thumbnail] could not build a preview — sending without one"
|
|
582
|
+
"[thumbnail] could not build a preview — sending without one",
|
|
589
583
|
);
|
|
590
584
|
return content;
|
|
591
585
|
} finally {
|
package/src/utils/typing.esm.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Typing simulation utility
|
|
2
|
-
import { createRequire } from
|
|
2
|
+
import { createRequire } from "module";
|
|
3
3
|
|
|
4
4
|
const require = createRequire(import.meta.url);
|
|
5
|
-
const logger = require(
|
|
5
|
+
const logger = require("./logger.cjs");
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Simulates typing with a random delay between 300-500ms
|
|
@@ -15,16 +15,16 @@ const logger = require('./logger.cjs');
|
|
|
15
15
|
export async function simulateTyping(sock, jid, minDelay = 300, maxDelay = 500) {
|
|
16
16
|
try {
|
|
17
17
|
// Send typing indicator
|
|
18
|
-
await sock.sendPresenceUpdate(
|
|
18
|
+
await sock.sendPresenceUpdate("composing", jid);
|
|
19
19
|
|
|
20
20
|
// Random delay between min and max
|
|
21
21
|
const delay = Math.floor(Math.random() * (maxDelay - minDelay + 1)) + minDelay;
|
|
22
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
22
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
23
23
|
|
|
24
24
|
// Stop typing indicator
|
|
25
|
-
await sock.sendPresenceUpdate(
|
|
25
|
+
await sock.sendPresenceUpdate("paused", jid);
|
|
26
26
|
} catch (error) {
|
|
27
|
-
logger.error({ err: error },
|
|
27
|
+
logger.error({ err: error }, "[Typing] Error simulating typing");
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -45,7 +45,7 @@ export async function sendMessageWithTyping(sock, jid, message, withTyping = tru
|
|
|
45
45
|
const sent = await sock.sendMessage(jid, message);
|
|
46
46
|
return sent;
|
|
47
47
|
} catch (error) {
|
|
48
|
-
logger.error({ err: error },
|
|
48
|
+
logger.error({ err: error }, "[Typing] Error sending message with typing");
|
|
49
49
|
throw error;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -74,22 +74,22 @@ export function calculateTypingDelay(text, charsPerSecond = 40) {
|
|
|
74
74
|
*/
|
|
75
75
|
export async function sendMessageWithRealisticTyping(sock, jid, message) {
|
|
76
76
|
try {
|
|
77
|
-
const text = message.text || message.caption ||
|
|
77
|
+
const text = message.text || message.caption || "";
|
|
78
78
|
const delay = calculateTypingDelay(text);
|
|
79
79
|
|
|
80
80
|
// Send typing indicator
|
|
81
|
-
await sock.sendPresenceUpdate(
|
|
81
|
+
await sock.sendPresenceUpdate("composing", jid);
|
|
82
82
|
|
|
83
83
|
// Wait for calculated delay
|
|
84
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
84
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
85
85
|
|
|
86
86
|
// Stop typing and send message
|
|
87
|
-
await sock.sendPresenceUpdate(
|
|
87
|
+
await sock.sendPresenceUpdate("paused", jid);
|
|
88
88
|
const sent = await sock.sendMessage(jid, message);
|
|
89
89
|
|
|
90
90
|
return sent;
|
|
91
91
|
} catch (error) {
|
|
92
|
-
logger.error({ err: error },
|
|
92
|
+
logger.error({ err: error }, "[Typing] Error sending message with realistic typing");
|
|
93
93
|
throw error;
|
|
94
94
|
}
|
|
95
95
|
}
|
package/views/dashboard.ejs
CHANGED
|
@@ -29,108 +29,80 @@
|
|
|
29
29
|
|
|
30
30
|
<nav class="nav" aria-label="Primary navigation">
|
|
31
31
|
<a href="#overview" data-view="overview">
|
|
32
|
-
<svg
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
>
|
|
38
|
-
<rect x="3" y="3" width="7" height="7" rx="1" />
|
|
39
|
-
<rect x="14" y="3" width="7" height="7" rx="1" />
|
|
40
|
-
<rect x="3" y="14" width="7" height="7" rx="1" />
|
|
41
|
-
<rect x="14" y="14" width="7" height="7" rx="1" />
|
|
32
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
33
|
+
<rect width="7" height="7" x="3" y="3" rx="1" />
|
|
34
|
+
<rect width="7" height="7" x="14" y="3" rx="1" />
|
|
35
|
+
<rect width="7" height="7" x="14" y="14" rx="1" />
|
|
36
|
+
<rect width="7" height="7" x="3" y="14" rx="1" />
|
|
42
37
|
</svg>
|
|
43
|
-
Overview
|
|
38
|
+
<span data-i18n="overview">Overview</span>
|
|
44
39
|
</a>
|
|
45
40
|
<a href="#connection" data-view="connection">
|
|
46
|
-
<svg
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<path d="M12 20a8 8 0 1 0-7-4l-1 4 4-1a8 8 0 0 0 4 1z" />
|
|
41
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
42
|
+
<path d="M4.9 19.1C1 15.2 1 8.8 4.9 4.9" />
|
|
43
|
+
<path d="M7.8 16.2c-2.3-2.3-2.3-6.1 0-8.5" />
|
|
44
|
+
<circle cx="12" cy="12" r="2" />
|
|
45
|
+
<path d="M16.2 7.8c2.3 2.3 2.3 6.1 0 8.5" />
|
|
46
|
+
<path d="M19.1 4.9C23 8.8 23 15.1 19.1 19" />
|
|
53
47
|
</svg>
|
|
54
|
-
Connection
|
|
48
|
+
<span data-i18n="connection">Connection</span>
|
|
55
49
|
</a>
|
|
56
50
|
|
|
57
|
-
<div class="nav-label">Control</div>
|
|
51
|
+
<div class="nav-label" data-i18n="control">Control</div>
|
|
58
52
|
<a href="#commands" data-view="commands">
|
|
59
|
-
<svg
|
|
60
|
-
viewBox="0 0 24 24"
|
|
61
|
-
fill="none"
|
|
62
|
-
stroke="currentColor"
|
|
63
|
-
stroke-width="2"
|
|
64
|
-
>
|
|
53
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
65
54
|
<polyline points="4 17 10 11 4 5" />
|
|
66
55
|
<line x1="12" y1="19" x2="20" y2="19" />
|
|
67
56
|
</svg>
|
|
68
|
-
Commands
|
|
57
|
+
<span data-i18n="commands">Commands</span>
|
|
69
58
|
</a>
|
|
70
59
|
<a href="#ai" data-view="ai">
|
|
71
|
-
<svg
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
<path d="
|
|
78
|
-
<circle cx="12" cy="12" r="4" />
|
|
60
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
61
|
+
<path d="M12 8V4H8" />
|
|
62
|
+
<rect width="16" height="12" x="4" y="8" rx="2" />
|
|
63
|
+
<path d="M2 14h2" />
|
|
64
|
+
<path d="M20 14h2" />
|
|
65
|
+
<path d="M15 13v2" />
|
|
66
|
+
<path d="M9 13v2" />
|
|
79
67
|
</svg>
|
|
80
|
-
AI & memory
|
|
68
|
+
<span data-i18n="ai">AI & memory</span>
|
|
81
69
|
</a>
|
|
82
70
|
<a href="#groups" data-view="groups">
|
|
83
|
-
<svg
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
>
|
|
89
|
-
<circle cx="9" cy="8" r="3" />
|
|
90
|
-
<circle cx="17" cy="9" r="2.5" />
|
|
91
|
-
<path d="M3 19a6 6 0 0 1 12 0M15 19a5 5 0 0 1 6-4" />
|
|
71
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
72
|
+
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
|
|
73
|
+
<circle cx="9" cy="7" r="4" />
|
|
74
|
+
<path d="M22 21v-2a4 4 0 0 0-3-3.87" />
|
|
75
|
+
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
|
|
92
76
|
</svg>
|
|
93
|
-
Groups
|
|
77
|
+
<span data-i18n="groups">Groups</span>
|
|
94
78
|
</a>
|
|
95
79
|
<a href="#roles" data-view="roles">
|
|
96
|
-
<svg
|
|
97
|
-
|
|
98
|
-
fill="none"
|
|
99
|
-
stroke="currentColor"
|
|
100
|
-
stroke-width="2"
|
|
101
|
-
>
|
|
102
|
-
<path d="M12 3l7 4v5c0 4-3 7-7 9-4-2-7-5-7-9V7z" />
|
|
80
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
81
|
+
<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" />
|
|
103
82
|
</svg>
|
|
104
|
-
Roles
|
|
83
|
+
<span data-i18n="roles">Roles</span>
|
|
105
84
|
</a>
|
|
106
85
|
|
|
107
|
-
<div class="nav-label">Data</div>
|
|
86
|
+
<div class="nav-label" data-i18n="data_label">Data</div>
|
|
108
87
|
<a href="#data" data-view="data">
|
|
109
|
-
<svg
|
|
110
|
-
|
|
111
|
-
fill="none"
|
|
112
|
-
stroke="currentColor"
|
|
113
|
-
stroke-width="2"
|
|
114
|
-
>
|
|
115
|
-
<ellipse cx="12" cy="6" rx="8" ry="3" />
|
|
116
|
-
<path d="M4 6v12c0 1.7 3.6 3 8 3s8-1.3 8-3V6" />
|
|
117
|
-
<path d="M4 12c0 1.7 3.6 3 8 3s8-1.3 8-3" />
|
|
88
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
89
|
+
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
|
|
118
90
|
</svg>
|
|
119
|
-
Tables
|
|
91
|
+
<span data-i18n="data">Tables</span>
|
|
120
92
|
</a>
|
|
121
93
|
<a href="#settings" data-view="settings">
|
|
122
|
-
<svg
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
<
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
/>
|
|
94
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
95
|
+
<line x1="4" x2="4" y1="21" y2="14" />
|
|
96
|
+
<line x1="4" x2="4" y1="10" y2="3" />
|
|
97
|
+
<line x1="12" x2="12" y1="21" y2="12" />
|
|
98
|
+
<line x1="12" x2="12" y1="8" y2="3" />
|
|
99
|
+
<line x1="20" x2="20" y1="21" y2="16" />
|
|
100
|
+
<line x1="20" x2="20" y1="12" y2="3" />
|
|
101
|
+
<line x1="1" x2="7" y1="14" y2="14" />
|
|
102
|
+
<line x1="9" x2="15" y1="8" y2="8" />
|
|
103
|
+
<line x1="17" x2="23" y1="16" y2="16" />
|
|
132
104
|
</svg>
|
|
133
|
-
Settings
|
|
105
|
+
<span data-i18n="settings">Settings</span>
|
|
134
106
|
</a>
|
|
135
107
|
</nav>
|
|
136
108
|
|
|
@@ -218,6 +190,20 @@
|
|
|
218
190
|
/>
|
|
219
191
|
</svg>
|
|
220
192
|
</button>
|
|
193
|
+
<button
|
|
194
|
+
class="btn btn-icon lang-btn"
|
|
195
|
+
id="lang-btn"
|
|
196
|
+
type="button"
|
|
197
|
+
aria-label="Switch language"
|
|
198
|
+
title="Switch language / تغيير اللغة"
|
|
199
|
+
>
|
|
200
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
201
|
+
<circle cx="12" cy="12" r="10" />
|
|
202
|
+
<line x1="2" y1="12" x2="22" y2="12" />
|
|
203
|
+
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
|
|
204
|
+
</svg>
|
|
205
|
+
<span class="lang-tag" id="lang-tag">AR</span>
|
|
206
|
+
</button>
|
|
221
207
|
</div>
|
|
222
208
|
<form action="/logout" method="post" class="logout-form">
|
|
223
209
|
<button type="submit" class="btn btn-sm logout-btn">
|
|
@@ -693,45 +679,87 @@
|
|
|
693
679
|
<!-- Settings ---------------------------------------------------->
|
|
694
680
|
<section class="view" id="view-settings">
|
|
695
681
|
<div class="section-head">
|
|
696
|
-
<span class="eyebrow">Workspace</span>
|
|
697
|
-
<h2>Settings</h2>
|
|
698
|
-
<p>
|
|
682
|
+
<span class="eyebrow" data-i18n="workspace">Workspace</span>
|
|
683
|
+
<h2 data-i18n="settings">Settings</h2>
|
|
684
|
+
<p data-i18n="settings_sub">
|
|
699
685
|
This page is the only place the bot is configured — there is no
|
|
700
686
|
config file. Clear a field to go back to its default.
|
|
701
687
|
</p>
|
|
702
688
|
</div>
|
|
703
689
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
<
|
|
707
|
-
<
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
<
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
<
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
690
|
+
<!-- Settings Category Navigation -->
|
|
691
|
+
<div class="settings-nav-tabs" role="tablist" id="settings-nav-tabs">
|
|
692
|
+
<button class="settings-tab active" data-tab="ai" type="button" role="tab" aria-selected="true">
|
|
693
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg>
|
|
694
|
+
<span data-i18n="settings_tab_ai">AI Assistant</span>
|
|
695
|
+
</button>
|
|
696
|
+
<button class="settings-tab" data-tab="general" type="button" role="tab" aria-selected="false">
|
|
697
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="4" x2="4" y1="21" y2="14"/><line x1="4" x2="4" y1="10" y2="3"/><line x1="12" x2="12" y1="21" y2="12"/><line x1="12" x2="12" y1="8" y2="3"/><line x1="20" x2="20" y1="21" y2="16"/><line x1="20" x2="20" y1="12" y2="3"/><line x1="1" x2="7" y1="14" y2="14"/><line x1="9" x2="15" y1="8" y2="8"/><line x1="17" x2="23" y1="16" y2="16"/></svg>
|
|
698
|
+
<span data-i18n="settings_tab_general">General</span>
|
|
699
|
+
</button>
|
|
700
|
+
<button class="settings-tab" data-tab="security" type="button" role="tab" aria-selected="false">
|
|
701
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/></svg>
|
|
702
|
+
<span data-i18n="settings_tab_security">Security</span>
|
|
703
|
+
</button>
|
|
704
|
+
<button class="settings-tab" data-tab="whatsapp" type="button" role="tab" aria-selected="false">
|
|
705
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>
|
|
706
|
+
<span data-i18n="settings_tab_whatsapp">WhatsApp Proxy</span>
|
|
707
|
+
</button>
|
|
708
|
+
<button class="settings-tab" data-tab="media" type="button" role="tab" aria-selected="false">
|
|
709
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="22" x2="2" y1="12" y2="12"/><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/><line x1="6" x2="6.01" y1="16" y2="16"/><line x1="10" x2="10.01" y1="16" y2="16"/></svg>
|
|
710
|
+
<span data-i18n="settings_tab_media">Media & Storage</span>
|
|
711
|
+
</button>
|
|
712
|
+
</div>
|
|
713
|
+
|
|
714
|
+
<div id="settings-tab-security" class="settings-tab-content" style="display: none;">
|
|
715
|
+
<div class="card security-card">
|
|
716
|
+
<div class="card-head"><h2 data-i18n="pw_heading">Control panel password</h2></div>
|
|
717
|
+
<div class="password-grid">
|
|
718
|
+
<label class="field">
|
|
719
|
+
<span data-i18n="pw_current">Current password</span>
|
|
720
|
+
<input
|
|
721
|
+
type="password"
|
|
722
|
+
id="pw-current"
|
|
723
|
+
autocomplete="current-password"
|
|
724
|
+
/>
|
|
725
|
+
</label>
|
|
726
|
+
<label class="field">
|
|
727
|
+
<span data-i18n="pw_next">New password</span>
|
|
728
|
+
<input
|
|
729
|
+
type="password"
|
|
730
|
+
id="pw-next"
|
|
731
|
+
autocomplete="new-password"
|
|
732
|
+
/>
|
|
733
|
+
</label>
|
|
734
|
+
<button class="btn btn-primary" id="pw-save" type="button" data-i18n="pw_change_btn">
|
|
735
|
+
Change password
|
|
736
|
+
</button>
|
|
737
|
+
</div>
|
|
738
|
+
<div class="hint" data-i18n="pw_hint">
|
|
739
|
+
This password is the only thing between the internet and a
|
|
740
|
+
linked WhatsApp account. It is stored as a hash — nobody,
|
|
741
|
+
including this page, can read it back.
|
|
742
|
+
</div>
|
|
731
743
|
</div>
|
|
732
744
|
</div>
|
|
733
745
|
|
|
734
746
|
<div id="settings-list"><div class="loading">Loading…</div></div>
|
|
747
|
+
|
|
748
|
+
<!-- Advanced AI Settings Modal -->
|
|
749
|
+
<div class="modal-backdrop" id="ai-advanced-modal" style="display: none;">
|
|
750
|
+
<div class="modal-card">
|
|
751
|
+
<div class="modal-head">
|
|
752
|
+
<h2 data-i18n="ai_advanced_title">Advanced AI Settings</h2>
|
|
753
|
+
<button class="btn btn-icon modal-close" id="ai-advanced-close" type="button" aria-label="Close">
|
|
754
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
|
|
755
|
+
</button>
|
|
756
|
+
</div>
|
|
757
|
+
<div class="modal-body" id="ai-advanced-fields"></div>
|
|
758
|
+
<div class="modal-foot">
|
|
759
|
+
<button class="btn btn-primary" id="ai-advanced-done" type="button" data-i18n="done">Done</button>
|
|
760
|
+
</div>
|
|
761
|
+
</div>
|
|
762
|
+
</div>
|
|
735
763
|
</section>
|
|
736
764
|
</main>
|
|
737
765
|
</div>
|