beast-agent 2.57.1 → 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/telegram.js +6 -2
- package/src/main.js +171 -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
|
@@ -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/main.js
CHANGED
|
@@ -2956,7 +2956,9 @@ async function handleTgIncoming(chatId, payload) {
|
|
|
2956
2956
|
return;
|
|
2957
2957
|
}
|
|
2958
2958
|
if (String(dmTg.chat) === String(chatId)) {
|
|
2959
|
-
|
|
2959
|
+
/* FORUM: konu (thread) → ilgili AJAN DM grubu; konusuz/genel → finance */
|
|
2960
|
+
const gid = finDmTgGidForThread(payload.messageThreadId);
|
|
2961
|
+
finTelegramToAgentDm(payload, gid);
|
|
2960
2962
|
return;
|
|
2961
2963
|
}
|
|
2962
2964
|
tgLog(`skip: grup mesajı chat=${chatId} (bağlı grup ${dmTg.chat})`);
|
|
@@ -9416,12 +9418,14 @@ function finCfg() {
|
|
|
9416
9418
|
iptal) düşer; fiyat alarmı, koruma (BE/trailing/kısmi TP), günlük limit ve
|
|
9417
9419
|
rapor bildirimleri panelde kalır. false = tüm olaylar kanallara gider. */
|
|
9418
9420
|
if (typeof f.notifyTradeOnly !== 'boolean') f.notifyTradeOnly = true;
|
|
9419
|
-
/* AJAN DM ↔ TELEGRAM KÖPRÜSÜ: açıkken
|
|
9420
|
-
|
|
9421
|
-
|
|
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. */
|
|
9422
9425
|
if (typeof f.dmTelegram !== 'boolean') f.dmTelegram = false;
|
|
9423
9426
|
if (typeof f.dmTelegramChat !== 'string') f.dmTelegramChat = '';
|
|
9424
9427
|
if (typeof f.dmTelegramTitle !== 'string') f.dmTelegramTitle = '';
|
|
9428
|
+
if (!f.dmTelegramTopics || typeof f.dmTelegramTopics !== 'object') f.dmTelegramTopics = {};
|
|
9425
9429
|
if (!Number.isFinite(Number(f.maxDailyLossPct))) f.maxDailyLossPct = 3;
|
|
9426
9430
|
/* GÜNLÜK LİMİT AKSİYONU: warn = yalnız uyarı; stop = tüm finance ajanlarını
|
|
9427
9431
|
durdur; flatten = durdur + tüm pozisyonları kapat */
|
|
@@ -10100,6 +10104,12 @@ function finDmTgBind(chatId, title) {
|
|
|
10100
10104
|
if (!id) return false;
|
|
10101
10105
|
const f = finCfg();
|
|
10102
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
|
+
}
|
|
10103
10113
|
f.dmTelegramChat = id;
|
|
10104
10114
|
f.dmTelegramTitle = t || f.dmTelegramTitle || id;
|
|
10105
10115
|
try { saveSettings(); } catch {}
|
|
@@ -10111,7 +10121,8 @@ function finDmTgBind(chatId, title) {
|
|
|
10111
10121
|
tg.send(
|
|
10112
10122
|
id,
|
|
10113
10123
|
'✅ Beast Finance AJAN DM bu gruba bağlandı.\n' +
|
|
10114
|
-
'Bundan sonra ajanların/alarmların mesajları
|
|
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.'
|
|
10115
10126
|
)
|
|
10116
10127
|
).catch(() => {});
|
|
10117
10128
|
}
|
|
@@ -10119,36 +10130,152 @@ function finDmTgBind(chatId, title) {
|
|
|
10119
10130
|
return true;
|
|
10120
10131
|
}
|
|
10121
10132
|
|
|
10122
|
-
/*
|
|
10123
|
-
|
|
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). */
|
|
10124
10231
|
function finAgentDmToTelegram(dm) {
|
|
10125
10232
|
try {
|
|
10126
10233
|
if (!dm || dm.viaTelegram) return;
|
|
10127
|
-
|
|
10234
|
+
const gid = String(dm.group || '');
|
|
10235
|
+
if (!gid) return; /* yalnız grup sohbetleri köprülenir (1:1 DM panelde kalır) */
|
|
10128
10236
|
const c = finDmTgCfg();
|
|
10129
10237
|
if (!c.on || !c.chat || !tg || !tg.connected) return;
|
|
10238
|
+
const title = String(dm.groupTitle || (gid === FIN_TEAM_GID ? FIN_TEAM_TITLE : gid));
|
|
10130
10239
|
const who = String(dm.fromTitle || (dm.system ? 'SİSTEM' : 'Ajan'));
|
|
10131
|
-
const
|
|
10132
|
-
|
|
10133
|
-
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
|
|
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
|
+
});
|
|
10137
10254
|
} catch {}
|
|
10138
10255
|
}
|
|
10139
10256
|
|
|
10140
|
-
/* Gelen: Telegram grubu → AJAN DM grubu (+
|
|
10141
|
-
|
|
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) {
|
|
10142
10260
|
try {
|
|
10143
10261
|
const body = String((payload && payload.text) || '').trim();
|
|
10144
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
|
+
);
|
|
10145
10272
|
const who = String((payload && (payload.senderName || payload.username)) || 'Telegram');
|
|
10146
10273
|
const label = `Sahip · Telegram (${who})`;
|
|
10147
10274
|
try {
|
|
10148
10275
|
if (typeof engine.agentDmGroupPost === 'function') {
|
|
10149
10276
|
engine.agentDmGroupPost({
|
|
10150
|
-
gid:
|
|
10151
|
-
title
|
|
10277
|
+
gid: key,
|
|
10278
|
+
title,
|
|
10152
10279
|
fromSid: 'tg:' + String((payload && payload.senderId) || ''),
|
|
10153
10280
|
fromTitle: label,
|
|
10154
10281
|
topic: 'telegram',
|
|
@@ -10157,14 +10284,31 @@ function finTelegramToAgentDm(payload) {
|
|
|
10157
10284
|
});
|
|
10158
10285
|
}
|
|
10159
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) : [];
|
|
10160
10290
|
const wake =
|
|
10161
|
-
`[AJAN DM — TELEGRAM
|
|
10291
|
+
`[AJAN DM — TELEGRAM${title ? ' · ' + title : ''} · ${label}]\n${body}\n` +
|
|
10162
10292
|
'Şimdi yap: sahibin Telegram grubundan gelen bu mesajı değerlendir; gerekiyorsa ekipçe aksiyon al. Kısa rapor ver.';
|
|
10163
10293
|
let sent = 0;
|
|
10164
10294
|
for (const [sid] of financeState.agents) {
|
|
10295
|
+
if (key !== FIN_TEAM_GID && members.length && !members.includes(String(sid))) continue;
|
|
10165
10296
|
if (finWakeAgentDm(sid, wake)) sent += 1;
|
|
10166
10297
|
}
|
|
10167
|
-
|
|
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ı)`);
|
|
10168
10312
|
return { ok: true, sent };
|
|
10169
10313
|
} catch {
|
|
10170
10314
|
return { ok: false };
|
|
@@ -11658,8 +11802,15 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
11658
11802
|
/* AJAN DM ↔ TELEGRAM köprüsü ayarları (grup SEÇİLMEZ — otomatik bağlanır) */
|
|
11659
11803
|
if (p.dmTelegram !== undefined) f.dmTelegram = !!p.dmTelegram;
|
|
11660
11804
|
if (p.dmTelegramChat !== undefined) {
|
|
11805
|
+
const prevChat = String(f.dmTelegramChat || '');
|
|
11661
11806
|
f.dmTelegramChat = String(p.dmTelegramChat || '').trim().slice(0, 64);
|
|
11662
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
|
+
}
|
|
11663
11814
|
}
|
|
11664
11815
|
if (p.dmTelegramTitle !== undefined) f.dmTelegramTitle = String(p.dmTelegramTitle || '').trim().slice(0, 80);
|
|
11665
11816
|
/* 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ö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.';
|
|
10314
10316
|
}
|
|
10315
10317
|
}
|
|
10316
10318
|
|