levix-bot 2.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.
Files changed (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -0
  3. package/SETUP.md +242 -0
  4. package/app.cjs +337 -0
  5. package/bin/levix.js +7 -0
  6. package/deploy/install.sh +182 -0
  7. package/deploy/levix.service +56 -0
  8. package/deploy/nginx/levix.leviro.net.conf +79 -0
  9. package/package.json +78 -0
  10. package/public/brand/apple-touch-icon.png +0 -0
  11. package/public/brand/banner.jpg +0 -0
  12. package/public/brand/banner.webp +0 -0
  13. package/public/brand/icon-16.png +0 -0
  14. package/public/brand/icon-192.png +0 -0
  15. package/public/brand/icon-32.png +0 -0
  16. package/public/brand/icon-512.png +0 -0
  17. package/public/brand/icon-512.webp +0 -0
  18. package/public/brand/mark.png +0 -0
  19. package/public/brand/mark.svg +16 -0
  20. package/public/brand/mark.webp +0 -0
  21. package/public/brand/wordmark.png +0 -0
  22. package/public/brand/wordmark.webp +0 -0
  23. package/public/dashboard.css +881 -0
  24. package/public/dashboard.js +1057 -0
  25. package/public/logo.svg +16 -0
  26. package/public/qrcode.min.js +1 -0
  27. package/public/socket.io.min.js +7 -0
  28. package/scheduler.cjs +197 -0
  29. package/src/auth/auth-storage.cjs +74 -0
  30. package/src/auth/use-database-auth-state.js +130 -0
  31. package/src/bootstrap/core.js +74 -0
  32. package/src/bootstrap/events.cjs +43 -0
  33. package/src/bootstrap/panel.js +97 -0
  34. package/src/cli.js +153 -0
  35. package/src/commands/autoschedule.cjs +84 -0
  36. package/src/commands/blacklist.cjs +116 -0
  37. package/src/commands/block.cjs +70 -0
  38. package/src/commands/calc.cjs +387 -0
  39. package/src/commands/debt.cjs +175 -0
  40. package/src/commands/deleteschedule.cjs +36 -0
  41. package/src/commands/gemini.cjs +727 -0
  42. package/src/commands/group/add.cjs +136 -0
  43. package/src/commands/group/all.cjs +43 -0
  44. package/src/commands/group/antiSpam.cjs +96 -0
  45. package/src/commands/group/antilink.cjs +167 -0
  46. package/src/commands/group/approveall.cjs +54 -0
  47. package/src/commands/group/clearwarns.cjs +41 -0
  48. package/src/commands/group/deletenote.cjs +33 -0
  49. package/src/commands/group/demote.cjs +62 -0
  50. package/src/commands/group/kick.cjs +85 -0
  51. package/src/commands/group/media.cjs +120 -0
  52. package/src/commands/group/members.cjs +59 -0
  53. package/src/commands/group/note.cjs +32 -0
  54. package/src/commands/group/promote.cjs +60 -0
  55. package/src/commands/group/removeall.cjs +42 -0
  56. package/src/commands/group/rules.cjs +31 -0
  57. package/src/commands/group/save.cjs +36 -0
  58. package/src/commands/group/setname.cjs +41 -0
  59. package/src/commands/group/setpp.cjs +51 -0
  60. package/src/commands/group/setrules.cjs +46 -0
  61. package/src/commands/group/setwarn.cjs +58 -0
  62. package/src/commands/group/tagadmins.cjs +50 -0
  63. package/src/commands/group/warn.cjs +105 -0
  64. package/src/commands/group/warns.cjs +66 -0
  65. package/src/commands/group/welcome.cjs +109 -0
  66. package/src/commands/group.cjs +160 -0
  67. package/src/commands/help.cjs +230 -0
  68. package/src/commands/listschedules.cjs +47 -0
  69. package/src/commands/loop.cjs +163 -0
  70. package/src/commands/memory.cjs +201 -0
  71. package/src/commands/mod.cjs +155 -0
  72. package/src/commands/notes.cjs +28 -0
  73. package/src/commands/perm.cjs +236 -0
  74. package/src/commands/ping.cjs +17 -0
  75. package/src/commands/poll.cjs +80 -0
  76. package/src/commands/prayer.cjs +64 -0
  77. package/src/commands/qr.cjs +39 -0
  78. package/src/commands/rand.cjs +130 -0
  79. package/src/commands/restart.cjs +25 -0
  80. package/src/commands/schedule.cjs +86 -0
  81. package/src/commands/score.cjs +130 -0
  82. package/src/commands/setprefix.cjs +40 -0
  83. package/src/commands/shortlink.cjs +74 -0
  84. package/src/commands/shutdown.cjs +45 -0
  85. package/src/commands/status.cjs +75 -0
  86. package/src/commands/stt.cjs +150 -0
  87. package/src/commands/todo.cjs +88 -0
  88. package/src/commands/tts.cjs +170 -0
  89. package/src/commands/unblock.cjs +57 -0
  90. package/src/commands/weather.cjs +79 -0
  91. package/src/config/ai-persona.md +17 -0
  92. package/src/config/baileys.config.js +51 -0
  93. package/src/config/brand.cjs +36 -0
  94. package/src/config/brand.esm.js +17 -0
  95. package/src/config/constants.js +16 -0
  96. package/src/config/defaults.cjs +91 -0
  97. package/src/config/lock.cjs +110 -0
  98. package/src/config/paths.cjs +90 -0
  99. package/src/config/runtime-config.cjs +219 -0
  100. package/src/config/secrets.cjs +130 -0
  101. package/src/config/settings.cjs +386 -0
  102. package/src/core/connection.js +199 -0
  103. package/src/core/events.js +61 -0
  104. package/src/core/socket.js +64 -0
  105. package/src/db/db.cjs +282 -0
  106. package/src/db/store.cjs +837 -0
  107. package/src/db/store.esm.js +17 -0
  108. package/src/domain/caddy.js +120 -0
  109. package/src/domain/command.js +380 -0
  110. package/src/domain/detect.js +173 -0
  111. package/src/domain/nginx.js +138 -0
  112. package/src/domain/system.js +148 -0
  113. package/src/handlers/command.handler.js +414 -0
  114. package/src/handlers/group.handler.js +73 -0
  115. package/src/handlers/message.handler.js +149 -0
  116. package/src/index.js +217 -0
  117. package/src/middleware/antispam.middleware.js +68 -0
  118. package/src/middleware/blacklist.middleware.js +29 -0
  119. package/src/middleware/forward-tracking.middleware.js +49 -0
  120. package/src/middleware/permissions.middleware.js +114 -0
  121. package/src/routes/dashboard.api.esm.js +779 -0
  122. package/src/services/aiAgent.cjs +317 -0
  123. package/src/services/aiTools.cjs +635 -0
  124. package/src/utils/datetime.cjs +61 -0
  125. package/src/utils/lid-helper.esm.js +143 -0
  126. package/src/utils/logger.cjs +72 -0
  127. package/src/utils/memory.cjs +385 -0
  128. package/src/utils/normalizeJid.cjs +65 -0
  129. package/src/utils/normalizeJid.esm.js +37 -0
  130. package/src/utils/openBrowser.cjs +129 -0
  131. package/src/utils/permissions.cjs +154 -0
  132. package/src/utils/permissions.esm.js +375 -0
  133. package/src/utils/recentMessageCache.esm.js +70 -0
  134. package/src/utils/requestOrigin.cjs +102 -0
  135. package/src/utils/sendBotMessage.cjs +43 -0
  136. package/src/utils/sendBotMessage.esm.js +139 -0
  137. package/src/utils/statusMessage.cjs +179 -0
  138. package/src/utils/storage-hub.cjs +49 -0
  139. package/src/utils/storage-hub.esm.js +24 -0
  140. package/src/utils/storage.cjs +80 -0
  141. package/src/utils/storage.esm.js +88 -0
  142. package/src/utils/textDecode.cjs +197 -0
  143. package/src/utils/thumbnail.cjs +591 -0
  144. package/src/utils/typing.esm.js +102 -0
  145. package/views/dashboard.ejs +329 -0
  146. package/views/login.ejs +105 -0
  147. package/views/qr.ejs +108 -0
  148. package/views/setup.ejs +122 -0
@@ -0,0 +1,88 @@
1
+ // ESM version of storage utilities.
2
+ //
3
+ // Same story as storage.cjs: one SQLite file, src/db/store.cjs holds the
4
+ // queries, and this module re-exports them so the ESM handlers, middleware and
5
+ // helpers keep the synchronous calls they were written against. Both halves
6
+ // share one singleton.
7
+
8
+ import store from "../db/store.esm.js";
9
+
10
+ // ===== Bot settings =====
11
+ export const getBotSetting = store.getBotSetting;
12
+ export const saveBotSetting = store.saveBotSetting;
13
+ export const deleteBotSetting = store.deleteBotSetting;
14
+
15
+ // ===== Group settings =====
16
+ export const getGroupSettings = store.getGroupSettings;
17
+ export const saveGroupSettings = store.saveGroupSettings;
18
+ export const getAllGroupSettings = store.getAllGroupSettings;
19
+
20
+ // ===== Warnings =====
21
+ export const getUserWarnings = store.getUserWarnings;
22
+ export const saveUserWarnings = store.saveUserWarnings;
23
+ export const clearUserWarnings = store.clearUserWarnings;
24
+ export const getAllWarnings = store.getAllWarnings;
25
+
26
+ // ===== Todos =====
27
+ export const getUserTodos = store.getUserTodos;
28
+ export const saveUserTodos = store.saveUserTodos;
29
+
30
+ // ===== Notes =====
31
+ export const saveNote = store.saveNote;
32
+ export const getNote = store.getNote;
33
+ export const getAllNotes = store.getAllNotes;
34
+ export const deleteNote = store.deleteNote;
35
+ export const getAllNotesFlat = store.getAllNotesFlat;
36
+
37
+ // ===== Pairing QR =====
38
+ export const saveQrCode = store.saveQrCode;
39
+ export const getQrCode = store.getQrCode;
40
+ export const deleteQrCode = store.deleteQrCode;
41
+
42
+ // ===== LID mapping (Baileys v7) =====
43
+ export const storeLidPnMapping = store.storeLidPnMapping;
44
+ export const storeLidPnMappings = store.storeLidPnMappings;
45
+ export const getLidForPn = store.getLidForPn;
46
+ export const getLidsForPns = store.getLidsForPns;
47
+ export const getPnForLid = store.getPnForLid;
48
+ export const getAllLidMappings = store.getAllLidMappings;
49
+
50
+ // ===== Forward scores =====
51
+ export const incrementForwardScore = store.incrementForwardScore;
52
+ export const getForwardScore = store.getForwardScore;
53
+ export const getTopForwardedMessages = store.getTopForwardedMessages;
54
+
55
+ // ===== User metadata & bot roles =====
56
+ export const saveUserMetadata = store.saveUserMetadata;
57
+ export const getUserMetadata = store.getUserMetadata;
58
+ export const isUserOwner = store.isUserOwner;
59
+ export const isUserBotAdmin = store.isUserBotAdmin;
60
+ export const getAllOwners = store.getAllOwners;
61
+ export const getAllBotAdmins = store.getAllBotAdmins;
62
+ export const getAllUsers = store.getAllUsers;
63
+ export const setUserRole = store.setUserRole;
64
+ export const updateUserLastSeen = store.updateUserLastSeen;
65
+
66
+ // ===== Debts =====
67
+ export const addDebt = store.addDebt;
68
+ export const getDebt = store.getDebt;
69
+ export const deleteDebt = store.deleteDebt;
70
+ export const listDebts = store.listDebts;
71
+ export const getRecentDebts = store.getRecentDebts;
72
+ export const countDebts = store.countDebts;
73
+
74
+ // ===== Dashboard counters =====
75
+ export const countGroups = store.countGroups;
76
+ export const countWarnings = store.countWarnings;
77
+ export const countTodos = store.countTodos;
78
+ export const getAllTodos = store.getAllTodos;
79
+ export const countNotes = store.countNotes;
80
+ export const countUsers = store.countUsers;
81
+
82
+ // ===== Scheduled messages =====
83
+ export const getSchedules = store.getSchedules;
84
+ export const getSchedule = store.getSchedule;
85
+ export const saveSchedule = store.saveSchedule;
86
+ export const setScheduleStatus = store.setScheduleStatus;
87
+ export const deleteSchedule = store.deleteSchedule;
88
+ export const countSchedules = store.countSchedules;
@@ -0,0 +1,197 @@
1
+ // Text decoding helpers shared by the scrapers, the downloaders and the AI
2
+ // agent. Arabic is where every shortcut shows up, so this is the one place
3
+ // that knows how to turn "what the network gave us" into real characters.
4
+ //
5
+ // Four separate problems, four fixes:
6
+ //
7
+ // 1. charset - a page served as windows-1256 (still common on Arab
8
+ // sites) decoded as UTF-8 is unreadable. decodeBuffer()
9
+ // reads the charset from the Content-Type header or the
10
+ // <meta charset> tag and decodes accordingly.
11
+ // 2. HTML entities - Facebook happily emits `&#1575;&#1604;` and `&quot;`
12
+ // in og:title. Nothing downstream decoded those.
13
+ // 3. JS/JSON escapes - the embedded JSON blobs carry `ال` and
14
+ // `\/`. Surrogate pairs (emoji) must survive.
15
+ // 4. mojibake - UTF-8 bytes that were already decoded as latin1
16
+ // (the classic two-characters-per-letter soup).
17
+
18
+ const NAMED_ENTITIES = {
19
+ amp: "&",
20
+ lt: "<",
21
+ gt: ">",
22
+ quot: '"',
23
+ apos: "'",
24
+ nbsp: " ",
25
+ hellip: "…",
26
+ mdash: "—",
27
+ ndash: "–",
28
+ laquo: "«",
29
+ raquo: "»",
30
+ ldquo: "“",
31
+ rdquo: "”",
32
+ lsquo: "‘",
33
+ rsquo: "’",
34
+ middot: "·",
35
+ bull: "•",
36
+ copy: "©",
37
+ reg: "®",
38
+ trade: "™",
39
+ deg: "°",
40
+ euro: "€",
41
+ pound: "£",
42
+ times: "×",
43
+ divide: "÷",
44
+ };
45
+
46
+ /** `&#1575;` / `&#x627;` / `&quot;` -> the real character. */
47
+ function decodeHtmlEntities(text) {
48
+ if (!text || typeof text !== "string" || !text.includes("&")) return text;
49
+ return text
50
+ .replace(/&#x([0-9a-f]+);/gi, (match, hex) => {
51
+ const code = parseInt(hex, 16);
52
+ return Number.isFinite(code) ? String.fromCodePoint(code) : match;
53
+ })
54
+ .replace(/&#(\d+);/g, (match, dec) => {
55
+ const code = Number(dec);
56
+ return Number.isFinite(code) ? String.fromCodePoint(code) : match;
57
+ })
58
+ .replace(/&([a-z]+);/gi, (match, name) => {
59
+ const value = NAMED_ENTITIES[name.toLowerCase()];
60
+ return value === undefined ? match : value;
61
+ });
62
+ }
63
+
64
+ /**
65
+ * `ا`, `\xe9`, `\/`, `\n` -> the real character.
66
+ * fromCharCode per unit keeps surrogate pairs (emoji) intact, because the two
67
+ * halves are emitted next to each other and re-pair naturally.
68
+ */
69
+ function decodeEscapes(text) {
70
+ if (!text || typeof text !== "string" || !text.includes("\\")) return text;
71
+ return text
72
+ .replace(/\\u([0-9a-f]{4})/gi, (match, hex) =>
73
+ String.fromCharCode(parseInt(hex, 16))
74
+ )
75
+ .replace(/\\x([0-9a-f]{2})/gi, (match, hex) =>
76
+ String.fromCharCode(parseInt(hex, 16))
77
+ )
78
+ .replace(/\\n/g, "\n")
79
+ .replace(/\\t/g, "\t")
80
+ .replace(/\\r/g, "")
81
+ .replace(/\\\//g, "/")
82
+ .replace(/\\"/g, '"')
83
+ .replace(/\\'/g, "'")
84
+ .replace(/\\\\/g, "\\");
85
+ }
86
+
87
+ // The tell-tale pair that appears when UTF-8 bytes are decoded as latin1: a
88
+ // lead byte (0xC2-0xDF) followed by a continuation byte that landed in the
89
+ // latin1 supplement or the cp1252 punctuation block.
90
+ const MOJIBAKE_HINT =
91
+ /[Â-ß][€-¿–—‘-„†-•…‰‹›€™ŒœŠšŸŽžƒˆ˜]/;
92
+
93
+ function textScore(text) {
94
+ let score = 0;
95
+ for (const char of text) {
96
+ const code = char.codePointAt(0);
97
+ // Arabic block, ASCII letters/digits and plain punctuation are "good"
98
+ if (code >= 0x0600 && code <= 0x06ff) score += 2;
99
+ else if (code < 0x80) score += 1;
100
+ // the latin1 supplement is what mojibake is made of
101
+ else if (code >= 0x80 && code <= 0xff) score -= 2;
102
+ }
103
+ return score;
104
+ }
105
+
106
+ /** Recover text that was already mis-decoded as latin1. */
107
+ function repairMojibake(text) {
108
+ if (!text || typeof text !== "string") return text;
109
+ if (!MOJIBAKE_HINT.test(text)) return text;
110
+ try {
111
+ const repaired = Buffer.from(text, "binary").toString("utf8");
112
+ if (repaired.includes("�")) return text;
113
+ return textScore(repaired) > textScore(text) ? repaired : text;
114
+ } catch {
115
+ return text;
116
+ }
117
+ }
118
+
119
+ // Plain spaces, tabs, non-breaking spaces, zero-width characters and the bidi
120
+ // marks Arabic pages are full of - all collapse to a single space.
121
+ const NOISE_SPACE = /[ \t ​-‏‪-‮]+/g;
122
+
123
+ /** Everything above, in the order that actually works. */
124
+ function decodeText(text) {
125
+ if (!text || typeof text !== "string") return text;
126
+ let out = decodeEscapes(text);
127
+ out = decodeHtmlEntities(out);
128
+ out = repairMojibake(out);
129
+ return out.replace(NOISE_SPACE, " ").trim();
130
+ }
131
+
132
+ /** Charset name from a Content-Type header and/or an HTML <meta> tag. */
133
+ function detectCharset(contentType, headSample) {
134
+ const fromHeader = /charset=["']?([\w-]+)/i.exec(contentType || "");
135
+ if (fromHeader) return fromHeader[1].toLowerCase();
136
+
137
+ if (headSample) {
138
+ const meta =
139
+ /<meta[^>]+charset=["']?([\w-]+)/i.exec(headSample) ||
140
+ /<meta[^>]+content=["'][^"']*charset=([\w-]+)/i.exec(headSample);
141
+ if (meta) return meta[1].toLowerCase();
142
+ }
143
+ return "utf-8";
144
+ }
145
+
146
+ /**
147
+ * Decode a response body with the right charset.
148
+ * Node ships full ICU, so windows-1256 / iso-8859-6 (the two encodings Arabic
149
+ * pages still use) decode natively; anything unknown falls back to UTF-8.
150
+ */
151
+ function decodeBuffer(buffer, contentType) {
152
+ if (!Buffer.isBuffer(buffer)) return String(buffer == null ? "" : buffer);
153
+
154
+ // Sniff the first 2 KB as latin1 to find a <meta charset> without committing
155
+ // to an encoding yet.
156
+ const sample = buffer.subarray(0, 2048).toString("latin1");
157
+ let charset = detectCharset(contentType, sample);
158
+ if (charset === "utf8") charset = "utf-8";
159
+
160
+ if (charset === "utf-8" || charset === "us-ascii" || charset === "ascii") {
161
+ return buffer.toString("utf8");
162
+ }
163
+
164
+ try {
165
+ return new TextDecoder(charset).decode(buffer);
166
+ } catch {
167
+ return buffer.toString("utf8");
168
+ }
169
+ }
170
+
171
+ /** HTML -> readable plain text (used when the agent opens a page). */
172
+ function stripHtml(html) {
173
+ if (!html || typeof html !== "string") return "";
174
+ return decodeHtmlEntities(
175
+ html
176
+ .replace(/<script[\s\S]*?<\/script>/gi, " ")
177
+ .replace(/<style[\s\S]*?<\/style>/gi, " ")
178
+ .replace(/<noscript[\s\S]*?<\/noscript>/gi, " ")
179
+ .replace(/<!--[\s\S]*?-->/g, " ")
180
+ .replace(/<\/(p|div|li|tr|h[1-6]|br)>/gi, "\n")
181
+ .replace(/<br\s*\/?>/gi, "\n")
182
+ .replace(/<[^>]+>/g, " ")
183
+ )
184
+ .replace(NOISE_SPACE, " ")
185
+ .replace(/\n{3,}/g, "\n\n")
186
+ .trim();
187
+ }
188
+
189
+ module.exports = {
190
+ decodeHtmlEntities,
191
+ decodeEscapes,
192
+ repairMojibake,
193
+ decodeText,
194
+ detectCharset,
195
+ decodeBuffer,
196
+ stripHtml,
197
+ };