@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 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
@@ -46,6 +46,11 @@ module.exports = function (RED) {
46
46
  }
47
47
  };
48
48
 
49
+ if (!client) {
50
+ node.error('No Telegram client available. Check account configuration.');
51
+ return;
52
+ }
53
+
49
54
  try {
50
55
  client.addEventHandler(handler, event);
51
56
  } catch (err) {
@@ -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
 
@@ -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
 
@@ -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;
@@ -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
 
@@ -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";
@@ -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 the sender's user ID is not in the ignore list.</p>
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 based on the sender's user ID.</li>
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
- let rawOptions = {};
461
- if (includeChats.length > 0) rawOptions.chats = includeChats;
462
- if (excludeChats.length > 0) rawOptions.blacklistChats = excludeChats;
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));
@@ -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,
@@ -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.1",
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.221.2"
47
+ "teleproto": "^1.223.1"
48
48
  },
49
49
  "devDependencies": {
50
50
  "mocha": "^11.7.1",
@@ -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 = [];