beast-agent 2.46.1 → 2.47.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/src/agent/channelsessions.js +78 -0
- package/src/agent/engine.js +5 -1
- package/src/main.js +342 -245
- package/src/preload.js +3 -0
- package/src/renderer/renderer.js +25 -3
- package/src/renderer/style.css +24 -0
package/package.json
CHANGED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* Kanal (WhatsApp/Telegram/Discord) kişi-başına oturum yardımcıları.
|
|
4
|
+
Eski "kanal tek oturumu" düzeninden güvenli göç + oturum anahtarı
|
|
5
|
+
normalizasyonu. Saf fonksiyonlar — test edilebilir.
|
|
6
|
+
|
|
7
|
+
ESKİ DÜZEN: tüm jid/chatId'ler AYNI oturuma bağlıydı → A'nın konuşması
|
|
8
|
+
B'ye bağlam olarak sızıyordu. YENİ DÜZEN: her sohbet kendi oturumunda.
|
|
9
|
+
Göç kuralı: paylaşılan oturum SAHİPTE kalır (sahip jid'i bulunamazsa ilk
|
|
10
|
+
jid'de); diğerleri ayrılır ve o ortak oturuma erişemez — geçmiş
|
|
11
|
+
kayıtlarından da düşürülür ki /open ile açamasınlar. */
|
|
12
|
+
|
|
13
|
+
/* jid içindeki rakamları çıkarır: "905xx:12@s.whatsapp.net" → "905xx" */
|
|
14
|
+
function jidDigits(jid) {
|
|
15
|
+
return String(jid || '')
|
|
16
|
+
.split('@')[0]
|
|
17
|
+
.split(':')[0]
|
|
18
|
+
.replace(/\D/g, '');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* DM oturum anahtarı: LID/cihaz eki yerine GERÇEK numara esas alınır —
|
|
22
|
+
aynı kişi iki ayrı oturum açmasın. pn verilmezse jid'in kendisi döner.
|
|
23
|
+
Gruplar kendi jid'iyle ayrılır. */
|
|
24
|
+
function dmSessionKey(jid, pn) {
|
|
25
|
+
const j = String(jid || '');
|
|
26
|
+
if (!j) return '';
|
|
27
|
+
if (j.endsWith('@g.us')) return j;
|
|
28
|
+
const p = jidDigits(pn);
|
|
29
|
+
if (p) return p + '@s.whatsapp.net';
|
|
30
|
+
const base = j.split('@')[0].split(':')[0];
|
|
31
|
+
if (/^\d+$/.test(base) && j.endsWith('@s.whatsapp.net')) return base + '@s.whatsapp.net';
|
|
32
|
+
return j;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Paylaşılan (birden çok sohbete bağlı) oturumları ayırır.
|
|
36
|
+
chats: Map<jid, sid>, history: Map<jid, sid[]> (yerinde değiştirilir).
|
|
37
|
+
döner: true → değişiklik oldu (diske yazılmalı). */
|
|
38
|
+
function migrateSharedSessions({ chats, history, ownerDigits, historyCap = 20 }) {
|
|
39
|
+
const bySid = new Map();
|
|
40
|
+
for (const [j, s] of chats) {
|
|
41
|
+
const sid = String(s);
|
|
42
|
+
if (!bySid.has(sid)) bySid.set(sid, []);
|
|
43
|
+
bySid.get(sid).push(String(j));
|
|
44
|
+
}
|
|
45
|
+
const own = String(ownerDigits || '').replace(/\D/g, '');
|
|
46
|
+
let dirty = false;
|
|
47
|
+
for (const [sid, jids] of bySid) {
|
|
48
|
+
if (jids.length < 2) continue;
|
|
49
|
+
/* sahip jid'i ortak oturumda kalır; sahip yoksa kanonik numara jid'i
|
|
50
|
+
(@s.whatsapp.net), o da yoksa ilk jid (mevcut bağlam korunur) */
|
|
51
|
+
const keep =
|
|
52
|
+
(own && jids.find((j) => jidDigits(j).endsWith(own))) ||
|
|
53
|
+
jids.find((j) => j.endsWith('@s.whatsapp.net')) ||
|
|
54
|
+
jids[0];
|
|
55
|
+
for (const j of jids) {
|
|
56
|
+
if (j === keep) continue;
|
|
57
|
+
chats.delete(j);
|
|
58
|
+
const h = (history.get(j) || []).filter((x) => String(x) !== sid);
|
|
59
|
+
if (h.length) history.set(j, h.slice(-historyCap));
|
|
60
|
+
else history.delete(j);
|
|
61
|
+
dirty = true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return dirty;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Birden çok kanal oturumunun sid'lerini tek Set'te toplar: otomatik işler
|
|
68
|
+
(cron/izleyici/fallout) bu oturumlara ASLA enjekte edilmez. */
|
|
69
|
+
function collectSessionIds(...maps) {
|
|
70
|
+
const set = new Set();
|
|
71
|
+
for (const m of maps) {
|
|
72
|
+
if (!m) continue;
|
|
73
|
+
for (const s of m.values()) if (s) set.add(String(s));
|
|
74
|
+
}
|
|
75
|
+
return set;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { jidDigits, dmSessionKey, migrateSharedSessions, collectSessionIds };
|
package/src/agent/engine.js
CHANGED
|
@@ -1522,9 +1522,13 @@ class Engine {
|
|
|
1522
1522
|
}
|
|
1523
1523
|
|
|
1524
1524
|
/* "Nerede kaldım?" (#5): en son aktif oturumun özeti + yarım kalan todolar.
|
|
1525
|
-
Saf toplama — gönderim/otomatik-devam main'e aittir.
|
|
1525
|
+
Saf toplama — gönderim/otomatik-devam main'e aittir. exclude: bu id'ler
|
|
1526
|
+
(kanal kişi sohbetleri) atlanır — özet yanlış kişiye gitmez. */
|
|
1527
|
+
lastWhereWasI(exclude) {
|
|
1528
|
+
const skip = exclude instanceof Set ? exclude : null;
|
|
1526
1529
|
let latest = null;
|
|
1527
1530
|
for (const v of this.listSessions()) {
|
|
1531
|
+
if (skip && skip.has(String(v.id))) continue;
|
|
1528
1532
|
if (!latest || String(v.updatedAt) > String(latest.updatedAt)) latest = v;
|
|
1529
1533
|
}
|
|
1530
1534
|
if (!latest) return null;
|
package/src/main.js
CHANGED
|
@@ -30,6 +30,7 @@ const piper = require('./agent/piper');
|
|
|
30
30
|
const fsguard = require('./agent/fsguard');
|
|
31
31
|
const log = require('./agent/logger');
|
|
32
32
|
const squeeze = require('./agent/squeeze');
|
|
33
|
+
const chansessions = require('./agent/channelsessions');
|
|
33
34
|
const QRCode = require('qrcode'); /* Expo Go QR (bc-expurl) — whatsapp ile aynı paket */
|
|
34
35
|
|
|
35
36
|
/* Renderer'a sır gönderirken kullanılan maske; kaydederken aynen geri gelirse
|
|
@@ -124,28 +125,20 @@ function notifyVersionUpdate() {
|
|
|
124
125
|
win.webContents.send('agent:event', { sessionId: sid, type: 'done', usage: null });
|
|
125
126
|
}
|
|
126
127
|
} catch {}
|
|
127
|
-
/* kanallar:
|
|
128
|
+
/* kanallar: SAHİBE tek satır (kişi sohbetleri kanal oturumudur —
|
|
129
|
+
sürüm duyurusu müşteri/kişi sohbetlerine KARIŞMAZ) */
|
|
128
130
|
(async () => {
|
|
129
131
|
try {
|
|
130
132
|
if (wa && wa.connected) {
|
|
131
133
|
const own = waOwnerNum();
|
|
132
|
-
|
|
133
|
-
if (jid && [...waChats.entries()].some(([j, s]) => j === jid && String(s) === String(sid))) {
|
|
134
|
-
await sendWaSafe(jid, waStyleTxt);
|
|
135
|
-
}
|
|
134
|
+
if (own) await sendWaSafe(own + '@s.whatsapp.net', waStyleTxt);
|
|
136
135
|
}
|
|
137
136
|
} catch {}
|
|
138
137
|
try {
|
|
139
|
-
if (tg && tg.connected)
|
|
140
|
-
const hit = [...tgChats.entries()].find(([, s]) => String(s) === String(sid));
|
|
141
|
-
if (hit) await sendTgSafe(hit[0], waStyleTxt);
|
|
142
|
-
}
|
|
138
|
+
if (tg && tg.connected) for (const id of tgOwnerIds()) await sendTgSafe(id, waStyleTxt);
|
|
143
139
|
} catch {}
|
|
144
140
|
try {
|
|
145
|
-
if (dc && dc.connected)
|
|
146
|
-
const hit = [...dcChats.entries()].find(([, s]) => String(s) === String(sid));
|
|
147
|
-
if (hit) await sendDcSafe(hit[0], waStyleTxt);
|
|
148
|
-
}
|
|
141
|
+
if (dc && dc.connected) for (const id of dcOwnerIds()) await sendDcSafe(id, waStyleTxt);
|
|
149
142
|
} catch {}
|
|
150
143
|
})();
|
|
151
144
|
}
|
|
@@ -325,24 +318,26 @@ startHealthServer(); /* splash/boot aşamasından itibaren /health ayakta */
|
|
|
325
318
|
try { setSearchChain(settings.searchChain); } catch {}
|
|
326
319
|
try { setTinyfishKey(settings.tinyfishKey || null); } catch {}
|
|
327
320
|
let wa = null;
|
|
328
|
-
let waChats = new Map(); // jid ->
|
|
329
|
-
let
|
|
321
|
+
let waChats = new Map(); // jid (numara/grup) -> KENDİ oturumu: her sohbet AYRI (gizlilik)
|
|
322
|
+
let waCronSid = null; // WhatsApp CRON TABAN OTURUMU: otomatik işler; hiçbir kişiye bağlı DEĞİL
|
|
330
323
|
let waHistory = new Map(); // jid -> [sid,...] bu sohbete ait tüm oturumlar
|
|
331
324
|
let waBcMode = new Set(); // jid -> BeastCode modu AKTİF (WhatsApp'tan uzaktan kodlama)
|
|
332
325
|
let waJidPn = new Map(); // jid -> gerçek telefon numarası (LID fallback için)
|
|
333
|
-
let waLastActiveJid = ''; // en son mesaj gelen WA sohbeti —
|
|
326
|
+
let waLastActiveJid = ''; // en son mesaj gelen WA sohbeti — cevap/dosya hedefi önceliği
|
|
334
327
|
let desktopActiveSid = ''; // masaüstü UI'da AÇIK olan oturum — proaktif not buraya işlenir
|
|
335
328
|
const WA_HISTORY_CAP = 20;
|
|
336
329
|
let tg = null;
|
|
337
|
-
let tgChats = new Map(); // telegram chatId ->
|
|
338
|
-
let
|
|
330
|
+
let tgChats = new Map(); // telegram chatId -> KENDİ oturumu: her sohbet AYRI (gizlilik)
|
|
331
|
+
let tgCronSid = null; // Telegram CRON TABAN OTURUMU: otomatik işler; hiçbir kişiye bağlı DEĞİL
|
|
339
332
|
let tgHistory = new Map(); // chatId -> [sid,...]
|
|
340
333
|
const TG_HISTORY_CAP = 20;
|
|
334
|
+
let tgLastActiveChatId = ''; // en son mesaj gelen TG sohbeti — cron yansıtma hedefi
|
|
341
335
|
let dc = null;
|
|
342
|
-
let dcChats = new Map(); // discord channelId ->
|
|
343
|
-
let
|
|
336
|
+
let dcChats = new Map(); // discord channelId -> KENDİ oturumu: her kanal AYRI (gizlilik)
|
|
337
|
+
let dcCronSid = null; // Discord CRON TABAN OTURUMU: otomatik işler; hiçbir kişiye bağlı DEĞİL
|
|
344
338
|
let dcHistory = new Map(); // channelId -> [sid,...]
|
|
345
339
|
const DC_HISTORY_CAP = 20;
|
|
340
|
+
let dcLastActiveChannelId = ''; // en son mesaj gelen DC kanalı — cron yansıtma hedefi
|
|
346
341
|
const DC_CHATS_FILE = path.join(APP_DIR, 'dc-chats.json');
|
|
347
342
|
let tray = null;
|
|
348
343
|
app.isQuitting = false;
|
|
@@ -379,14 +374,16 @@ try {
|
|
|
379
374
|
if (!h.includes(s)) h.push(s);
|
|
380
375
|
waHistory.set(j, h.slice(-WA_HISTORY_CAP));
|
|
381
376
|
}
|
|
382
|
-
/*
|
|
383
|
-
if (typeof raw.
|
|
377
|
+
/* CRON TABAN OTURUMU: kişi sohbetlerinden tamamen bağımsız oturum */
|
|
378
|
+
if (typeof raw.cron === 'string' && raw.cron) waCronSid = raw.cron;
|
|
384
379
|
/* BeastCode modu: hangi sohbetler uzaktan kodlama yapıyor */
|
|
385
380
|
if (Array.isArray(raw.bcMode)) {
|
|
386
381
|
for (const j of raw.bcMode) if (typeof j === 'string') waBcMode.add(j);
|
|
387
382
|
}
|
|
388
383
|
}
|
|
389
384
|
} catch {}
|
|
385
|
+
/* GÖÇ: eski "tek oturum" düzeni → kişi başına oturum (sahip bağlamı korunur) */
|
|
386
|
+
try { waMigrateSharedSessions(); } catch {}
|
|
390
387
|
|
|
391
388
|
function saveWaChats() {
|
|
392
389
|
try {
|
|
@@ -396,7 +393,7 @@ function saveWaChats() {
|
|
|
396
393
|
chats: Object.fromEntries(waChats),
|
|
397
394
|
history: Object.fromEntries([...waHistory.entries()].map(([j, a]) => [j, a.slice(-WA_HISTORY_CAP)])),
|
|
398
395
|
bcMode: [...waBcMode],
|
|
399
|
-
|
|
396
|
+
cron: waCronSid || '',
|
|
400
397
|
})
|
|
401
398
|
);
|
|
402
399
|
} catch {}
|
|
@@ -409,6 +406,23 @@ function waRememberSession(jid, sid) {
|
|
|
409
406
|
waHistory.set(jid, h.slice(-WA_HISTORY_CAP));
|
|
410
407
|
}
|
|
411
408
|
|
|
409
|
+
/* Oturum anahtarı: DM'lerde GERÇEK numara esas alınır — LID/cihaz eki aynı
|
|
410
|
+
kişiyi iki oturuma bölmesin; gruplar kendi jid'iyle ayrı kalır. */
|
|
411
|
+
function waSessionKey(jid) {
|
|
412
|
+
return chansessions.dmSessionKey(jid, waJidPn.get(String(jid || '')));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/* GÖÇ (tek oturum → kişi başına oturum): ortak oturum SAHİPTE kalır;
|
|
416
|
+
diğer sohbetler ayrılır, o oturuma erişemez (geçmişten de düşürülür). */
|
|
417
|
+
function waMigrateSharedSessions() {
|
|
418
|
+
return chansessions.migrateSharedSessions({
|
|
419
|
+
chats: waChats,
|
|
420
|
+
history: waHistory,
|
|
421
|
+
ownerDigits: waOwnerNum(),
|
|
422
|
+
historyCap: WA_HISTORY_CAP,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
412
426
|
/* ---------- BEASTCODE MODU (WA'dan uzaktan kodlama) ----------
|
|
413
427
|
/beastcode → sohbet BEAST CODE oturumuna döner: masaüstü IDE paneliyle
|
|
414
428
|
AYNI motor (engine bcCode + buildBcSystem) — todo planı, edit_file/run_command
|
|
@@ -428,15 +442,37 @@ function waBcWorkspace() {
|
|
|
428
442
|
canlı akar; panelde yazılan da WhatsApp'a düşer */
|
|
429
443
|
function waBcSession(jid) {
|
|
430
444
|
const s = bcGetSession(waBcWorkspace());
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
445
|
+
const key = waSessionKey(jid);
|
|
446
|
+
if (waChats.get(key) !== s.id) {
|
|
447
|
+
waChats.set(key, s.id);
|
|
448
|
+
waRememberSession(key, s.id);
|
|
434
449
|
saveWaChats();
|
|
435
450
|
if (wa) wa.setWatchJids([...waChats.keys()]);
|
|
436
451
|
}
|
|
437
452
|
return s;
|
|
438
453
|
}
|
|
439
454
|
|
|
455
|
+
/* /beastagent: kişinin KENDİ sohbet oturumuna dön (Beast Code oturumundan çık).
|
|
456
|
+
Geçmişteki en güncel yaşayan sohbet oturumu kullanılır; yoksa taze açılır. */
|
|
457
|
+
function waRestoreChatSession(jid) {
|
|
458
|
+
const key = waSessionKey(jid);
|
|
459
|
+
const bcSid = waChats.get(key);
|
|
460
|
+
const hist = [...(waHistory.get(key) || [])].reverse();
|
|
461
|
+
for (const h of hist) {
|
|
462
|
+
if (!h || h === bcSid || !sessionFileAlive(h)) continue;
|
|
463
|
+
try {
|
|
464
|
+
const s = engine.cache.get(h) || engine._load(h);
|
|
465
|
+
if (s && s.bcCode) continue; /* Beast Code oturumlarına dönülmez */
|
|
466
|
+
waChats.set(key, h);
|
|
467
|
+
saveWaChats();
|
|
468
|
+
if (wa) wa.setWatchJids([...waChats.keys()]);
|
|
469
|
+
return h;
|
|
470
|
+
} catch {}
|
|
471
|
+
}
|
|
472
|
+
waChats.delete(key);
|
|
473
|
+
return ensureWaSession(jid);
|
|
474
|
+
}
|
|
475
|
+
|
|
440
476
|
function settingsLog(line) {
|
|
441
477
|
try {
|
|
442
478
|
fs.appendFileSync(
|
|
@@ -1416,49 +1452,53 @@ async function tryWaSlash(jid, rawText, senderNum, payload0) {
|
|
|
1416
1452
|
if (cmd === 'help') {
|
|
1417
1453
|
out = waSlashHelp();
|
|
1418
1454
|
} else if (cmd === 'new') {
|
|
1419
|
-
/* /new:
|
|
1420
|
-
|
|
1421
|
-
const
|
|
1455
|
+
/* /new: YALNIZ bu sohbetin oturumu kapatılır, bu sohbete taze oturum
|
|
1456
|
+
açılır — diğer numaraların/grupların oturumlarına DOKUNULMAZ */
|
|
1457
|
+
const key = waSessionKey(jid);
|
|
1458
|
+
const oldSid = waChats.get(key) || '';
|
|
1422
1459
|
if (oldSid) {
|
|
1423
1460
|
let olds = null;
|
|
1424
1461
|
try { olds = engine.cache.get(oldSid) || null; } catch {}
|
|
1425
1462
|
if (!olds || !olds.bcCode) {
|
|
1426
1463
|
/* Beast Code panel oturumuysa dokunma — yalnız pointer taşınır */
|
|
1427
1464
|
try { engine.deleteSession(oldSid); } catch {}
|
|
1428
|
-
|
|
1465
|
+
const h = (waHistory.get(key) || []).filter((x) => x !== oldSid);
|
|
1466
|
+
if (h.length) waHistory.set(key, h);
|
|
1467
|
+
else waHistory.delete(key);
|
|
1429
1468
|
}
|
|
1430
1469
|
}
|
|
1431
1470
|
const v = engine.createSession();
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
waChats.set(jid, v.id);
|
|
1435
|
-
waRememberSession(jid, v.id);
|
|
1471
|
+
waChats.set(key, v.id);
|
|
1472
|
+
waRememberSession(key, v.id);
|
|
1436
1473
|
saveWaChats();
|
|
1437
1474
|
wa.setWatchJids([...waChats.keys()]);
|
|
1438
|
-
out = `*Yeni oturum* \`${v.code}\` açıldı —
|
|
1475
|
+
out = `*Yeni oturum* \`${v.code}\` açıldı — bu sohbet artık buradan devam eder.\nGeçmiş: \`/sessions\` · Var olan oturuma geçiş: \`/open <kod>\``;
|
|
1439
1476
|
} else if (cmd === 'open') {
|
|
1440
1477
|
if (!arg) {
|
|
1441
1478
|
out = 'Kullanım: `/open <kod>` — kodları görmek için /sessions';
|
|
1442
1479
|
} else {
|
|
1443
1480
|
const hit = engine.findByCode(arg);
|
|
1444
|
-
|
|
1445
|
-
|
|
1481
|
+
const key = waSessionKey(jid);
|
|
1482
|
+
/* GİZLİLİK: yalnız BU sohbetin kendi geçmişindeki oturuma geçilebilir */
|
|
1483
|
+
const own = new Set([...(waHistory.get(key) || []), waChats.get(key) || '']);
|
|
1484
|
+
if (!hit || !own.has(hit.id)) {
|
|
1485
|
+
out = `\`${arg.toUpperCase()}\` bu sohbete ait bir oturum değil. Listeyi görmek için /sessions`;
|
|
1446
1486
|
} else {
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
for (const j of [...waChats.keys()]) waChats.set(j, hit.id);
|
|
1450
|
-
waRememberSession(jid, hit.id);
|
|
1487
|
+
waChats.set(key, hit.id);
|
|
1488
|
+
waRememberSession(key, hit.id);
|
|
1451
1489
|
saveWaChats();
|
|
1452
1490
|
wa.setWatchJids([...waChats.keys()]);
|
|
1453
1491
|
out = `*Geçildi:* \`${hit.code}\` — ${escapeWa(hit.title)}`;
|
|
1454
1492
|
}
|
|
1455
1493
|
}
|
|
1456
1494
|
} else if (cmd === 'sessions') {
|
|
1457
|
-
const
|
|
1458
|
-
const
|
|
1495
|
+
const key = waSessionKey(jid);
|
|
1496
|
+
const h = [...new Set([...(waHistory.get(key) || []), ...(waChats.has(key) ? [waChats.get(key)] : [])])];
|
|
1497
|
+
const activeSid = waChats.get(key);
|
|
1459
1498
|
const rows = [];
|
|
1460
1499
|
for (const sid of h.slice(-8).reverse()) {
|
|
1461
1500
|
try {
|
|
1501
|
+
if (!sessionFileAlive(sid)) continue;
|
|
1462
1502
|
const s = engine.openSession(sid);
|
|
1463
1503
|
rows.push(`${s.id === activeSid ? '\u2726' : '-'} \`${s.code || '?'}\` ${escapeWa(s.title).slice(0, 40)}${s.id === activeSid ? ' *(aktif)*' : ''}`);
|
|
1464
1504
|
} catch {}
|
|
@@ -1591,7 +1631,7 @@ async function tryWaSlash(jid, rawText, senderNum, payload0) {
|
|
|
1591
1631
|
} else if (cmd === 'change') {
|
|
1592
1632
|
out = modelChangeText(arg);
|
|
1593
1633
|
} else if (cmd === 'notes') {
|
|
1594
|
-
const sid = waChats.get(jid);
|
|
1634
|
+
const sid = waChats.get(waSessionKey(jid));
|
|
1595
1635
|
out = sid ? notesText(sid) : 'Bu sohbetin oturumu yok — önce bir şeyler yaz.';
|
|
1596
1636
|
} else if (cmd === 'notify') {
|
|
1597
1637
|
const a = String(arg || '').toLowerCase();
|
|
@@ -1602,7 +1642,7 @@ async function tryWaSlash(jid, rawText, senderNum, payload0) {
|
|
|
1602
1642
|
}
|
|
1603
1643
|
out = `Hata mail bildirimi: ${settings.notifyOwnerFail !== false ? 'AÇIK' : 'KAPALI'} (değiştirmek için /notify on|off)`;
|
|
1604
1644
|
} else if (cmd === 'clear') {
|
|
1605
|
-
const sid = waChats.get(jid);
|
|
1645
|
+
const sid = waChats.get(waSessionKey(jid));
|
|
1606
1646
|
if (sid && engine.clearMessages(sid)) {
|
|
1607
1647
|
out = '*Bu oturumun geçmişi temizlendi.* Aynı kodla sıfırdan devam edebilirsin.';
|
|
1608
1648
|
} else {
|
|
@@ -1732,7 +1772,7 @@ async function tryWaSlash(jid, rawText, senderNum, payload0) {
|
|
|
1732
1772
|
? `*Tüm oturumların todoları temizlendi* (${n} oturum, ${items} madde).`
|
|
1733
1773
|
: 'Temizlenecek todo yok.';
|
|
1734
1774
|
} else {
|
|
1735
|
-
const sid = waChats.get(jid) ||
|
|
1775
|
+
const sid = waChats.get(waSessionKey(jid)) || '';
|
|
1736
1776
|
if (!sid) {
|
|
1737
1777
|
out = 'Bu sohbette açık oturum yok — todo listesi de yok.';
|
|
1738
1778
|
} else {
|
|
@@ -1774,14 +1814,13 @@ async function tryWaSlash(jid, rawText, senderNum, payload0) {
|
|
|
1774
1814
|
if (!waBcMode.has(jid)) {
|
|
1775
1815
|
out = 'Zaten sohbet modundasın — kodlamak için /beastcode yaz.';
|
|
1776
1816
|
} else {
|
|
1777
|
-
const sid = waChats.get(jid);
|
|
1817
|
+
const sid = waChats.get(waSessionKey(jid));
|
|
1778
1818
|
if (sid && engine.isBusy(sid)) {
|
|
1779
1819
|
try { engine.interrupt(sid, 'kullanıcı /beastagent ile sohbet moduna döndü'); } catch {}
|
|
1780
1820
|
}
|
|
1781
1821
|
waBcMode.delete(jid);
|
|
1782
|
-
/*
|
|
1783
|
-
|
|
1784
|
-
const chatSid = ensureWaSession(jid);
|
|
1822
|
+
/* KİŞİ BAŞINA OTURUM: kişinin kendi sohbet oturumuna dönülür */
|
|
1823
|
+
const chatSid = waRestoreChatSession(jid);
|
|
1785
1824
|
const chatS = engine.cache.get(chatSid);
|
|
1786
1825
|
saveWaChats();
|
|
1787
1826
|
if (wa) wa.setWatchJids([...waChats.keys()]);
|
|
@@ -1794,7 +1833,7 @@ async function tryWaSlash(jid, rawText, senderNum, payload0) {
|
|
|
1794
1833
|
} else if (waBcMode.has(jid) && (cmd === 'plan' || cmd === 'build' || cmd === 'auto')) {
|
|
1795
1834
|
/* opencode ajan değişimi (WA): /plan → plan ajanı (salt-okur),
|
|
1796
1835
|
/build · /auto → build ajanı (varsayılan; auto opencode'da yoktur) */
|
|
1797
|
-
const sid = waChats.get(jid);
|
|
1836
|
+
const sid = waChats.get(waSessionKey(jid));
|
|
1798
1837
|
const s = sid ? engine.cache.get(sid) : null;
|
|
1799
1838
|
if (s && s.bcCode) {
|
|
1800
1839
|
const agent = engine.setBcAgent(s.id, cmd === 'plan' ? 'plan' : 'build');
|
|
@@ -1899,12 +1938,11 @@ function waGroupSenderInfo(payload) {
|
|
|
1899
1938
|
return { label, name: (hitP && hitP.name) || '', isOwner: !!(hitP && hitP.owner) };
|
|
1900
1939
|
}
|
|
1901
1940
|
|
|
1902
|
-
/* ---------- KANAL
|
|
1903
|
-
Her
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
Masaüstü chat UI'daki "+ Yeni Sohbet" bu kuralın dışındadır. */
|
|
1941
|
+
/* ---------- KİŞİ BAŞINA KANAL OTURUMU (WA/TG/DC) ----------
|
|
1942
|
+
Her numara/grup/sohbet KENDİ oturumunda konuşur — A'nın geçmişi B'ye
|
|
1943
|
+
bağlam olarak sızmaz. Oturum yalnızca o sohbet silinirse yenilenir;
|
|
1944
|
+
cron/otomatik işler ayrı "taban oturumlarında" koşar (bkz. cronBaseSession).
|
|
1945
|
+
Eski "tek oturum" düzeni açılışta güvenli biçimde göç ettirilir. */
|
|
1908
1946
|
function sessionFileAlive(sid) {
|
|
1909
1947
|
try {
|
|
1910
1948
|
return !!sid && fs.existsSync(path.join(engine.sessionsDir, String(sid) + '.jsonl'));
|
|
@@ -1912,9 +1950,6 @@ function sessionFileAlive(sid) {
|
|
|
1912
1950
|
return false;
|
|
1913
1951
|
}
|
|
1914
1952
|
}
|
|
1915
|
-
|
|
1916
|
-
/* kanalın yaşayan tek oturumunu döndürür; geçersizse eski bağlamdan devralır,
|
|
1917
|
-
o da yoksa (ilk kullanım) yeni açar ve tüm jid'leri ona bağlar */
|
|
1918
1953
|
/* WA'dan gelen belge orijinallerini oturumun media klasörüne yazar;
|
|
1919
1954
|
döner: media/<sid>/<dosya> (relatif yol — ajan read_file ile açabilir) */
|
|
1920
1955
|
function saveSessionMedia(sid, name, buf) {
|
|
@@ -1932,54 +1967,40 @@ function saveSessionMedia(sid, name, buf) {
|
|
|
1932
1967
|
}
|
|
1933
1968
|
}
|
|
1934
1969
|
|
|
1970
|
+
/* KİŞİ BAŞINA OTURUM: jid yoksa CRON TABAN OTURUMU döner (otomatik işler
|
|
1971
|
+
hiçbir kişinin sohbetine karışmaz); varsa o sohbetin kendi oturumu açılır. */
|
|
1935
1972
|
function ensureWaSession(jid) {
|
|
1936
|
-
if (
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
saveWaChats();
|
|
1953
|
-
}
|
|
1954
|
-
let dirty = false;
|
|
1955
|
-
for (const [j, s] of waChats.entries()) {
|
|
1956
|
-
if (s !== waSingleSid) {
|
|
1957
|
-
waChats.set(j, waSingleSid);
|
|
1958
|
-
dirty = true;
|
|
1959
|
-
}
|
|
1960
|
-
}
|
|
1961
|
-
if (jid != null && waChats.get(String(jid)) !== waSingleSid) {
|
|
1962
|
-
waChats.set(String(jid), waSingleSid);
|
|
1963
|
-
dirty = true;
|
|
1964
|
-
}
|
|
1965
|
-
/* jid'siz çağrı (cron taban oturumu) sohbet geçmişini kirletmez */
|
|
1966
|
-
if (jid != null) waRememberSession(String(jid), waSingleSid);
|
|
1967
|
-
if (dirty) {
|
|
1973
|
+
if (jid == null || jid === '') return ensureWaCronSession();
|
|
1974
|
+
const key = waSessionKey(jid);
|
|
1975
|
+
const sid = waChats.get(key);
|
|
1976
|
+
if (sid && sessionFileAlive(sid)) return sid;
|
|
1977
|
+
const v = engine.createSession();
|
|
1978
|
+
waChats.set(key, v.id);
|
|
1979
|
+
waRememberSession(key, v.id);
|
|
1980
|
+
saveWaChats();
|
|
1981
|
+
if (wa) wa.setWatchJids([...waChats.keys()]);
|
|
1982
|
+
return v.id;
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
/* Cron/otomatik işlerin WhatsApp taban oturumu: hiçbir jid'e bağlı DEĞİL */
|
|
1986
|
+
function ensureWaCronSession() {
|
|
1987
|
+
if (!waCronSid || !sessionFileAlive(waCronSid)) {
|
|
1988
|
+
waCronSid = engine.createSession().id;
|
|
1968
1989
|
saveWaChats();
|
|
1969
|
-
if (wa) wa.setWatchJids([...waChats.keys()]);
|
|
1970
1990
|
}
|
|
1971
|
-
return
|
|
1991
|
+
return waCronSid;
|
|
1972
1992
|
}
|
|
1973
1993
|
|
|
1974
|
-
/*
|
|
1975
|
-
|
|
1976
|
-
yoksa (restart vb.) ilk bağlı sohbet kullanılır. null = bağlı sohbet yok. */
|
|
1994
|
+
/* Cevap/dosya hedefi: oturuma bağlı sohbetler arasında EN SON mesaj gelen
|
|
1995
|
+
önceliklidir; o bilgi yoksa (restart vb.) bağlı sohbet kullanılır. */
|
|
1977
1996
|
function waReplyJid(sid) {
|
|
1997
|
+
const s = String(sid || '');
|
|
1978
1998
|
try {
|
|
1979
|
-
|
|
1999
|
+
const lastKey = waLastActiveJid ? waSessionKey(waLastActiveJid) : '';
|
|
2000
|
+
if (lastKey && String(waChats.get(lastKey) || '') === s) return lastKey;
|
|
1980
2001
|
} catch {}
|
|
1981
|
-
for (const [j,
|
|
1982
|
-
if (
|
|
2002
|
+
for (const [j, v] of waChats) {
|
|
2003
|
+
if (String(v) === s) return j;
|
|
1983
2004
|
}
|
|
1984
2005
|
return null;
|
|
1985
2006
|
}
|
|
@@ -2382,6 +2403,9 @@ async function handleWaIncoming(jid, payload, senderNum) {
|
|
|
2382
2403
|
if (!engine) return;
|
|
2383
2404
|
if (typeof payload === 'string') payload = { text: payload };
|
|
2384
2405
|
const isGroup = !!payload.isGroup || jid.endsWith('@g.us');
|
|
2406
|
+
/* LID → gerçek numara eşlemesi SLASH KOMUTLARINDAN ÖNCE kurulmalı:
|
|
2407
|
+
oturum anahtarı (/new, /sessions, /open) gerçek numaraya göre çalışır */
|
|
2408
|
+
if (!isGroup && senderNum) waJidPn.set(jid, String(senderNum));
|
|
2385
2409
|
/* İzin listesi kaydı — grup kapıları ve doğal dil komutları bunu kullanır:
|
|
2386
2410
|
izinli değilse yalnız sohbet; agentic iş yaptıramaz. */
|
|
2387
2411
|
const senderHit = waFind(senderNum);
|
|
@@ -2575,15 +2599,8 @@ async function processWaMessage(jid, payload, senderNum, requeues = 0) {
|
|
|
2575
2599
|
waLog(`skip flush: izinli eşleşme yok (sender=+${senderNum || '?'})`);
|
|
2576
2600
|
return;
|
|
2577
2601
|
}
|
|
2578
|
-
/*
|
|
2579
|
-
|
|
2580
|
-
Sıra korunur: floş katmanı debounceli ve sıralı işler. */
|
|
2581
|
-
let sid = waChats.get(jid) || waSingleSid;
|
|
2582
|
-
if (!isGroup && requeues > 0) {
|
|
2583
|
-
// retry sonrası izin yeniden kontrol edilir (yukarıda hit zaten alınıyor)
|
|
2584
|
-
}
|
|
2585
|
-
/* KANAL TEK OTURUMU: eskisi silinmedikçe hep aynı oturumdan devam */
|
|
2586
|
-
sid = ensureWaSession(jid);
|
|
2602
|
+
/* KİŞİ BAŞINA OTURUM: her numara/grup kendi oturumunda; oturum yoksa açılır */
|
|
2603
|
+
const sid = ensureWaSession(jid);
|
|
2587
2604
|
waLastActiveJid = String(jid); // cevap/dosya hedefi: en son yazan sohbet
|
|
2588
2605
|
// Cevap verilecek — karşı telefonda "yazıyor…" göstergesi (medya işlenene dek sürer)
|
|
2589
2606
|
wa.setComposing(jid, true);
|
|
@@ -2792,16 +2809,31 @@ function ensureWa() {
|
|
|
2792
2809
|
if (Array.isArray(arr)) tgHistory.set(c, arr.filter((x) => typeof x === 'string').slice(-TG_HISTORY_CAP));
|
|
2793
2810
|
}
|
|
2794
2811
|
}
|
|
2795
|
-
/*
|
|
2796
|
-
if (raw && typeof raw.
|
|
2812
|
+
/* CRON TABAN OTURUMU: kişi sohbetlerinden tamamen bağımsız oturum */
|
|
2813
|
+
if (raw && typeof raw.cron === 'string' && raw.cron) tgCronSid = raw.cron;
|
|
2797
2814
|
for (const [c, s] of tgChats.entries()) {
|
|
2798
2815
|
const h = tgHistory.get(c) || [];
|
|
2799
2816
|
if (!h.includes(s)) h.push(s);
|
|
2800
2817
|
tgHistory.set(c, h.slice(-TG_HISTORY_CAP));
|
|
2801
2818
|
}
|
|
2802
2819
|
} catch {}
|
|
2820
|
+
/* GÖÇ: eski "tek oturum" düzeni → kişi başına oturum (sahip bağlamı korunur) */
|
|
2821
|
+
try {
|
|
2822
|
+
tgMigrateSharedSessions();
|
|
2823
|
+
} catch {}
|
|
2803
2824
|
})();
|
|
2804
2825
|
|
|
2826
|
+
/* GÖÇ (Telegram): ortak oturum sahipte kalır; diğer sohbetler ayrılır. */
|
|
2827
|
+
function tgMigrateSharedSessions() {
|
|
2828
|
+
const owners = tgOwnerIds();
|
|
2829
|
+
return chansessions.migrateSharedSessions({
|
|
2830
|
+
chats: tgChats,
|
|
2831
|
+
history: tgHistory,
|
|
2832
|
+
ownerDigits: (owners[0] || '').replace(/\D/g, ''),
|
|
2833
|
+
historyCap: TG_HISTORY_CAP,
|
|
2834
|
+
});
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2805
2837
|
function saveTgChats() {
|
|
2806
2838
|
try {
|
|
2807
2839
|
fs.writeFileSync(
|
|
@@ -2809,7 +2841,7 @@ function saveTgChats() {
|
|
|
2809
2841
|
JSON.stringify({
|
|
2810
2842
|
chats: Object.fromEntries(tgChats),
|
|
2811
2843
|
history: Object.fromEntries([...tgHistory.entries()].map(([c, a]) => [c, a.slice(-TG_HISTORY_CAP)])),
|
|
2812
|
-
|
|
2844
|
+
cron: tgCronSid || '',
|
|
2813
2845
|
})
|
|
2814
2846
|
);
|
|
2815
2847
|
} catch {}
|
|
@@ -2891,11 +2923,9 @@ async function processTgMessage(chatId, payload, requeues = 0) {
|
|
|
2891
2923
|
.catch(() => {});
|
|
2892
2924
|
return;
|
|
2893
2925
|
}
|
|
2894
|
-
/*
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
/* KANAL TEK OTURUMU: eskisi silinmedikçe hep aynı oturumdan devam */
|
|
2898
|
-
sid = ensureTgSession(chatId);
|
|
2926
|
+
/* KİŞİ BAŞINA OTURUM: her chatId kendi oturumunda; oturum yoksa açılır */
|
|
2927
|
+
const sid = ensureTgSession(chatId);
|
|
2928
|
+
tgLastActiveChatId = String(chatId);
|
|
2899
2929
|
/* Kişi bazlı granül izin: all/web/read/chat */
|
|
2900
2930
|
let perm = hit.perm || (hit.lockdown ? 'chat' : 'all');
|
|
2901
2931
|
engine.setSessionPerm(sid, perm);
|
|
@@ -3018,16 +3048,31 @@ function dcFind(senderId, username) {
|
|
|
3018
3048
|
if (Array.isArray(arr)) dcHistory.set(c, arr.filter((x) => typeof x === 'string').slice(-DC_HISTORY_CAP));
|
|
3019
3049
|
}
|
|
3020
3050
|
}
|
|
3021
|
-
/*
|
|
3022
|
-
if (raw && typeof raw.
|
|
3051
|
+
/* CRON TABAN OTURUMU: kişi sohbetlerinden tamamen bağımsız oturum */
|
|
3052
|
+
if (raw && typeof raw.cron === 'string' && raw.cron) dcCronSid = raw.cron;
|
|
3023
3053
|
for (const [c, s] of dcChats.entries()) {
|
|
3024
3054
|
const h = dcHistory.get(c) || [];
|
|
3025
3055
|
if (!h.includes(s)) h.push(s);
|
|
3026
3056
|
dcHistory.set(c, h.slice(-DC_HISTORY_CAP));
|
|
3027
3057
|
}
|
|
3028
3058
|
} catch {}
|
|
3059
|
+
/* GÖÇ: eski "tek oturum" düzeni → kişi başına oturum (sahip bağlamı korunur) */
|
|
3060
|
+
try {
|
|
3061
|
+
dcMigrateSharedSessions();
|
|
3062
|
+
} catch {}
|
|
3029
3063
|
})();
|
|
3030
3064
|
|
|
3065
|
+
/* GÖÇ (Discord): ortak oturum sahipte kalır; diğer kanallar ayrılır. */
|
|
3066
|
+
function dcMigrateSharedSessions() {
|
|
3067
|
+
const owners = dcOwnerIds();
|
|
3068
|
+
return chansessions.migrateSharedSessions({
|
|
3069
|
+
chats: dcChats,
|
|
3070
|
+
history: dcHistory,
|
|
3071
|
+
ownerDigits: (owners[0] || '').replace(/\D/g, ''),
|
|
3072
|
+
historyCap: DC_HISTORY_CAP,
|
|
3073
|
+
});
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3031
3076
|
function saveDcChats() {
|
|
3032
3077
|
try {
|
|
3033
3078
|
fs.writeFileSync(
|
|
@@ -3035,7 +3080,7 @@ function saveDcChats() {
|
|
|
3035
3080
|
JSON.stringify({
|
|
3036
3081
|
chats: Object.fromEntries(dcChats),
|
|
3037
3082
|
history: Object.fromEntries([...dcHistory.entries()].map(([c, a]) => [c, a.slice(-DC_HISTORY_CAP)])),
|
|
3038
|
-
|
|
3083
|
+
cron: dcCronSid || '',
|
|
3039
3084
|
})
|
|
3040
3085
|
);
|
|
3041
3086
|
} catch {}
|
|
@@ -3111,11 +3156,9 @@ async function processDcMessage(channelId, payload) {
|
|
|
3111
3156
|
.catch(() => {});
|
|
3112
3157
|
return;
|
|
3113
3158
|
}
|
|
3114
|
-
/*
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
/* KANAL TEK OTURUMU: eskisi silinmedikçe hep aynı oturumdan devam */
|
|
3118
|
-
sid = ensureDcSession(channelId);
|
|
3159
|
+
/* KİŞİ BAŞINA OTURUM: her kanal kendi oturumunda; oturum yoksa açılır */
|
|
3160
|
+
const sid = ensureDcSession(channelId);
|
|
3161
|
+
dcLastActiveChannelId = String(channelId);
|
|
3119
3162
|
/* Kişi bazlı granül izin: all/web/read/chat */
|
|
3120
3163
|
let perm = hit.perm || (hit.lockdown ? 'chat' : 'all');
|
|
3121
3164
|
engine.setSessionPerm(sid, perm);
|
|
@@ -3430,14 +3473,14 @@ function reloadBackend() {
|
|
|
3430
3473
|
} catch {}
|
|
3431
3474
|
try {
|
|
3432
3475
|
if (tg && tg.connected) {
|
|
3433
|
-
const
|
|
3434
|
-
if (
|
|
3476
|
+
const tgid = tgReplyChat(ev.sessionId);
|
|
3477
|
+
if (tgid) await sendTgSafe(tgid, txt);
|
|
3435
3478
|
}
|
|
3436
3479
|
} catch {}
|
|
3437
3480
|
try {
|
|
3438
3481
|
if (dc && dc.connected) {
|
|
3439
|
-
const
|
|
3440
|
-
if (
|
|
3482
|
+
const dchid = dcReplyChannel(ev.sessionId);
|
|
3483
|
+
if (dchid) await sendDcSafe(dchid, txt);
|
|
3441
3484
|
}
|
|
3442
3485
|
} catch {}
|
|
3443
3486
|
})();
|
|
@@ -3445,9 +3488,8 @@ function reloadBackend() {
|
|
|
3445
3488
|
}
|
|
3446
3489
|
// Telegram oturumlarının son cevabını geri gönder (WA ile aynı akış)
|
|
3447
3490
|
if ((ev.type === 'done' || ev.type === 'error') && !cjob && tg && tg.connected) {
|
|
3448
|
-
const
|
|
3449
|
-
if (
|
|
3450
|
-
const tgid = hitT[0];
|
|
3491
|
+
const tgid = tgReplyChat(ev.sessionId);
|
|
3492
|
+
if (tgid) {
|
|
3451
3493
|
(async () => {
|
|
3452
3494
|
try {
|
|
3453
3495
|
if (ev.type === 'error') {
|
|
@@ -3466,9 +3508,8 @@ function reloadBackend() {
|
|
|
3466
3508
|
}
|
|
3467
3509
|
// Discord oturumlarının son cevabını geri gönder (TG ile aynı akış)
|
|
3468
3510
|
if ((ev.type === 'done' || ev.type === 'error') && !cjob && dc && dc.connected) {
|
|
3469
|
-
const
|
|
3470
|
-
if (
|
|
3471
|
-
const dchid = hitD[0];
|
|
3511
|
+
const dchid = dcReplyChannel(ev.sessionId);
|
|
3512
|
+
if (dchid) {
|
|
3472
3513
|
(async () => {
|
|
3473
3514
|
try {
|
|
3474
3515
|
if (ev.type === 'error') {
|
|
@@ -3486,10 +3527,10 @@ function reloadBackend() {
|
|
|
3486
3527
|
}
|
|
3487
3528
|
}
|
|
3488
3529
|
/* CRON → TÜM BAĞLI ENTEGRASYONLAR: cron işi bittiğinde cevap (ya da
|
|
3489
|
-
hata) WhatsApp
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
düşer, yansıtılmaz. */
|
|
3530
|
+
hata) sahibe WhatsApp + Telegram + Discord'dan yansıtılır. Cron
|
|
3531
|
+
oturumu hiçbir kişi sohbetine bağlı olmadığı için ham cevap normal
|
|
3532
|
+
akıştan GİTMEZ — çift gönderim olmaz. Bayat (TTL aşımı) kayıt Take
|
|
3533
|
+
ile düşer, yansıtılmaz. */
|
|
3493
3534
|
if (cjob && !ev.aborted) {
|
|
3494
3535
|
(async () => {
|
|
3495
3536
|
try {
|
|
@@ -3561,7 +3602,9 @@ function falloutResume() {
|
|
|
3561
3602
|
Ayarlardan kapatılabilir (settings.whereWasI.enabled). */
|
|
3562
3603
|
function whereWasISummary() {
|
|
3563
3604
|
try {
|
|
3564
|
-
|
|
3605
|
+
/* KANAL İZOLASYONU: kişi sohbetleri özet dışıdır — özet masaüstü
|
|
3606
|
+
çalışmasından seçilir ve yanlış kişiye gönderilmez */
|
|
3607
|
+
const last = engine.lastWhereWasI(channelSessionIds());
|
|
3565
3608
|
if (!last) return null;
|
|
3566
3609
|
const lines = [];
|
|
3567
3610
|
lines.push(`Son oturum: \`${last.code}\` — ${last.title}`);
|
|
@@ -7412,9 +7455,25 @@ function cronAnswerPendingTake(sid) {
|
|
|
7412
7455
|
yapılır (ensureWa/Tg/DcSession ile aynı kural). Ayrıca meşgul oturuma
|
|
7413
7456
|
send() mesajı DÜŞÜRÜR (false döner, cevap kaybolur) — o yüzden
|
|
7414
7457
|
meşgul oturumlar da atlanır. */
|
|
7458
|
+
/* Kanal (WA/TG/DC) kişi sohbetleri + kanal cron oturumları: otomatik işler
|
|
7459
|
+
(izleyici/fallout/proaktif) bu oturumlara ASLA enjekte edilmez. */
|
|
7460
|
+
function channelSessionIds() {
|
|
7461
|
+
return chansessions.collectSessionIds(
|
|
7462
|
+
waChats,
|
|
7463
|
+
tgChats,
|
|
7464
|
+
dcChats,
|
|
7465
|
+
new Map([
|
|
7466
|
+
['wa-cron', waCronSid],
|
|
7467
|
+
['tg-cron', tgCronSid],
|
|
7468
|
+
['dc-cron', dcCronSid],
|
|
7469
|
+
])
|
|
7470
|
+
);
|
|
7471
|
+
}
|
|
7472
|
+
|
|
7415
7473
|
function reuseOrLatestSession(preferredId) {
|
|
7416
7474
|
const sid = String(preferredId || '');
|
|
7417
|
-
|
|
7475
|
+
const chan = channelSessionIds();
|
|
7476
|
+
if (sid && sessionFileAlive(sid) && !chan.has(sid)) {
|
|
7418
7477
|
try {
|
|
7419
7478
|
if (engine._load(sid) && !engine.isBusy(sid)) return sid;
|
|
7420
7479
|
} catch {}
|
|
@@ -7422,35 +7481,25 @@ function reuseOrLatestSession(preferredId) {
|
|
|
7422
7481
|
try {
|
|
7423
7482
|
const list = engine.listSessions(); // updatedAt'e göre yeni→eski sıralı
|
|
7424
7483
|
for (const v of list) {
|
|
7484
|
+
if (chan.has(String(v.id))) continue; /* kişi sohbetine otomatik iş düşmez */
|
|
7425
7485
|
if (!engine.isBusy(v.id)) return String(v.id);
|
|
7426
7486
|
}
|
|
7427
7487
|
} catch {}
|
|
7428
7488
|
return engine.createSession().id;
|
|
7429
7489
|
}
|
|
7430
7490
|
|
|
7431
|
-
/* CRON TABAN OTURUMU: cron işleri hiçbir
|
|
7432
|
-
Her kanalın
|
|
7433
|
-
|
|
7434
|
-
kullanılmayan kurulumlarda cron altyapısı Telegram/Discord üzerinden
|
|
7435
|
-
yaşar; hiçbiri yoksa masaüstü sohbete düşer. */
|
|
7491
|
+
/* CRON TABAN OTURUMU: cron işleri hiçbir KİŞİ sohbetine/oturumuna BAĞLI DEĞİL.
|
|
7492
|
+
Her kanalın kendine ait ayrı cron oturumu vardır — sıra: WA → TG → DC →
|
|
7493
|
+
en güncel masaüstü oturumu. Cevap done olayında sahibe yansıtılır. */
|
|
7436
7494
|
function cronBaseSession() {
|
|
7437
7495
|
try {
|
|
7438
|
-
if (wa && wa.connected)
|
|
7439
|
-
if (waSingleSid && sessionFileAlive(waSingleSid)) return waSingleSid;
|
|
7440
|
-
return ensureWaSession();
|
|
7441
|
-
}
|
|
7496
|
+
if (wa && wa.connected) return ensureWaCronSession();
|
|
7442
7497
|
} catch {}
|
|
7443
7498
|
try {
|
|
7444
|
-
if (tg && tg.connected)
|
|
7445
|
-
if (tgSingleSid && sessionFileAlive(tgSingleSid)) return tgSingleSid;
|
|
7446
|
-
return ensureTgSession();
|
|
7447
|
-
}
|
|
7499
|
+
if (tg && tg.connected) return ensureTgCronSession();
|
|
7448
7500
|
} catch {}
|
|
7449
7501
|
try {
|
|
7450
|
-
if (dc && dc.connected)
|
|
7451
|
-
if (dcSingleSid && sessionFileAlive(dcSingleSid)) return dcSingleSid;
|
|
7452
|
-
return ensureDcSession();
|
|
7453
|
-
}
|
|
7502
|
+
if (dc && dc.connected) return ensureDcCronSession();
|
|
7454
7503
|
} catch {}
|
|
7455
7504
|
return reuseOrLatestSession('');
|
|
7456
7505
|
}
|
|
@@ -7472,41 +7521,34 @@ function dcOwnerIds() {
|
|
|
7472
7521
|
const owner = objs.find((e) => e.owner) || (objs.length === 1 ? objs[0] : null);
|
|
7473
7522
|
return owner ? [String(owner.id)] : [];
|
|
7474
7523
|
}
|
|
7524
|
+
/* Cron cevabının gideceği yerler: kişi sohbetleri DEĞİL, sahip hedeflenir.
|
|
7525
|
+
(Cron oturumu hiçbir jid/chatId'ye bağlı değildir; ham cevap normal akıştan
|
|
7526
|
+
gitmez — yansıtma burada yapılır.) */
|
|
7475
7527
|
function cronMirrorTargets(cronSid) {
|
|
7476
|
-
const sid = String(cronSid || '');
|
|
7477
7528
|
const out = [];
|
|
7478
|
-
/* WHATSAPP
|
|
7479
|
-
|
|
7480
|
-
hedeflenir — cevap kaybolmaz. */
|
|
7529
|
+
/* WHATSAPP: cron oturumu hiçbir kişiye bağlı değildir — cevap SAHİBE gider.
|
|
7530
|
+
Kişi sohbetlerine cron cevabı KARIŞMAZ. */
|
|
7481
7531
|
try {
|
|
7482
|
-
if (wa && wa.connected
|
|
7532
|
+
if (wa && wa.connected) {
|
|
7483
7533
|
const own = waOwnerNum();
|
|
7484
|
-
const jid =
|
|
7534
|
+
const jid = own ? own + '@s.whatsapp.net' : '';
|
|
7485
7535
|
if (jid) out.push({ kind: 'wa', send: (t) => sendWaSafe(jid, t) });
|
|
7486
7536
|
}
|
|
7487
7537
|
} catch {}
|
|
7488
|
-
/* TELEGRAM:
|
|
7489
|
-
kanalın tek oturumuna bağlı sohbet(ler), yoksa sahip kaydı hedeflenir */
|
|
7538
|
+
/* TELEGRAM: sahip kaydı hedeflenir; sahip tanımsızsa en son yazan sohbet */
|
|
7490
7539
|
try {
|
|
7491
|
-
if (tg && tg.connected
|
|
7492
|
-
const
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
for (const id of (bound.length ? bound : tgOwnerIds())) {
|
|
7496
|
-
out.push({ kind: 'tg', send: (t) => sendTgSafe(id, t) });
|
|
7497
|
-
}
|
|
7540
|
+
if (tg && tg.connected) {
|
|
7541
|
+
const ids = tgOwnerIds();
|
|
7542
|
+
const targets = ids.length ? ids : (tgLastActiveChatId ? [tgLastActiveChatId] : []);
|
|
7543
|
+
for (const id of targets) out.push({ kind: 'tg', send: (t) => sendTgSafe(id, t) });
|
|
7498
7544
|
}
|
|
7499
7545
|
} catch {}
|
|
7500
|
-
/* DISCORD:
|
|
7501
|
-
kanalın tek oturumuna bağlı kanal(lar), yoksa sahip kaydı hedeflenir */
|
|
7546
|
+
/* DISCORD: sahip kaydı hedeflenir; sahip tanımsızsa en son yazan kanal */
|
|
7502
7547
|
try {
|
|
7503
|
-
if (dc && dc.connected
|
|
7504
|
-
const
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
for (const id of (bound.length ? bound : dcOwnerIds())) {
|
|
7508
|
-
out.push({ kind: 'dc', send: (t) => sendDcSafe(id, t) });
|
|
7509
|
-
}
|
|
7548
|
+
if (dc && dc.connected) {
|
|
7549
|
+
const ids = dcOwnerIds();
|
|
7550
|
+
const targets = ids.length ? ids : (dcLastActiveChannelId ? [dcLastActiveChannelId] : []);
|
|
7551
|
+
for (const id of targets) out.push({ kind: 'dc', send: (t) => sendDcSafe(id, t) });
|
|
7510
7552
|
}
|
|
7511
7553
|
} catch {}
|
|
7512
7554
|
return out;
|
|
@@ -7870,76 +7912,70 @@ function empatiInjectText(ev, out) {
|
|
|
7870
7912
|
);
|
|
7871
7913
|
}
|
|
7872
7914
|
|
|
7873
|
-
/*
|
|
7915
|
+
/* KİŞİ BAŞINA OTURUM (Telegram): her chatId kendi oturumunda; chatId yoksa
|
|
7916
|
+
CRON TABAN OTURUMU döner (otomatik işler kişi sohbetlerine karışmaz). */
|
|
7874
7917
|
function ensureTgSession(chatId) {
|
|
7875
|
-
if (
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
tgSingleSid = best || engine.createSession().id;
|
|
7918
|
+
if (chatId == null || chatId === '') return ensureTgCronSession();
|
|
7919
|
+
const key = String(chatId);
|
|
7920
|
+
const sid = tgChats.get(key);
|
|
7921
|
+
if (sid && sessionFileAlive(sid)) return sid;
|
|
7922
|
+
const v = engine.createSession();
|
|
7923
|
+
tgChats.set(key, v.id);
|
|
7924
|
+
tgRememberSession(key, v.id);
|
|
7925
|
+
saveTgChats();
|
|
7926
|
+
return v.id;
|
|
7927
|
+
}
|
|
7928
|
+
|
|
7929
|
+
/* Cron/otomatik işlerin Telegram taban oturumu: hiçbir sohbete bağlı DEĞİL */
|
|
7930
|
+
function ensureTgCronSession() {
|
|
7931
|
+
if (!tgCronSid || !sessionFileAlive(tgCronSid)) {
|
|
7932
|
+
tgCronSid = engine.createSession().id;
|
|
7891
7933
|
saveTgChats();
|
|
7892
7934
|
}
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
dirty = true;
|
|
7935
|
+
return tgCronSid;
|
|
7936
|
+
}
|
|
7937
|
+
|
|
7938
|
+
/* Telegram cevap hedefi: oturuma bağlı sohbet (en son yazan öncelikli) */
|
|
7939
|
+
function tgReplyChat(sid) {
|
|
7940
|
+
const s = String(sid || '');
|
|
7941
|
+
if (tgLastActiveChatId && String(tgChats.get(tgLastActiveChatId) || '') === s) return tgLastActiveChatId;
|
|
7942
|
+
for (const [c, v] of tgChats) {
|
|
7943
|
+
if (String(v) === s) return c;
|
|
7903
7944
|
}
|
|
7904
|
-
|
|
7905
|
-
if (chatId != null) tgRememberSession(String(chatId), tgSingleSid);
|
|
7906
|
-
if (dirty) saveTgChats();
|
|
7907
|
-
return tgSingleSid;
|
|
7945
|
+
return '';
|
|
7908
7946
|
}
|
|
7947
|
+
|
|
7948
|
+
/* KİŞİ BAŞINA OTURUM (Discord): her kanal kendi oturumunda; channelId yoksa
|
|
7949
|
+
CRON TABAN OTURUMU döner (otomatik işler kişi sohbetlerine karışmaz). */
|
|
7909
7950
|
function ensureDcSession(channelId) {
|
|
7910
|
-
if (
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
dcSingleSid = best || engine.createSession().id;
|
|
7951
|
+
if (channelId == null || channelId === '') return ensureDcCronSession();
|
|
7952
|
+
const key = String(channelId);
|
|
7953
|
+
const sid = dcChats.get(key);
|
|
7954
|
+
if (sid && sessionFileAlive(sid)) return sid;
|
|
7955
|
+
const v = engine.createSession();
|
|
7956
|
+
dcChats.set(key, v.id);
|
|
7957
|
+
dcRememberSession(key, v.id);
|
|
7958
|
+
saveDcChats();
|
|
7959
|
+
return v.id;
|
|
7960
|
+
}
|
|
7961
|
+
|
|
7962
|
+
/* Cron/otomatik işlerin Discord taban oturumu: hiçbir kanala bağlı DEĞİL */
|
|
7963
|
+
function ensureDcCronSession() {
|
|
7964
|
+
if (!dcCronSid || !sessionFileAlive(dcCronSid)) {
|
|
7965
|
+
dcCronSid = engine.createSession().id;
|
|
7926
7966
|
saveDcChats();
|
|
7927
7967
|
}
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
dirty = true;
|
|
7968
|
+
return dcCronSid;
|
|
7969
|
+
}
|
|
7970
|
+
|
|
7971
|
+
/* Discord cevap hedefi: oturuma bağlı kanal (en son yazan öncelikli) */
|
|
7972
|
+
function dcReplyChannel(sid) {
|
|
7973
|
+
const s = String(sid || '');
|
|
7974
|
+
if (dcLastActiveChannelId && String(dcChats.get(dcLastActiveChannelId) || '') === s) return dcLastActiveChannelId;
|
|
7975
|
+
for (const [c, v] of dcChats) {
|
|
7976
|
+
if (String(v) === s) return c;
|
|
7938
7977
|
}
|
|
7939
|
-
|
|
7940
|
-
if (channelId != null) dcRememberSession(String(channelId), dcSingleSid);
|
|
7941
|
-
if (dirty) saveDcChats();
|
|
7942
|
-
return dcSingleSid;
|
|
7978
|
+
return '';
|
|
7943
7979
|
}
|
|
7944
7980
|
|
|
7945
7981
|
/* Masaüstü hedefi: kullanıcının AÇIK sohbeti önceliklidir — proaktif not
|
|
@@ -7947,13 +7983,14 @@ function ensureDcSession(channelId) {
|
|
|
7947
7983
|
Açık sohbet bilinmiyorsa en güncel meşgul olmayan sohbet (kanal oturumları
|
|
7948
7984
|
hariç — onlara kanal yoluyla zaten enjekte edildi); o da yoksa yeni oturum. */
|
|
7949
7985
|
function empatiDesktopSid(exclude) {
|
|
7950
|
-
const skip = exclude instanceof Set ? exclude :
|
|
7986
|
+
const skip = exclude instanceof Set ? exclude : new Set();
|
|
7951
7987
|
try {
|
|
7952
7988
|
if (desktopActiveSid && sessionFileAlive(desktopActiveSid)) return String(desktopActiveSid);
|
|
7953
7989
|
} catch {}
|
|
7954
7990
|
try {
|
|
7991
|
+
const chan = channelSessionIds();
|
|
7955
7992
|
for (const v of engine.listSessions()) {
|
|
7956
|
-
if (skip
|
|
7993
|
+
if (skip.has(String(v.id)) || chan.has(String(v.id))) continue;
|
|
7957
7994
|
if (!engine.isBusy(v.id)) return String(v.id);
|
|
7958
7995
|
}
|
|
7959
7996
|
} catch {}
|
|
@@ -13365,6 +13402,26 @@ ipcMain.handle('wa:queue:get', () => mqueue.stats());
|
|
|
13365
13402
|
|
|
13366
13403
|
ipcMain.handle('wa:sessions', () => [...waChats.values()]);
|
|
13367
13404
|
|
|
13405
|
+
/* Sohbet geçmişi etiketi: soldaki listede oturumun HANGİ NUMARAYA/GRUBA ait
|
|
13406
|
+
olduğu görünür — DM'de "WhatsApp +905xx (Ad)", grupta "WhatsApp 1203… (grup)". */
|
|
13407
|
+
function waChatLabelFor(key) {
|
|
13408
|
+
const k = String(key || '');
|
|
13409
|
+
const digits = chansessions.jidDigits(k);
|
|
13410
|
+
if (k.endsWith('@g.us')) return 'WhatsApp ' + digits + ' (grup)';
|
|
13411
|
+
const hit = waFind(digits);
|
|
13412
|
+
const name = hit && hit.name ? String(hit.name) : '';
|
|
13413
|
+
return 'WhatsApp +' + digits + (name ? ' (' + name + ')' : '');
|
|
13414
|
+
}
|
|
13415
|
+
|
|
13416
|
+
ipcMain.handle('wa:sessions:info', () => {
|
|
13417
|
+
const out = [];
|
|
13418
|
+
for (const [key, sid] of waChats) {
|
|
13419
|
+
if (!sid) continue;
|
|
13420
|
+
out.push({ sid: String(sid), label: waChatLabelFor(key), group: String(key).endsWith('@g.us') });
|
|
13421
|
+
}
|
|
13422
|
+
return out;
|
|
13423
|
+
});
|
|
13424
|
+
|
|
13368
13425
|
/* TTS sayısal ayar kelepçesi: geçersiz/boş değerde varsayılana döner */
|
|
13369
13426
|
function clampTtsNum(v, lo, hi, def) {
|
|
13370
13427
|
const n = Number(v);
|
|
@@ -13459,6 +13516,26 @@ ipcMain.handle('tg:allow:set', (_e, list) => {
|
|
|
13459
13516
|
});
|
|
13460
13517
|
ipcMain.handle('tg:sessions', () => [...tgChats.values()]);
|
|
13461
13518
|
|
|
13519
|
+
/* Sohbet geçmişi etiketi (Telegram): "Telegram <ad|chatId>" */
|
|
13520
|
+
function tgChatLabelFor(key) {
|
|
13521
|
+
const k = String(key || '');
|
|
13522
|
+
let name = '';
|
|
13523
|
+
try {
|
|
13524
|
+
const hit = tgFind(k, '');
|
|
13525
|
+
name = hit && hit.name ? String(hit.name) : '';
|
|
13526
|
+
} catch {}
|
|
13527
|
+
return 'Telegram ' + (name || k);
|
|
13528
|
+
}
|
|
13529
|
+
|
|
13530
|
+
ipcMain.handle('tg:sessions:info', () => {
|
|
13531
|
+
const out = [];
|
|
13532
|
+
for (const [key, sid] of tgChats) {
|
|
13533
|
+
if (!sid) continue;
|
|
13534
|
+
out.push({ sid: String(sid), label: tgChatLabelFor(key) });
|
|
13535
|
+
}
|
|
13536
|
+
return out;
|
|
13537
|
+
});
|
|
13538
|
+
|
|
13462
13539
|
/* ---------- Discord IPC ---------- */
|
|
13463
13540
|
ipcMain.handle('dc:status:get', () => {
|
|
13464
13541
|
if (!dc) return { configured: !!settings.dcToken, status: 'disconnected', user: null, connected: false };
|
|
@@ -13495,6 +13572,26 @@ ipcMain.handle('dc:allow:set', (_e, list) => {
|
|
|
13495
13572
|
});
|
|
13496
13573
|
ipcMain.handle('dc:sessions', () => [...dcChats.values()]);
|
|
13497
13574
|
|
|
13575
|
+
/* Sohbet geçmişi etiketi (Discord): "Discord <ad|kanalId>" */
|
|
13576
|
+
function dcChatLabelFor(key) {
|
|
13577
|
+
const k = String(key || '');
|
|
13578
|
+
let name = '';
|
|
13579
|
+
try {
|
|
13580
|
+
const hit = dcFind(k, '');
|
|
13581
|
+
name = hit && hit.name ? String(hit.name) : '';
|
|
13582
|
+
} catch {}
|
|
13583
|
+
return 'Discord ' + (name || k);
|
|
13584
|
+
}
|
|
13585
|
+
|
|
13586
|
+
ipcMain.handle('dc:sessions:info', () => {
|
|
13587
|
+
const out = [];
|
|
13588
|
+
for (const [key, sid] of dcChats) {
|
|
13589
|
+
if (!sid) continue;
|
|
13590
|
+
out.push({ sid: String(sid), label: dcChatLabelFor(key) });
|
|
13591
|
+
}
|
|
13592
|
+
return out;
|
|
13593
|
+
});
|
|
13594
|
+
|
|
13498
13595
|
/* ---------- e-posta IPC ---------- */
|
|
13499
13596
|
|
|
13500
13597
|
ipcMain.handle('email:get', () => {
|
package/src/preload.js
CHANGED
|
@@ -246,6 +246,9 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
246
246
|
emailSend: (msg) => ipcRenderer.invoke('email:send', msg),
|
|
247
247
|
captureScreen: () => ipcRenderer.invoke('screen:capture'),
|
|
248
248
|
waListSessions: () => ipcRenderer.invoke('wa:sessions'),
|
|
249
|
+
waSessionsInfo: () => ipcRenderer.invoke('wa:sessions:info'),
|
|
250
|
+
tgSessionsInfo: () => ipcRenderer.invoke('tg:sessions:info'),
|
|
251
|
+
dcSessionsInfo: () => ipcRenderer.invoke('dc:sessions:info'),
|
|
249
252
|
botsList: () => ipcRenderer.invoke('bots:list'),
|
|
250
253
|
botsAdd: (input) => ipcRenderer.invoke('bots:add', input),
|
|
251
254
|
botsDmList: () => ipcRenderer.invoke('bots:dm:list'),
|
package/src/renderer/renderer.js
CHANGED
|
@@ -1020,11 +1020,29 @@ async function renderSessions(list) {
|
|
|
1020
1020
|
await loadSessionOrder();
|
|
1021
1021
|
let waSet = new Set();
|
|
1022
1022
|
let tgSet = new Set();
|
|
1023
|
+
let dcSet = new Set();
|
|
1024
|
+
/* KANAL ETİKETLERİ: oturumun hangi numaraya/grupa ait olduğu listede görünür */
|
|
1025
|
+
const chanLabel = new Map();
|
|
1023
1026
|
try {
|
|
1024
|
-
|
|
1027
|
+
for (const it of (await beast.waSessionsInfo()) || []) {
|
|
1028
|
+
if (!it || !it.sid) continue;
|
|
1029
|
+
waSet.add(it.sid);
|
|
1030
|
+
if (it.label) chanLabel.set(String(it.sid), it.label);
|
|
1031
|
+
}
|
|
1025
1032
|
} catch {}
|
|
1026
1033
|
try {
|
|
1027
|
-
|
|
1034
|
+
for (const it of (await beast.tgSessionsInfo()) || []) {
|
|
1035
|
+
if (!it || !it.sid) continue;
|
|
1036
|
+
tgSet.add(it.sid);
|
|
1037
|
+
if (it.label) chanLabel.set(String(it.sid), it.label);
|
|
1038
|
+
}
|
|
1039
|
+
} catch {}
|
|
1040
|
+
try {
|
|
1041
|
+
for (const it of (await beast.dcSessionsInfo()) || []) {
|
|
1042
|
+
if (!it || !it.sid) continue;
|
|
1043
|
+
dcSet.add(it.sid);
|
|
1044
|
+
if (it.label) chanLabel.set(String(it.sid), it.label);
|
|
1045
|
+
}
|
|
1028
1046
|
} catch {}
|
|
1029
1047
|
els.sessList.innerHTML = '';
|
|
1030
1048
|
/* BEAST FINANCE izolasyonu: finance modunda YALNIZ finance sohbetleri,
|
|
@@ -1044,10 +1062,14 @@ async function renderSessions(list) {
|
|
|
1044
1062
|
row.dataset.sid = s.id; /* en üstteki sohbeti otomatik aktif etmek için */
|
|
1045
1063
|
row.title = _t('sess_drag');
|
|
1046
1064
|
row.draggable = true;
|
|
1065
|
+
const chan = chanLabel.get(String(s.id)) || '';
|
|
1047
1066
|
row.innerHTML =
|
|
1048
1067
|
(waSet.has(s.id) ? '<span class="sess-wa" title="WhatsApp">W</span>' : '') +
|
|
1049
1068
|
(tgSet.has(s.id) ? '<span class="sess-tg" title="Telegram">T</span>' : '') +
|
|
1050
|
-
|
|
1069
|
+
(dcSet.has(s.id) ? '<span class="sess-dc" title="Discord">D</span>' : '') +
|
|
1070
|
+
(chan
|
|
1071
|
+
? `<span class="sess-main"><span class="sess-chan">${escapeHtml(chan)}</span><span class="sess-title">${escapeHtml(s.title || 'Yeni Sohbet')}</span></span>`
|
|
1072
|
+
: `<span class="sess-title">${escapeHtml(s.title || 'Yeni Sohbet')}</span>`) +
|
|
1051
1073
|
`<span class="sess-code" title="Oturum kodu">${escapeHtml(s.code || '')}</span>` +
|
|
1052
1074
|
`<button class="sess-del" title="Sil">×</button>`;
|
|
1053
1075
|
row.addEventListener('click', () => openSession(s.id));
|
package/src/renderer/style.css
CHANGED
|
@@ -234,6 +234,30 @@ body:not(.ide-mode):not(.studio-mode):not(.sandbox-mode) #watchPane { display: f
|
|
|
234
234
|
align-items: center;
|
|
235
235
|
justify-content: center;
|
|
236
236
|
}
|
|
237
|
+
.sess-dc {
|
|
238
|
+
width: 15px;
|
|
239
|
+
height: 15px;
|
|
240
|
+
border-radius: 4px;
|
|
241
|
+
background: #5865f2;
|
|
242
|
+
color: #fff;
|
|
243
|
+
font-size: 10px;
|
|
244
|
+
font-weight: 800;
|
|
245
|
+
display: flex;
|
|
246
|
+
align-items: center;
|
|
247
|
+
justify-content: center;
|
|
248
|
+
}
|
|
249
|
+
/* kanal sohbeti etiketi: hangi numara/grup olduğu + mesaj başlığı alt satırda */
|
|
250
|
+
.sess-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
|
|
251
|
+
.sess-chan {
|
|
252
|
+
font-size: 10.5px;
|
|
253
|
+
font-weight: 700;
|
|
254
|
+
color: var(--accent);
|
|
255
|
+
white-space: nowrap;
|
|
256
|
+
overflow: hidden;
|
|
257
|
+
text-overflow: ellipsis;
|
|
258
|
+
}
|
|
259
|
+
.sess-main .sess-title { font-size: 11.5px; color: var(--muted); }
|
|
260
|
+
.sess.active .sess-main .sess-title { color: var(--text); }
|
|
237
261
|
.sess-title { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }
|
|
238
262
|
.sess-code {
|
|
239
263
|
color: var(--muted);
|