@patricktobias86/node-red-telegram-account 1.2.1 → 1.2.3
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/CHANGELOG.md +8 -0
- package/nodes/command.js +5 -0
- package/nodes/delete-message.js +5 -0
- package/nodes/get-entity.js +5 -0
- package/nodes/iter-dialogs.js +6 -0
- package/nodes/iter-messages.js +6 -0
- package/nodes/promote-admin.js +6 -0
- package/nodes/receiver.html +4 -2
- package/nodes/receiver.js +61 -7
- package/nodes/send-file.js +5 -0
- package/nodes/send-message.js +6 -0
- package/package.json +2 -2
- package/test/receiver.test.js +81 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,10 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.2.2] - 2026-03-07
|
|
6
|
+
### Added
|
|
7
|
+
- Added guarded checks before the Telegram client is called to avoid errors when the client is not available.
|
|
8
|
+
|
|
5
9
|
## [1.2.1] - 2026-01-22
|
|
6
10
|
### Added
|
|
7
11
|
- Receiver node option to disable emitting edited message updates (useful to prevent duplicate outputs when counters/markup change on channel posts).
|
|
8
12
|
|
|
13
|
+
## [1.2.2] - 2026-01-31
|
|
14
|
+
### Fixed
|
|
15
|
+
- Receiver node chat/sender filters now work (include/exclude chats and senders).
|
|
16
|
+
|
|
9
17
|
## [1.2.0] - 2026-01-12
|
|
10
18
|
### Changed
|
|
11
19
|
- Switched underlying Telegram MTProto client dependency from `telegram` (GramJS) to `teleproto`.
|
package/nodes/command.js
CHANGED
package/nodes/delete-message.js
CHANGED
|
@@ -18,6 +18,11 @@ module.exports = function (RED) {
|
|
|
18
18
|
/** @type {TelegramClient} */
|
|
19
19
|
const client = msg.payload?.client ? msg.payload.client : this.config.client;
|
|
20
20
|
|
|
21
|
+
if (!client) {
|
|
22
|
+
node.error('No Telegram client available. Check account configuration.');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
try {
|
|
22
27
|
const response = await client.deleteMessages(chatId, messageIds, { revoke });
|
|
23
28
|
|
package/nodes/get-entity.js
CHANGED
|
@@ -14,6 +14,11 @@ module.exports = function (RED) {
|
|
|
14
14
|
/** @type {TelegramClient} */
|
|
15
15
|
const client = msg.payload?.client ? msg.payload.client : this.config.client;
|
|
16
16
|
|
|
17
|
+
if (!client) {
|
|
18
|
+
node.error('No Telegram client available. Check account configuration.');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
try {
|
|
18
23
|
let entity;
|
|
19
24
|
|
package/nodes/iter-dialogs.js
CHANGED
|
@@ -15,6 +15,12 @@ module.exports = function (RED) {
|
|
|
15
15
|
|
|
16
16
|
/** @type {TelegramClient} */
|
|
17
17
|
const client = msg.payload?.client ? msg.payload.client : this.config.client;
|
|
18
|
+
|
|
19
|
+
if (!client) {
|
|
20
|
+
node.error('No Telegram client available. Check account configuration.');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
const limit = msg.payload.limit || config.limit;
|
|
19
25
|
const offsetDate = msg.payload.offsetDate || config.offsetDate;
|
|
20
26
|
const offsetId = msg.payload.offsetId || config.offsetId;
|
package/nodes/iter-messages.js
CHANGED
|
@@ -15,6 +15,12 @@ module.exports = function (RED) {
|
|
|
15
15
|
|
|
16
16
|
/** @type {TelegramClient} */
|
|
17
17
|
const client = msg.payload?.client ? msg.payload.client : this.config.client;
|
|
18
|
+
|
|
19
|
+
if (!client) {
|
|
20
|
+
node.error('No Telegram client available. Check account configuration.');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
const chatId = msg.payload?.chatId ? msg.payload.chatId : config.chatId;
|
|
19
25
|
let peerId = chatId === "me" ? chatId : utils.parseID(chatId);
|
|
20
26
|
|
package/nodes/promote-admin.js
CHANGED
|
@@ -15,6 +15,12 @@ module.exports = function (RED) {
|
|
|
15
15
|
node.log('promote-admin input: ' + JSON.stringify(msg));
|
|
16
16
|
}
|
|
17
17
|
const client = msg.payload?.client || this.config.client;
|
|
18
|
+
|
|
19
|
+
if (!client) {
|
|
20
|
+
node.error('No Telegram client available. Check account configuration.');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
const chatId = msg.payload.chatId || config.chatId;
|
|
19
25
|
const userId = msg.payload.userId || config.userId;
|
|
20
26
|
const rank = msg.payload.rank || config.rank || "admin";
|
package/nodes/receiver.html
CHANGED
|
@@ -218,7 +218,7 @@
|
|
|
218
218
|
</dl>
|
|
219
219
|
|
|
220
220
|
<h3>Details</h3>
|
|
221
|
-
<p>The <b>receiver</b> node uses the Telegram client to listen for all new messages in real-time. It emits a message to the next connected Node-RED node whenever a new Telegram message is received, provided
|
|
221
|
+
<p>The <b>receiver</b> node uses the Telegram client to listen for all new messages in real-time. It emits a message to the next connected Node-RED node whenever a new Telegram message is received, provided it passes the configured filters.</p>
|
|
222
222
|
|
|
223
223
|
<h3>Example</h3>
|
|
224
224
|
<pre>
|
|
@@ -256,7 +256,9 @@
|
|
|
256
256
|
<h3>Notes</h3>
|
|
257
257
|
<ul>
|
|
258
258
|
<li>Ensure the Telegram bot has sufficient permissions to receive messages in the configured chat or channel.</li>
|
|
259
|
-
<li>The <b>Ignore List</b> only filters messages
|
|
259
|
+
<li>The <b>Ignore List</b> only filters messages where the sender can be identified as a user.</li>
|
|
260
|
+
<li><b>Include/Exclude chats</b> match against <code>payload.chatId</code>.</li>
|
|
261
|
+
<li><b>Include/Exclude senders</b> match against <code>payload.senderId</code> (user/chat/channel IDs depending on the update).</li>
|
|
260
262
|
<li>Use <b>Ignore message types</b> to drop updates that contain media you don't want to process, such as large videos or documents.</li>
|
|
261
263
|
<li>For advanced filtering based on message content, consider chaining this node with additional processing nodes in Node-RED.</li>
|
|
262
264
|
</ul>
|
package/nodes/receiver.js
CHANGED
|
@@ -377,6 +377,26 @@ module.exports = function (RED) {
|
|
|
377
377
|
const includeSenders = splitList(config.includeSenders || "");
|
|
378
378
|
const excludeSenders = splitList(config.excludeSenders || "");
|
|
379
379
|
|
|
380
|
+
const toNormalizedIdSet = (values) => {
|
|
381
|
+
const result = new Set();
|
|
382
|
+
for (const value of values) {
|
|
383
|
+
if (value == null) {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
const normalized = String(value).trim();
|
|
387
|
+
if (!normalized) {
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
result.add(normalized);
|
|
391
|
+
}
|
|
392
|
+
return result;
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const includeChatSet = toNormalizedIdSet(includeChats);
|
|
396
|
+
const excludeChatSet = toNormalizedIdSet(excludeChats);
|
|
397
|
+
const includeSenderSet = toNormalizedIdSet(includeSenders);
|
|
398
|
+
const excludeSenderSet = toNormalizedIdSet(excludeSenders);
|
|
399
|
+
|
|
380
400
|
const extractPhotoSize = (photo) => {
|
|
381
401
|
if (!photo || !Array.isArray(photo.sizes)) {
|
|
382
402
|
return null;
|
|
@@ -457,13 +477,9 @@ module.exports = function (RED) {
|
|
|
457
477
|
node.send([null, { payload }]);
|
|
458
478
|
};
|
|
459
479
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
if (includeSenders.length > 0) rawOptions.senders = includeSenders;
|
|
464
|
-
if (excludeSenders.length > 0) rawOptions.blacklistSenders = excludeSenders;
|
|
465
|
-
|
|
466
|
-
const event = new Raw(rawOptions);
|
|
480
|
+
// teleproto's Raw event builder only supports `types` and `func` options.
|
|
481
|
+
// Chat/sender filtering is implemented below after we normalize the message metadata.
|
|
482
|
+
const event = new Raw({});
|
|
467
483
|
const handler = (rawUpdate) => {
|
|
468
484
|
const debug = node.debugEnabled;
|
|
469
485
|
if (debug) {
|
|
@@ -523,6 +539,44 @@ module.exports = function (RED) {
|
|
|
523
539
|
null;
|
|
524
540
|
|
|
525
541
|
const chatId = peerChatId(peer);
|
|
542
|
+
const chatIdString = chatId != null ? String(chatId) : null;
|
|
543
|
+
const senderIdString = senderId != null ? String(senderId) : null;
|
|
544
|
+
|
|
545
|
+
if (includeChatSet.size > 0) {
|
|
546
|
+
const allowed = chatIdString != null && includeChatSet.has(chatIdString);
|
|
547
|
+
if (!allowed) {
|
|
548
|
+
debugLog(`receiver ignoring message due to includeChats; chatId=${chatIdString ?? 'unknown'}`);
|
|
549
|
+
debugSend({ event: 'ignored', reason: 'includeChats', chatId });
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (excludeChatSet.size > 0) {
|
|
555
|
+
const blocked = chatIdString != null && excludeChatSet.has(chatIdString);
|
|
556
|
+
if (blocked) {
|
|
557
|
+
debugLog(`receiver ignoring message due to excludeChats; chatId=${chatIdString}`);
|
|
558
|
+
debugSend({ event: 'ignored', reason: 'excludeChats', chatId });
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (includeSenderSet.size > 0) {
|
|
564
|
+
const allowed = senderIdString != null && includeSenderSet.has(senderIdString);
|
|
565
|
+
if (!allowed) {
|
|
566
|
+
debugLog(`receiver ignoring message due to includeSenders; senderId=${senderIdString ?? 'unknown'} senderType=${senderType}`);
|
|
567
|
+
debugSend({ event: 'ignored', reason: 'includeSenders', senderId, senderType });
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (excludeSenderSet.size > 0) {
|
|
573
|
+
const blocked = senderIdString != null && excludeSenderSet.has(senderIdString);
|
|
574
|
+
if (blocked) {
|
|
575
|
+
debugLog(`receiver ignoring message due to excludeSenders; senderId=${senderIdString} senderType=${senderType}`);
|
|
576
|
+
debugSend({ event: 'ignored', reason: 'excludeSenders', senderId, senderType });
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
526
580
|
|
|
527
581
|
if (ignoredMessageTypes.size > 0) {
|
|
528
582
|
const shouldIgnoreType = Array.from(messageTypes).some((type) => ignoredMessageTypes.has(type));
|
package/nodes/send-file.js
CHANGED
|
@@ -36,6 +36,11 @@ module.exports = function (RED) {
|
|
|
36
36
|
/** @type {TelegramClient} */
|
|
37
37
|
const client = msg.payload?.client ? msg.payload.client : this.config.client;
|
|
38
38
|
|
|
39
|
+
if (!client) {
|
|
40
|
+
node.error('No Telegram client available. Check account configuration.');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
39
44
|
try {
|
|
40
45
|
const params = {
|
|
41
46
|
file: files,
|
package/nodes/send-message.js
CHANGED
|
@@ -34,6 +34,12 @@ module.exports = function (RED) {
|
|
|
34
34
|
|
|
35
35
|
/** @type {TelegramClient} */
|
|
36
36
|
const client = msg.payload?.client ? msg.payload.client : this.config.client;
|
|
37
|
+
|
|
38
|
+
if (!client) {
|
|
39
|
+
node.error('No Telegram client available. Check account configuration.');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
37
43
|
let peerId = chatId === "me" ? chatId : parseID(chatId);
|
|
38
44
|
|
|
39
45
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patricktobias86/node-red-telegram-account",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "Node-RED nodes to communicate with TeleProto.",
|
|
5
5
|
"main": "nodes/config.js",
|
|
6
6
|
"keywords": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"registry": "https://registry.npmjs.org"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"teleproto": "^1.
|
|
47
|
+
"teleproto": "^1.223.1"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"mocha": "^11.7.1",
|
package/test/receiver.test.js
CHANGED
|
@@ -101,6 +101,87 @@ describe('Receiver node', function() {
|
|
|
101
101
|
assert.strictEqual(sent.length, 1);
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
+
it('filters by includeChats', function() {
|
|
105
|
+
const { NodeCtor, addCalls } = load();
|
|
106
|
+
const sent = [];
|
|
107
|
+
const node = new NodeCtor({config:'c', ignore:'', ignoreMessageTypes:'', maxFileSizeMb:'', includeChats:'111'});
|
|
108
|
+
node.send = (msg) => sent.push(msg);
|
|
109
|
+
const handler = addCalls[0].fn;
|
|
110
|
+
|
|
111
|
+
handler({
|
|
112
|
+
className: 'UpdateNewMessage',
|
|
113
|
+
message: { fromId: { userId: 123 }, peerId:{ userId: 111 }, message: 'allowed' }
|
|
114
|
+
});
|
|
115
|
+
handler({
|
|
116
|
+
className: 'UpdateNewMessage',
|
|
117
|
+
message: { fromId: { userId: 123 }, peerId:{ userId: 222 }, message: 'blocked' }
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
assert.strictEqual(sent.length, 1);
|
|
121
|
+
assert.strictEqual(sent[0].payload.chatId, 111);
|
|
122
|
+
assert.strictEqual(sent[0].payload.message.message, 'allowed');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('filters by excludeChats', function() {
|
|
126
|
+
const { NodeCtor, addCalls } = load();
|
|
127
|
+
const sent = [];
|
|
128
|
+
const node = new NodeCtor({config:'c', ignore:'', ignoreMessageTypes:'', maxFileSizeMb:'', excludeChats:'222'});
|
|
129
|
+
node.send = (msg) => sent.push(msg);
|
|
130
|
+
const handler = addCalls[0].fn;
|
|
131
|
+
|
|
132
|
+
handler({
|
|
133
|
+
className: 'UpdateNewMessage',
|
|
134
|
+
message: { fromId: { userId: 123 }, peerId:{ userId: 111 }, message: 'allowed' }
|
|
135
|
+
});
|
|
136
|
+
handler({
|
|
137
|
+
className: 'UpdateNewMessage',
|
|
138
|
+
message: { fromId: { userId: 123 }, peerId:{ userId: 222 }, message: 'blocked' }
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
assert.strictEqual(sent.length, 1);
|
|
142
|
+
assert.strictEqual(sent[0].payload.chatId, 111);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('filters by includeSenders', function() {
|
|
146
|
+
const { NodeCtor, addCalls } = load();
|
|
147
|
+
const sent = [];
|
|
148
|
+
const node = new NodeCtor({config:'c', ignore:'', ignoreMessageTypes:'', maxFileSizeMb:'', includeSenders:'123'});
|
|
149
|
+
node.send = (msg) => sent.push(msg);
|
|
150
|
+
const handler = addCalls[0].fn;
|
|
151
|
+
|
|
152
|
+
handler({
|
|
153
|
+
className: 'UpdateNewMessage',
|
|
154
|
+
message: { fromId: { userId: 123 }, peerId:{ userId: 111 }, message: 'allowed' }
|
|
155
|
+
});
|
|
156
|
+
handler({
|
|
157
|
+
className: 'UpdateNewMessage',
|
|
158
|
+
message: { fromId: { userId: 456 }, peerId:{ userId: 111 }, message: 'blocked' }
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
assert.strictEqual(sent.length, 1);
|
|
162
|
+
assert.strictEqual(sent[0].payload.senderId, 123);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('filters by excludeSenders', function() {
|
|
166
|
+
const { NodeCtor, addCalls } = load();
|
|
167
|
+
const sent = [];
|
|
168
|
+
const node = new NodeCtor({config:'c', ignore:'', ignoreMessageTypes:'', maxFileSizeMb:'', excludeSenders:'456'});
|
|
169
|
+
node.send = (msg) => sent.push(msg);
|
|
170
|
+
const handler = addCalls[0].fn;
|
|
171
|
+
|
|
172
|
+
handler({
|
|
173
|
+
className: 'UpdateNewMessage',
|
|
174
|
+
message: { fromId: { userId: 123 }, peerId:{ userId: 111 }, message: 'allowed' }
|
|
175
|
+
});
|
|
176
|
+
handler({
|
|
177
|
+
className: 'UpdateNewMessage',
|
|
178
|
+
message: { fromId: { userId: 456 }, peerId:{ userId: 111 }, message: 'blocked' }
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
assert.strictEqual(sent.length, 1);
|
|
182
|
+
assert.strictEqual(sent[0].payload.senderId, 123);
|
|
183
|
+
});
|
|
184
|
+
|
|
104
185
|
it('populates chatId and senderId for string ids', function() {
|
|
105
186
|
const { NodeCtor, addCalls } = load();
|
|
106
187
|
const sent = [];
|