beast-agent 2.56.0 → 2.57.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/engine.js +66 -1
- package/src/agent/telegram.js +2 -0
- package/src/agent/watchers.js +4 -3
- package/src/main.js +212 -15
- package/src/renderer/index.html +12 -1
- package/src/renderer/renderer.js +56 -0
- package/src/renderer/style.css +20 -0
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
|
},
|
|
@@ -7109,6 +7109,11 @@ Engine.prototype._pushAgentDm = function (dm) {
|
|
|
7109
7109
|
this._agentDms = this._agentDms || [];
|
|
7110
7110
|
this._agentDms.push(dm);
|
|
7111
7111
|
if (this._agentDms.length > 600) this._agentDms.splice(0, this._agentDms.length - 600);
|
|
7112
|
+
/* DIŞ KÖPRÜ KANCASI (main.js: Telegram): panel kaydı düşen her DM tek
|
|
7113
|
+
noktadan dışarı bildirilir — köprü hangi mesajı göndereceğine kendi karar verir. */
|
|
7114
|
+
if (typeof this.onAgentDm === 'function') {
|
|
7115
|
+
try { this.onAgentDm(dm); } catch {}
|
|
7116
|
+
}
|
|
7112
7117
|
};
|
|
7113
7118
|
|
|
7114
7119
|
/* ---------- EKİP GRUBU: aynı görevde koşan paralel ajanlar otomatik
|
|
@@ -7214,6 +7219,66 @@ Engine.prototype._agentTeamPost = function (gid, fromSid, fromTitle, text) {
|
|
|
7214
7219
|
} catch {}
|
|
7215
7220
|
};
|
|
7216
7221
|
|
|
7222
|
+
/* Grubu hazırla (yoksa kur): Telegram köprüsü ve finans alarmları grubu önceden
|
|
7223
|
+
hiç kurulmamış olsa da panele düşebilsin. */
|
|
7224
|
+
Engine.prototype._agentGroupEnsure = function (gid, title, opts) {
|
|
7225
|
+
gid = String(gid || '');
|
|
7226
|
+
if (!gid) return null;
|
|
7227
|
+
let g = this._agentGroups.get(gid);
|
|
7228
|
+
if (!g) {
|
|
7229
|
+
g = {
|
|
7230
|
+
id: gid,
|
|
7231
|
+
title: String(title || gid),
|
|
7232
|
+
members: [],
|
|
7233
|
+
titles: {},
|
|
7234
|
+
createdAt: nowIso(),
|
|
7235
|
+
closed: false,
|
|
7236
|
+
team: !(opts && opts.system),
|
|
7237
|
+
};
|
|
7238
|
+
this._agentGroups.set(gid, g);
|
|
7239
|
+
}
|
|
7240
|
+
if (title && !g.title) g.title = String(title);
|
|
7241
|
+
g.closed = false;
|
|
7242
|
+
delete g.closedAt;
|
|
7243
|
+
return g;
|
|
7244
|
+
};
|
|
7245
|
+
|
|
7246
|
+
/* DIŞ KAYNAKLI (kullanıcı/Telegram) GRUP POSTU: panelde gerçek mesaj olarak
|
|
7247
|
+
görünür + istenirse koşan üyelere DM gibi teslim edilir. Sistem postlarından
|
|
7248
|
+
farkı: model tarafına da düşebilir (wake kararı çağıranın). */
|
|
7249
|
+
Engine.prototype.agentDmGroupPost = function (input) {
|
|
7250
|
+
try {
|
|
7251
|
+
const i = input || {};
|
|
7252
|
+
const gid = String(i.gid || '');
|
|
7253
|
+
const g = this._agentGroupEnsure(gid, i.title || '', { system: !!i.system });
|
|
7254
|
+
if (!g) return { ok: false, error: 'gid gerekli' };
|
|
7255
|
+
const dm = {
|
|
7256
|
+
at: nowIso(),
|
|
7257
|
+
from: String(i.fromSid || 'user'),
|
|
7258
|
+
fromTitle: String(i.fromTitle || 'Kullanıcı'),
|
|
7259
|
+
to: '',
|
|
7260
|
+
toTitle: 'ekip',
|
|
7261
|
+
group: gid,
|
|
7262
|
+
groupTitle: g.title,
|
|
7263
|
+
topic: String(i.topic || 'grup').slice(0, 60),
|
|
7264
|
+
text: String(i.text || '').slice(0, 4000),
|
|
7265
|
+
...(i.system ? { system: true } : {}),
|
|
7266
|
+
...(i.viaTelegram ? { viaTelegram: true } : {}),
|
|
7267
|
+
};
|
|
7268
|
+
this._pushAgentDm(dm);
|
|
7269
|
+
/* canlı event TEK SEFER: ilk koşan üye (yoksa gönderen) — panelde çoğalmaz */
|
|
7270
|
+
const firstRunning = (g.members || []).find((m) => {
|
|
7271
|
+
const j = this._bgJobs && this._bgJobs.get(m);
|
|
7272
|
+
return j && j.status === 'running';
|
|
7273
|
+
});
|
|
7274
|
+
emitSafe(this, firstRunning || String(i.fromSid || ''), { type: 'agent-dm', ...dm });
|
|
7275
|
+
this._persistAgentDms();
|
|
7276
|
+
return { ok: true, group: g.title };
|
|
7277
|
+
} catch (e) {
|
|
7278
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
7279
|
+
}
|
|
7280
|
+
};
|
|
7281
|
+
|
|
7217
7282
|
/* Sistem promptuna ekip zorunluluğu */
|
|
7218
7283
|
Engine.prototype._agentTeamPromptLine = function (gid) {
|
|
7219
7284
|
const g = this._agentGroups && this._agentGroups.get(String(gid));
|
package/src/agent/telegram.js
CHANGED
|
@@ -106,6 +106,8 @@ class TelegramBridge {
|
|
|
106
106
|
username: String(msg.from.username || ''),
|
|
107
107
|
senderName: String(msg.from.first_name || msg.from.username || ''),
|
|
108
108
|
isGroup: !!(msg.chat && (msg.chat.type === 'group' || msg.chat.type === 'supergroup')),
|
|
109
|
+
chatTitle: String((msg.chat && (msg.chat.title || msg.chat.username)) || ''),
|
|
110
|
+
chatType: String((msg.chat && msg.chat.type) || 'private'),
|
|
109
111
|
};
|
|
110
112
|
try {
|
|
111
113
|
if (this.onIncoming) this.onIncoming(String(chatId), payload);
|
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
|
@@ -332,6 +332,18 @@ let tgCronSid = null; // Telegram CRON TABAN OTURUMU: otomatik işler; hiçbir k
|
|
|
332
332
|
let tgHistory = new Map(); // chatId -> [sid,...]
|
|
333
333
|
const TG_HISTORY_CAP = 20;
|
|
334
334
|
let tgLastActiveChatId = ''; // en son mesaj gelen TG sohbeti — cron yansıtma hedefi
|
|
335
|
+
/* Botun gördüğü Telegram GRUPLARI: chatId -> {title, at}. Ajan DM ↔ Telegram
|
|
336
|
+
köprüsü kurulumunda grup seçimi bu listeden yapılır (BotFather'da gizlilik
|
|
337
|
+
kapatılmalı ya da bot gruba admin olmalı ki mesajlar düşsün). */
|
|
338
|
+
let tgGroups = {};
|
|
339
|
+
try {
|
|
340
|
+
const raw = settings.tgGroups;
|
|
341
|
+
if (raw && typeof raw === 'object') {
|
|
342
|
+
for (const [id, v] of Object.entries(raw)) {
|
|
343
|
+
if (v && typeof v === 'object') tgGroups[String(id)] = { title: String(v.title || ''), at: Number(v.at) || 0 };
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
} catch {}
|
|
335
347
|
let dc = null;
|
|
336
348
|
let dcChats = new Map(); // discord channelId -> KENDİ oturumu: her kanal AYRI (gizlilik)
|
|
337
349
|
let dcCronSid = null; // Discord CRON TABAN OTURUMU: otomatik işler; hiçbir kişiye bağlı DEĞİL
|
|
@@ -672,6 +684,29 @@ function tgLog(line) {
|
|
|
672
684
|
try { log.info('telegram', line); } catch {}
|
|
673
685
|
}
|
|
674
686
|
|
|
687
|
+
/* Gelen grup mesajından grubu hatırla — ajan DM köprüsü grup seçicisi bu
|
|
688
|
+
listeden beslenir. Başlık değişmedikçe diske yazılmaz (yalnız tazeleme). */
|
|
689
|
+
function tgRememberGroup(chatId, title) {
|
|
690
|
+
const id = String(chatId || '');
|
|
691
|
+
if (!id) return;
|
|
692
|
+
const cur = tgGroups[id];
|
|
693
|
+
const t = String(title || '');
|
|
694
|
+
const at = Date.now();
|
|
695
|
+
if (cur && (cur.title === t || !t)) {
|
|
696
|
+
cur.at = at;
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
tgGroups[id] = { title: t || (cur && cur.title) || id, at };
|
|
700
|
+
settings.tgGroups = tgGroups;
|
|
701
|
+
try { saveSettings(); } catch {}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function tgGroupsList() {
|
|
705
|
+
return Object.entries(tgGroups)
|
|
706
|
+
.map(([id, v]) => ({ id, title: String((v && v.title) || id), at: Number((v && v.at) || 0) }))
|
|
707
|
+
.sort((a, b) => b.at - a.at);
|
|
708
|
+
}
|
|
709
|
+
|
|
675
710
|
function tgFind(senderId, username) {
|
|
676
711
|
const list = settings.tgAllow || [];
|
|
677
712
|
if (!list.length) return null; // boş liste = kimseye cevap yok
|
|
@@ -2895,9 +2930,36 @@ async function tgFlush(chatId) {
|
|
|
2895
2930
|
async function handleTgIncoming(chatId, payload) {
|
|
2896
2931
|
try {
|
|
2897
2932
|
if (!engine) return;
|
|
2898
|
-
/*
|
|
2933
|
+
/* GRUP: normal grup desteği kapalı; ancak AJAN DM ↔ TELEGRAM köprüsünde
|
|
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. */
|
|
2899
2937
|
if (payload.isGroup) {
|
|
2900
|
-
|
|
2938
|
+
tgRememberGroup(chatId, payload.chatTitle);
|
|
2939
|
+
const dmTg = finDmTgCfg();
|
|
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;
|
|
2955
|
+
finTelegramToAgentDm(payload);
|
|
2956
|
+
return;
|
|
2957
|
+
}
|
|
2958
|
+
if (String(dmTg.chat) === String(chatId)) {
|
|
2959
|
+
finTelegramToAgentDm(payload);
|
|
2960
|
+
return;
|
|
2961
|
+
}
|
|
2962
|
+
tgLog(`skip: grup mesajı chat=${chatId} (bağlı grup ${dmTg.chat})`);
|
|
2901
2963
|
return;
|
|
2902
2964
|
}
|
|
2903
2965
|
const hit = tgFind(payload.senderId, payload.username);
|
|
@@ -3566,6 +3628,10 @@ function reloadBackend() {
|
|
|
3566
3628
|
düşünce sıradaki planlı turu BEKLEMEZ — ajan boştaysa tur hemen başlar
|
|
3567
3629
|
(ajan-ajan DM'lerinde engine kancayı çağırmaz; ping-pong korunur). */
|
|
3568
3630
|
engine.onDmQueued = (job) => { try { finWakeAgent(job && job.id); } catch {} };
|
|
3631
|
+
/* AJAN DM → TELEGRAM: AJAN DM grubuna düşen her kayıt (alarm/sistem postu
|
|
3632
|
+
dahil) tek kancadan geçer; köprü ayarı açıksa seçili Telegram grubuna
|
|
3633
|
+
yansıtılır (Telegram'dan gelen mesajlar viaTelegram ile geri gönderilmez). */
|
|
3634
|
+
engine.onAgentDm = (dm) => { try { finAgentDmToTelegram(dm); } catch {} };
|
|
3569
3635
|
/* PANEL RUN köprüsü: ajanın panel_run aracı → ÇALIŞTIR panelindeki yönetilen
|
|
3570
3636
|
süreç koşucusu (sandbox:run IPC ile aynı makine). */
|
|
3571
3637
|
engine.sbRunHook = sbRunStartManaged;
|
|
@@ -7683,14 +7749,15 @@ function watcherFire(w, value) {
|
|
|
7683
7749
|
? 'izlenen değer değişti'
|
|
7684
7750
|
: `kural sağlandı (son değer ${value}, koşul ${w.op} ${w.value ?? ''})`;
|
|
7685
7751
|
toastNotify(`İzleyici: ${w.name}`, target, 'watchers');
|
|
7686
|
-
/* FINANCE AJANI: izleyici alarmı AJAN DM
|
|
7687
|
-
tur beklemeden uyandırır (
|
|
7688
|
-
eski davranış: mesaj doğrudan
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7752
|
+
/* FINANCE AJANI: izleyici alarmı AJAN DM GRUBUNA bildirim olarak düşer ve
|
|
7753
|
+
koşan TÜM finance ajanlarını tur beklemeden uyandırır (finWakeAgents:
|
|
7754
|
+
grup postu + dm+wake). Diğer oturumlarda eski davranış: mesaj doğrudan
|
|
7755
|
+
sohbete iner. */
|
|
7756
|
+
if (financeState.agents.has(String(sid)) || (financeState.mode && financeState.agents.size)) {
|
|
7757
|
+
finWakeAgents(
|
|
7692
7758
|
`[FİNANS OLAYI — İZLEYİCİ ALARMI: ${w.name}]\n- ${target}\n` +
|
|
7693
|
-
`Şimdi yap: izleyiciyi kurma nedenini hatırla; durumu değerlendir, gerekiyorsa işlem/uyarı üret. Kısa rapor ver
|
|
7759
|
+
`Şimdi yap: izleyiciyi kurma nedenini hatırla; durumu değerlendir, gerekiyorsa işlem/uyarı üret. Kısa rapor ver.`,
|
|
7760
|
+
{ kind: 'watcher', watcher: w.id }
|
|
7694
7761
|
);
|
|
7695
7762
|
return;
|
|
7696
7763
|
}
|
|
@@ -9349,6 +9416,12 @@ function finCfg() {
|
|
|
9349
9416
|
iptal) düşer; fiyat alarmı, koruma (BE/trailing/kısmi TP), günlük limit ve
|
|
9350
9417
|
rapor bildirimleri panelde kalır. false = tüm olaylar kanallara gider. */
|
|
9351
9418
|
if (typeof f.notifyTradeOnly !== 'boolean') f.notifyTradeOnly = true;
|
|
9419
|
+
/* AJAN DM ↔ TELEGRAM KÖPRÜSÜ: açıkken `team:finance` AJAN DM grubundaki
|
|
9420
|
+
mesajlar seçilen Telegram grubuna düşer; o gruba yazılan mesaj AJAN DM
|
|
9421
|
+
grubunda görünür ve koşan finance ajanları tur beklemeden uyanır. */
|
|
9422
|
+
if (typeof f.dmTelegram !== 'boolean') f.dmTelegram = false;
|
|
9423
|
+
if (typeof f.dmTelegramChat !== 'string') f.dmTelegramChat = '';
|
|
9424
|
+
if (typeof f.dmTelegramTitle !== 'string') f.dmTelegramTitle = '';
|
|
9352
9425
|
if (!Number.isFinite(Number(f.maxDailyLossPct))) f.maxDailyLossPct = 3;
|
|
9353
9426
|
/* GÜNLÜK LİMİT AKSİYONU: warn = yalnız uyarı; stop = tüm finance ajanlarını
|
|
9354
9427
|
durdur; flatten = durdur + tüm pozisyonları kapat */
|
|
@@ -10001,6 +10074,103 @@ async function finStatsRefresh(force) {
|
|
|
10001
10074
|
}
|
|
10002
10075
|
}
|
|
10003
10076
|
|
|
10077
|
+
/* ---- AJAN DM ↔ TELEGRAM KÖPRÜSÜ ----
|
|
10078
|
+
Beast Finance görünüm ayarlarından açılır: `team:finance` AJAN DM grubundaki
|
|
10079
|
+
her mesaj (alarm/sistem postları dahil) seçilen Telegram grubuna yansır;
|
|
10080
|
+
o gruba yazılan mesaj AJAN DM grubunda görünür ve koşan finance ajanları
|
|
10081
|
+
tur beklemeden uyandırılır. Ayarlar: finCfg().dmTelegram / dmTelegramChat. */
|
|
10082
|
+
const FIN_TEAM_GID = 'team:finance';
|
|
10083
|
+
const FIN_TEAM_TITLE = 'Beast Finance EKİP';
|
|
10084
|
+
|
|
10085
|
+
/* Köprü ayarı: açık mı + bağlı grup (chatId). Grup SEÇİLMEZ — köprü açıkken
|
|
10086
|
+
gruba yazılan ilk mesaj o grubu otomatik bağlar (finDmTgBind). */
|
|
10087
|
+
function finDmTgCfg() {
|
|
10088
|
+
let f = {};
|
|
10089
|
+
try { f = finCfg(); } catch {}
|
|
10090
|
+
return {
|
|
10091
|
+
on: !!(f && f.dmTelegram),
|
|
10092
|
+
chat: String((f && f.dmTelegramChat) || ''),
|
|
10093
|
+
title: String((f && f.dmTelegramTitle) || ''),
|
|
10094
|
+
};
|
|
10095
|
+
}
|
|
10096
|
+
|
|
10097
|
+
/* Otomatik bağlama: gelen grup mesajı (izinli gönderici) AJAN DM aynası olur. */
|
|
10098
|
+
function finDmTgBind(chatId, title) {
|
|
10099
|
+
const id = String(chatId || '');
|
|
10100
|
+
if (!id) return false;
|
|
10101
|
+
const f = finCfg();
|
|
10102
|
+
const t = String(title || '').trim();
|
|
10103
|
+
f.dmTelegramChat = id;
|
|
10104
|
+
f.dmTelegramTitle = t || f.dmTelegramTitle || id;
|
|
10105
|
+
try { saveSettings(); } catch {}
|
|
10106
|
+
tgLog(`ajan dm telegram grubu OTOMATİK bağlandı: ${id}${t ? ' (' + t + ')' : ''}`);
|
|
10107
|
+
/* gruba onay: kullanıcı bağlandığını net görsün */
|
|
10108
|
+
try {
|
|
10109
|
+
if (tg && tg.connected) {
|
|
10110
|
+
Promise.resolve(
|
|
10111
|
+
tg.send(
|
|
10112
|
+
id,
|
|
10113
|
+
'✅ Beast Finance AJAN DM bu gruba bağlandı.\n' +
|
|
10114
|
+
'Bundan sonra ajanların/alarmların mesajları burada; buraya yazdıkların AJAN DM grubunda görünür ve ajanları uyandırır.'
|
|
10115
|
+
)
|
|
10116
|
+
).catch(() => {});
|
|
10117
|
+
}
|
|
10118
|
+
} catch {}
|
|
10119
|
+
return true;
|
|
10120
|
+
}
|
|
10121
|
+
|
|
10122
|
+
/* Giden: AJAN DM grubu → Telegram (Telegram'dan gelenler geri gönderilmez;
|
|
10123
|
+
grup henüz otomatik bağlanmadıysa sessizce beklenir). */
|
|
10124
|
+
function finAgentDmToTelegram(dm) {
|
|
10125
|
+
try {
|
|
10126
|
+
if (!dm || dm.viaTelegram) return;
|
|
10127
|
+
if (String(dm.group || '') !== FIN_TEAM_GID) return;
|
|
10128
|
+
const c = finDmTgCfg();
|
|
10129
|
+
if (!c.on || !c.chat || !tg || !tg.connected) return;
|
|
10130
|
+
const who = String(dm.fromTitle || (dm.system ? 'SİSTEM' : 'Ajan'));
|
|
10131
|
+
const body =
|
|
10132
|
+
`👥 *${String(dm.groupTitle || FIN_TEAM_TITLE)}* · ${who}\n` +
|
|
10133
|
+
String(dm.text || '').slice(0, 3800);
|
|
10134
|
+
Promise.resolve(tg.send(c.chat, body)).catch((e) =>
|
|
10135
|
+
tgLog('ajan dm → telegram hata: ' + String((e && e.message) || e))
|
|
10136
|
+
);
|
|
10137
|
+
} catch {}
|
|
10138
|
+
}
|
|
10139
|
+
|
|
10140
|
+
/* Gelen: Telegram grubu → AJAN DM grubu (+ tüm koşan finance ajanlarını uyandır). */
|
|
10141
|
+
function finTelegramToAgentDm(payload) {
|
|
10142
|
+
try {
|
|
10143
|
+
const body = String((payload && payload.text) || '').trim();
|
|
10144
|
+
if (!body || !engine) return { ok: false };
|
|
10145
|
+
const who = String((payload && (payload.senderName || payload.username)) || 'Telegram');
|
|
10146
|
+
const label = `Sahip · Telegram (${who})`;
|
|
10147
|
+
try {
|
|
10148
|
+
if (typeof engine.agentDmGroupPost === 'function') {
|
|
10149
|
+
engine.agentDmGroupPost({
|
|
10150
|
+
gid: FIN_TEAM_GID,
|
|
10151
|
+
title: FIN_TEAM_TITLE,
|
|
10152
|
+
fromSid: 'tg:' + String((payload && payload.senderId) || ''),
|
|
10153
|
+
fromTitle: label,
|
|
10154
|
+
topic: 'telegram',
|
|
10155
|
+
text: body,
|
|
10156
|
+
viaTelegram: true,
|
|
10157
|
+
});
|
|
10158
|
+
}
|
|
10159
|
+
} catch {}
|
|
10160
|
+
const wake =
|
|
10161
|
+
`[AJAN DM — TELEGRAM GRUBU · ${label}]\n${body}\n` +
|
|
10162
|
+
'Şimdi yap: sahibin Telegram grubundan gelen bu mesajı değerlendir; gerekiyorsa ekipçe aksiyon al. Kısa rapor ver.';
|
|
10163
|
+
let sent = 0;
|
|
10164
|
+
for (const [sid] of financeState.agents) {
|
|
10165
|
+
if (finWakeAgentDm(sid, wake)) sent += 1;
|
|
10166
|
+
}
|
|
10167
|
+
financeLog(`[telegram] ajan dm grubuna mesaj düştü (${sent} ajan uyandı)`);
|
|
10168
|
+
return { ok: true, sent };
|
|
10169
|
+
} catch {
|
|
10170
|
+
return { ok: false };
|
|
10171
|
+
}
|
|
10172
|
+
}
|
|
10173
|
+
|
|
10004
10174
|
/* ---- FİNANS OLAY → AJAN DM UYANDIRMA ----
|
|
10005
10175
|
Stop/TP/bekleyen emir aktivasyonu/fiyat alarmı gibi olaylar koşan finance
|
|
10006
10176
|
ajanlarına AJAN DM'i olarak düşer ve BOŞTAKİ ajanı TUR SANİYESİNİ
|
|
@@ -10031,20 +10201,32 @@ function finWakeAgentDm(sid, text) {
|
|
|
10031
10201
|
}
|
|
10032
10202
|
}
|
|
10033
10203
|
|
|
10034
|
-
/* Tüm koşan finance ajanlarını olayla uyandır. Dönüş: uyandırılan ajan sayısı.
|
|
10204
|
+
/* Tüm koşan finance ajanlarını olayla uyandır. Dönüş: uyandırılan ajan sayısı.
|
|
10205
|
+
Olay HER durumda ekip DM grubuna sistem postu olarak düşer (koşan ajan yoksa
|
|
10206
|
+
bile panelde görünür — grup yoksa kurulur). */
|
|
10035
10207
|
function finWakeAgents(text, meta) {
|
|
10036
10208
|
const body = String(text || '').trim();
|
|
10037
10209
|
if (!body || !engine) return 0;
|
|
10038
|
-
if (!financeState.agents.size) {
|
|
10039
|
-
financeLog('[olay] koşan finance ajanı yok — uyarı yalnız günlük/bildirimde' + (meta && meta.kind ? ' (' + meta.kind + ')' : ''));
|
|
10040
|
-
return 0;
|
|
10041
|
-
}
|
|
10042
10210
|
/* Panel görünürlüğü: olay ekip DM grubuna sistem postu olarak düşer */
|
|
10043
10211
|
try {
|
|
10044
|
-
if (typeof engine.
|
|
10212
|
+
if (typeof engine.agentDmGroupPost === 'function') {
|
|
10213
|
+
engine.agentDmGroupPost({
|
|
10214
|
+
gid: FIN_TEAM_GID,
|
|
10215
|
+
title: FIN_TEAM_TITLE,
|
|
10216
|
+
fromSid: 'finance',
|
|
10217
|
+
fromTitle: 'Beast Finance',
|
|
10218
|
+
topic: 'ortak görev',
|
|
10219
|
+
text: body,
|
|
10220
|
+
system: true,
|
|
10221
|
+
});
|
|
10222
|
+
} else if (typeof engine._agentTeamPost === 'function') {
|
|
10045
10223
|
engine._agentTeamPost('team:finance', 'finance', 'Beast Finance', body);
|
|
10046
10224
|
}
|
|
10047
10225
|
} catch {}
|
|
10226
|
+
if (!financeState.agents.size) {
|
|
10227
|
+
financeLog('[olay] koşan finance ajanı yok — uyarı yalnız günlük/bildirimde' + (meta && meta.kind ? ' (' + meta.kind + ')' : ''));
|
|
10228
|
+
return 0;
|
|
10229
|
+
}
|
|
10048
10230
|
let sent = 0;
|
|
10049
10231
|
for (const [sid] of financeState.agents) {
|
|
10050
10232
|
if (finWakeAgentDm(sid, body)) sent += 1;
|
|
@@ -11473,6 +11655,21 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
11473
11655
|
if (p.notifyTrades !== undefined) f.notifyTrades = !!p.notifyTrades;
|
|
11474
11656
|
if (p.notifyWatchdog !== undefined) f.notifyWatchdog = !!p.notifyWatchdog;
|
|
11475
11657
|
if (p.notifyTradeOnly !== undefined) f.notifyTradeOnly = !!p.notifyTradeOnly;
|
|
11658
|
+
/* AJAN DM ↔ TELEGRAM köprüsü ayarları (grup SEÇİLMEZ — otomatik bağlanır) */
|
|
11659
|
+
if (p.dmTelegram !== undefined) f.dmTelegram = !!p.dmTelegram;
|
|
11660
|
+
if (p.dmTelegramChat !== undefined) {
|
|
11661
|
+
f.dmTelegramChat = String(p.dmTelegramChat || '').trim().slice(0, 64);
|
|
11662
|
+
if (!f.dmTelegramChat) f.dmTelegramTitle = ''; /* koparıldı → başlık da gitsin */
|
|
11663
|
+
}
|
|
11664
|
+
if (p.dmTelegramTitle !== undefined) f.dmTelegramTitle = String(p.dmTelegramTitle || '').trim().slice(0, 80);
|
|
11665
|
+
/* köprü yeni açıldı ve botun gördüğü TEK grup varsa hemen bağla */
|
|
11666
|
+
if (p.dmTelegram === true && !f.dmTelegramChat) {
|
|
11667
|
+
const known = tgGroupsList();
|
|
11668
|
+
if (known.length === 1) {
|
|
11669
|
+
f.dmTelegramChat = String(known[0].id);
|
|
11670
|
+
f.dmTelegramTitle = String(known[0].title || known[0].id);
|
|
11671
|
+
}
|
|
11672
|
+
}
|
|
11476
11673
|
if (p.maxDailyLossPct !== undefined) f.maxDailyLossPct = Math.max(0, Math.min(50, Number(p.maxDailyLossPct) || 0));
|
|
11477
11674
|
if (p.dailyLossAction !== undefined) {
|
|
11478
11675
|
const v = String(p.dailyLossAction || 'warn').toLowerCase();
|
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>
|
|
@@ -891,6 +891,17 @@
|
|
|
891
891
|
<span class="fsr-main"><b>AKIŞ</b><i>Ajan ve MT5 olay günlüğü</i></span>
|
|
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
|
+
<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: AJAN DM'de konuşulanlar (alarmlar dahil) gruba düşer, grupta yazdıkların AJAN DM'de görünür ve finance ajanları uyanır. Grup seçmene gerek yok.">
|
|
896
|
+
<input id="finDmTgOn" type="checkbox" />
|
|
897
|
+
<span class="fsr-main"><b>AJAN DM ↔ TELEGRAM</b><i>Ajan DM grubu Telegram'da canlı — otomatik bağlanır</i></span>
|
|
898
|
+
</label>
|
|
899
|
+
<div id="finDmTgWrap" class="fin-set-sub" hidden>
|
|
900
|
+
<span class="fin-set-lbl">TELEGRAM GRUBU</span>
|
|
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
|
+
<div id="finDmTgHint" class="fin-set-note" style="margin:2px 0 0"></div>
|
|
904
|
+
</div>
|
|
894
905
|
</div>
|
|
895
906
|
</div>
|
|
896
907
|
</div>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -203,6 +203,11 @@ const els = {
|
|
|
203
203
|
finShowNotify: $('#finShowNotify'),
|
|
204
204
|
finShowPerf: $('#finShowPerf'),
|
|
205
205
|
finShowFlow: $('#finShowFlow'),
|
|
206
|
+
finDmTgOn: $('#finDmTgOn'),
|
|
207
|
+
finDmTgWrap: $('#finDmTgWrap'),
|
|
208
|
+
finDmTgState: $('#finDmTgState'),
|
|
209
|
+
finDmTgUnbind: $('#finDmTgUnbind'),
|
|
210
|
+
finDmTgHint: $('#finDmTgHint'),
|
|
206
211
|
finRiskCard: $('#finRiskCard'),
|
|
207
212
|
finNotifyCard: $('#finNotifyCard'),
|
|
208
213
|
finPerfCard: $('#finPerfCard'),
|
|
@@ -10119,6 +10124,10 @@ async function finSnapshot() {
|
|
|
10119
10124
|
if (!finView.risk && r.cfg && r.cfg.watchdog !== false) finSaveCfg({ watchdog: false });
|
|
10120
10125
|
finRenderStats(r.stats, r.equity, r.account && r.account.currency ? r.account.currency : '');
|
|
10121
10126
|
finRenderAlerts(r.alerts);
|
|
10127
|
+
/* görünüm ayarları açıkken AJAN DM ↔ TELEGRAM durumu cfg ile senkron kalsın */
|
|
10128
|
+
if (els.finSettingsOverlay && !els.finSettingsOverlay.hidden) {
|
|
10129
|
+
finDmTgPaint();
|
|
10130
|
+
}
|
|
10122
10131
|
}
|
|
10123
10132
|
|
|
10124
10133
|
function finLogLine(line) {
|
|
@@ -10280,8 +10289,34 @@ function finViewApply() {
|
|
|
10280
10289
|
if (!finView.risk) finSaveCfg({ watchdog: false });
|
|
10281
10290
|
}
|
|
10282
10291
|
|
|
10292
|
+
/* ---------- AJAN DM ↔ TELEGRAM (görünüm ayarları) ----------
|
|
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. */
|
|
10296
|
+
function finDmTgPaint() {
|
|
10297
|
+
if (!els.finDmTgOn) return;
|
|
10298
|
+
const cfg = finCfgCache || {};
|
|
10299
|
+
const on = cfg.dmTelegram === true;
|
|
10300
|
+
els.finDmTgOn.checked = on;
|
|
10301
|
+
const row = els.finDmTgOn.closest('.fin-set-row');
|
|
10302
|
+
if (row) row.classList.toggle('on', on);
|
|
10303
|
+
if (els.finDmTgWrap) els.finDmTgWrap.hidden = !on;
|
|
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.';
|
|
10309
|
+
}
|
|
10310
|
+
if (els.finDmTgUnbind) els.finDmTgUnbind.hidden = !chat;
|
|
10311
|
+
if (els.finDmTgHint) {
|
|
10312
|
+
els.finDmTgHint.textContent =
|
|
10313
|
+
'Grupta konuşulan her şey AJAN DM\u2019de görünür; ajanların/alarmların yazdıkları da gruba düşer. Grup değiştirmek için bağlantıyı kopar, yeni grupta /start yaz.';
|
|
10314
|
+
}
|
|
10315
|
+
}
|
|
10316
|
+
|
|
10283
10317
|
function finSettingsOpen() {
|
|
10284
10318
|
if (els.finSettingsOverlay) els.finSettingsOverlay.hidden = false;
|
|
10319
|
+
finDmTgPaint();
|
|
10285
10320
|
}
|
|
10286
10321
|
function finSettingsCloseModal() {
|
|
10287
10322
|
if (els.finSettingsOverlay) els.finSettingsOverlay.hidden = true;
|
|
@@ -10315,6 +10350,27 @@ if (els.finShowRisk) els.finShowRisk.addEventListener('change', () => finViewTog
|
|
|
10315
10350
|
if (els.finShowNotify) els.finShowNotify.addEventListener('change', () => finViewToggle('notify', els.finShowNotify.checked));
|
|
10316
10351
|
if (els.finShowPerf) els.finShowPerf.addEventListener('change', () => finViewToggle('perf', els.finShowPerf.checked));
|
|
10317
10352
|
if (els.finShowFlow) els.finShowFlow.addEventListener('change', () => finViewToggle('flow', els.finShowFlow.checked));
|
|
10353
|
+
/* AJAN DM ↔ TELEGRAM köprüsü kontrolleri (grup otomatik bağlanır) */
|
|
10354
|
+
if (els.finDmTgOn) {
|
|
10355
|
+
els.finDmTgOn.addEventListener('change', () => {
|
|
10356
|
+
const on = els.finDmTgOn.checked;
|
|
10357
|
+
if (finCfgCache) finCfgCache.dmTelegram = on;
|
|
10358
|
+
finSaveCfg({ dmTelegram: on });
|
|
10359
|
+
finDmTgPaint();
|
|
10360
|
+
toast(on ? 'AJAN DM ↔ Telegram açıldı — gruba /start yazınca otomatik bağlanır' : 'AJAN DM ↔ Telegram kapatıldı');
|
|
10361
|
+
});
|
|
10362
|
+
}
|
|
10363
|
+
if (els.finDmTgUnbind) {
|
|
10364
|
+
els.finDmTgUnbind.addEventListener('click', () => {
|
|
10365
|
+
if (finCfgCache) {
|
|
10366
|
+
finCfgCache.dmTelegramChat = '';
|
|
10367
|
+
finCfgCache.dmTelegramTitle = '';
|
|
10368
|
+
}
|
|
10369
|
+
finSaveCfg({ dmTelegramChat: '', dmTelegramTitle: '' });
|
|
10370
|
+
finDmTgPaint();
|
|
10371
|
+
toast('Telegram grubu koparıldı — yeni grupta /start yazınca bağlanır');
|
|
10372
|
+
});
|
|
10373
|
+
}
|
|
10318
10374
|
finViewApply();
|
|
10319
10375
|
|
|
10320
10376
|
/* ---------- BÖLÜM SIRALAMA (sürükle-bırak) ----------
|
package/src/renderer/style.css
CHANGED
|
@@ -5869,3 +5869,23 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
|
|
|
5869
5869
|
.fin-set-row .fsr-main b { font-size: 12px; color: var(--text); letter-spacing: 0.4px; }
|
|
5870
5870
|
.fin-set-row .fsr-main i { font-style: normal; font-size: 10.5px; color: var(--muted); }
|
|
5871
5871
|
.fin-set-note { margin-top: 8px; font-size: 10.5px; color: var(--muted); line-height: 1.4; }
|
|
5872
|
+
.fin-set-sep { height: 1px; margin: 10px 0; background: var(--border); }
|
|
5873
|
+
.fin-set-sub {
|
|
5874
|
+
display: flex;
|
|
5875
|
+
flex-direction: column;
|
|
5876
|
+
gap: 6px;
|
|
5877
|
+
margin: 2px 0 6px;
|
|
5878
|
+
padding: 9px 10px;
|
|
5879
|
+
background: var(--panel);
|
|
5880
|
+
border: 1px solid var(--border);
|
|
5881
|
+
border-radius: 9px;
|
|
5882
|
+
}
|
|
5883
|
+
.fin-set-sub[hidden] { display: none; }
|
|
5884
|
+
.fin-set-lbl { font-size: 10.5px; color: var(--muted); letter-spacing: 0.5px; }
|
|
5885
|
+
.fin-dm-tg-state { font-size: 11.5px; color: var(--text); line-height: 1.4; word-break: break-word; }
|
|
5886
|
+
.fin-set-btn {
|
|
5887
|
+
align-self: flex-start;
|
|
5888
|
+
font-size: 10.5px;
|
|
5889
|
+
padding: 3px 8px;
|
|
5890
|
+
cursor: pointer;
|
|
5891
|
+
}
|