beast-agent 2.57.1 → 2.58.1
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/telegram.js +59 -7
- package/src/main.js +210 -20
- package/src/renderer/index.html +2 -2
- package/src/renderer/renderer.js +3 -1
package/package.json
CHANGED
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');
|
|
@@ -108,6 +118,8 @@ class TelegramBridge {
|
|
|
108
118
|
isGroup: !!(msg.chat && (msg.chat.type === 'group' || msg.chat.type === 'supergroup')),
|
|
109
119
|
chatTitle: String((msg.chat && (msg.chat.title || msg.chat.username)) || ''),
|
|
110
120
|
chatType: String((msg.chat && msg.chat.type) || 'private'),
|
|
121
|
+
isForum: !!(msg.chat && msg.chat.is_forum),
|
|
122
|
+
messageThreadId: msg.message_thread_id || null,
|
|
111
123
|
};
|
|
112
124
|
try {
|
|
113
125
|
if (this.onIncoming) this.onIncoming(String(chatId), payload);
|
|
@@ -137,8 +149,9 @@ class TelegramBridge {
|
|
|
137
149
|
};
|
|
138
150
|
}
|
|
139
151
|
|
|
140
|
-
/* Metin gönder — 4096 sınırı için parçalara böl
|
|
141
|
-
|
|
152
|
+
/* Metin gönder — 4096 sınırı için parçalara böl; threadId verilirse forum
|
|
153
|
+
konusuna (topic) gönderir (message_thread_id). */
|
|
154
|
+
async send(chatId, text, threadId) {
|
|
142
155
|
const t = String(text || '');
|
|
143
156
|
if (!t.trim()) return false;
|
|
144
157
|
const chunks = [];
|
|
@@ -148,10 +161,49 @@ class TelegramBridge {
|
|
|
148
161
|
chat_id: chatId,
|
|
149
162
|
text: part,
|
|
150
163
|
disable_web_page_preview: true,
|
|
164
|
+
...(threadId ? { message_thread_id: Number(threadId) } : {}),
|
|
151
165
|
});
|
|
152
166
|
}
|
|
153
167
|
return true;
|
|
154
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
|
+
}
|
|
155
207
|
}
|
|
156
208
|
|
|
157
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) {
|
|
@@ -2956,7 +2957,9 @@ async function handleTgIncoming(chatId, payload) {
|
|
|
2956
2957
|
return;
|
|
2957
2958
|
}
|
|
2958
2959
|
if (String(dmTg.chat) === String(chatId)) {
|
|
2959
|
-
|
|
2960
|
+
/* FORUM: konu (thread) → ilgili AJAN DM grubu; konusuz/genel → finance */
|
|
2961
|
+
const gid = finDmTgGidForThread(payload.messageThreadId);
|
|
2962
|
+
finTelegramToAgentDm(payload, gid);
|
|
2960
2963
|
return;
|
|
2961
2964
|
}
|
|
2962
2965
|
tgLog(`skip: grup mesajı chat=${chatId} (bağlı grup ${dmTg.chat})`);
|
|
@@ -9416,12 +9419,14 @@ function finCfg() {
|
|
|
9416
9419
|
iptal) düşer; fiyat alarmı, koruma (BE/trailing/kısmi TP), günlük limit ve
|
|
9417
9420
|
rapor bildirimleri panelde kalır. false = tüm olaylar kanallara gider. */
|
|
9418
9421
|
if (typeof f.notifyTradeOnly !== 'boolean') f.notifyTradeOnly = true;
|
|
9419
|
-
/* AJAN DM ↔ TELEGRAM KÖPRÜSÜ: açıkken
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
+
/* AJAN DM ↔ TELEGRAM KÖPRÜSÜ: açıkken AJAN DM gruplarındaki mesajlar seçilen
|
|
9423
|
+
Telegram grubuna düşer; o gruba yazılan mesaj AJAN DM grubunda görünür ve
|
|
9424
|
+
koşan finance ajanları tur beklemeden uyanır. Forum (Konular) destekli
|
|
9425
|
+
grupta her AJAN DM grubu AYRI BAŞLIK (topic) olarak açılır canlı akar. */
|
|
9422
9426
|
if (typeof f.dmTelegram !== 'boolean') f.dmTelegram = false;
|
|
9423
9427
|
if (typeof f.dmTelegramChat !== 'string') f.dmTelegramChat = '';
|
|
9424
9428
|
if (typeof f.dmTelegramTitle !== 'string') f.dmTelegramTitle = '';
|
|
9429
|
+
if (!f.dmTelegramTopics || typeof f.dmTelegramTopics !== 'object') f.dmTelegramTopics = {};
|
|
9425
9430
|
if (!Number.isFinite(Number(f.maxDailyLossPct))) f.maxDailyLossPct = 3;
|
|
9426
9431
|
/* GÜNLÜK LİMİT AKSİYONU: warn = yalnız uyarı; stop = tüm finance ajanlarını
|
|
9427
9432
|
durdur; flatten = durdur + tüm pozisyonları kapat */
|
|
@@ -10100,6 +10105,12 @@ function finDmTgBind(chatId, title) {
|
|
|
10100
10105
|
if (!id) return false;
|
|
10101
10106
|
const f = finCfg();
|
|
10102
10107
|
const t = String(title || '').trim();
|
|
10108
|
+
/* GRUP DEĞİŞTİ: eski konu eşlemeleri yeni chat'te geçersiz — sıfırla */
|
|
10109
|
+
if (String(f.dmTelegramChat || '') !== id) {
|
|
10110
|
+
f.dmTelegramTopics = {};
|
|
10111
|
+
finDmTgThreads.clear();
|
|
10112
|
+
finDmTgBlockedAt.clear();
|
|
10113
|
+
}
|
|
10103
10114
|
f.dmTelegramChat = id;
|
|
10104
10115
|
f.dmTelegramTitle = t || f.dmTelegramTitle || id;
|
|
10105
10116
|
try { saveSettings(); } catch {}
|
|
@@ -10111,7 +10122,10 @@ function finDmTgBind(chatId, title) {
|
|
|
10111
10122
|
tg.send(
|
|
10112
10123
|
id,
|
|
10113
10124
|
'✅ Beast Finance AJAN DM bu gruba bağlandı.\n' +
|
|
10114
|
-
'Bundan sonra ajanların/alarmların mesajları
|
|
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' +
|
|
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.'
|
|
10115
10129
|
)
|
|
10116
10130
|
).catch(() => {});
|
|
10117
10131
|
}
|
|
@@ -10119,36 +10133,188 @@ function finDmTgBind(chatId, title) {
|
|
|
10119
10133
|
return true;
|
|
10120
10134
|
}
|
|
10121
10135
|
|
|
10122
|
-
/*
|
|
10123
|
-
|
|
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
|
+
|
|
10155
|
+
/* ---- TELEGRAM KONU (TOPIC) EŞLEMESİ ----
|
|
10156
|
+
Forum destekli Telegram grubunda her AJAN DM grubu AYRI BAŞLIK olarak açılır:
|
|
10157
|
+
gid → message_thread_id eşlemesi settings.finance.dmTelegramTopics'ta kalıcı.
|
|
10158
|
+
Konu açılamazsa (forum değil / bot yetkisiz) mesajlar grup adı etiketiyle
|
|
10159
|
+
genel akışa düşer — köprü yine çalışır. */
|
|
10160
|
+
const finDmTgThreads = new Map(); // gid -> threadId (>0) | 0 (forum değil — kalıcı)
|
|
10161
|
+
const finDmTgThreadPend = new Map(); // gid -> in-flight açma promise'i
|
|
10162
|
+
const finDmTgBlockedAt = new Map(); // gid -> son hata ts (geçici: 10 dk sonra yeniden dener)
|
|
10163
|
+
const FIN_DM_TG_RETRY_MS = 10 * 60 * 1000;
|
|
10164
|
+
let finDmTgChatMeta = { id: '', at: 0, isForum: false };
|
|
10165
|
+
let finDmTgSendChain = Promise.resolve(); // gönderim sırası korunsun
|
|
10166
|
+
|
|
10167
|
+
function finDmTgChatInfo(chatId) {
|
|
10168
|
+
const id = String(chatId || '');
|
|
10169
|
+
if (finDmTgChatMeta.id === id && Date.now() - finDmTgChatMeta.at < 5 * 60000) {
|
|
10170
|
+
return Promise.resolve(finDmTgChatMeta);
|
|
10171
|
+
}
|
|
10172
|
+
return Promise.resolve(tg.api('getChat', { chat_id: id }))
|
|
10173
|
+
.then((r) => {
|
|
10174
|
+
finDmTgChatMeta = { id, at: Date.now(), isForum: !!(r && r.is_forum) };
|
|
10175
|
+
return finDmTgChatMeta;
|
|
10176
|
+
})
|
|
10177
|
+
.catch(() => {
|
|
10178
|
+
finDmTgChatMeta = { id, at: Date.now(), isForum: false };
|
|
10179
|
+
return finDmTgChatMeta;
|
|
10180
|
+
});
|
|
10181
|
+
}
|
|
10182
|
+
|
|
10183
|
+
function finDmTgThreadCached(gid) {
|
|
10184
|
+
if (finDmTgThreads.has(gid)) return Number(finDmTgThreads.get(gid)) || 0;
|
|
10185
|
+
const f = finCfg();
|
|
10186
|
+
const rec = f.dmTelegramTopics && f.dmTelegramTopics[gid];
|
|
10187
|
+
const tid = rec && rec.threadId ? Number(rec.threadId) || 0 : 0;
|
|
10188
|
+
if (tid) finDmTgThreads.set(gid, tid);
|
|
10189
|
+
return tid;
|
|
10190
|
+
}
|
|
10191
|
+
|
|
10192
|
+
/* gid için topic'i hazırla (yoksa aç). Dönüş: threadId (0 = konu yok/genel akış) */
|
|
10193
|
+
function finDmTgThreadEnsure(chatId, gid, title) {
|
|
10194
|
+
const cached = finDmTgThreadCached(gid);
|
|
10195
|
+
if (cached > 0) return Promise.resolve(cached);
|
|
10196
|
+
if (finDmTgThreads.has(gid)) return Promise.resolve(0); // forum değil → genel akış
|
|
10197
|
+
const blockedAt = finDmTgBlockedAt.get(gid) || 0;
|
|
10198
|
+
if (blockedAt && Date.now() - blockedAt < FIN_DM_TG_RETRY_MS) return Promise.resolve(0);
|
|
10199
|
+
if (finDmTgThreadPend.has(gid)) return finDmTgThreadPend.get(gid);
|
|
10200
|
+
const p = (async () => {
|
|
10201
|
+
try {
|
|
10202
|
+
const info = await finDmTgChatInfo(chatId);
|
|
10203
|
+
if (!info.isForum) { finDmTgThreads.set(gid, 0); return 0; }
|
|
10204
|
+
const r = await tg.api('createForumTopic', {
|
|
10205
|
+
chat_id: chatId,
|
|
10206
|
+
name: String(title || gid).slice(0, 128),
|
|
10207
|
+
});
|
|
10208
|
+
const tid = Number(r && r.message_thread_id) || 0;
|
|
10209
|
+
if (!tid) { finDmTgBlockedAt.set(gid, Date.now()); return 0; }
|
|
10210
|
+
finDmTgThreads.set(gid, tid);
|
|
10211
|
+
finDmTgBlockedAt.delete(gid);
|
|
10212
|
+
const f = finCfg();
|
|
10213
|
+
if (!f.dmTelegramTopics || typeof f.dmTelegramTopics !== 'object') f.dmTelegramTopics = {};
|
|
10214
|
+
f.dmTelegramTopics[gid] = { threadId: tid, title: String(title || gid), at: Date.now() };
|
|
10215
|
+
try { saveSettings(); } catch {}
|
|
10216
|
+
tgLog(`telegram konu açıldı: "${title}" (${gid}) → thread ${tid}`);
|
|
10217
|
+
return tid;
|
|
10218
|
+
} catch (e) {
|
|
10219
|
+
/* geçici hata (ağ/yetki): kalıcı işaretleme — 10 dk sonra yeniden denenir */
|
|
10220
|
+
finDmTgBlockedAt.set(gid, Date.now());
|
|
10221
|
+
tgLog(`telegram konu açılamadı (${gid}): ` + String((e && e.message) || e));
|
|
10222
|
+
return 0;
|
|
10223
|
+
} finally {
|
|
10224
|
+
finDmTgThreadPend.delete(gid);
|
|
10225
|
+
}
|
|
10226
|
+
})();
|
|
10227
|
+
finDmTgThreadPend.set(gid, p);
|
|
10228
|
+
return p;
|
|
10229
|
+
}
|
|
10230
|
+
|
|
10231
|
+
/* message_thread_id → AJAN DM gid (bilinmeyen konu: genel akış → finance) */
|
|
10232
|
+
function finDmTgGidForThread(threadId) {
|
|
10233
|
+
const tid = Number(threadId) || 0;
|
|
10234
|
+
if (!tid) return '';
|
|
10235
|
+
const f = finCfg();
|
|
10236
|
+
const topics = f.dmTelegramTopics && typeof f.dmTelegramTopics === 'object' ? f.dmTelegramTopics : {};
|
|
10237
|
+
for (const [gid, rec] of Object.entries(topics)) {
|
|
10238
|
+
if (rec && Number(rec.threadId) === tid) return gid;
|
|
10239
|
+
}
|
|
10240
|
+
return '';
|
|
10241
|
+
}
|
|
10242
|
+
|
|
10243
|
+
/* Gönderimleri SIRAYLA işle: konu açma + mesaj sırası bozulmasın. */
|
|
10244
|
+
function finDmTgQueue(task) {
|
|
10245
|
+
finDmTgSendChain = finDmTgSendChain
|
|
10246
|
+
.then(() => task())
|
|
10247
|
+
.catch((e) => tgLog('ajan dm → telegram kuyruk hata: ' + String((e && e.message) || e)));
|
|
10248
|
+
return finDmTgSendChain;
|
|
10249
|
+
}
|
|
10250
|
+
|
|
10251
|
+
/* Giden: HERHANGİ bir AJAN DM grubu → Telegram (topic varsa kendi başlığına;
|
|
10252
|
+
Telegram'dan gelenler geri gönderilmez; grup bağlanmadıysa beklenir). */
|
|
10124
10253
|
function finAgentDmToTelegram(dm) {
|
|
10125
10254
|
try {
|
|
10126
10255
|
if (!dm || dm.viaTelegram) return;
|
|
10127
|
-
|
|
10256
|
+
const gid = String(dm.group || '');
|
|
10257
|
+
if (!gid) return; /* yalnız grup sohbetleri köprülenir (1:1 DM panelde kalır) */
|
|
10128
10258
|
const c = finDmTgCfg();
|
|
10129
10259
|
if (!c.on || !c.chat || !tg || !tg.connected) return;
|
|
10260
|
+
const title = String(dm.groupTitle || (gid === FIN_TEAM_GID ? FIN_TEAM_TITLE : gid));
|
|
10130
10261
|
const who = String(dm.fromTitle || (dm.system ? 'SİSTEM' : 'Ajan'));
|
|
10131
|
-
const
|
|
10132
|
-
|
|
10133
|
-
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
|
|
10262
|
+
const text = String(dm.text || '').slice(0, 3800);
|
|
10263
|
+
const img = String(dm.image || '');
|
|
10264
|
+
const hasImg = /^data:image\//i.test(img);
|
|
10265
|
+
finDmTgQueue(async () => {
|
|
10266
|
+
const tid = await finDmTgThreadEnsure(c.chat, gid, title);
|
|
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) {
|
|
10284
|
+
try {
|
|
10285
|
+
await tg.send(c.chat, head, target);
|
|
10286
|
+
return;
|
|
10287
|
+
} catch {
|
|
10288
|
+
/* konu silinmiş/erişim yok → genel akışa düş (mesaj kaybolmasın) */
|
|
10289
|
+
}
|
|
10290
|
+
}
|
|
10291
|
+
await tg.send(c.chat, `👥 [${title}] ${head}`);
|
|
10292
|
+
});
|
|
10137
10293
|
} catch {}
|
|
10138
10294
|
}
|
|
10139
10295
|
|
|
10140
|
-
/* Gelen: Telegram grubu → AJAN DM grubu (+
|
|
10141
|
-
|
|
10296
|
+
/* Gelen: Telegram grubu/konusu → ilgili AJAN DM grubu (+ üye ajanları uyandır).
|
|
10297
|
+
gid verilmezse finance grubuna düşer (genel akış / konusuz mesaj). */
|
|
10298
|
+
function finTelegramToAgentDm(payload, gid) {
|
|
10142
10299
|
try {
|
|
10143
10300
|
const body = String((payload && payload.text) || '').trim();
|
|
10144
10301
|
if (!body || !engine) return { ok: false };
|
|
10302
|
+
const key = String(gid || FIN_TEAM_GID);
|
|
10303
|
+
const f = finCfg();
|
|
10304
|
+
const topics = f.dmTelegramTopics && typeof f.dmTelegramTopics === 'object' ? f.dmTelegramTopics : {};
|
|
10305
|
+
const title = String(
|
|
10306
|
+
(topics[key] && topics[key].title) ||
|
|
10307
|
+
(key === FIN_TEAM_GID ? FIN_TEAM_TITLE : '') ||
|
|
10308
|
+
(payload && payload.chatTitle) ||
|
|
10309
|
+
key
|
|
10310
|
+
);
|
|
10145
10311
|
const who = String((payload && (payload.senderName || payload.username)) || 'Telegram');
|
|
10146
10312
|
const label = `Sahip · Telegram (${who})`;
|
|
10147
10313
|
try {
|
|
10148
10314
|
if (typeof engine.agentDmGroupPost === 'function') {
|
|
10149
10315
|
engine.agentDmGroupPost({
|
|
10150
|
-
gid:
|
|
10151
|
-
title
|
|
10316
|
+
gid: key,
|
|
10317
|
+
title,
|
|
10152
10318
|
fromSid: 'tg:' + String((payload && payload.senderId) || ''),
|
|
10153
10319
|
fromTitle: label,
|
|
10154
10320
|
topic: 'telegram',
|
|
@@ -10157,14 +10323,31 @@ function finTelegramToAgentDm(payload) {
|
|
|
10157
10323
|
});
|
|
10158
10324
|
}
|
|
10159
10325
|
} catch {}
|
|
10326
|
+
/* Uyandırma: grubun KOŞAN üyeleri; finance grubu için tüm finance ajanları */
|
|
10327
|
+
const g = engine._agentGroups && engine._agentGroups.get(key);
|
|
10328
|
+
const members = g && Array.isArray(g.members) ? g.members.map(String) : [];
|
|
10160
10329
|
const wake =
|
|
10161
|
-
`[AJAN DM — TELEGRAM
|
|
10330
|
+
`[AJAN DM — TELEGRAM${title ? ' · ' + title : ''} · ${label}]\n${body}\n` +
|
|
10162
10331
|
'Şimdi yap: sahibin Telegram grubundan gelen bu mesajı değerlendir; gerekiyorsa ekipçe aksiyon al. Kısa rapor ver.';
|
|
10163
10332
|
let sent = 0;
|
|
10164
10333
|
for (const [sid] of financeState.agents) {
|
|
10334
|
+
if (key !== FIN_TEAM_GID && members.length && !members.includes(String(sid))) continue;
|
|
10165
10335
|
if (finWakeAgentDm(sid, wake)) sent += 1;
|
|
10166
10336
|
}
|
|
10167
|
-
|
|
10337
|
+
/* finance dışı koşan üyeler (paralel ajan grupları) da mesajı görsün */
|
|
10338
|
+
const jobs = engine._bgJobs || new Map();
|
|
10339
|
+
for (const m of members) {
|
|
10340
|
+
const sid = String(m);
|
|
10341
|
+
if (financeState.agents.has(sid)) continue;
|
|
10342
|
+
const j = jobs.get(sid);
|
|
10343
|
+
if (!j || j.status !== 'running') continue;
|
|
10344
|
+
try {
|
|
10345
|
+
(engine._pendingReports = engine._pendingReports || []).push({ parentId: sid, text: wake, dm: true, wake: true });
|
|
10346
|
+
engine.flushPendingReports(sid);
|
|
10347
|
+
sent += 1;
|
|
10348
|
+
} catch {}
|
|
10349
|
+
}
|
|
10350
|
+
financeLog(`[telegram] ajan dm grubuna mesaj düştü: ${title} (${sent} ajan uyandı)`);
|
|
10168
10351
|
return { ok: true, sent };
|
|
10169
10352
|
} catch {
|
|
10170
10353
|
return { ok: false };
|
|
@@ -11658,8 +11841,15 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
11658
11841
|
/* AJAN DM ↔ TELEGRAM köprüsü ayarları (grup SEÇİLMEZ — otomatik bağlanır) */
|
|
11659
11842
|
if (p.dmTelegram !== undefined) f.dmTelegram = !!p.dmTelegram;
|
|
11660
11843
|
if (p.dmTelegramChat !== undefined) {
|
|
11844
|
+
const prevChat = String(f.dmTelegramChat || '');
|
|
11661
11845
|
f.dmTelegramChat = String(p.dmTelegramChat || '').trim().slice(0, 64);
|
|
11662
11846
|
if (!f.dmTelegramChat) f.dmTelegramTitle = ''; /* koparıldı → başlık da gitsin */
|
|
11847
|
+
/* grup değişti/koparıldı: eski konu eşlemeleri geçersiz — sıfırla */
|
|
11848
|
+
if (f.dmTelegramChat !== prevChat) {
|
|
11849
|
+
f.dmTelegramTopics = {};
|
|
11850
|
+
finDmTgThreads.clear();
|
|
11851
|
+
finDmTgBlockedAt.clear();
|
|
11852
|
+
}
|
|
11663
11853
|
}
|
|
11664
11854
|
if (p.dmTelegramTitle !== undefined) f.dmTelegramTitle = String(p.dmTelegramTitle || '').trim().slice(0, 80);
|
|
11665
11855
|
/* köprü yeni açıldı ve botun gördüğü TEK grup varsa hemen bağla */
|
package/src/renderer/index.html
CHANGED
|
@@ -892,9 +892,9 @@
|
|
|
892
892
|
</label>
|
|
893
893
|
<div class="fin-set-note">RİSK OTOMASYONU gizliyken koruma döngüsü (watchdog) kapatılır; tekrar gösterdiğinde ilgili kutucuktan açabilirsin.</div>
|
|
894
894
|
<div class="fin-set-sep"></div>
|
|
895
|
-
<label class="fin-set-row" title="Açıkken botu eklediğin Telegram grubuna yazılan ilk mesaj o grubu AJAN DM aynası yapar
|
|
895
|
+
<label class="fin-set-row" title="Açıkken botu eklediğin Telegram grubuna yazılan ilk mesaj o grubu AJAN DM aynası yapar. Her AJAN DM grubu Telegram'da AYRI BAŞLIK (topic) olarak canlı akar; başlığa yazdıkların ilgili AJAN DM grubunda görünür ve o grubun ajanları uyanır. Grup/Konu seçmene gerek yok.">
|
|
896
896
|
<input id="finDmTgOn" type="checkbox" />
|
|
897
|
-
<span class="fsr-main"><b>AJAN DM ↔ TELEGRAM</b><i>
|
|
897
|
+
<span class="fsr-main"><b>AJAN DM ↔ TELEGRAM</b><i>Her ajan grubu Telegram'da ayrı başlık — canlı</i></span>
|
|
898
898
|
</label>
|
|
899
899
|
<div id="finDmTgWrap" class="fin-set-sub" hidden>
|
|
900
900
|
<span class="fin-set-lbl">TELEGRAM GRUBU</span>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -10310,7 +10310,9 @@ function finDmTgPaint() {
|
|
|
10310
10310
|
if (els.finDmTgUnbind) els.finDmTgUnbind.hidden = !chat;
|
|
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 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
|
+
'Bir başlığa yazdığın mesaj ilgili AJAN DM grubunda görünür ve o grubun ajanları uyanır.';
|
|
10314
10316
|
}
|
|
10315
10317
|
}
|
|
10316
10318
|
|