beast-agent 2.58.0 → 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 +53 -5
- package/src/main.js +44 -5
- package/src/renderer/renderer.js +1 -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');
|
|
@@ -156,6 +166,44 @@ class TelegramBridge {
|
|
|
156
166
|
}
|
|
157
167
|
return true;
|
|
158
168
|
}
|
|
169
|
+
|
|
170
|
+
/* GÖRSEL gönder — data URL (base64) multipart ile yüklenir. PNG/JPG/WEBP
|
|
171
|
+
sendPhoto, GIF sendDocument; caption 1024, dosya 9.5MB üstü atlanır.
|
|
172
|
+
threadId verilirse forum konusuna gider. */
|
|
173
|
+
async sendPhoto(chatId, dataUrl, caption, threadId) {
|
|
174
|
+
try {
|
|
175
|
+
const m = /^data:image\/(png|jpe?g|webp|gif);base64,([A-Za-z0-9+/=\s]+)$/i.exec(String(dataUrl || '').trim());
|
|
176
|
+
if (!m) return false;
|
|
177
|
+
const isJpg = /^jpe?g$/i.test(m[1]);
|
|
178
|
+
const ext = isJpg ? 'jpg' : m[1].toLowerCase();
|
|
179
|
+
const mime = isJpg ? 'image/jpeg' : 'image/' + ext;
|
|
180
|
+
const buf = Buffer.from(m[2].replace(/\s+/g, ''), 'base64');
|
|
181
|
+
if (!buf.length || buf.length > 9.5 * 1024 * 1024) return false;
|
|
182
|
+
const gif = ext === 'gif';
|
|
183
|
+
const method = gif ? 'sendDocument' : 'sendPhoto';
|
|
184
|
+
const field = gif ? 'document' : 'photo';
|
|
185
|
+
const boundary = '----Beast' + crypto.randomBytes(10).toString('hex');
|
|
186
|
+
const parts = [];
|
|
187
|
+
const addField = (name, value) => {
|
|
188
|
+
parts.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${String(value)}\r\n`));
|
|
189
|
+
};
|
|
190
|
+
addField('chat_id', String(chatId));
|
|
191
|
+
if (caption) addField('caption', String(caption).slice(0, 1024));
|
|
192
|
+
if (threadId) addField('message_thread_id', String(Number(threadId)));
|
|
193
|
+
parts.push(
|
|
194
|
+
Buffer.from(
|
|
195
|
+
`--${boundary}\r\nContent-Disposition: form-data; name="${field}"; filename="beast.${ext}"\r\nContent-Type: ${mime}\r\n\r\n`
|
|
196
|
+
)
|
|
197
|
+
);
|
|
198
|
+
parts.push(buf);
|
|
199
|
+
parts.push(Buffer.from('\r\n'));
|
|
200
|
+
parts.push(Buffer.from(`--${boundary}--\r\n`));
|
|
201
|
+
await this._post(method, Buffer.concat(parts), { 'Content-Type': 'multipart/form-data; boundary=' + boundary }, 60000);
|
|
202
|
+
return true;
|
|
203
|
+
} catch {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
159
207
|
}
|
|
160
208
|
|
|
161
209
|
module.exports = { TelegramBridge };
|
package/src/main.js
CHANGED
|
@@ -2944,6 +2944,7 @@ async function handleTgIncoming(chatId, payload) {
|
|
|
2944
2944
|
const allowed = tgFind(payload.senderId, payload.username);
|
|
2945
2945
|
if (!allowed) {
|
|
2946
2946
|
tgLog(`skip: grup mesajı chat=${chatId} — gönderici izin listesinde değil`);
|
|
2947
|
+
if (String(dmTg.chat) === String(chatId)) finDmTgNotAllowed(chatId);
|
|
2947
2948
|
return;
|
|
2948
2949
|
}
|
|
2949
2950
|
if (!dmTg.chat) {
|
|
@@ -10122,7 +10123,9 @@ function finDmTgBind(chatId, title) {
|
|
|
10122
10123
|
id,
|
|
10123
10124
|
'✅ Beast Finance AJAN DM bu gruba bağlandı.\n' +
|
|
10124
10125
|
'Bundan sonra ajanların/alarmların mesajları buraya düşer; buraya yazdıkların AJAN DM grubunda görünür ve ajanları uyandırır.\n' +
|
|
10125
|
-
'
|
|
10126
|
+
'ÖNEMLİ: Normal mesajlarını görebilmem için beni bu grupta YÖNETİCİ yap (Konuları Yönet izniyle) ya da BotFather → /setprivacy → Disable yap. ' +
|
|
10127
|
+
'Aksi halde yalnız /komutları ve bana yanıtları görebilirim.\n' +
|
|
10128
|
+
'Grup Konular (Topics) destekliyorsa her AJAN DM grubu ayrı başlık olarak canlı akar.'
|
|
10126
10129
|
)
|
|
10127
10130
|
).catch(() => {});
|
|
10128
10131
|
}
|
|
@@ -10130,6 +10133,25 @@ function finDmTgBind(chatId, title) {
|
|
|
10130
10133
|
return true;
|
|
10131
10134
|
}
|
|
10132
10135
|
|
|
10136
|
+
/* İzin listesi dışı gönderici bağlı gruba yazdıysa sessiz kalma: 10 dk'da bir
|
|
10137
|
+
gruba kısa uyarı — kullanıcı mesajının neden ajanlara gitmediğini anlasın. */
|
|
10138
|
+
const finDmTgWarnAt = new Map();
|
|
10139
|
+
function finDmTgNotAllowed(chatId) {
|
|
10140
|
+
const id = String(chatId || '');
|
|
10141
|
+
const last = finDmTgWarnAt.get(id) || 0;
|
|
10142
|
+
if (!id || Date.now() - last < 10 * 60 * 1000) return;
|
|
10143
|
+
finDmTgWarnAt.set(id, Date.now());
|
|
10144
|
+
try {
|
|
10145
|
+
Promise.resolve(
|
|
10146
|
+
tg.send(
|
|
10147
|
+
id,
|
|
10148
|
+
'⚠️ Bu Telegram hesabı izin listesinde yok — mesajın ajanlara iletilemedi.\n' +
|
|
10149
|
+
'Beast → Entegrasyonlar → Telegram bölümünde bu hesabı (ID ya da @kullanıcı adı) izin listesine ekle.'
|
|
10150
|
+
)
|
|
10151
|
+
).catch(() => {});
|
|
10152
|
+
} catch {}
|
|
10153
|
+
}
|
|
10154
|
+
|
|
10133
10155
|
/* ---- TELEGRAM KONU (TOPIC) EŞLEMESİ ----
|
|
10134
10156
|
Forum destekli Telegram grubunda her AJAN DM grubu AYRI BAŞLIK olarak açılır:
|
|
10135
10157
|
gid → message_thread_id eşlemesi settings.finance.dmTelegramTopics'ta kalıcı.
|
|
@@ -10238,18 +10260,35 @@ function finAgentDmToTelegram(dm) {
|
|
|
10238
10260
|
const title = String(dm.groupTitle || (gid === FIN_TEAM_GID ? FIN_TEAM_TITLE : gid));
|
|
10239
10261
|
const who = String(dm.fromTitle || (dm.system ? 'SİSTEM' : 'Ajan'));
|
|
10240
10262
|
const text = String(dm.text || '').slice(0, 3800);
|
|
10263
|
+
const img = String(dm.image || '');
|
|
10264
|
+
const hasImg = /^data:image\//i.test(img);
|
|
10241
10265
|
finDmTgQueue(async () => {
|
|
10242
10266
|
const tid = await finDmTgThreadEnsure(c.chat, gid, title);
|
|
10243
|
-
const
|
|
10244
|
-
|
|
10267
|
+
const target = tid > 0 ? Number(tid) : undefined;
|
|
10268
|
+
const full = `${who}:\n${text}`;
|
|
10269
|
+
/* GÖRSEL: Telegram'a FOTO olarak gider (caption = gönderen + metin);
|
|
10270
|
+
metnin caption'a sığmayan kalanı ayrı mesajla gider. */
|
|
10271
|
+
if (hasImg) {
|
|
10272
|
+
const cap = full.slice(0, 1000);
|
|
10273
|
+
let ok = false;
|
|
10274
|
+
try { ok = await tg.sendPhoto(c.chat, img, cap, target); } catch { ok = false; }
|
|
10275
|
+
if (ok) {
|
|
10276
|
+
const rest = full.slice(1000);
|
|
10277
|
+
if (rest.trim()) await tg.send(c.chat, '…' + rest, target).catch(() => {});
|
|
10278
|
+
return;
|
|
10279
|
+
}
|
|
10280
|
+
/* foto gönderilemedi (bozuk/büyük) → metne düş, görsel notuyla */
|
|
10281
|
+
}
|
|
10282
|
+
const head = hasImg ? `${who}: (görsel gönderilemedi)\n${text}` : full;
|
|
10283
|
+
if (target) {
|
|
10245
10284
|
try {
|
|
10246
|
-
await tg.send(c.chat,
|
|
10285
|
+
await tg.send(c.chat, head, target);
|
|
10247
10286
|
return;
|
|
10248
10287
|
} catch {
|
|
10249
10288
|
/* konu silinmiş/erişim yok → genel akışa düş (mesaj kaybolmasın) */
|
|
10250
10289
|
}
|
|
10251
10290
|
}
|
|
10252
|
-
await tg.send(c.chat,
|
|
10291
|
+
await tg.send(c.chat, `👥 [${title}] ${head}`);
|
|
10253
10292
|
});
|
|
10254
10293
|
} catch {}
|
|
10255
10294
|
}
|
package/src/renderer/renderer.js
CHANGED
|
@@ -10311,7 +10311,7 @@ function finDmTgPaint() {
|
|
|
10311
10311
|
if (els.finDmTgHint) {
|
|
10312
10312
|
els.finDmTgHint.textContent =
|
|
10313
10313
|
'Her AJAN DM grubu (Beast Finance EKİP, sembol ekipleri vb.) Telegram\u2019da AYRI BAŞLIK (topic) olarak canlı akar. ' +
|
|
10314
|
-
'Telegram grubunda Konular (Topics) açık ve bot
|
|
10314
|
+
'Telegram grubunda Konular (Topics) açık ve bot YÖNETİCİ (Konuları Yönet) olmalı — yönetici değilse normal mesajlarını bot göremez (BotFather → /setprivacy → Disable da olur); Konular kapalıysa mesajlar tek akışa grup etiketiyle düşer. ' +
|
|
10315
10315
|
'Bir başlığa yazdığın mesaj ilgili AJAN DM grubunda görünür ve o grubun ajanları uyanır.';
|
|
10316
10316
|
}
|
|
10317
10317
|
}
|