beast-agent 2.57.0 → 2.58.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/engine.js +1 -1
- package/src/agent/telegram.js +6 -2
- package/src/agent/watchers.js +4 -3
- package/src/main.js +231 -32
- package/src/preload.js +0 -1
- package/src/renderer/index.html +5 -5
- package/src/renderer/renderer.js +26 -57
- package/src/renderer/style.css +1 -1
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -6178,7 +6178,7 @@ const TOOLS = [
|
|
|
6178
6178
|
everyMin: { type: 'number', description: 'Check interval in minutes (default 15, min 1, max 1440)' },
|
|
6179
6179
|
everySec: { type: 'number', description: 'Check interval in SECONDS (10-8640) — overrides everyMin for fast watchers (e.g. 30 = every 30s)' },
|
|
6180
6180
|
cooldownMin: { type: 'number', description: 'İki bildirim arası en az dakika (varsayılan 60). >0 ise alarm TEKRARLIDIR: koşul sürdükçe soğuma dolduğunda yeniden bildirir (ör. 5 = 5 dakikada bir hatırlat). 0 = yalnız koşul yeniden sağlandığında (kenar tetikleme).' },
|
|
6181
|
-
once: { type: 'boolean', description: 'true = tek seferlik izleyici: ilk tetiklemede
|
|
6181
|
+
once: { type: 'boolean', description: 'true = tek seferlik izleyici: ilk tetiklemede listeden OTOMATİK SİLİNİR (varsayılan false = tekrarlı)' },
|
|
6182
6182
|
},
|
|
6183
6183
|
required: ['name', 'kind'],
|
|
6184
6184
|
},
|
package/src/agent/telegram.js
CHANGED
|
@@ -108,6 +108,8 @@ class TelegramBridge {
|
|
|
108
108
|
isGroup: !!(msg.chat && (msg.chat.type === 'group' || msg.chat.type === 'supergroup')),
|
|
109
109
|
chatTitle: String((msg.chat && (msg.chat.title || msg.chat.username)) || ''),
|
|
110
110
|
chatType: String((msg.chat && msg.chat.type) || 'private'),
|
|
111
|
+
isForum: !!(msg.chat && msg.chat.is_forum),
|
|
112
|
+
messageThreadId: msg.message_thread_id || null,
|
|
111
113
|
};
|
|
112
114
|
try {
|
|
113
115
|
if (this.onIncoming) this.onIncoming(String(chatId), payload);
|
|
@@ -137,8 +139,9 @@ class TelegramBridge {
|
|
|
137
139
|
};
|
|
138
140
|
}
|
|
139
141
|
|
|
140
|
-
/* Metin gönder — 4096 sınırı için parçalara böl
|
|
141
|
-
|
|
142
|
+
/* Metin gönder — 4096 sınırı için parçalara böl; threadId verilirse forum
|
|
143
|
+
konusuna (topic) gönderir (message_thread_id). */
|
|
144
|
+
async send(chatId, text, threadId) {
|
|
142
145
|
const t = String(text || '');
|
|
143
146
|
if (!t.trim()) return false;
|
|
144
147
|
const chunks = [];
|
|
@@ -148,6 +151,7 @@ class TelegramBridge {
|
|
|
148
151
|
chat_id: chatId,
|
|
149
152
|
text: part,
|
|
150
153
|
disable_web_page_preview: true,
|
|
154
|
+
...(threadId ? { message_thread_id: Number(threadId) } : {}),
|
|
151
155
|
});
|
|
152
156
|
}
|
|
153
157
|
return true;
|
package/src/agent/watchers.js
CHANGED
|
@@ -194,7 +194,7 @@ function normalize(input) {
|
|
|
194
194
|
everyMin: Math.max(1, Math.round(everySec / 60)), // geriye dönük uyum
|
|
195
195
|
everySec,
|
|
196
196
|
cooldownMin,
|
|
197
|
-
once: !!i.once, /* tek seferlik: ilk tetiklemede
|
|
197
|
+
once: !!i.once, /* tek seferlik: ilk tetiklemede listeden OTOMATİK SİLİNİR */
|
|
198
198
|
prompt,
|
|
199
199
|
sessionId: i.sessionId || null,
|
|
200
200
|
enabled: true,
|
|
@@ -378,10 +378,11 @@ async function tickOnce(deps = {}) {
|
|
|
378
378
|
w.lastError = '';
|
|
379
379
|
pushLog(w.id, r.triggered ? 'trigger' : 'ok', value);
|
|
380
380
|
if (r.triggered) {
|
|
381
|
-
/* TEK SEFERLİK: ilk tetiklemede izleyici kapanır (yeniden etkinleştirilebilir) */
|
|
382
|
-
if (w.once) w.enabled = false;
|
|
383
381
|
if (typeof hooks.onTrigger === 'function') hooks.onTrigger({ ...w }, value);
|
|
384
382
|
events.push({ id: w.id, name: w.name, value });
|
|
383
|
+
/* TEK SEFERLİK: ilk tetiklemede izleyici listeden OTOMATİK SİLİNİR —
|
|
384
|
+
pasif olarak listede kalmaz (logları da temizlenir). */
|
|
385
|
+
if (w.once) remove(w.id);
|
|
385
386
|
}
|
|
386
387
|
} catch (e) {
|
|
387
388
|
w.lastCheckAt = new Date(now).toISOString();
|
package/src/main.js
CHANGED
|
@@ -2931,20 +2931,37 @@ async function handleTgIncoming(chatId, payload) {
|
|
|
2931
2931
|
try {
|
|
2932
2932
|
if (!engine) return;
|
|
2933
2933
|
/* GRUP: normal grup desteği kapalı; ancak AJAN DM ↔ TELEGRAM köprüsünde
|
|
2934
|
-
|
|
2934
|
+
köprü AÇIKKEN gruba yazılan ilk mesaj o grubu OTOMATİK bağlar —
|
|
2935
|
+
grup seçme adımı yok. Bağlı gruptan gelen mesaj AJAN DM grubuna düşer
|
|
2936
|
+
ve finance ajanları uyanır. */
|
|
2935
2937
|
if (payload.isGroup) {
|
|
2936
2938
|
tgRememberGroup(chatId, payload.chatTitle);
|
|
2937
2939
|
const dmTg = finDmTgCfg();
|
|
2938
|
-
if (dmTg.on
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2940
|
+
if (!dmTg.on) {
|
|
2941
|
+
tgLog(`skip: grup mesajı chat=${chatId} (köprü kapalı)`);
|
|
2942
|
+
return;
|
|
2943
|
+
}
|
|
2944
|
+
const allowed = tgFind(payload.senderId, payload.username);
|
|
2945
|
+
if (!allowed) {
|
|
2946
|
+
tgLog(`skip: grup mesajı chat=${chatId} — gönderici izin listesinde değil`);
|
|
2947
|
+
return;
|
|
2948
|
+
}
|
|
2949
|
+
if (!dmTg.chat) {
|
|
2950
|
+
/* İLK MESAJ = BAĞLAMA: /start gibi kısa komutlar yalnız bağlar,
|
|
2951
|
+
normal mesaj hem bağlar hem AJAN DM'e düşer. */
|
|
2952
|
+
finDmTgBind(chatId, payload.chatTitle);
|
|
2953
|
+
const boot = /^\/(start|baglan|bağlan)\b/i.test(String(payload.text || '').trim());
|
|
2954
|
+
if (boot) return;
|
|
2944
2955
|
finTelegramToAgentDm(payload);
|
|
2945
2956
|
return;
|
|
2946
2957
|
}
|
|
2947
|
-
|
|
2958
|
+
if (String(dmTg.chat) === String(chatId)) {
|
|
2959
|
+
/* FORUM: konu (thread) → ilgili AJAN DM grubu; konusuz/genel → finance */
|
|
2960
|
+
const gid = finDmTgGidForThread(payload.messageThreadId);
|
|
2961
|
+
finTelegramToAgentDm(payload, gid);
|
|
2962
|
+
return;
|
|
2963
|
+
}
|
|
2964
|
+
tgLog(`skip: grup mesajı chat=${chatId} (bağlı grup ${dmTg.chat})`);
|
|
2948
2965
|
return;
|
|
2949
2966
|
}
|
|
2950
2967
|
const hit = tgFind(payload.senderId, payload.username);
|
|
@@ -9401,12 +9418,14 @@ function finCfg() {
|
|
|
9401
9418
|
iptal) düşer; fiyat alarmı, koruma (BE/trailing/kısmi TP), günlük limit ve
|
|
9402
9419
|
rapor bildirimleri panelde kalır. false = tüm olaylar kanallara gider. */
|
|
9403
9420
|
if (typeof f.notifyTradeOnly !== 'boolean') f.notifyTradeOnly = true;
|
|
9404
|
-
/* AJAN DM ↔ TELEGRAM KÖPRÜSÜ: açıkken
|
|
9405
|
-
|
|
9406
|
-
|
|
9421
|
+
/* AJAN DM ↔ TELEGRAM KÖPRÜSÜ: açıkken AJAN DM gruplarındaki mesajlar seçilen
|
|
9422
|
+
Telegram grubuna düşer; o gruba yazılan mesaj AJAN DM grubunda görünür ve
|
|
9423
|
+
koşan finance ajanları tur beklemeden uyanır. Forum (Konular) destekli
|
|
9424
|
+
grupta her AJAN DM grubu AYRI BAŞLIK (topic) olarak açılır canlı akar. */
|
|
9407
9425
|
if (typeof f.dmTelegram !== 'boolean') f.dmTelegram = false;
|
|
9408
9426
|
if (typeof f.dmTelegramChat !== 'string') f.dmTelegramChat = '';
|
|
9409
9427
|
if (typeof f.dmTelegramTitle !== 'string') f.dmTelegramTitle = '';
|
|
9428
|
+
if (!f.dmTelegramTopics || typeof f.dmTelegramTopics !== 'object') f.dmTelegramTopics = {};
|
|
9410
9429
|
if (!Number.isFinite(Number(f.maxDailyLossPct))) f.maxDailyLossPct = 3;
|
|
9411
9430
|
/* GÜNLÜK LİMİT AKSİYONU: warn = yalnız uyarı; stop = tüm finance ajanlarını
|
|
9412
9431
|
durdur; flatten = durdur + tüm pozisyonları kapat */
|
|
@@ -10067,46 +10086,196 @@ async function finStatsRefresh(force) {
|
|
|
10067
10086
|
const FIN_TEAM_GID = 'team:finance';
|
|
10068
10087
|
const FIN_TEAM_TITLE = 'Beast Finance EKİP';
|
|
10069
10088
|
|
|
10070
|
-
/* Köprü
|
|
10089
|
+
/* Köprü ayarı: açık mı + bağlı grup (chatId). Grup SEÇİLMEZ — köprü açıkken
|
|
10090
|
+
gruba yazılan ilk mesaj o grubu otomatik bağlar (finDmTgBind). */
|
|
10071
10091
|
function finDmTgCfg() {
|
|
10072
10092
|
let f = {};
|
|
10073
10093
|
try { f = finCfg(); } catch {}
|
|
10074
10094
|
return {
|
|
10075
|
-
on: !!(f && f.dmTelegram
|
|
10095
|
+
on: !!(f && f.dmTelegram),
|
|
10076
10096
|
chat: String((f && f.dmTelegramChat) || ''),
|
|
10077
10097
|
title: String((f && f.dmTelegramTitle) || ''),
|
|
10078
10098
|
};
|
|
10079
10099
|
}
|
|
10080
10100
|
|
|
10081
|
-
/*
|
|
10101
|
+
/* Otomatik bağlama: gelen grup mesajı (izinli gönderici) AJAN DM aynası olur. */
|
|
10102
|
+
function finDmTgBind(chatId, title) {
|
|
10103
|
+
const id = String(chatId || '');
|
|
10104
|
+
if (!id) return false;
|
|
10105
|
+
const f = finCfg();
|
|
10106
|
+
const t = String(title || '').trim();
|
|
10107
|
+
/* GRUP DEĞİŞTİ: eski konu eşlemeleri yeni chat'te geçersiz — sıfırla */
|
|
10108
|
+
if (String(f.dmTelegramChat || '') !== id) {
|
|
10109
|
+
f.dmTelegramTopics = {};
|
|
10110
|
+
finDmTgThreads.clear();
|
|
10111
|
+
finDmTgBlockedAt.clear();
|
|
10112
|
+
}
|
|
10113
|
+
f.dmTelegramChat = id;
|
|
10114
|
+
f.dmTelegramTitle = t || f.dmTelegramTitle || id;
|
|
10115
|
+
try { saveSettings(); } catch {}
|
|
10116
|
+
tgLog(`ajan dm telegram grubu OTOMATİK bağlandı: ${id}${t ? ' (' + t + ')' : ''}`);
|
|
10117
|
+
/* gruba onay: kullanıcı bağlandığını net görsün */
|
|
10118
|
+
try {
|
|
10119
|
+
if (tg && tg.connected) {
|
|
10120
|
+
Promise.resolve(
|
|
10121
|
+
tg.send(
|
|
10122
|
+
id,
|
|
10123
|
+
'✅ Beast Finance AJAN DM bu gruba bağlandı.\n' +
|
|
10124
|
+
'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
|
+
'NOT: Grup Konular (Topics) destekliyorsa her AJAN DM grubu ayrı başlık olarak canlı akar — botu yönetici yapıp Konuları Yönet izni ver.'
|
|
10126
|
+
)
|
|
10127
|
+
).catch(() => {});
|
|
10128
|
+
}
|
|
10129
|
+
} catch {}
|
|
10130
|
+
return true;
|
|
10131
|
+
}
|
|
10132
|
+
|
|
10133
|
+
/* ---- TELEGRAM KONU (TOPIC) EŞLEMESİ ----
|
|
10134
|
+
Forum destekli Telegram grubunda her AJAN DM grubu AYRI BAŞLIK olarak açılır:
|
|
10135
|
+
gid → message_thread_id eşlemesi settings.finance.dmTelegramTopics'ta kalıcı.
|
|
10136
|
+
Konu açılamazsa (forum değil / bot yetkisiz) mesajlar grup adı etiketiyle
|
|
10137
|
+
genel akışa düşer — köprü yine çalışır. */
|
|
10138
|
+
const finDmTgThreads = new Map(); // gid -> threadId (>0) | 0 (forum değil — kalıcı)
|
|
10139
|
+
const finDmTgThreadPend = new Map(); // gid -> in-flight açma promise'i
|
|
10140
|
+
const finDmTgBlockedAt = new Map(); // gid -> son hata ts (geçici: 10 dk sonra yeniden dener)
|
|
10141
|
+
const FIN_DM_TG_RETRY_MS = 10 * 60 * 1000;
|
|
10142
|
+
let finDmTgChatMeta = { id: '', at: 0, isForum: false };
|
|
10143
|
+
let finDmTgSendChain = Promise.resolve(); // gönderim sırası korunsun
|
|
10144
|
+
|
|
10145
|
+
function finDmTgChatInfo(chatId) {
|
|
10146
|
+
const id = String(chatId || '');
|
|
10147
|
+
if (finDmTgChatMeta.id === id && Date.now() - finDmTgChatMeta.at < 5 * 60000) {
|
|
10148
|
+
return Promise.resolve(finDmTgChatMeta);
|
|
10149
|
+
}
|
|
10150
|
+
return Promise.resolve(tg.api('getChat', { chat_id: id }))
|
|
10151
|
+
.then((r) => {
|
|
10152
|
+
finDmTgChatMeta = { id, at: Date.now(), isForum: !!(r && r.is_forum) };
|
|
10153
|
+
return finDmTgChatMeta;
|
|
10154
|
+
})
|
|
10155
|
+
.catch(() => {
|
|
10156
|
+
finDmTgChatMeta = { id, at: Date.now(), isForum: false };
|
|
10157
|
+
return finDmTgChatMeta;
|
|
10158
|
+
});
|
|
10159
|
+
}
|
|
10160
|
+
|
|
10161
|
+
function finDmTgThreadCached(gid) {
|
|
10162
|
+
if (finDmTgThreads.has(gid)) return Number(finDmTgThreads.get(gid)) || 0;
|
|
10163
|
+
const f = finCfg();
|
|
10164
|
+
const rec = f.dmTelegramTopics && f.dmTelegramTopics[gid];
|
|
10165
|
+
const tid = rec && rec.threadId ? Number(rec.threadId) || 0 : 0;
|
|
10166
|
+
if (tid) finDmTgThreads.set(gid, tid);
|
|
10167
|
+
return tid;
|
|
10168
|
+
}
|
|
10169
|
+
|
|
10170
|
+
/* gid için topic'i hazırla (yoksa aç). Dönüş: threadId (0 = konu yok/genel akış) */
|
|
10171
|
+
function finDmTgThreadEnsure(chatId, gid, title) {
|
|
10172
|
+
const cached = finDmTgThreadCached(gid);
|
|
10173
|
+
if (cached > 0) return Promise.resolve(cached);
|
|
10174
|
+
if (finDmTgThreads.has(gid)) return Promise.resolve(0); // forum değil → genel akış
|
|
10175
|
+
const blockedAt = finDmTgBlockedAt.get(gid) || 0;
|
|
10176
|
+
if (blockedAt && Date.now() - blockedAt < FIN_DM_TG_RETRY_MS) return Promise.resolve(0);
|
|
10177
|
+
if (finDmTgThreadPend.has(gid)) return finDmTgThreadPend.get(gid);
|
|
10178
|
+
const p = (async () => {
|
|
10179
|
+
try {
|
|
10180
|
+
const info = await finDmTgChatInfo(chatId);
|
|
10181
|
+
if (!info.isForum) { finDmTgThreads.set(gid, 0); return 0; }
|
|
10182
|
+
const r = await tg.api('createForumTopic', {
|
|
10183
|
+
chat_id: chatId,
|
|
10184
|
+
name: String(title || gid).slice(0, 128),
|
|
10185
|
+
});
|
|
10186
|
+
const tid = Number(r && r.message_thread_id) || 0;
|
|
10187
|
+
if (!tid) { finDmTgBlockedAt.set(gid, Date.now()); return 0; }
|
|
10188
|
+
finDmTgThreads.set(gid, tid);
|
|
10189
|
+
finDmTgBlockedAt.delete(gid);
|
|
10190
|
+
const f = finCfg();
|
|
10191
|
+
if (!f.dmTelegramTopics || typeof f.dmTelegramTopics !== 'object') f.dmTelegramTopics = {};
|
|
10192
|
+
f.dmTelegramTopics[gid] = { threadId: tid, title: String(title || gid), at: Date.now() };
|
|
10193
|
+
try { saveSettings(); } catch {}
|
|
10194
|
+
tgLog(`telegram konu açıldı: "${title}" (${gid}) → thread ${tid}`);
|
|
10195
|
+
return tid;
|
|
10196
|
+
} catch (e) {
|
|
10197
|
+
/* geçici hata (ağ/yetki): kalıcı işaretleme — 10 dk sonra yeniden denenir */
|
|
10198
|
+
finDmTgBlockedAt.set(gid, Date.now());
|
|
10199
|
+
tgLog(`telegram konu açılamadı (${gid}): ` + String((e && e.message) || e));
|
|
10200
|
+
return 0;
|
|
10201
|
+
} finally {
|
|
10202
|
+
finDmTgThreadPend.delete(gid);
|
|
10203
|
+
}
|
|
10204
|
+
})();
|
|
10205
|
+
finDmTgThreadPend.set(gid, p);
|
|
10206
|
+
return p;
|
|
10207
|
+
}
|
|
10208
|
+
|
|
10209
|
+
/* message_thread_id → AJAN DM gid (bilinmeyen konu: genel akış → finance) */
|
|
10210
|
+
function finDmTgGidForThread(threadId) {
|
|
10211
|
+
const tid = Number(threadId) || 0;
|
|
10212
|
+
if (!tid) return '';
|
|
10213
|
+
const f = finCfg();
|
|
10214
|
+
const topics = f.dmTelegramTopics && typeof f.dmTelegramTopics === 'object' ? f.dmTelegramTopics : {};
|
|
10215
|
+
for (const [gid, rec] of Object.entries(topics)) {
|
|
10216
|
+
if (rec && Number(rec.threadId) === tid) return gid;
|
|
10217
|
+
}
|
|
10218
|
+
return '';
|
|
10219
|
+
}
|
|
10220
|
+
|
|
10221
|
+
/* Gönderimleri SIRAYLA işle: konu açma + mesaj sırası bozulmasın. */
|
|
10222
|
+
function finDmTgQueue(task) {
|
|
10223
|
+
finDmTgSendChain = finDmTgSendChain
|
|
10224
|
+
.then(() => task())
|
|
10225
|
+
.catch((e) => tgLog('ajan dm → telegram kuyruk hata: ' + String((e && e.message) || e)));
|
|
10226
|
+
return finDmTgSendChain;
|
|
10227
|
+
}
|
|
10228
|
+
|
|
10229
|
+
/* Giden: HERHANGİ bir AJAN DM grubu → Telegram (topic varsa kendi başlığına;
|
|
10230
|
+
Telegram'dan gelenler geri gönderilmez; grup bağlanmadıysa beklenir). */
|
|
10082
10231
|
function finAgentDmToTelegram(dm) {
|
|
10083
10232
|
try {
|
|
10084
10233
|
if (!dm || dm.viaTelegram) return;
|
|
10085
|
-
|
|
10234
|
+
const gid = String(dm.group || '');
|
|
10235
|
+
if (!gid) return; /* yalnız grup sohbetleri köprülenir (1:1 DM panelde kalır) */
|
|
10086
10236
|
const c = finDmTgCfg();
|
|
10087
|
-
if (!c.on || !tg || !tg.connected) return;
|
|
10237
|
+
if (!c.on || !c.chat || !tg || !tg.connected) return;
|
|
10238
|
+
const title = String(dm.groupTitle || (gid === FIN_TEAM_GID ? FIN_TEAM_TITLE : gid));
|
|
10088
10239
|
const who = String(dm.fromTitle || (dm.system ? 'SİSTEM' : 'Ajan'));
|
|
10089
|
-
const
|
|
10090
|
-
|
|
10091
|
-
|
|
10092
|
-
|
|
10093
|
-
|
|
10094
|
-
|
|
10240
|
+
const text = String(dm.text || '').slice(0, 3800);
|
|
10241
|
+
finDmTgQueue(async () => {
|
|
10242
|
+
const tid = await finDmTgThreadEnsure(c.chat, gid, title);
|
|
10243
|
+
const plain = `👥 [${title}] ${who}:\n${text}`;
|
|
10244
|
+
if (tid > 0) {
|
|
10245
|
+
try {
|
|
10246
|
+
await tg.send(c.chat, `${who}:\n${text}`, tid);
|
|
10247
|
+
return;
|
|
10248
|
+
} catch {
|
|
10249
|
+
/* konu silinmiş/erişim yok → genel akışa düş (mesaj kaybolmasın) */
|
|
10250
|
+
}
|
|
10251
|
+
}
|
|
10252
|
+
await tg.send(c.chat, plain);
|
|
10253
|
+
});
|
|
10095
10254
|
} catch {}
|
|
10096
10255
|
}
|
|
10097
10256
|
|
|
10098
|
-
/* Gelen: Telegram grubu → AJAN DM grubu (+
|
|
10099
|
-
|
|
10257
|
+
/* Gelen: Telegram grubu/konusu → ilgili AJAN DM grubu (+ üye ajanları uyandır).
|
|
10258
|
+
gid verilmezse finance grubuna düşer (genel akış / konusuz mesaj). */
|
|
10259
|
+
function finTelegramToAgentDm(payload, gid) {
|
|
10100
10260
|
try {
|
|
10101
10261
|
const body = String((payload && payload.text) || '').trim();
|
|
10102
10262
|
if (!body || !engine) return { ok: false };
|
|
10263
|
+
const key = String(gid || FIN_TEAM_GID);
|
|
10264
|
+
const f = finCfg();
|
|
10265
|
+
const topics = f.dmTelegramTopics && typeof f.dmTelegramTopics === 'object' ? f.dmTelegramTopics : {};
|
|
10266
|
+
const title = String(
|
|
10267
|
+
(topics[key] && topics[key].title) ||
|
|
10268
|
+
(key === FIN_TEAM_GID ? FIN_TEAM_TITLE : '') ||
|
|
10269
|
+
(payload && payload.chatTitle) ||
|
|
10270
|
+
key
|
|
10271
|
+
);
|
|
10103
10272
|
const who = String((payload && (payload.senderName || payload.username)) || 'Telegram');
|
|
10104
10273
|
const label = `Sahip · Telegram (${who})`;
|
|
10105
10274
|
try {
|
|
10106
10275
|
if (typeof engine.agentDmGroupPost === 'function') {
|
|
10107
10276
|
engine.agentDmGroupPost({
|
|
10108
|
-
gid:
|
|
10109
|
-
title
|
|
10277
|
+
gid: key,
|
|
10278
|
+
title,
|
|
10110
10279
|
fromSid: 'tg:' + String((payload && payload.senderId) || ''),
|
|
10111
10280
|
fromTitle: label,
|
|
10112
10281
|
topic: 'telegram',
|
|
@@ -10115,14 +10284,31 @@ function finTelegramToAgentDm(payload) {
|
|
|
10115
10284
|
});
|
|
10116
10285
|
}
|
|
10117
10286
|
} catch {}
|
|
10287
|
+
/* Uyandırma: grubun KOŞAN üyeleri; finance grubu için tüm finance ajanları */
|
|
10288
|
+
const g = engine._agentGroups && engine._agentGroups.get(key);
|
|
10289
|
+
const members = g && Array.isArray(g.members) ? g.members.map(String) : [];
|
|
10118
10290
|
const wake =
|
|
10119
|
-
`[AJAN DM — TELEGRAM
|
|
10291
|
+
`[AJAN DM — TELEGRAM${title ? ' · ' + title : ''} · ${label}]\n${body}\n` +
|
|
10120
10292
|
'Şimdi yap: sahibin Telegram grubundan gelen bu mesajı değerlendir; gerekiyorsa ekipçe aksiyon al. Kısa rapor ver.';
|
|
10121
10293
|
let sent = 0;
|
|
10122
10294
|
for (const [sid] of financeState.agents) {
|
|
10295
|
+
if (key !== FIN_TEAM_GID && members.length && !members.includes(String(sid))) continue;
|
|
10123
10296
|
if (finWakeAgentDm(sid, wake)) sent += 1;
|
|
10124
10297
|
}
|
|
10125
|
-
|
|
10298
|
+
/* finance dışı koşan üyeler (paralel ajan grupları) da mesajı görsün */
|
|
10299
|
+
const jobs = engine._bgJobs || new Map();
|
|
10300
|
+
for (const m of members) {
|
|
10301
|
+
const sid = String(m);
|
|
10302
|
+
if (financeState.agents.has(sid)) continue;
|
|
10303
|
+
const j = jobs.get(sid);
|
|
10304
|
+
if (!j || j.status !== 'running') continue;
|
|
10305
|
+
try {
|
|
10306
|
+
(engine._pendingReports = engine._pendingReports || []).push({ parentId: sid, text: wake, dm: true, wake: true });
|
|
10307
|
+
engine.flushPendingReports(sid);
|
|
10308
|
+
sent += 1;
|
|
10309
|
+
} catch {}
|
|
10310
|
+
}
|
|
10311
|
+
financeLog(`[telegram] ajan dm grubuna mesaj düştü: ${title} (${sent} ajan uyandı)`);
|
|
10126
10312
|
return { ok: true, sent };
|
|
10127
10313
|
} catch {
|
|
10128
10314
|
return { ok: false };
|
|
@@ -11613,13 +11799,28 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
11613
11799
|
if (p.notifyTrades !== undefined) f.notifyTrades = !!p.notifyTrades;
|
|
11614
11800
|
if (p.notifyWatchdog !== undefined) f.notifyWatchdog = !!p.notifyWatchdog;
|
|
11615
11801
|
if (p.notifyTradeOnly !== undefined) f.notifyTradeOnly = !!p.notifyTradeOnly;
|
|
11616
|
-
/* AJAN DM ↔ TELEGRAM köprüsü ayarları */
|
|
11802
|
+
/* AJAN DM ↔ TELEGRAM köprüsü ayarları (grup SEÇİLMEZ — otomatik bağlanır) */
|
|
11617
11803
|
if (p.dmTelegram !== undefined) f.dmTelegram = !!p.dmTelegram;
|
|
11618
11804
|
if (p.dmTelegramChat !== undefined) {
|
|
11805
|
+
const prevChat = String(f.dmTelegramChat || '');
|
|
11619
11806
|
f.dmTelegramChat = String(p.dmTelegramChat || '').trim().slice(0, 64);
|
|
11620
|
-
if (!f.dmTelegramChat) f.
|
|
11807
|
+
if (!f.dmTelegramChat) f.dmTelegramTitle = ''; /* koparıldı → başlık da gitsin */
|
|
11808
|
+
/* grup değişti/koparıldı: eski konu eşlemeleri geçersiz — sıfırla */
|
|
11809
|
+
if (f.dmTelegramChat !== prevChat) {
|
|
11810
|
+
f.dmTelegramTopics = {};
|
|
11811
|
+
finDmTgThreads.clear();
|
|
11812
|
+
finDmTgBlockedAt.clear();
|
|
11813
|
+
}
|
|
11621
11814
|
}
|
|
11622
11815
|
if (p.dmTelegramTitle !== undefined) f.dmTelegramTitle = String(p.dmTelegramTitle || '').trim().slice(0, 80);
|
|
11816
|
+
/* köprü yeni açıldı ve botun gördüğü TEK grup varsa hemen bağla */
|
|
11817
|
+
if (p.dmTelegram === true && !f.dmTelegramChat) {
|
|
11818
|
+
const known = tgGroupsList();
|
|
11819
|
+
if (known.length === 1) {
|
|
11820
|
+
f.dmTelegramChat = String(known[0].id);
|
|
11821
|
+
f.dmTelegramTitle = String(known[0].title || known[0].id);
|
|
11822
|
+
}
|
|
11823
|
+
}
|
|
11623
11824
|
if (p.maxDailyLossPct !== undefined) f.maxDailyLossPct = Math.max(0, Math.min(50, Number(p.maxDailyLossPct) || 0));
|
|
11624
11825
|
if (p.dailyLossAction !== undefined) {
|
|
11625
11826
|
const v = String(p.dailyLossAction || 'warn').toLowerCase();
|
|
@@ -14024,8 +14225,6 @@ ipcMain.handle('tg:allow:set', (_e, list) => {
|
|
|
14024
14225
|
saveSettings();
|
|
14025
14226
|
return settings.tgAllow;
|
|
14026
14227
|
});
|
|
14027
|
-
/* Botun gördüğü gruplar (AJAN DM ↔ TELEGRAM grup seçicisi) */
|
|
14028
|
-
ipcMain.handle('tg:groups:list', () => tgGroupsList());
|
|
14029
14228
|
ipcMain.handle('tg:sessions', () => [...tgChats.values()]);
|
|
14030
14229
|
|
|
14031
14230
|
/* Sohbet geçmişi etiketi (Telegram): "Telegram <ad|chatId>" */
|
package/src/preload.js
CHANGED
|
@@ -221,7 +221,6 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
221
221
|
tgStop: () => ipcRenderer.invoke('tg:stop'),
|
|
222
222
|
tgGetAllow: () => ipcRenderer.invoke('tg:allow:get'),
|
|
223
223
|
tgSetAllow: (list) => ipcRenderer.invoke('tg:allow:set', list),
|
|
224
|
-
tgGroupsList: () => ipcRenderer.invoke('tg:groups:list'),
|
|
225
224
|
tgListSessions: () => ipcRenderer.invoke('tg:sessions'),
|
|
226
225
|
onTgEvent: (cb) => ipcRenderer.on('tg:event', (_e, ev) => cb(ev)),
|
|
227
226
|
dcGetStatus: () => ipcRenderer.invoke('dc:status:get'),
|
package/src/renderer/index.html
CHANGED
|
@@ -800,7 +800,7 @@
|
|
|
800
800
|
</div>
|
|
801
801
|
<div class="mini-add-row">
|
|
802
802
|
<input id="wAddCd" class="inp" type="number" min="0" max="10080" value="60" title="soğuma (dk): koşul sürdükçe en fazla bu aralıkta bir bildirir — 0 = yalnız koşul yeniden sağlanınca" style="width:70px" />
|
|
803
|
-
<label style="display:flex;align-items:center;gap:5px;font-size:12px;color:var(--muted);white-space:nowrap"><input id="wAddOnce" type="checkbox" /> tek seferlik</label>
|
|
803
|
+
<label style="display:flex;align-items:center;gap:5px;font-size:12px;color:var(--muted);white-space:nowrap"><input id="wAddOnce" type="checkbox" /> tek seferlik <i style="font-style:normal;opacity:.75">(tetiklenince listeden silinir)</i></label>
|
|
804
804
|
</div>
|
|
805
805
|
</div>
|
|
806
806
|
<div id="watchList" class="mini-list"></div>
|
|
@@ -892,14 +892,14 @@
|
|
|
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
|
|
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>
|
|
901
|
-
<
|
|
902
|
-
<button id="
|
|
901
|
+
<div id="finDmTgState" class="fin-dm-tg-state">—</div>
|
|
902
|
+
<button id="finDmTgUnbind" class="fin-set-btn" title="Bağlantıyı kopar — sonra gruba yazılan ilk mesaj yeni grup olarak otomatik bağlanır">✕︎ Bağlantıyı kopar</button>
|
|
903
903
|
<div id="finDmTgHint" class="fin-set-note" style="margin:2px 0 0"></div>
|
|
904
904
|
</div>
|
|
905
905
|
</div>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -205,8 +205,8 @@ const els = {
|
|
|
205
205
|
finShowFlow: $('#finShowFlow'),
|
|
206
206
|
finDmTgOn: $('#finDmTgOn'),
|
|
207
207
|
finDmTgWrap: $('#finDmTgWrap'),
|
|
208
|
-
|
|
209
|
-
|
|
208
|
+
finDmTgState: $('#finDmTgState'),
|
|
209
|
+
finDmTgUnbind: $('#finDmTgUnbind'),
|
|
210
210
|
finDmTgHint: $('#finDmTgHint'),
|
|
211
211
|
finRiskCard: $('#finRiskCard'),
|
|
212
212
|
finNotifyCard: $('#finNotifyCard'),
|
|
@@ -10124,9 +10124,8 @@ async function finSnapshot() {
|
|
|
10124
10124
|
if (!finView.risk && r.cfg && r.cfg.watchdog !== false) finSaveCfg({ watchdog: false });
|
|
10125
10125
|
finRenderStats(r.stats, r.equity, r.account && r.account.currency ? r.account.currency : '');
|
|
10126
10126
|
finRenderAlerts(r.alerts);
|
|
10127
|
-
/* görünüm ayarları açıkken AJAN DM ↔ TELEGRAM
|
|
10128
|
-
|
|
10129
|
-
if (els.finSettingsOverlay && !els.finSettingsOverlay.hidden && document.activeElement !== els.finDmTgChat) {
|
|
10127
|
+
/* görünüm ayarları açıkken AJAN DM ↔ TELEGRAM durumu cfg ile senkron kalsın */
|
|
10128
|
+
if (els.finSettingsOverlay && !els.finSettingsOverlay.hidden) {
|
|
10130
10129
|
finDmTgPaint();
|
|
10131
10130
|
}
|
|
10132
10131
|
}
|
|
@@ -10291,10 +10290,9 @@ function finViewApply() {
|
|
|
10291
10290
|
}
|
|
10292
10291
|
|
|
10293
10292
|
/* ---------- AJAN DM ↔ TELEGRAM (görünüm ayarları) ----------
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
|
|
10293
|
+
Grup SEÇİLMEZ: köprü açıkken botu eklediğin gruba yazılan ilk mesaj o grubu
|
|
10294
|
+
otomatik bağlar (sunucu tarafı: finDmTgBind). Bağlantıyı koparınca yeni
|
|
10295
|
+
grupta ilk mesaj yeniden bağlar. */
|
|
10298
10296
|
function finDmTgPaint() {
|
|
10299
10297
|
if (!els.finDmTgOn) return;
|
|
10300
10298
|
const cfg = finCfgCache || {};
|
|
@@ -10303,50 +10301,24 @@ function finDmTgPaint() {
|
|
|
10303
10301
|
const row = els.finDmTgOn.closest('.fin-set-row');
|
|
10304
10302
|
if (row) row.classList.toggle('on', on);
|
|
10305
10303
|
if (els.finDmTgWrap) els.finDmTgWrap.hidden = !on;
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
opt0.value = '';
|
|
10312
|
-
opt0.textContent = finDmTgGroups.length ? '— Grup seç —' : '— Görünen grup yok —';
|
|
10313
|
-
sel.appendChild(opt0);
|
|
10314
|
-
let has = false;
|
|
10315
|
-
for (const g of finDmTgGroups) {
|
|
10316
|
-
const o = document.createElement('option');
|
|
10317
|
-
o.value = String(g.id);
|
|
10318
|
-
o.textContent = String(g.title || g.id) + ' (' + g.id + ')';
|
|
10319
|
-
if (String(g.id) === cur) { o.selected = true; has = true; }
|
|
10320
|
-
sel.appendChild(o);
|
|
10321
|
-
}
|
|
10322
|
-
/* listelenmemiş kayıtlı grup kaybolmasın — mevcut seçim korunur */
|
|
10323
|
-
if (cur && !has) {
|
|
10324
|
-
const o = document.createElement('option');
|
|
10325
|
-
o.value = cur;
|
|
10326
|
-
o.textContent = String(cfg.dmTelegramTitle || cur) + ' (' + cur + ')';
|
|
10327
|
-
o.selected = true;
|
|
10328
|
-
sel.appendChild(o);
|
|
10329
|
-
}
|
|
10304
|
+
const chat = String(cfg.dmTelegramChat || '');
|
|
10305
|
+
if (els.finDmTgState) {
|
|
10306
|
+
els.finDmTgState.textContent = chat
|
|
10307
|
+
? '✅ Bağlı: ' + String(cfg.dmTelegramTitle || chat) + ' (' + chat + ')'
|
|
10308
|
+
: 'Bağlı grup yok — botu gruba ekle ve gruba /start yaz; o grup otomatik bağlanır.';
|
|
10330
10309
|
}
|
|
10310
|
+
if (els.finDmTgUnbind) els.finDmTgUnbind.hidden = !chat;
|
|
10331
10311
|
if (els.finDmTgHint) {
|
|
10332
|
-
els.finDmTgHint.textContent =
|
|
10333
|
-
|
|
10334
|
-
|
|
10312
|
+
els.finDmTgHint.textContent =
|
|
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önetici (Konuları Yönet) olmalı — 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.';
|
|
10335
10316
|
}
|
|
10336
10317
|
}
|
|
10337
10318
|
|
|
10338
|
-
async function finDmTgRefresh() {
|
|
10339
|
-
try {
|
|
10340
|
-
const list = await beast.tgGroupsList();
|
|
10341
|
-
finDmTgGroups = Array.isArray(list) ? list : [];
|
|
10342
|
-
} catch { finDmTgGroups = []; }
|
|
10343
|
-
finDmTgPaint();
|
|
10344
|
-
}
|
|
10345
|
-
|
|
10346
10319
|
function finSettingsOpen() {
|
|
10347
10320
|
if (els.finSettingsOverlay) els.finSettingsOverlay.hidden = false;
|
|
10348
10321
|
finDmTgPaint();
|
|
10349
|
-
finDmTgRefresh();
|
|
10350
10322
|
}
|
|
10351
10323
|
function finSettingsCloseModal() {
|
|
10352
10324
|
if (els.finSettingsOverlay) els.finSettingsOverlay.hidden = true;
|
|
@@ -10380,30 +10352,27 @@ if (els.finShowRisk) els.finShowRisk.addEventListener('change', () => finViewTog
|
|
|
10380
10352
|
if (els.finShowNotify) els.finShowNotify.addEventListener('change', () => finViewToggle('notify', els.finShowNotify.checked));
|
|
10381
10353
|
if (els.finShowPerf) els.finShowPerf.addEventListener('change', () => finViewToggle('perf', els.finShowPerf.checked));
|
|
10382
10354
|
if (els.finShowFlow) els.finShowFlow.addEventListener('change', () => finViewToggle('flow', els.finShowFlow.checked));
|
|
10383
|
-
/* AJAN DM ↔ TELEGRAM köprüsü kontrolleri */
|
|
10355
|
+
/* AJAN DM ↔ TELEGRAM köprüsü kontrolleri (grup otomatik bağlanır) */
|
|
10384
10356
|
if (els.finDmTgOn) {
|
|
10385
10357
|
els.finDmTgOn.addEventListener('change', () => {
|
|
10386
10358
|
const on = els.finDmTgOn.checked;
|
|
10387
10359
|
if (finCfgCache) finCfgCache.dmTelegram = on;
|
|
10388
10360
|
finSaveCfg({ dmTelegram: on });
|
|
10389
10361
|
finDmTgPaint();
|
|
10390
|
-
toast(on ? 'AJAN DM ↔ Telegram
|
|
10391
|
-
if (on) finDmTgRefresh();
|
|
10362
|
+
toast(on ? 'AJAN DM ↔ Telegram açıldı — gruba /start yazınca otomatik bağlanır' : 'AJAN DM ↔ Telegram kapatıldı');
|
|
10392
10363
|
});
|
|
10393
10364
|
}
|
|
10394
|
-
if (els.
|
|
10395
|
-
els.
|
|
10396
|
-
const id = els.finDmTgChat.value || '';
|
|
10397
|
-
const g = finDmTgGroups.find((x) => String(x.id) === String(id));
|
|
10365
|
+
if (els.finDmTgUnbind) {
|
|
10366
|
+
els.finDmTgUnbind.addEventListener('click', () => {
|
|
10398
10367
|
if (finCfgCache) {
|
|
10399
|
-
finCfgCache.dmTelegramChat =
|
|
10400
|
-
finCfgCache.dmTelegramTitle =
|
|
10368
|
+
finCfgCache.dmTelegramChat = '';
|
|
10369
|
+
finCfgCache.dmTelegramTitle = '';
|
|
10401
10370
|
}
|
|
10402
|
-
finSaveCfg({ dmTelegramChat:
|
|
10403
|
-
|
|
10371
|
+
finSaveCfg({ dmTelegramChat: '', dmTelegramTitle: '' });
|
|
10372
|
+
finDmTgPaint();
|
|
10373
|
+
toast('Telegram grubu koparıldı — yeni grupta /start yazınca bağlanır');
|
|
10404
10374
|
});
|
|
10405
10375
|
}
|
|
10406
|
-
if (els.finDmTgRefresh) els.finDmTgRefresh.addEventListener('click', () => finDmTgRefresh());
|
|
10407
10376
|
finViewApply();
|
|
10408
10377
|
|
|
10409
10378
|
/* ---------- BÖLÜM SIRALAMA (sürükle-bırak) ----------
|
package/src/renderer/style.css
CHANGED
|
@@ -5882,7 +5882,7 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
|
|
|
5882
5882
|
}
|
|
5883
5883
|
.fin-set-sub[hidden] { display: none; }
|
|
5884
5884
|
.fin-set-lbl { font-size: 10.5px; color: var(--muted); letter-spacing: 0.5px; }
|
|
5885
|
-
.fin-
|
|
5885
|
+
.fin-dm-tg-state { font-size: 11.5px; color: var(--text); line-height: 1.4; word-break: break-word; }
|
|
5886
5886
|
.fin-set-btn {
|
|
5887
5887
|
align-self: flex-start;
|
|
5888
5888
|
font-size: 10.5px;
|