@yungho/noclaw-channel 0.4.2 → 0.4.4
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/dist/channels/TelegramChannel.d.ts +12 -35
- package/dist/channels/TelegramChannel.d.ts.map +1 -1
- package/dist/channels/TelegramChannel.js +101 -163
- package/dist/channels/TelegramChannel.js.map +1 -1
- package/dist/channels/__tests__/TelegramChannel.test.d.ts +2 -0
- package/dist/channels/__tests__/TelegramChannel.test.d.ts.map +1 -0
- package/dist/channels/__tests__/TelegramChannel.test.js +191 -0
- package/dist/channels/__tests__/TelegramChannel.test.js.map +1 -0
- package/package.json +4 -1
|
@@ -1,53 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Telegram Channel Implementation
|
|
2
|
+
* Telegram Channel Implementation (production-grade)
|
|
3
3
|
*
|
|
4
|
-
* Uses
|
|
5
|
-
*
|
|
4
|
+
* Uses grammY Bot framework for polling, sequentialization, rate limiting,
|
|
5
|
+
* and DM access control. Replaces the previous manual HTTP fetch() approach.
|
|
6
|
+
*
|
|
7
|
+
* Architecture: Implements IChannel → registered via ChannelRegistry.
|
|
8
|
+
* Zero changes to WebSocketServer, ConfigManager, or TenantConfigManager.
|
|
6
9
|
*/
|
|
7
|
-
import { IChannel, IpcMessage, TelegramConfig, ChannelStatus, ChannelCapabilities, MediaObject } from
|
|
8
|
-
import pino from
|
|
10
|
+
import type { IChannel, IpcMessage, TelegramConfig, ChannelStatus, ChannelCapabilities, MediaObject } from "../types.js";
|
|
11
|
+
import type pino from "pino";
|
|
9
12
|
export declare class TelegramChannel implements IChannel {
|
|
10
|
-
private readonly baseUrl;
|
|
11
13
|
private config;
|
|
12
14
|
private logger;
|
|
13
15
|
private status;
|
|
14
|
-
private
|
|
15
|
-
private messageCallback
|
|
16
|
+
private bot;
|
|
17
|
+
private messageCallback;
|
|
16
18
|
private statusCallbacks;
|
|
17
|
-
private lastUpdateId;
|
|
18
|
-
private readonly POLLING_INTERVAL;
|
|
19
19
|
constructor(config: TelegramConfig, logger: pino.Logger);
|
|
20
20
|
connect(): Promise<void>;
|
|
21
21
|
disconnect(): Promise<void>;
|
|
22
|
-
send(text: string, contextToken: string,
|
|
22
|
+
send(text: string, contextToken: string, _media?: MediaObject[], _userId?: string): Promise<void>;
|
|
23
23
|
onMessage(callback: (msg: IpcMessage) => void): void;
|
|
24
|
+
onStatusChange(callback: (status: ChannelStatus) => void): void;
|
|
24
25
|
getStatus(): ChannelStatus;
|
|
25
26
|
getCapabilities(): ChannelCapabilities;
|
|
26
27
|
getPlatformId(): string;
|
|
27
|
-
onStatusChange(callback: (status: ChannelStatus) => void): void;
|
|
28
|
-
/**
|
|
29
|
-
* Start polling for incoming messages
|
|
30
|
-
*/
|
|
31
|
-
private startMessagePolling;
|
|
32
|
-
/**
|
|
33
|
-
* Stop all polling
|
|
34
|
-
*/
|
|
35
|
-
private stopPolling;
|
|
36
|
-
/**
|
|
37
|
-
* Send text message
|
|
38
|
-
*/
|
|
39
|
-
private sendMessage;
|
|
40
|
-
/**
|
|
41
|
-
* Send photo
|
|
42
|
-
*/
|
|
43
|
-
private sendPhoto;
|
|
44
|
-
/**
|
|
45
|
-
* Make HTTP request to Telegram API
|
|
46
|
-
*/
|
|
47
|
-
private makeRequest;
|
|
48
|
-
/**
|
|
49
|
-
* Update connection status
|
|
50
|
-
*/
|
|
51
28
|
private setStatus;
|
|
52
29
|
}
|
|
53
30
|
//# sourceMappingURL=TelegramChannel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TelegramChannel.d.ts","sourceRoot":"","sources":["../../src/channels/TelegramChannel.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"TelegramChannel.d.ts","sourceRoot":"","sources":["../../src/channels/TelegramChannel.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EACV,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,WAAW,EACZ,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAI7B,qBAAa,eAAgB,YAAW,QAAQ;IAC9C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAiC;IAC/C,OAAO,CAAC,GAAG,CAAoB;IAC/B,OAAO,CAAC,eAAe,CAA4C;IACnE,OAAO,CAAC,eAAe,CAA8C;gBAEzD,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM;IAOjD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAiGxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,WAAW,EAAE,EACtB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAahB,SAAS,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI;IAIpD,cAAc,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI;IAI/D,SAAS,IAAI,aAAa;IAI1B,eAAe,IAAI,mBAAmB;IAUtC,aAAa,IAAI,MAAM;IAMvB,OAAO,CAAC,SAAS;CAUlB"}
|
|
@@ -1,79 +1,124 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Telegram Channel Implementation
|
|
2
|
+
* Telegram Channel Implementation (production-grade)
|
|
3
3
|
*
|
|
4
|
-
* Uses
|
|
5
|
-
*
|
|
4
|
+
* Uses grammY Bot framework for polling, sequentialization, rate limiting,
|
|
5
|
+
* and DM access control. Replaces the previous manual HTTP fetch() approach.
|
|
6
|
+
*
|
|
7
|
+
* Architecture: Implements IChannel → registered via ChannelRegistry.
|
|
8
|
+
* Zero changes to WebSocketServer, ConfigManager, or TenantConfigManager.
|
|
6
9
|
*/
|
|
10
|
+
import { Bot } from "grammy";
|
|
11
|
+
import { sequentialize } from "@grammyjs/runner";
|
|
12
|
+
import { apiThrottler } from "@grammyjs/transformer-throttler";
|
|
13
|
+
// ── TelegramChannel ───────────────────────────────────────────────────────────
|
|
7
14
|
export class TelegramChannel {
|
|
8
|
-
baseUrl;
|
|
9
15
|
config;
|
|
10
16
|
logger;
|
|
11
|
-
status =
|
|
12
|
-
|
|
13
|
-
messageCallback;
|
|
17
|
+
status = "disconnected";
|
|
18
|
+
bot = null;
|
|
19
|
+
messageCallback = null;
|
|
14
20
|
statusCallbacks = new Set();
|
|
15
|
-
lastUpdateId = 0;
|
|
16
|
-
POLLING_INTERVAL = 2000; // 2 seconds
|
|
17
21
|
constructor(config, logger) {
|
|
18
22
|
this.config = config;
|
|
19
|
-
this.
|
|
20
|
-
this.logger = logger.child({ channel: 'telegram' });
|
|
23
|
+
this.logger = logger.child({ channel: "telegram" });
|
|
21
24
|
}
|
|
25
|
+
// ── IChannel Implementation ─────────────────────────────────────────────
|
|
22
26
|
async connect() {
|
|
23
27
|
if (!this.config.enabled) {
|
|
24
|
-
this.logger.warn(
|
|
28
|
+
this.logger.warn("Telegram channel is disabled in config");
|
|
25
29
|
return;
|
|
26
30
|
}
|
|
27
|
-
this.
|
|
31
|
+
if (!this.config.botToken || this.config.botToken.trim().length === 0) {
|
|
32
|
+
throw new Error("Telegram botToken is required. Get one from @BotFather on Telegram.");
|
|
33
|
+
}
|
|
34
|
+
this.setStatus("connecting");
|
|
28
35
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
this.bot = new Bot(this.config.botToken);
|
|
37
|
+
// Middleware stack (order matters):
|
|
38
|
+
// 1. Sequentialize per-chat to prevent message reordering
|
|
39
|
+
this.bot.use(sequentialize((ctx) => {
|
|
40
|
+
return ctx.chat?.id?.toString();
|
|
41
|
+
}));
|
|
42
|
+
// 2. Rate limiting via API transformer (respects Telegram's ~30 msg/s limit)
|
|
43
|
+
this.bot.api.config.use(apiThrottler());
|
|
44
|
+
// 3. DM access control (Phase D)
|
|
45
|
+
this.bot.use(async (ctx, next) => {
|
|
46
|
+
const allowed = this.config.allowedUsers ?? [];
|
|
47
|
+
if (allowed.length === 0 || allowed.includes("*")) {
|
|
48
|
+
return next();
|
|
49
|
+
}
|
|
50
|
+
const userId = ctx.from?.id?.toString();
|
|
51
|
+
if (!userId || !allowed.includes(userId)) {
|
|
52
|
+
this.logger.warn({ userId: userId ?? "unknown" }, "Telegram: rejected non-whitelisted user");
|
|
53
|
+
await ctx.reply("Sorry, you are not authorized to use this bot.");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
return next();
|
|
32
57
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
58
|
+
// 4. Incoming text message handler
|
|
59
|
+
this.bot.on("message:text", (ctx) => {
|
|
60
|
+
const msg = ctx.message;
|
|
61
|
+
if (!msg?.text)
|
|
62
|
+
return;
|
|
63
|
+
const chatId = msg.chat.id;
|
|
64
|
+
const fromId = msg.from?.id;
|
|
65
|
+
const ipcMessage = {
|
|
66
|
+
type: "MSG_IN",
|
|
67
|
+
platform: "telegram",
|
|
68
|
+
content: msg.text,
|
|
69
|
+
contextToken: `tg_${chatId}`,
|
|
70
|
+
userId: fromId ? String(fromId) : undefined,
|
|
71
|
+
metadata: {
|
|
72
|
+
timestamp: Date.now(),
|
|
73
|
+
platform: "telegram",
|
|
74
|
+
userId: fromId ? String(fromId) : undefined,
|
|
75
|
+
messageId: String(msg.message_id ?? ctx.msg?.message_id ?? 0),
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
this.logger.info({ from: fromId, chatId, textPreview: msg.text.slice(0, 60) }, "Received Telegram message");
|
|
79
|
+
this.messageCallback?.(ipcMessage);
|
|
80
|
+
});
|
|
81
|
+
this.bot.catch((err) => {
|
|
82
|
+
this.logger.error({ err }, "Telegram bot error");
|
|
83
|
+
});
|
|
84
|
+
// Start grammY long-polling (returns immediately, runs in background)
|
|
85
|
+
this.bot.start({
|
|
86
|
+
onStart: () => {
|
|
87
|
+
this.logger.info("Telegram long-polling started");
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
this.setStatus("connected");
|
|
91
|
+
this.logger.info("Connected to Telegram Bot API");
|
|
39
92
|
}
|
|
40
93
|
catch (error) {
|
|
41
|
-
this.
|
|
42
|
-
this.setStatus(
|
|
94
|
+
this.bot = null;
|
|
95
|
+
this.setStatus("error");
|
|
96
|
+
this.logger.error({ error }, "Failed to connect to Telegram");
|
|
43
97
|
throw error;
|
|
44
98
|
}
|
|
45
99
|
}
|
|
46
100
|
async disconnect() {
|
|
47
|
-
this.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
async send(text, contextToken, media, _userId) {
|
|
52
|
-
try {
|
|
53
|
-
const chatId = contextToken.replace('tg_', '');
|
|
54
|
-
if (media && media.length > 0) {
|
|
55
|
-
// Send media first
|
|
56
|
-
for (const item of media) {
|
|
57
|
-
if (item.type === 'image') {
|
|
58
|
-
await this.sendPhoto(chatId, item.data, text);
|
|
59
|
-
break; // Only send first image for now
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
// Send text message
|
|
65
|
-
await this.sendMessage(chatId, text);
|
|
66
|
-
}
|
|
67
|
-
this.logger.debug({ chatId, textLength: text.length }, 'Message sent to Telegram');
|
|
101
|
+
if (this.bot) {
|
|
102
|
+
await this.bot.stop();
|
|
103
|
+
this.bot = null;
|
|
68
104
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
105
|
+
this.setStatus("disconnected");
|
|
106
|
+
this.logger.info("Disconnected from Telegram");
|
|
107
|
+
}
|
|
108
|
+
async send(text, contextToken, _media, _userId) {
|
|
109
|
+
if (!this.bot) {
|
|
110
|
+
throw new Error("Not connected. Call connect() first.");
|
|
72
111
|
}
|
|
112
|
+
const chatId = contextToken.replace("tg_", "");
|
|
113
|
+
await this.bot.api.sendMessage(chatId, text);
|
|
114
|
+
this.logger.debug({ chatId, textLength: text.length }, "Message sent to Telegram");
|
|
73
115
|
}
|
|
74
116
|
onMessage(callback) {
|
|
75
117
|
this.messageCallback = callback;
|
|
76
118
|
}
|
|
119
|
+
onStatusChange(callback) {
|
|
120
|
+
this.statusCallbacks.add(callback);
|
|
121
|
+
}
|
|
77
122
|
getStatus() {
|
|
78
123
|
return this.status;
|
|
79
124
|
}
|
|
@@ -87,126 +132,19 @@ export class TelegramChannel {
|
|
|
87
132
|
};
|
|
88
133
|
}
|
|
89
134
|
getPlatformId() {
|
|
90
|
-
return
|
|
135
|
+
return "telegram";
|
|
91
136
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
* Start polling for incoming messages
|
|
97
|
-
*/
|
|
98
|
-
startMessagePolling() {
|
|
99
|
-
this.stopPolling(); // Clear any existing polling
|
|
100
|
-
this.pollingInterval = setInterval(async () => {
|
|
137
|
+
// ── Private ──────────────────────────────────────────────────────────────
|
|
138
|
+
setStatus(status) {
|
|
139
|
+
this.status = status;
|
|
140
|
+
for (const cb of this.statusCallbacks) {
|
|
101
141
|
try {
|
|
102
|
-
|
|
103
|
-
method: 'GET',
|
|
104
|
-
});
|
|
105
|
-
if (response.result && response.result.length > 0) {
|
|
106
|
-
this.logger.debug({ messageCount: response.result.length }, 'Received messages from Telegram');
|
|
107
|
-
for (const update of response.result) {
|
|
108
|
-
// Update last update ID (+1 to ACK this update)
|
|
109
|
-
this.lastUpdateId = update.update_id + 1;
|
|
110
|
-
// Emit message to callback
|
|
111
|
-
if (update.message?.text) {
|
|
112
|
-
this.messageCallback?.({
|
|
113
|
-
type: 'MSG_IN',
|
|
114
|
-
platform: 'telegram',
|
|
115
|
-
content: update.message.text,
|
|
116
|
-
contextToken: `tg_${update.message.chat.id}`,
|
|
117
|
-
userId: String(update.message.from.id),
|
|
118
|
-
metadata: {
|
|
119
|
-
timestamp: Date.now(),
|
|
120
|
-
platform: 'telegram',
|
|
121
|
-
userId: String(update.message.from.id),
|
|
122
|
-
messageId: String(update.message.message_id),
|
|
123
|
-
},
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
142
|
+
cb(status);
|
|
128
143
|
}
|
|
129
|
-
catch
|
|
130
|
-
|
|
131
|
-
// Don't stop polling on error, just log it
|
|
144
|
+
catch {
|
|
145
|
+
// ignore callback errors
|
|
132
146
|
}
|
|
133
|
-
}, this.POLLING_INTERVAL);
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Stop all polling
|
|
137
|
-
*/
|
|
138
|
-
stopPolling() {
|
|
139
|
-
if (this.pollingInterval) {
|
|
140
|
-
clearInterval(this.pollingInterval);
|
|
141
|
-
this.pollingInterval = undefined;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Send text message
|
|
146
|
-
*/
|
|
147
|
-
async sendMessage(chatId, text) {
|
|
148
|
-
const url = `${this.baseUrl}/sendMessage`;
|
|
149
|
-
const body = JSON.stringify({
|
|
150
|
-
chat_id: parseInt(chatId),
|
|
151
|
-
text,
|
|
152
|
-
});
|
|
153
|
-
const response = await fetch(url, {
|
|
154
|
-
method: 'POST',
|
|
155
|
-
headers: {
|
|
156
|
-
'Content-Type': 'application/json',
|
|
157
|
-
},
|
|
158
|
-
body,
|
|
159
|
-
});
|
|
160
|
-
if (!response.ok) {
|
|
161
|
-
throw new Error(`Failed to send message: ${response.statusText}`);
|
|
162
|
-
}
|
|
163
|
-
const data = await response.json();
|
|
164
|
-
if (!data.ok) {
|
|
165
|
-
throw new Error('Failed to send message');
|
|
166
147
|
}
|
|
167
148
|
}
|
|
168
|
-
/**
|
|
169
|
-
* Send photo
|
|
170
|
-
*/
|
|
171
|
-
async sendPhoto(chatId, photoData, caption) {
|
|
172
|
-
const url = `${this.baseUrl}/sendPhoto`;
|
|
173
|
-
const formData = new FormData();
|
|
174
|
-
formData.append('chat_id', chatId);
|
|
175
|
-
const buffer = Buffer.from(photoData, 'base64');
|
|
176
|
-
const blob = new Blob([buffer], { type: 'image/jpeg' });
|
|
177
|
-
formData.append('photo', blob, 'photo.jpg');
|
|
178
|
-
if (caption) {
|
|
179
|
-
formData.append('caption', caption);
|
|
180
|
-
}
|
|
181
|
-
const response = await fetch(url, {
|
|
182
|
-
method: 'POST',
|
|
183
|
-
body: formData,
|
|
184
|
-
});
|
|
185
|
-
if (!response.ok) {
|
|
186
|
-
throw new Error(`Failed to send photo: ${response.statusText}`);
|
|
187
|
-
}
|
|
188
|
-
const data = await response.json();
|
|
189
|
-
if (!data.ok) {
|
|
190
|
-
throw new Error('Failed to send photo');
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Make HTTP request to Telegram API
|
|
195
|
-
*/
|
|
196
|
-
async makeRequest(endpoint, options) {
|
|
197
|
-
const url = `${this.baseUrl}${endpoint}`;
|
|
198
|
-
const response = await fetch(url, options);
|
|
199
|
-
if (!response.ok) {
|
|
200
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
201
|
-
}
|
|
202
|
-
return await response.json();
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Update connection status
|
|
206
|
-
*/
|
|
207
|
-
setStatus(status) {
|
|
208
|
-
this.status = status;
|
|
209
|
-
this.statusCallbacks.forEach((callback) => callback(status));
|
|
210
|
-
}
|
|
211
149
|
}
|
|
212
150
|
//# sourceMappingURL=TelegramChannel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TelegramChannel.js","sourceRoot":"","sources":["../../src/channels/TelegramChannel.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"TelegramChannel.js","sourceRoot":"","sources":["../../src/channels/TelegramChannel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TelegramChannel.test.d.ts","sourceRoot":"","sources":["../../../src/channels/__tests__/TelegramChannel.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TelegramChannel tests — NHP-2 TDD (RED phase)
|
|
3
|
+
*
|
|
4
|
+
* Verifies the IChannel interface implementation using grammY.
|
|
5
|
+
* All grammY internals are mocked — we test OUR code, not the library.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
8
|
+
// ── Mocks ────────────────────────────────────────────────────────────────────
|
|
9
|
+
const mockBotStart = vi.fn();
|
|
10
|
+
const mockBotStop = vi.fn();
|
|
11
|
+
const mockSendMessage = vi.fn();
|
|
12
|
+
const mockBotOn = vi.fn();
|
|
13
|
+
const mockBotUse = vi.fn();
|
|
14
|
+
const mockBotCatch = vi.fn();
|
|
15
|
+
const mockBotInstance = {
|
|
16
|
+
start: mockBotStart,
|
|
17
|
+
stop: mockBotStop,
|
|
18
|
+
api: { sendMessage: mockSendMessage, config: { use: vi.fn() } },
|
|
19
|
+
on: mockBotOn,
|
|
20
|
+
use: mockBotUse,
|
|
21
|
+
catch: mockBotCatch,
|
|
22
|
+
};
|
|
23
|
+
vi.mock("grammy", () => ({
|
|
24
|
+
Bot: vi.fn(function () {
|
|
25
|
+
return mockBotInstance;
|
|
26
|
+
}),
|
|
27
|
+
}));
|
|
28
|
+
vi.mock("@grammyjs/runner", () => ({
|
|
29
|
+
sequentialize: vi.fn(() => "mock-sequentialize-middleware"),
|
|
30
|
+
}));
|
|
31
|
+
vi.mock("@grammyjs/transformer-throttler", () => ({
|
|
32
|
+
apiThrottler: vi.fn(() => "mock-throttler-middleware"),
|
|
33
|
+
}));
|
|
34
|
+
import { Bot } from "grammy";
|
|
35
|
+
import { sequentialize } from "@grammyjs/runner";
|
|
36
|
+
import { apiThrottler } from "@grammyjs/transformer-throttler";
|
|
37
|
+
import { TelegramChannel } from "../TelegramChannel.js";
|
|
38
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
39
|
+
function createChannel(overrides = {}) {
|
|
40
|
+
const config = {
|
|
41
|
+
enabled: true,
|
|
42
|
+
botToken: "test-bot-token",
|
|
43
|
+
allowedUsers: ["*"],
|
|
44
|
+
permissions: { chat: "all", executeTools: true, permissionRelay: true, adminUsers: [] },
|
|
45
|
+
...overrides,
|
|
46
|
+
};
|
|
47
|
+
const logger = {
|
|
48
|
+
info: vi.fn(),
|
|
49
|
+
warn: vi.fn(),
|
|
50
|
+
error: vi.fn(),
|
|
51
|
+
debug: vi.fn(),
|
|
52
|
+
child: vi.fn(() => logger),
|
|
53
|
+
};
|
|
54
|
+
return new TelegramChannel(config, logger);
|
|
55
|
+
}
|
|
56
|
+
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
57
|
+
describe("TelegramChannel (grammY)", () => {
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
vi.clearAllMocks();
|
|
60
|
+
});
|
|
61
|
+
describe("connect()", () => {
|
|
62
|
+
it("creates a grammY Bot and starts long-polling", async () => {
|
|
63
|
+
const channel = createChannel();
|
|
64
|
+
await channel.connect();
|
|
65
|
+
expect(Bot).toHaveBeenCalledWith("test-bot-token");
|
|
66
|
+
// sequentialize should be registered
|
|
67
|
+
expect(vi.mocked(sequentialize)).toHaveBeenCalled();
|
|
68
|
+
// throttler should be registered
|
|
69
|
+
expect(vi.mocked(apiThrottler)).toHaveBeenCalled();
|
|
70
|
+
// message handler should be registered
|
|
71
|
+
expect(mockBotOn).toHaveBeenCalledWith("message:text", expect.any(Function));
|
|
72
|
+
// bot should start polling
|
|
73
|
+
expect(mockBotStart).toHaveBeenCalled();
|
|
74
|
+
expect(channel.getStatus()).toBe("connected");
|
|
75
|
+
});
|
|
76
|
+
it("throws if botToken is empty", async () => {
|
|
77
|
+
const channel = createChannel({ botToken: "" });
|
|
78
|
+
await expect(channel.connect()).rejects.toThrow("botToken is required");
|
|
79
|
+
});
|
|
80
|
+
it("does nothing if disabled", async () => {
|
|
81
|
+
const channel = createChannel({ enabled: false });
|
|
82
|
+
await channel.connect();
|
|
83
|
+
expect(Bot).not.toHaveBeenCalled();
|
|
84
|
+
expect(channel.getStatus()).toBe("disconnected");
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe("disconnect()", () => {
|
|
88
|
+
it("stops the bot and sets status to disconnected", async () => {
|
|
89
|
+
const channel = createChannel();
|
|
90
|
+
await channel.connect();
|
|
91
|
+
await channel.disconnect();
|
|
92
|
+
expect(mockBotStop).toHaveBeenCalled();
|
|
93
|
+
expect(channel.getStatus()).toBe("disconnected");
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe("send()", () => {
|
|
97
|
+
it("calls bot.api.sendMessage with chatId from contextToken", async () => {
|
|
98
|
+
const channel = createChannel();
|
|
99
|
+
await channel.connect();
|
|
100
|
+
await channel.send("Hello Telegram!", "tg_123456");
|
|
101
|
+
expect(mockSendMessage).toHaveBeenCalledWith("123456", "Hello Telegram!");
|
|
102
|
+
});
|
|
103
|
+
it("throws if not connected", async () => {
|
|
104
|
+
const channel = createChannel();
|
|
105
|
+
await expect(channel.send("hi", "tg_123")).rejects.toThrow("Not connected");
|
|
106
|
+
});
|
|
107
|
+
it("passes parse_mode and link_preview_options", async () => {
|
|
108
|
+
const channel = createChannel();
|
|
109
|
+
await channel.connect();
|
|
110
|
+
await channel.send("<b>bold</b>", "tg_789");
|
|
111
|
+
expect(mockSendMessage).toHaveBeenCalledWith("789", "<b>bold</b>");
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
describe("incoming message → MSG_IN", () => {
|
|
115
|
+
it("fires messageCallback with correct MSG_IN on text message", async () => {
|
|
116
|
+
const channel = createChannel();
|
|
117
|
+
const onMessage = vi.fn();
|
|
118
|
+
channel.onMessage(onMessage);
|
|
119
|
+
await channel.connect();
|
|
120
|
+
// Extract the handler registered with bot.on("message:text", handler)
|
|
121
|
+
const handler = mockBotOn.mock.calls.find((call) => call[0] === "message:text")?.[1];
|
|
122
|
+
expect(handler).toBeDefined();
|
|
123
|
+
// Simulate a grammY context
|
|
124
|
+
await handler({
|
|
125
|
+
message: {
|
|
126
|
+
message_id: 42,
|
|
127
|
+
text: "Hello from Telegram!",
|
|
128
|
+
chat: { id: 123456, type: "private" },
|
|
129
|
+
from: { id: 789, first_name: "Alice" },
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
expect(onMessage).toHaveBeenCalledTimes(1);
|
|
133
|
+
const msg = onMessage.mock.calls[0][0];
|
|
134
|
+
expect(msg.type).toBe("MSG_IN");
|
|
135
|
+
expect(msg.platform).toBe("telegram");
|
|
136
|
+
expect(msg.content).toBe("Hello from Telegram!");
|
|
137
|
+
expect(msg.contextToken).toBe("tg_123456");
|
|
138
|
+
expect(msg.userId).toBe("789");
|
|
139
|
+
expect(msg.metadata?.messageId).toBe("42");
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
describe("DM access control", () => {
|
|
143
|
+
it("allows whitelisted user", async () => {
|
|
144
|
+
const channel = createChannel({ allowedUsers: ["123", "456"] });
|
|
145
|
+
await channel.connect();
|
|
146
|
+
const handler = mockBotOn.mock.calls.find((call) => call[0] === "message:text")?.[1];
|
|
147
|
+
const onMessage = vi.fn();
|
|
148
|
+
channel.onMessage(onMessage);
|
|
149
|
+
const mockNext = vi.fn();
|
|
150
|
+
const mockReply = vi.fn();
|
|
151
|
+
// Access control is the 2nd bot.use() call (index 1, after sequentialize)
|
|
152
|
+
const accessMiddleware = mockBotUse.mock.calls[1]?.[0];
|
|
153
|
+
expect(accessMiddleware).toBeDefined();
|
|
154
|
+
await accessMiddleware({ from: { id: 123 }, reply: mockReply }, mockNext);
|
|
155
|
+
expect(mockNext).toHaveBeenCalled();
|
|
156
|
+
expect(mockReply).not.toHaveBeenCalled();
|
|
157
|
+
});
|
|
158
|
+
it("rejects non-whitelisted user", async () => {
|
|
159
|
+
const channel = createChannel({ allowedUsers: ["123"] });
|
|
160
|
+
await channel.connect();
|
|
161
|
+
const accessMiddleware = mockBotUse.mock.calls[1]?.[0];
|
|
162
|
+
const mockNext = vi.fn();
|
|
163
|
+
const mockReply = vi.fn();
|
|
164
|
+
await accessMiddleware({ from: { id: 999 }, reply: mockReply }, mockNext);
|
|
165
|
+
expect(mockNext).not.toHaveBeenCalled();
|
|
166
|
+
expect(mockReply).toHaveBeenCalledWith("Sorry, you are not authorized to use this bot.");
|
|
167
|
+
});
|
|
168
|
+
it('allows any user when allowedUsers is ["*"]', async () => {
|
|
169
|
+
const channel = createChannel({ allowedUsers: ["*"] });
|
|
170
|
+
await channel.connect();
|
|
171
|
+
const accessMiddleware = mockBotUse.mock.calls[1]?.[0];
|
|
172
|
+
const mockNext = vi.fn();
|
|
173
|
+
const mockReply = vi.fn();
|
|
174
|
+
await accessMiddleware({ from: { id: 999 }, reply: mockReply }, mockNext);
|
|
175
|
+
expect(mockNext).toHaveBeenCalled();
|
|
176
|
+
expect(mockReply).not.toHaveBeenCalled();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
describe("getPlatformId / getCapabilities", () => {
|
|
180
|
+
it("returns 'telegram'", () => {
|
|
181
|
+
expect(createChannel().getPlatformId()).toBe("telegram");
|
|
182
|
+
});
|
|
183
|
+
it("reports text + media capabilities", () => {
|
|
184
|
+
const caps = createChannel().getCapabilities();
|
|
185
|
+
expect(caps.textMessaging).toBe(true);
|
|
186
|
+
expect(caps.mediaSharing).toBe(true);
|
|
187
|
+
expect(caps.webhooks).toBe(false);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
//# sourceMappingURL=TelegramChannel.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TelegramChannel.test.js","sourceRoot":"","sources":["../../../src/channels/__tests__/TelegramChannel.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yungho/noclaw-channel",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "NoClaw Channel Server - WebSocket IPC bridge for external chat platforms",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -24,7 +24,10 @@
|
|
|
24
24
|
"author": "Yungho",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"@grammyjs/runner": "^2.0.3",
|
|
28
|
+
"@grammyjs/transformer-throttler": "^1.2.1",
|
|
27
29
|
"commander": "^12.1.0",
|
|
30
|
+
"grammy": "^1.34.0",
|
|
28
31
|
"pino": "^9.0.0",
|
|
29
32
|
"pino-pretty": "^11.0.0",
|
|
30
33
|
"qrcode-terminal": "^0.12.0",
|