bot-mwsm 3.0.3 → 3.0.5
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/.github/workflows/Mwsm.yml +37 -49
- package/Dockerfile +100 -0
- package/README.md +10 -18
- package/bash/mwsm.sh +607 -427
- package/bash/setup.sh +116 -47
- package/img/menu-mwsm.png +0 -0
- package/mwsm.db +0 -0
- package/mwsm.js +464 -424
- package/package.json +6 -5
- package/version.json +2 -2
- package/bot-mwsm-3.0.2.tgz +0 -0
package/mwsm.js
CHANGED
|
@@ -1219,331 +1219,367 @@ async function WwjsVersion(GET) {
|
|
|
1219
1219
|
// ==================================================
|
|
1220
1220
|
|
|
1221
1221
|
async function getEmbedding(text) {
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1222
|
+
try {
|
|
1223
|
+
if (!text) return null;
|
|
1224
|
+
const mwsmHost = Debug('OPTIONS').mwsmhost;
|
|
1225
|
+
const mwsmPort = Debug('OPTIONS').mwsmport;
|
|
1226
|
+
|
|
1227
|
+
const localResp = await axios.post(
|
|
1228
|
+
`http://${mwsmHost}:${mwsmPort}/embed`, {
|
|
1229
|
+
text
|
|
1230
|
+
}, {
|
|
1231
|
+
timeout: 10000
|
|
1232
|
+
}
|
|
1233
|
+
);
|
|
1234
|
+
|
|
1235
|
+
if (localResp.data?.embedding) return localResp.data.embedding;
|
|
1236
|
+
throw new Error('No embedding returned');
|
|
1237
|
+
} catch {
|
|
1238
|
+
// 🔹 fallback determinístico (garante vetor estável)
|
|
1239
|
+
return Array.from(text)
|
|
1240
|
+
.map((ch, i) => ((ch.charCodeAt(0) + i * 13) % 255) / 255)
|
|
1241
|
+
.slice(0, 256);
|
|
1242
|
+
}
|
|
1241
1243
|
}
|
|
1242
1244
|
|
|
1243
1245
|
// ==================================================
|
|
1244
1246
|
// 🕒 Timezone e Cumprimentos Dinâmicos
|
|
1245
1247
|
// ==================================================
|
|
1246
1248
|
async function getTimezoneByUF(uf) {
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1249
|
+
return new Promise((resolve) => {
|
|
1250
|
+
db.get("SELECT timezone FROM localzone WHERE uf = ?", [uf], (err, row) => {
|
|
1251
|
+
if (err || !row) return resolve("America/Sao_Paulo"); // padrão SP
|
|
1252
|
+
resolve(row.timezone);
|
|
1253
|
+
});
|
|
1254
|
+
});
|
|
1253
1255
|
}
|
|
1254
1256
|
|
|
1255
1257
|
function getGreetingPeriod(timezone) {
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1258
|
+
try {
|
|
1259
|
+
// Captura a hora local correta sem dupla conversão
|
|
1260
|
+
const now = new Date();
|
|
1261
|
+
const localHour = new Intl.DateTimeFormat("pt-BR", {
|
|
1262
|
+
timeZone: timezone,
|
|
1263
|
+
hour: "numeric",
|
|
1264
|
+
hour12: false
|
|
1265
|
+
}).format(now);
|
|
1266
|
+
|
|
1267
|
+
const hour = parseInt(localHour, 10);
|
|
1268
|
+
|
|
1269
|
+
if (hour >= 5 && hour < 12) return "bom dia";
|
|
1270
|
+
if (hour >= 12 && hour < 18) return "boa tarde";
|
|
1271
|
+
if (hour >= 18 && hour < 24) return "boa noite";
|
|
1272
|
+
if (hour >= 0 && hour < 5) return "boa madrugada";
|
|
1273
|
+
} catch (err) {
|
|
1274
|
+
console.error("[ERRO] Falha ao determinar saudação:", err);
|
|
1275
|
+
return "";
|
|
1276
|
+
}
|
|
1267
1277
|
}
|
|
1268
1278
|
|
|
1279
|
+
|
|
1269
1280
|
// ==================================================
|
|
1270
1281
|
// 🔒 Filtro Temático (Palavras-chave no BD)
|
|
1271
1282
|
// ==================================================
|
|
1272
1283
|
async function isRelevantQuestion(text) {
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1284
|
+
return new Promise((resolve) => {
|
|
1285
|
+
db.all("SELECT filter FROM keywords", [], (err, rows) => {
|
|
1286
|
+
if (err) {
|
|
1287
|
+
console.error("Keyword filter error:", err.message);
|
|
1288
|
+
return resolve(true); // não bloqueia em erro
|
|
1289
|
+
}
|
|
1290
|
+
if (!rows || rows.length === 0) return resolve(true);
|
|
1291
|
+
|
|
1292
|
+
const keywords = rows.map(r => (r.filter || "").toLowerCase().trim());
|
|
1293
|
+
text = (text || "").toLowerCase().trim();
|
|
1294
|
+
const found = keywords.some(k => text.includes(k));
|
|
1295
|
+
resolve(found);
|
|
1296
|
+
});
|
|
1297
|
+
});
|
|
1287
1298
|
}
|
|
1288
1299
|
|
|
1289
1300
|
// ==================================================
|
|
1290
1301
|
// 🎯 Lógica Principal da IA
|
|
1291
1302
|
// ==================================================
|
|
1292
1303
|
async function askAI(question) {
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1304
|
+
return new Promise(async (resolve) => {
|
|
1305
|
+
try {
|
|
1306
|
+
const text = (question || "").toLowerCase().trim();
|
|
1307
|
+
|
|
1308
|
+
// 🔹 1️⃣ Verifica se é um cumprimento antes do filtro
|
|
1309
|
+
const isGreeting = await new Promise((resolveGreet) => {
|
|
1310
|
+
db.all("SELECT word FROM greetings", [], (err, rows) => {
|
|
1311
|
+
if (err || !rows?.length) return resolveGreet(false);
|
|
1312
|
+
const greetings = rows.map(r => (r.word || "").toLowerCase());
|
|
1313
|
+
resolveGreet(greetings.some(g => text.includes(g)));
|
|
1314
|
+
});
|
|
1315
|
+
});
|
|
1316
|
+
|
|
1317
|
+
if (isGreeting) {
|
|
1318
|
+
const uf = Debug('OPTIONS').timezone || 'SP';
|
|
1319
|
+
const tz = await getTimezoneByUF(uf);
|
|
1320
|
+
const turno = getGreetingPeriod(tz);
|
|
1321
|
+
|
|
1322
|
+
if (turno) {
|
|
1323
|
+
return resolve(`👋 Olá, ${turno}! Como posso te ajudar com sua conexão de internet?`);
|
|
1324
|
+
} else {
|
|
1325
|
+
return resolve(`👋 Olá! Como posso te ajudar com sua conexão de internet?`);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// 🔹 2️⃣ Se não for cumprimento, aplica o filtro temático
|
|
1330
|
+
const isRelevant = await isRelevantQuestion(text);
|
|
1331
|
+
if (!isRelevant) {
|
|
1332
|
+
console.log(`> ${Debug('OPTIONS').appname} : Brain: Filter → Ignored.`);
|
|
1333
|
+
return resolve("⚠️ Posso ajudar apenas com dúvidas sobre sua conexão de internet e suporte técnico.");
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// 🔹 3️⃣ A partir daqui segue o fluxo normal da IA
|
|
1337
|
+
const aiMode = parseInt(Debug('OPTIONS').aimode);
|
|
1338
|
+
const apiKey = Debug('OPTIONS').keygen;
|
|
1339
|
+
var Engine = Debug('OPTIONS').engine;
|
|
1340
|
+
const threshold = parseFloat(Debug('OPTIONS').threshold);
|
|
1341
|
+
const systemPrompt = Debug('OPTIONS').prompt;
|
|
1342
|
+
const aiTimeout = parseInt(Debug('OPTIONS').aitimeout);
|
|
1343
|
+
const appName = Debug('OPTIONS').appname;
|
|
1344
|
+
|
|
1345
|
+
if (!apiKey) return resolve("⚠️ IA não configurada.");
|
|
1346
|
+
|
|
1347
|
+
if (Engine.toLowerCase() === 'freerouter') {
|
|
1348
|
+
const Levels = Debug('ENGINE', '*', 'ALL').filter(item => item.level === 0);
|
|
1349
|
+
const ActiveModel = Levels.find(item => item.active === 1);
|
|
1350
|
+
|
|
1351
|
+
if (ActiveModel) {
|
|
1352
|
+
Regedit = ActiveModel;
|
|
1353
|
+
} else if (Levels.length > 0) {
|
|
1354
|
+
const randomIndex = Math.floor(Math.random() * Levels.length);
|
|
1355
|
+
Regedit = Levels[randomIndex];
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
if (Regedit) {
|
|
1359
|
+
Engine = Regedit.title;
|
|
1360
|
+
Module = Regedit.module;
|
|
1361
|
+
Level = parseInt(Regedit.level);
|
|
1362
|
+
}
|
|
1363
|
+
} else {
|
|
1364
|
+
Regedit = await Debug("ENGINE", "*", "DIRECT", Engine);
|
|
1365
|
+
if (Regedit) {
|
|
1366
|
+
Engine = Regedit.title;
|
|
1367
|
+
Module = Regedit.module;
|
|
1368
|
+
Level = parseInt(Regedit.level);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
if (!Module) return resolve("⚠️ Indisponível no momento.");
|
|
1372
|
+
|
|
1373
|
+
const qEmbedding = await getEmbedding(question);
|
|
1374
|
+
let bestMatch = null;
|
|
1375
|
+
let bestScore = 0;
|
|
1376
|
+
|
|
1377
|
+
if (aiMode === 0) {
|
|
1378
|
+
console.log(`> ${appName} : Brain: Cloud | Relevance: 0%`);
|
|
1379
|
+
const aiAnswer = await fetchCloudAnswer(question, apiKey, Engine, Module, systemPrompt, aiTimeout, Level);
|
|
1380
|
+
return resolve(aiAnswer);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// 🔸 Busca local
|
|
1384
|
+
const rows = await Debug('INTELIGENCE', '*', 'ALL');
|
|
1385
|
+
for (const r of rows) {
|
|
1386
|
+
if (!r.embedding) continue;
|
|
1387
|
+
try {
|
|
1388
|
+
const emb = JSON.parse(r.embedding);
|
|
1389
|
+
const score = cosineSimilarity(emb, qEmbedding);
|
|
1390
|
+
if (score > bestScore) {
|
|
1391
|
+
bestScore = score;
|
|
1392
|
+
bestMatch = r;
|
|
1393
|
+
}
|
|
1394
|
+
} catch {}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
// 🔹 Resposta local encontrada
|
|
1398
|
+
if (bestMatch && bestScore >= threshold) {
|
|
1399
|
+
console.log(`> ${appName} : Brain: Local | Relevance: ${(bestScore * 100).toFixed(0)}%`);
|
|
1400
|
+
db.run("UPDATE inteligence SET usage_count = usage_count + 1 WHERE id = ?", [bestMatch.id]);
|
|
1401
|
+
return resolve(bestMatch.answer);
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
// 🔹 Caso não tenha resposta local — vai para nuvem
|
|
1405
|
+
console.log(`> ${appName} : Brain: Cloud | Relevance: ${(bestScore * 100).toFixed(0)}%`);
|
|
1406
|
+
const aiAnswer = await fetchCloudAnswer(question, apiKey, Engine, Module, systemPrompt, aiTimeout, Level);
|
|
1407
|
+
|
|
1408
|
+
// 🔸 Aprendizado local (modo 2)
|
|
1409
|
+
if (aiMode === 2 && aiAnswer && !aiAnswer.startsWith("⚠")) {
|
|
1410
|
+
try {
|
|
1411
|
+
await enforceKnowledgeLimit();
|
|
1412
|
+
const _embedding = await getEmbedding(question);
|
|
1413
|
+
const embeddingStr = _embedding ? JSON.stringify(_embedding) : null;
|
|
1414
|
+
|
|
1415
|
+
db.serialize(() => {
|
|
1416
|
+
db.get("SELECT id FROM inteligence WHERE question = ?", [question], (err, row) => {
|
|
1417
|
+
if (err) return console.error("DB check error:", err.message);
|
|
1418
|
+
|
|
1419
|
+
const sql = row ?
|
|
1420
|
+
"UPDATE inteligence SET answer=?, embedding=?, source=?, usage_count=usage_count+1 WHERE id=?" :
|
|
1421
|
+
"INSERT INTO inteligence (question, answer, embedding, source, usage_count) VALUES (?, ?, ?, ?, 1)";
|
|
1422
|
+
|
|
1423
|
+
const params = row ?
|
|
1424
|
+
[aiAnswer, embeddingStr, "local", row.id] :
|
|
1425
|
+
[question, aiAnswer, embeddingStr, "local"];
|
|
1426
|
+
|
|
1427
|
+
db.run(sql, params, (e) => e && console.error("DB write error:", e.message));
|
|
1428
|
+
});
|
|
1429
|
+
});
|
|
1430
|
+
} catch (e) {
|
|
1431
|
+
console.error("Embedding generation failed:", e?.message || e);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
return resolve(aiAnswer);
|
|
1436
|
+
} catch (err) {
|
|
1437
|
+
console.error("IA error:", err.message || err);
|
|
1438
|
+
return resolve("⚠️ Não consegui acessar a inteligência artificial no momento.");
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1410
1441
|
}
|
|
1411
1442
|
|
|
1412
1443
|
// ==================================================
|
|
1413
1444
|
// 🌐 Comunicação com API (OpenRouter) + Limite de Tentativas
|
|
1414
1445
|
// ==================================================
|
|
1415
1446
|
async function fetchCloudAnswer(question, apiKey, Engine, Module, systemPrompt, aiTimeout, Level = 0) {
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1447
|
+
const tried = [];
|
|
1448
|
+
try {
|
|
1449
|
+
let variants = [];
|
|
1450
|
+
|
|
1451
|
+
if (Engine.toLowerCase() === "freerouter") {
|
|
1452
|
+
variants = await new Promise((resolve) => {
|
|
1453
|
+
db.all("SELECT * FROM engine WHERE level = 0 ORDER BY active DESC, id ASC", [], (err, rows) => {
|
|
1454
|
+
if (err) return resolve([]);
|
|
1455
|
+
resolve(rows);
|
|
1456
|
+
});
|
|
1457
|
+
});
|
|
1458
|
+
} else {
|
|
1459
|
+
variants = await new Promise((resolve) => {
|
|
1460
|
+
db.all("SELECT * FROM engine WHERE title = ? AND level = ? ORDER BY active DESC, id ASC", [Engine, Level], (err, rows) => {
|
|
1461
|
+
if (err) return resolve([]);
|
|
1462
|
+
resolve(rows);
|
|
1463
|
+
});
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
if (!variants.length) throw new Error("⚠️ Erro ao buscar resposta da IA online.");
|
|
1468
|
+
|
|
1469
|
+
const activeVariant = variants.find(v => v.active === 1);
|
|
1470
|
+
const orderedVariants = activeVariant ?
|
|
1471
|
+
[activeVariant, ...variants.filter(v => v.id !== activeVariant.id)] :
|
|
1472
|
+
variants;
|
|
1473
|
+
|
|
1474
|
+
const maxAttempts = parseInt(Debug('OPTIONS').aimaxattempts) || 0;
|
|
1475
|
+
let attempts = 0;
|
|
1476
|
+
|
|
1477
|
+
for (const variant of orderedVariants) {
|
|
1478
|
+
if (maxAttempts && attempts >= maxAttempts) {
|
|
1479
|
+
console.log(`> ${Debug('OPTIONS').appname} : Brain: Max Attempts (${maxAttempts}) reached.`);
|
|
1480
|
+
break;
|
|
1481
|
+
}
|
|
1482
|
+
attempts++;
|
|
1483
|
+
|
|
1484
|
+
tried.push(variant.module);
|
|
1485
|
+
try {
|
|
1486
|
+
const response = await axios.post(
|
|
1487
|
+
"https://openrouter.ai/api/v1/chat/completions", {
|
|
1488
|
+
model: variant.module,
|
|
1489
|
+
messages: [{
|
|
1490
|
+
role: "system",
|
|
1491
|
+
content: systemPrompt
|
|
1492
|
+
},
|
|
1493
|
+
{
|
|
1494
|
+
role: "user",
|
|
1495
|
+
content: question
|
|
1496
|
+
}
|
|
1497
|
+
]
|
|
1498
|
+
}, {
|
|
1499
|
+
headers: {
|
|
1500
|
+
Authorization: `Bearer ${apiKey}`,
|
|
1501
|
+
"Content-Type": "application/json"
|
|
1502
|
+
},
|
|
1503
|
+
timeout: aiTimeout
|
|
1504
|
+
}
|
|
1505
|
+
);
|
|
1506
|
+
|
|
1507
|
+
const aiAnswer =
|
|
1508
|
+
response.data?.choices?.[0]?.message?.content?.trim() ||
|
|
1509
|
+
"Desculpe, não consegui entender sua solicitação.";
|
|
1510
|
+
aiAnswer = aiAnswer.replace(/◁think▷[\s\S]*?◁\/think▷/g, "").trim();
|
|
1511
|
+
db.run("UPDATE engine SET active = 0 WHERE title = ?", [variant.title]);
|
|
1512
|
+
db.run("UPDATE engine SET active = 1 WHERE id = ?", [variant.id]);
|
|
1513
|
+
|
|
1514
|
+
console.log(`> ${Debug('OPTIONS').appname} : AskAI: ${variant.title} ${variant.variant} → ATIVA`);
|
|
1515
|
+
return aiAnswer.replace(/\s+/g, " ").trim();
|
|
1516
|
+
} catch (err) {
|
|
1517
|
+
console.log(`> ${Debug('OPTIONS').appname} : AskAI: ${variant.title} ${variant.variant} → INATIVA`);
|
|
1518
|
+
db.run("UPDATE engine SET active = 0 WHERE id = ?", [variant.id]);
|
|
1519
|
+
continue;
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
return "⚠️ Erro ao buscar resposta da IA online.";
|
|
1524
|
+
} catch {
|
|
1525
|
+
return "⚠️ Erro ao buscar resposta da IA online.";
|
|
1526
|
+
}
|
|
1493
1527
|
}
|
|
1494
1528
|
|
|
1495
1529
|
// ==================================================
|
|
1496
1530
|
// 📊 Similaridade Vetorial
|
|
1497
1531
|
// ==================================================
|
|
1498
1532
|
function cosineSimilarity(vecA, vecB) {
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1533
|
+
if (!vecA || !vecB || vecA.length !== vecB.length) return 0;
|
|
1534
|
+
let dot = 0,
|
|
1535
|
+
normA = 0,
|
|
1536
|
+
normB = 0;
|
|
1537
|
+
for (let i = 0; i < vecA.length; i++) {
|
|
1538
|
+
dot += vecA[i] * vecB[i];
|
|
1539
|
+
normA += vecA[i] * vecA[i];
|
|
1540
|
+
normB += vecB[i] * vecB[i];
|
|
1541
|
+
}
|
|
1542
|
+
return normA && normB ? dot / (Math.sqrt(normA) * Math.sqrt(normB)) : 0;
|
|
1507
1543
|
}
|
|
1508
1544
|
|
|
1509
1545
|
// ==================================================
|
|
1510
1546
|
// 🧹 Limpeza Dinâmica e Inteligente do Conhecimento
|
|
1511
1547
|
// ==================================================
|
|
1512
1548
|
async function enforceKnowledgeLimit() {
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1549
|
+
try {
|
|
1550
|
+
const maxKnowledge = parseInt(Debug("OPTIONS").maxknowledge) || 1000;
|
|
1551
|
+
if (!maxKnowledge || maxKnowledge < 100) return;
|
|
1552
|
+
|
|
1553
|
+
db.all("SELECT COUNT(*) as total FROM inteligence", async (err, rows) => {
|
|
1554
|
+
if (err) return console.error("DB count error:", err.message);
|
|
1555
|
+
const total = rows[0]?.total || 0;
|
|
1556
|
+
if (total <= maxKnowledge) return;
|
|
1557
|
+
|
|
1558
|
+
const excess = total - maxKnowledge;
|
|
1559
|
+
db.run(
|
|
1560
|
+
`DELETE FROM inteligence
|
|
1525
1561
|
WHERE id IN (
|
|
1526
1562
|
SELECT id FROM inteligence
|
|
1527
1563
|
ORDER BY usage_count ASC, id ASC
|
|
1528
1564
|
LIMIT ?
|
|
1529
1565
|
)`,
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1566
|
+
[excess],
|
|
1567
|
+
function(delErr) {
|
|
1568
|
+
if (delErr) console.error("Cleanup error:", delErr.message);
|
|
1569
|
+
else console.log(`✅ ${this.changes} registros antigos removidos.`);
|
|
1570
|
+
}
|
|
1571
|
+
);
|
|
1572
|
+
});
|
|
1573
|
+
} catch (err) {
|
|
1574
|
+
console.error("Erro no enforceKnowledgeLimit:", err.message);
|
|
1575
|
+
}
|
|
1540
1576
|
}
|
|
1541
1577
|
|
|
1542
1578
|
// ==================================================
|
|
1543
1579
|
// 🔹 Embedding Local (Backup Seguro)
|
|
1544
1580
|
// ==================================================
|
|
1545
1581
|
async function getLocalEmbedding(text) {
|
|
1546
|
-
|
|
1582
|
+
return Array.from(text).map((c, i) => ((c.charCodeAt(0) + i * 7) % 255) / 255);
|
|
1547
1583
|
}
|
|
1548
1584
|
|
|
1549
1585
|
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
@@ -4642,154 +4678,158 @@ const Build = async (SET) => {
|
|
|
4642
4678
|
// WhatsApp Bot
|
|
4643
4679
|
client.on('message', async msg => {
|
|
4644
4680
|
|
|
4645
|
-
// ==================================================
|
|
4646
|
-
// 🤖 Bot Menu e Controle Inteligente de IA
|
|
4647
|
-
// ==================================================
|
|
4648
|
-
const lastRequestTimes = new Map();
|
|
4649
|
-
let lastGlobalRequest = 0;
|
|
4650
|
-
let globalQueue = Promise.resolve();
|
|
4651
|
-
|
|
4652
|
-
if (activeMenus.has(msg.from)) {
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4681
|
+
// ==================================================
|
|
4682
|
+
// 🤖 Bot Menu e Controle Inteligente de IA
|
|
4683
|
+
// ==================================================
|
|
4684
|
+
const lastRequestTimes = new Map();
|
|
4685
|
+
let lastGlobalRequest = 0;
|
|
4686
|
+
let globalQueue = Promise.resolve();
|
|
4687
|
+
|
|
4688
|
+
if (activeMenus.has(msg.from)) {
|
|
4689
|
+
if (activeSupportIA.has(msg.from)) {
|
|
4690
|
+
try {
|
|
4691
|
+
const _iaExit = (msg.body || '').toString().trim().toLowerCase();
|
|
4692
|
+
|
|
4693
|
+
// 🔹 Encerrar atendimento
|
|
4694
|
+
if (["0", "sair", "tchau", "tchal"].includes(_iaExit)) {
|
|
4695
|
+
activeSupportIA.delete(msg.from);
|
|
4696
|
+
activeMenus.delete(msg.from);
|
|
4697
|
+
await client.sendMessage(msg.from, "✅ Atendimento encerrado. Obrigado pelo contato!");
|
|
4698
|
+
return;
|
|
4699
|
+
}
|
|
4700
|
+
|
|
4701
|
+
// 🔹 Voltar ao menu principal
|
|
4702
|
+
if (_iaExit === "menu") {
|
|
4703
|
+
activeSupportIA.delete(msg.from);
|
|
4704
|
+
activeMenus.set(msg.from, true);
|
|
4705
|
+
await client.sendMessage(
|
|
4706
|
+
msg.from,
|
|
4707
|
+
'📋 *Menu Principal*\n\n' +
|
|
4708
|
+
'1️⃣ Boleto\n' +
|
|
4709
|
+
'2️⃣ Suporte\n' +
|
|
4710
|
+
'0️⃣ Encerrar\n\n' +
|
|
4711
|
+
'👉 Responda com o número da opção desejada.'
|
|
4712
|
+
);
|
|
4713
|
+
return;
|
|
4714
|
+
}
|
|
4715
|
+
} catch (e) {
|
|
4716
|
+
console.error('IA exit handler error:', e?.message || e);
|
|
4717
|
+
}
|
|
4718
|
+
|
|
4719
|
+
try {
|
|
4720
|
+
const chat = await msg.getChat();
|
|
4721
|
+
const Engine = Debug('OPTIONS').engine;
|
|
4722
|
+
const Level = parseInt(Debug("ENGINE", "*", "DIRECT", Engine).level || 0);
|
|
4723
|
+
const perUserDelay = parseInt(Debug('OPTIONS').airequestdelay) || 3000;
|
|
4724
|
+
const globalDelay = parseInt(Debug('OPTIONS').aiglobaldelay) || 1000;
|
|
4725
|
+
|
|
4726
|
+
const now = Date.now();
|
|
4727
|
+
const lastUser = lastRequestTimes.get(msg.from) || 0;
|
|
4728
|
+
const sinceUser = now - lastUser;
|
|
4729
|
+
const sinceGlobal = now - lastGlobalRequest;
|
|
4730
|
+
const waitUser = Math.max(0, perUserDelay - sinceUser);
|
|
4731
|
+
const waitGlobal = Math.max(0, globalDelay - sinceGlobal);
|
|
4732
|
+
const totalWait = Math.max(waitUser, waitGlobal);
|
|
4733
|
+
|
|
4734
|
+
// ==================================================
|
|
4735
|
+
// 🔹 Modo Free — com fila e controle global
|
|
4736
|
+
// ==================================================
|
|
4737
|
+
if (Level === 0) {
|
|
4738
|
+
globalQueue = globalQueue.then(async () => {
|
|
4739
|
+
try {
|
|
4740
|
+
if (totalWait > 0) {
|
|
4741
|
+
const typingInterval = setInterval(async () => {
|
|
4742
|
+
try {
|
|
4743
|
+
await chat.sendStateTyping();
|
|
4744
|
+
} catch {}
|
|
4745
|
+
}, 4000);
|
|
4746
|
+
|
|
4747
|
+
await new Promise(r => setTimeout(r, totalWait));
|
|
4748
|
+
clearInterval(typingInterval);
|
|
4749
|
+
try {
|
|
4750
|
+
await chat.clearState();
|
|
4751
|
+
} catch {}
|
|
4752
|
+
}
|
|
4753
|
+
|
|
4754
|
+
lastRequestTimes.set(msg.from, Date.now());
|
|
4755
|
+
lastGlobalRequest = Date.now();
|
|
4756
|
+
|
|
4757
|
+
const tLevel = parseInt(Debug('OPTIONS').typingspeed);
|
|
4758
|
+
const multiplier = 1 + (5 - tLevel) * 0.25;
|
|
4759
|
+
const baseTime = 800 * multiplier;
|
|
4760
|
+
const extraPerChar = 25 * multiplier;
|
|
4761
|
+
const maxTime = 4000 * multiplier;
|
|
4762
|
+
const estimatedDelay = Math.min(baseTime + msg.body.length * extraPerChar, maxTime);
|
|
4763
|
+
|
|
4764
|
+
await chat.sendStateTyping();
|
|
4765
|
+
await new Promise(resolve => setTimeout(resolve, estimatedDelay));
|
|
4766
|
+
await chat.clearState();
|
|
4767
|
+
|
|
4768
|
+
const reply = await askAI(msg.body);
|
|
4769
|
+
await client.sendMessage(msg.from, reply);
|
|
4770
|
+
} catch (err) {
|
|
4771
|
+
console.error("Erro ao processar IA (free):", err.message);
|
|
4772
|
+
try {
|
|
4773
|
+
const reply = await askAI(msg.body);
|
|
4774
|
+
await client.sendMessage(msg.from, reply);
|
|
4775
|
+
} catch (e2) {
|
|
4776
|
+
console.error("Erro secundário:", e2.message);
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4779
|
+
}).catch(e => console.error("Erro na fila global:", e.message));
|
|
4780
|
+
}
|
|
4781
|
+
|
|
4782
|
+
// ==================================================
|
|
4783
|
+
// 🔹 Modo Premium — Resposta direta (sem fila)
|
|
4784
|
+
// ==================================================
|
|
4785
|
+
else {
|
|
4786
|
+
const tLevel = parseInt(Debug('OPTIONS').typingspeed);
|
|
4787
|
+
const multiplier = 1 + (5 - tLevel) * 0.25;
|
|
4788
|
+
const baseTime = 800 * multiplier;
|
|
4789
|
+
const extraPerChar = 25 * multiplier;
|
|
4790
|
+
const maxTime = 4000 * multiplier;
|
|
4791
|
+
const estimatedDelay = Math.min(baseTime + msg.body.length * extraPerChar, maxTime);
|
|
4792
|
+
|
|
4793
|
+
await chat.sendStateTyping();
|
|
4794
|
+
await new Promise(r => setTimeout(r, estimatedDelay));
|
|
4795
|
+
await chat.clearState();
|
|
4796
|
+
|
|
4797
|
+
const reply = await askAI(msg.body);
|
|
4798
|
+
await client.sendMessage(msg.from, reply);
|
|
4799
|
+
}
|
|
4800
|
+
|
|
4801
|
+
} catch (err) {
|
|
4802
|
+
console.error("Erro ao simular digitando:", err.message);
|
|
4803
|
+
try {
|
|
4804
|
+
const reply = await askAI(msg.body);
|
|
4805
|
+
await client.sendMessage(msg.from, reply);
|
|
4806
|
+
} catch (e2) {
|
|
4807
|
+
console.error("Erro no fallback de IA:", e2.message);
|
|
4808
|
+
}
|
|
4809
|
+
}
|
|
4810
|
+
return;
|
|
4811
|
+
}
|
|
4812
|
+
|
|
4813
|
+
// ==================================================
|
|
4814
|
+
// 📋 Menu principal
|
|
4815
|
+
// ==================================================
|
|
4816
|
+
if (msg.body.startsWith('1')) {
|
|
4817
|
+
await client.sendMessage(msg.from, '🔗 Aqui está o link do seu boleto: https://seudominio.com/boleto');
|
|
4818
|
+
return;
|
|
4819
|
+
}
|
|
4820
|
+
|
|
4821
|
+
if (msg.body.startsWith('2')) {
|
|
4822
|
+
await client.sendMessage(msg.from, '🤖 Você está agora em atendimento de suporte com IA. Envie sua dúvida.');
|
|
4823
|
+
activeSupportIA.set(msg.from, true);
|
|
4824
|
+
return;
|
|
4825
|
+
}
|
|
4826
|
+
|
|
4827
|
+
if (msg.body.startsWith('0')) {
|
|
4828
|
+
await client.sendMessage(msg.from, '✅ Atendimento encerrado. Obrigado pelo contato!');
|
|
4829
|
+
activeMenus.delete(msg.from);
|
|
4830
|
+
return;
|
|
4831
|
+
}
|
|
4832
|
+
}
|
|
4793
4833
|
|
|
4794
4834
|
if (msg.body.toLowerCase() === 'menu') {
|
|
4795
4835
|
activeMenus.set(msg.from, true);
|
|
@@ -4976,4 +5016,4 @@ function hideElement(sel) {
|
|
|
4976
5016
|
|
|
4977
5017
|
function setText(sel, txt) {
|
|
4978
5018
|
if (typeof sel === "string") $(sel).text(txt);
|
|
4979
|
-
}
|
|
5019
|
+
}
|