beast-agent 2.58.0 → 2.58.2
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/engine.js +28 -5
- package/src/agent/llm.js +54 -14
- package/src/agent/telegram.js +53 -5
- package/src/main.js +44 -5
- package/src/renderer/renderer.js +1 -1
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -811,9 +811,13 @@ class Engine {
|
|
|
811
811
|
payload = squeeze.apply(payload, { cacheKey: String(session.id || '') });
|
|
812
812
|
}
|
|
813
813
|
const cands = this._chatCandidates(sel, wasVision);
|
|
814
|
+
/* AYLIK KOTASI DOLAN SAĞLAYICILAR: aynı sağlayıcının diğer modellerini
|
|
815
|
+
denemek boşuna (kota hesap/workspace bazlı) — bu turda atlanır. */
|
|
816
|
+
const quotaOut = new Set();
|
|
814
817
|
let lastErr = null;
|
|
815
818
|
for (let i = 0; i < cands.length; i++) {
|
|
816
819
|
const c = cands[i];
|
|
820
|
+
if (quotaOut.has(String(c.providerId))) continue;
|
|
817
821
|
let retried413 = false;
|
|
818
822
|
try {
|
|
819
823
|
/* provider bazlı girdi limiti: payload aşıyorsa sıkıştırılıp öyle gönderilir.
|
|
@@ -903,6 +907,15 @@ class Engine {
|
|
|
903
907
|
if (e && (e.name === 'AbortError' || (signal && signal.aborted))) throw e;
|
|
904
908
|
lastErr = e;
|
|
905
909
|
const msgStr = String((e && e.message) || '');
|
|
910
|
+
/* AYLIK KOTA: bu sağlayıcı tur boyunca kapalı — kullanıcıya net söyle,
|
|
911
|
+
FALLOUT varsa farklı sağlayıcıya geç */
|
|
912
|
+
if (e && e.usageLimit) {
|
|
913
|
+
quotaOut.add(String(c.providerId));
|
|
914
|
+
emitSafe(this, session.id, {
|
|
915
|
+
type: 'status',
|
|
916
|
+
status: `⛔ ${c.providerName}: aylık kullanım limiti doldu — aynı sağlayıcının diğer modelleri atlanıyor`,
|
|
917
|
+
});
|
|
918
|
+
}
|
|
906
919
|
/* 413 TPM: sağlayıcı gerçek istek boyutunu söylüyor → kalibre et,
|
|
907
920
|
aynı modeli yeniden sıkıştırıp BİR KEZ daha dene */
|
|
908
921
|
const m413 = /Requested\s+([\d,]+)/i.exec(msgStr) || (e && e.status === 413 ? [, 0] : null);
|
|
@@ -925,7 +938,11 @@ class Engine {
|
|
|
925
938
|
([400, 404, 422].includes(e.status) ||
|
|
926
939
|
/\bno endpoints\b/i.test(msgStr) ||
|
|
927
940
|
/\bimage input\b/i.test(msgStr));
|
|
928
|
-
|
|
941
|
+
/* sıradaki aday: kotası dolmuş sağlayıcılar atlanır */
|
|
942
|
+
let next = null;
|
|
943
|
+
for (let j = i + 1; j < cands.length; j++) {
|
|
944
|
+
if (!quotaOut.has(String(cands[j].providerId))) { next = cands[j]; break; }
|
|
945
|
+
}
|
|
929
946
|
if (!next) break; // zincir bitti — hatayı yukarı fırlat
|
|
930
947
|
if (imgIssue) {
|
|
931
948
|
emitSafe(this, session.id, {
|
|
@@ -1417,15 +1434,21 @@ class Engine {
|
|
|
1417
1434
|
files = fs.readdirSync(this.sessionsDir).filter((f) => f.endsWith('.jsonl'));
|
|
1418
1435
|
} catch {}
|
|
1419
1436
|
const out = [];
|
|
1437
|
+
const fin = [];
|
|
1420
1438
|
for (const f of files) {
|
|
1421
1439
|
const id = f.replace(/\.jsonl$/, '');
|
|
1422
1440
|
const v = this._view(this._load(id));
|
|
1423
1441
|
if (v.isBg) continue;
|
|
1424
1442
|
if (v.isBotDm) continue; // botlar arası DM — yalnız admin DM Log ekranında
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1443
|
+
/* FİNANS GEÇMİŞİ KAYBOLMAZ: normal sohbet listesi 100 ile sınırlı olsa da
|
|
1444
|
+
finance sohbetleri AYRICA listelenir — 100+ oturumu olan kullanıcıda
|
|
1445
|
+
eski finance sohbetleri listeden düşüp "kaybolmuş" görünmez. */
|
|
1446
|
+
(v.finance ? fin : out).push(v);
|
|
1447
|
+
}
|
|
1448
|
+
const byNew = (a, b) => String(b.updatedAt).localeCompare(String(a.updatedAt));
|
|
1449
|
+
out.sort(byNew);
|
|
1450
|
+
fin.sort(byNew);
|
|
1451
|
+
return out.slice(0, 100).concat(fin.slice(0, 60)).sort(byNew);
|
|
1429
1452
|
}
|
|
1430
1453
|
|
|
1431
1454
|
/* Panel oturumları: belirli bir bgTitle taşıyan gizli oturumlar (Beast Code,
|
package/src/agent/llm.js
CHANGED
|
@@ -64,7 +64,27 @@ const HINTS = {
|
|
|
64
64
|
504: 'sağlayıcı zaman aşımı',
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
/* AYLIK KOTA / KULLANIM LİMİTİ: 429 gövdesindeki "usage limit / quota" sinyali
|
|
68
|
+
hız limitinden FARKLI — tekrar denemek çözmez, aynı sağlayıcının diğer
|
|
69
|
+
modelleri de kapalıdır. Bu ayrım motorun FALLOUT kararını da besler. */
|
|
70
|
+
function isUsageLimitDetail(detail) {
|
|
71
|
+
return /GoUsageLimit|usage[_ ]?limit|insufficient_quota|monthly limit|kullanım limiti/i.test(
|
|
72
|
+
String(detail || '')
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
67
76
|
function friendlyError(status, statusText, detail) {
|
|
77
|
+
const d = String(detail || '');
|
|
78
|
+
if (status === 429 && isUsageLimitDetail(d)) {
|
|
79
|
+
let msg = 'HTTP 429 — AYLIK KULLANIM LİMİTİ DOLDU (hız limiti değil)';
|
|
80
|
+
const reset = /Resets?\s+in\s+([^."}]+)/i.exec(d);
|
|
81
|
+
if (reset) msg += ` — sıfırlanma: ${reset[1].trim()} sonra`;
|
|
82
|
+
const link = /(https?:\/\/[^\s"}]+)/.exec(d);
|
|
83
|
+
if (link) msg += `\nDevam için: ${link[1]}`;
|
|
84
|
+
msg +=
|
|
85
|
+
'\n(çözüm: sağlayıcı panelinden bakiyeden kullanımı aç, başka model/sağlayıcı seç ya da sıfırlanmayı bekle)';
|
|
86
|
+
return msg;
|
|
87
|
+
}
|
|
68
88
|
const hint = HINTS[status];
|
|
69
89
|
let msg = `HTTP ${status} ${statusText}`;
|
|
70
90
|
if (hint) msg += ` — ${hint}`;
|
|
@@ -72,9 +92,33 @@ function friendlyError(status, statusText, detail) {
|
|
|
72
92
|
if ([500, 502, 503, 504].includes(status)) {
|
|
73
93
|
msg += '\n(geçici olabilir: birkaç saniye sonra tekrar dene ya da model seçiciyi açıp başka bir model dene)';
|
|
74
94
|
}
|
|
95
|
+
if (isTransientDetail(status, d)) {
|
|
96
|
+
msg += '\n(sağlayıcı geçici hatası — otomatik tekrar denenir; sürerse başka model/sağlayıcı dene)';
|
|
97
|
+
}
|
|
75
98
|
return msg;
|
|
76
99
|
}
|
|
77
100
|
|
|
101
|
+
/* SAĞLAYICI GEÇİCİ HATASI: bazı sağlayıcılar upstream arızasını HTTP 400
|
|
102
|
+
gövdesinde "server_error" olarak döner — istemci hatası değildir, tekrar
|
|
103
|
+
denenmeli. Bu bayrak olmadan 400 kalıcı sayılıp model boşuna "çökmüş" olur. */
|
|
104
|
+
function isTransientDetail(status, detail) {
|
|
105
|
+
if (status !== 400 && status !== 500) return false;
|
|
106
|
+
return /"(type|code)"\s*:\s*"server_error"|Error from provider|Upstream (request failed|response was not valid JSON)|temporar/i.test(
|
|
107
|
+
String(detail || '')
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* HTTP hatasını zengin bayraklarla kur — usageLimit/transient bilgisi retry ve
|
|
112
|
+
FALLOUT katmanlarına taşınır (boşa tekrar/boşa çökme olmaz). */
|
|
113
|
+
function httpError(status, statusText, detail, headers) {
|
|
114
|
+
const err = new Error(friendlyError(status, statusText, detail));
|
|
115
|
+
err.status = status;
|
|
116
|
+
if (isUsageLimitDetail(detail)) err.usageLimit = true;
|
|
117
|
+
if (isTransientDetail(status, detail)) err.transient = true;
|
|
118
|
+
err.retryAfterMs = parseRetryAfter(headers && (headers.get('retry-after-ms') || headers.get('retry-after')));
|
|
119
|
+
return err;
|
|
120
|
+
}
|
|
121
|
+
|
|
78
122
|
/* opencode cache disiplini (provider/transform.ts:1262-1276 port): destekleyen
|
|
79
123
|
sağlayıcılara oturum-sabit önbellek anahtarı gönderilir — aynı önek tekrar
|
|
80
124
|
kullanıldığında girdi tokenleri cache'ten okunur (~%90 ucuz + hızlı TTFT) */
|
|
@@ -302,9 +346,15 @@ async function withRetries(fn, { signal, onRetry } = {}) {
|
|
|
302
346
|
return await fn();
|
|
303
347
|
} catch (e) {
|
|
304
348
|
const aborted = e && (e.name === 'AbortError' || (signal && signal.aborted));
|
|
349
|
+
if (aborted) throw e;
|
|
350
|
+
/* AYLIK KOTA DOLDU: tekrar denemek boşa — hemen fırlat ki motor
|
|
351
|
+
FALLOUT ile sıradaki sağlayıcıya/modele geçebilsin. */
|
|
352
|
+
if (e && e.usageLimit) throw e;
|
|
305
353
|
const status = e && e.status;
|
|
306
|
-
const netErr =
|
|
307
|
-
|
|
354
|
+
const netErr = status === undefined;
|
|
355
|
+
/* 400 gövdesinde "server_error/upstream" diyen sağlayıcı hataları geçicidir
|
|
356
|
+
(transient) — kalıcı 400 gibi davranıp modeli boşuna "çökertme". */
|
|
357
|
+
const retriable = status === undefined || RETRYABLE_STATUS.has(status) || !!(e && e.transient);
|
|
308
358
|
if (!retriable) throw e;
|
|
309
359
|
const cap = netErr ? NET_MAX_RETRIES : MAX_RETRIES;
|
|
310
360
|
if (attempt >= cap) throw e;
|
|
@@ -409,12 +459,7 @@ async function streamOnce(sel, body, { signal, onDelta, onRetry } = {}, drops =
|
|
|
409
459
|
try {
|
|
410
460
|
detail = (await r.text()).slice(0, 300);
|
|
411
461
|
} catch {}
|
|
412
|
-
|
|
413
|
-
err.status = r.status;
|
|
414
|
-
err.retryAfterMs = parseRetryAfter(
|
|
415
|
-
r.headers && (r.headers.get('retry-after-ms') || r.headers.get('retry-after'))
|
|
416
|
-
);
|
|
417
|
-
throw err;
|
|
462
|
+
throw httpError(r.status, r.statusText, detail, r.headers);
|
|
418
463
|
}
|
|
419
464
|
return r;
|
|
420
465
|
},
|
|
@@ -590,12 +635,7 @@ async function chatOnce(sel, body, { signal, onRetry, onParamDrop, onEffortDowng
|
|
|
590
635
|
try {
|
|
591
636
|
detail = (await r.text()).slice(0, 300);
|
|
592
637
|
} catch {}
|
|
593
|
-
|
|
594
|
-
err.status = r.status;
|
|
595
|
-
err.retryAfterMs = parseRetryAfter(
|
|
596
|
-
r.headers && (r.headers.get('retry-after-ms') || r.headers.get('retry-after'))
|
|
597
|
-
);
|
|
598
|
-
throw err;
|
|
638
|
+
throw httpError(r.status, r.statusText, detail, r.headers);
|
|
599
639
|
}
|
|
600
640
|
return r;
|
|
601
641
|
},
|
package/src/agent/telegram.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
- Aynı allow list mantığı: main tarafındaki tgFind() listesindeki kişilere cevap verir */
|
|
8
8
|
|
|
9
9
|
const https = require('https');
|
|
10
|
+
const crypto = require('crypto');
|
|
10
11
|
|
|
11
12
|
const API_BASE = 'https://api.telegram.org/bot';
|
|
12
13
|
const SEND_CHUNK = 3800; // Telegram mesaj sınırı 4096 — güvenli pay
|
|
@@ -30,16 +31,15 @@ class TelegramBridge {
|
|
|
30
31
|
this.emit({ type: 'status', status, user: user || null });
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
/*
|
|
34
|
-
|
|
34
|
+
/* Ham POST — JSON ve multipart (görsel) aynı iskeleti kullanır */
|
|
35
|
+
_post(method, payload, headers, timeoutMs) {
|
|
35
36
|
return new Promise((resolve, reject) => {
|
|
36
|
-
const payload = JSON.stringify(body);
|
|
37
37
|
const req = https.request(
|
|
38
38
|
`${API_BASE}${this.token}/${method}`,
|
|
39
39
|
{
|
|
40
40
|
method: 'POST',
|
|
41
|
-
headers: {
|
|
42
|
-
timeout:
|
|
41
|
+
headers: { ...headers, 'Content-Length': Buffer.byteLength(payload) },
|
|
42
|
+
timeout: timeoutMs || 15000,
|
|
43
43
|
},
|
|
44
44
|
(res) => {
|
|
45
45
|
let data = '';
|
|
@@ -64,6 +64,16 @@ class TelegramBridge {
|
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/* Bot API çağrısı — JSON POST, promise sarmalı */
|
|
68
|
+
api(method, body = {}, opts = {}) {
|
|
69
|
+
return this._post(
|
|
70
|
+
method,
|
|
71
|
+
JSON.stringify(body),
|
|
72
|
+
{ 'Content-Type': 'application/json' },
|
|
73
|
+
opts.timeout || 15000
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
67
77
|
async start() {
|
|
68
78
|
if (!this.token) {
|
|
69
79
|
this._setStatus('error');
|
|
@@ -156,6 +166,44 @@ class TelegramBridge {
|
|
|
156
166
|
}
|
|
157
167
|
return true;
|
|
158
168
|
}
|
|
169
|
+
|
|
170
|
+
/* GÖRSEL gönder — data URL (base64) multipart ile yüklenir. PNG/JPG/WEBP
|
|
171
|
+
sendPhoto, GIF sendDocument; caption 1024, dosya 9.5MB üstü atlanır.
|
|
172
|
+
threadId verilirse forum konusuna gider. */
|
|
173
|
+
async sendPhoto(chatId, dataUrl, caption, threadId) {
|
|
174
|
+
try {
|
|
175
|
+
const m = /^data:image\/(png|jpe?g|webp|gif);base64,([A-Za-z0-9+/=\s]+)$/i.exec(String(dataUrl || '').trim());
|
|
176
|
+
if (!m) return false;
|
|
177
|
+
const isJpg = /^jpe?g$/i.test(m[1]);
|
|
178
|
+
const ext = isJpg ? 'jpg' : m[1].toLowerCase();
|
|
179
|
+
const mime = isJpg ? 'image/jpeg' : 'image/' + ext;
|
|
180
|
+
const buf = Buffer.from(m[2].replace(/\s+/g, ''), 'base64');
|
|
181
|
+
if (!buf.length || buf.length > 9.5 * 1024 * 1024) return false;
|
|
182
|
+
const gif = ext === 'gif';
|
|
183
|
+
const method = gif ? 'sendDocument' : 'sendPhoto';
|
|
184
|
+
const field = gif ? 'document' : 'photo';
|
|
185
|
+
const boundary = '----Beast' + crypto.randomBytes(10).toString('hex');
|
|
186
|
+
const parts = [];
|
|
187
|
+
const addField = (name, value) => {
|
|
188
|
+
parts.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${String(value)}\r\n`));
|
|
189
|
+
};
|
|
190
|
+
addField('chat_id', String(chatId));
|
|
191
|
+
if (caption) addField('caption', String(caption).slice(0, 1024));
|
|
192
|
+
if (threadId) addField('message_thread_id', String(Number(threadId)));
|
|
193
|
+
parts.push(
|
|
194
|
+
Buffer.from(
|
|
195
|
+
`--${boundary}\r\nContent-Disposition: form-data; name="${field}"; filename="beast.${ext}"\r\nContent-Type: ${mime}\r\n\r\n`
|
|
196
|
+
)
|
|
197
|
+
);
|
|
198
|
+
parts.push(buf);
|
|
199
|
+
parts.push(Buffer.from('\r\n'));
|
|
200
|
+
parts.push(Buffer.from(`--${boundary}--\r\n`));
|
|
201
|
+
await this._post(method, Buffer.concat(parts), { 'Content-Type': 'multipart/form-data; boundary=' + boundary }, 60000);
|
|
202
|
+
return true;
|
|
203
|
+
} catch {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
159
207
|
}
|
|
160
208
|
|
|
161
209
|
module.exports = { TelegramBridge };
|
package/src/main.js
CHANGED
|
@@ -2944,6 +2944,7 @@ async function handleTgIncoming(chatId, payload) {
|
|
|
2944
2944
|
const allowed = tgFind(payload.senderId, payload.username);
|
|
2945
2945
|
if (!allowed) {
|
|
2946
2946
|
tgLog(`skip: grup mesajı chat=${chatId} — gönderici izin listesinde değil`);
|
|
2947
|
+
if (String(dmTg.chat) === String(chatId)) finDmTgNotAllowed(chatId);
|
|
2947
2948
|
return;
|
|
2948
2949
|
}
|
|
2949
2950
|
if (!dmTg.chat) {
|
|
@@ -10122,7 +10123,9 @@ function finDmTgBind(chatId, title) {
|
|
|
10122
10123
|
id,
|
|
10123
10124
|
'✅ Beast Finance AJAN DM bu gruba bağlandı.\n' +
|
|
10124
10125
|
'Bundan sonra ajanların/alarmların mesajları buraya düşer; buraya yazdıkların AJAN DM grubunda görünür ve ajanları uyandırır.\n' +
|
|
10125
|
-
'
|
|
10126
|
+
'ÖNEMLİ: Normal mesajlarını görebilmem için beni bu grupta YÖNETİCİ yap (Konuları Yönet izniyle) ya da BotFather → /setprivacy → Disable yap. ' +
|
|
10127
|
+
'Aksi halde yalnız /komutları ve bana yanıtları görebilirim.\n' +
|
|
10128
|
+
'Grup Konular (Topics) destekliyorsa her AJAN DM grubu ayrı başlık olarak canlı akar.'
|
|
10126
10129
|
)
|
|
10127
10130
|
).catch(() => {});
|
|
10128
10131
|
}
|
|
@@ -10130,6 +10133,25 @@ function finDmTgBind(chatId, title) {
|
|
|
10130
10133
|
return true;
|
|
10131
10134
|
}
|
|
10132
10135
|
|
|
10136
|
+
/* İzin listesi dışı gönderici bağlı gruba yazdıysa sessiz kalma: 10 dk'da bir
|
|
10137
|
+
gruba kısa uyarı — kullanıcı mesajının neden ajanlara gitmediğini anlasın. */
|
|
10138
|
+
const finDmTgWarnAt = new Map();
|
|
10139
|
+
function finDmTgNotAllowed(chatId) {
|
|
10140
|
+
const id = String(chatId || '');
|
|
10141
|
+
const last = finDmTgWarnAt.get(id) || 0;
|
|
10142
|
+
if (!id || Date.now() - last < 10 * 60 * 1000) return;
|
|
10143
|
+
finDmTgWarnAt.set(id, Date.now());
|
|
10144
|
+
try {
|
|
10145
|
+
Promise.resolve(
|
|
10146
|
+
tg.send(
|
|
10147
|
+
id,
|
|
10148
|
+
'⚠️ Bu Telegram hesabı izin listesinde yok — mesajın ajanlara iletilemedi.\n' +
|
|
10149
|
+
'Beast → Entegrasyonlar → Telegram bölümünde bu hesabı (ID ya da @kullanıcı adı) izin listesine ekle.'
|
|
10150
|
+
)
|
|
10151
|
+
).catch(() => {});
|
|
10152
|
+
} catch {}
|
|
10153
|
+
}
|
|
10154
|
+
|
|
10133
10155
|
/* ---- TELEGRAM KONU (TOPIC) EŞLEMESİ ----
|
|
10134
10156
|
Forum destekli Telegram grubunda her AJAN DM grubu AYRI BAŞLIK olarak açılır:
|
|
10135
10157
|
gid → message_thread_id eşlemesi settings.finance.dmTelegramTopics'ta kalıcı.
|
|
@@ -10238,18 +10260,35 @@ function finAgentDmToTelegram(dm) {
|
|
|
10238
10260
|
const title = String(dm.groupTitle || (gid === FIN_TEAM_GID ? FIN_TEAM_TITLE : gid));
|
|
10239
10261
|
const who = String(dm.fromTitle || (dm.system ? 'SİSTEM' : 'Ajan'));
|
|
10240
10262
|
const text = String(dm.text || '').slice(0, 3800);
|
|
10263
|
+
const img = String(dm.image || '');
|
|
10264
|
+
const hasImg = /^data:image\//i.test(img);
|
|
10241
10265
|
finDmTgQueue(async () => {
|
|
10242
10266
|
const tid = await finDmTgThreadEnsure(c.chat, gid, title);
|
|
10243
|
-
const
|
|
10244
|
-
|
|
10267
|
+
const target = tid > 0 ? Number(tid) : undefined;
|
|
10268
|
+
const full = `${who}:\n${text}`;
|
|
10269
|
+
/* GÖRSEL: Telegram'a FOTO olarak gider (caption = gönderen + metin);
|
|
10270
|
+
metnin caption'a sığmayan kalanı ayrı mesajla gider. */
|
|
10271
|
+
if (hasImg) {
|
|
10272
|
+
const cap = full.slice(0, 1000);
|
|
10273
|
+
let ok = false;
|
|
10274
|
+
try { ok = await tg.sendPhoto(c.chat, img, cap, target); } catch { ok = false; }
|
|
10275
|
+
if (ok) {
|
|
10276
|
+
const rest = full.slice(1000);
|
|
10277
|
+
if (rest.trim()) await tg.send(c.chat, '…' + rest, target).catch(() => {});
|
|
10278
|
+
return;
|
|
10279
|
+
}
|
|
10280
|
+
/* foto gönderilemedi (bozuk/büyük) → metne düş, görsel notuyla */
|
|
10281
|
+
}
|
|
10282
|
+
const head = hasImg ? `${who}: (görsel gönderilemedi)\n${text}` : full;
|
|
10283
|
+
if (target) {
|
|
10245
10284
|
try {
|
|
10246
|
-
await tg.send(c.chat,
|
|
10285
|
+
await tg.send(c.chat, head, target);
|
|
10247
10286
|
return;
|
|
10248
10287
|
} catch {
|
|
10249
10288
|
/* konu silinmiş/erişim yok → genel akışa düş (mesaj kaybolmasın) */
|
|
10250
10289
|
}
|
|
10251
10290
|
}
|
|
10252
|
-
await tg.send(c.chat,
|
|
10291
|
+
await tg.send(c.chat, `👥 [${title}] ${head}`);
|
|
10253
10292
|
});
|
|
10254
10293
|
} catch {}
|
|
10255
10294
|
}
|
package/src/renderer/renderer.js
CHANGED
|
@@ -10311,7 +10311,7 @@ function finDmTgPaint() {
|
|
|
10311
10311
|
if (els.finDmTgHint) {
|
|
10312
10312
|
els.finDmTgHint.textContent =
|
|
10313
10313
|
'Her AJAN DM grubu (Beast Finance EKİP, sembol ekipleri vb.) Telegram\u2019da AYRI BAŞLIK (topic) olarak canlı akar. ' +
|
|
10314
|
-
'Telegram grubunda Konular (Topics) açık ve bot
|
|
10314
|
+
'Telegram grubunda Konular (Topics) açık ve bot YÖNETİCİ (Konuları Yönet) olmalı — yönetici değilse normal mesajlarını bot göremez (BotFather → /setprivacy → Disable da olur); Konular kapalıysa mesajlar tek akışa grup etiketiyle düşer. ' +
|
|
10315
10315
|
'Bir başlığa yazdığın mesaj ilgili AJAN DM grubunda görünür ve o grubun ajanları uyanır.';
|
|
10316
10316
|
}
|
|
10317
10317
|
}
|