@patricktobias86/node-red-telegram-account 1.1.7 → 1.1.8

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
@@ -9,3 +9,7 @@ All notable changes to this project will be documented in this file.
9
9
  ### Changed
10
10
  - Session management now tracks active clients in a `Map` for safer reuse.
11
11
 
12
+ ## [1.1.8] - 2025-07-22
13
+ ### Fixed
14
+ - Receiver and Command nodes now remove their event listeners when closed to prevent duplicate messages after redeploys.
15
+
package/README.md CHANGED
@@ -18,8 +18,8 @@ See [docs/NODES.md](docs/NODES.md) for a detailed description of every node. Bel
18
18
 
19
19
  - **config** – stores your API credentials and caches sessions for reuse.
20
20
  - **auth** – interactive login that outputs a `stringSession`.
21
- - **receiver** – emits messages for every incoming update (with optional ignore list).
22
- - **command** – triggers when an incoming message matches a command or regex.
21
+ - **receiver** – emits messages for every incoming update (with optional ignore list). Event listeners are cleaned up on node close so redeploys won't duplicate messages.
22
+ - **command** – triggers when an incoming message matches a command or regex. Event listeners are removed on redeploy to prevent duplicates.
23
23
  - **send-message** – sends text or media messages with rich options.
24
24
  - **send-files** – uploads one or more files with captions and buttons.
25
25
  - **get-entity** – resolves usernames, IDs or t.me links into Telegram entities.
package/docs/NODES.md CHANGED
@@ -8,8 +8,8 @@ Below is a short description of each node. For a full list of configuration opti
8
8
  |------|-------------|
9
9
  | **config** | Configuration node storing API credentials and connection options. Other nodes reference this to share a Telegram client and reuse the session. Connections are tracked in a Map with a reference count so multiple nodes can wait for the same connection. |
10
10
  | **auth** | Starts an interactive login flow. Produces a `stringSession` that can be reused with the `config` node. |
11
- | **receiver** | Emits an output message for every incoming Telegram message. Can ignore specific user IDs. |
12
- | **command** | Listens for new messages and triggers when a message matches a configured command or regular expression. |
11
+ | **receiver** | Emits an output message for every incoming Telegram message. Can ignore specific user IDs. Event handlers are automatically removed when the node is closed. |
12
+ | **command** | Listens for new messages and triggers when a message matches a configured command or regular expression. The event listener is cleaned up on node close to avoid duplicates. |
13
13
  | **send-message** | Sends text messages or media files to a chat. Supports parse mode, buttons, scheduling, and more. |
14
14
  | **send-files** | Uploads one or more files to a chat with optional caption, thumbnails and other parameters. |
15
15
  | **get-entity** | Resolves a username, user ID or t.me URL into a Telegram entity object. |
package/nodes/command.js CHANGED
@@ -9,44 +9,46 @@ module.exports = function (RED) {
9
9
  /** @type {TelegramClient} */
10
10
  const client = this.config.client;
11
11
 
12
-
13
-
14
- try {
15
- client.addEventHandler((update) => {
16
- const message = update.message.message
12
+ const event = new NewMessage();
13
+ const handler = (update) => {
14
+ const message = update.message.message;
17
15
  if (message) {
18
16
  if (config.regex) {
19
17
  const regex = new RegExp(config.command);
20
18
 
21
19
  if (regex.test(message)) {
22
-
23
20
  var msg = {
24
21
  payload: {
25
22
  update
26
23
  }
27
24
  };
28
-
29
-
30
25
  node.send(msg);
31
26
  }
32
27
  } else if (message === config.command) {
33
-
34
28
  var msg = {
35
29
  payload: {
36
30
  update
37
31
  }
38
32
  };
39
-
40
-
41
33
  node.send(msg);
42
34
  }
43
35
  }
44
- }, new NewMessage());
45
-
36
+ };
37
+
38
+ try {
39
+ client.addEventHandler(handler, event);
46
40
  } catch (err) {
47
41
  node.error('Authorization error: ' + err.message);
48
42
  }
49
43
 
44
+ this.on('close', () => {
45
+ try {
46
+ client.removeEventHandler(handler, event);
47
+ } catch (err) {
48
+ node.error('Handler removal error: ' + err.message);
49
+ }
50
+ });
51
+
50
52
  }
51
53
 
52
54
  RED.nodes.registerType('command', Command);
package/nodes/receiver.js CHANGED
@@ -8,24 +8,31 @@ module.exports = function (RED) {
8
8
  const client = this.config.client;
9
9
  const ignore = config.ignore.split(/\n/);
10
10
 
11
-
12
- try {
13
- client.addEventHandler((update) => {
14
- if(update.message.fromId != null && !ignore.includes(update.message.fromId.userId.toString())){
15
-
16
- node.send({
17
- payload:{
18
- update
19
- }
20
- } )
11
+ const event = new NewMessage();
12
+ const handler = (update) => {
13
+ if (update.message.fromId != null && !ignore.includes(update.message.fromId.userId.toString())) {
14
+ node.send({
15
+ payload: {
16
+ update
17
+ }
18
+ });
21
19
  }
22
-
23
- }, new NewMessage());
24
-
20
+ };
21
+
22
+ try {
23
+ client.addEventHandler(handler, event);
25
24
  } catch (err) {
26
25
  node.error('Authorization error: ' + err.message);
27
26
  }
28
27
 
28
+ this.on('close', () => {
29
+ try {
30
+ client.removeEventHandler(handler, event);
31
+ } catch (err) {
32
+ node.error('Handler removal error: ' + err.message);
33
+ }
34
+ });
35
+
29
36
  }
30
37
 
31
38
  RED.nodes.registerType('receiver', Receiver);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patricktobias86/node-red-telegram-account",
3
- "version": "1.1.7",
3
+ "version": "1.1.8",
4
4
  "description": "Node-RED nodes to communicate with GramJS.",
5
5
  "main": "nodes/config.js",
6
6
  "keywords": [
@@ -0,0 +1,43 @@
1
+ const assert = require('assert');
2
+ const proxyquire = require('proxyquire').noPreserveCache();
3
+
4
+ function load() {
5
+ const addCalls = [];
6
+ const removeCalls = [];
7
+ class TelegramClientStub {
8
+ addEventHandler(fn, event) { addCalls.push({fn, event}); }
9
+ removeEventHandler(fn, event) { removeCalls.push({fn, event}); }
10
+ }
11
+ class NewMessageStub {}
12
+
13
+ let NodeCtor;
14
+ const configNode = { client: new TelegramClientStub() };
15
+ const RED = {
16
+ nodes: {
17
+ createNode(node) {
18
+ node._events = {};
19
+ node.on = (e, fn) => { node._events[e] = fn; };
20
+ },
21
+ registerType(name, ctor) { NodeCtor = ctor; },
22
+ getNode() { return configNode; }
23
+ }
24
+ };
25
+
26
+ proxyquire('../nodes/command.js', {
27
+ 'telegram/events': { NewMessage: NewMessageStub }
28
+ })(RED);
29
+
30
+ return { NodeCtor, addCalls, removeCalls };
31
+ }
32
+
33
+ describe('Command node', function() {
34
+ it('removes event handler on close', function() {
35
+ const { NodeCtor, addCalls, removeCalls } = load();
36
+ const node = new NodeCtor({config:'c', command:'cmd', regex:false});
37
+ assert.strictEqual(addCalls.length, 1);
38
+ node._events.close();
39
+ assert.strictEqual(removeCalls.length, 1);
40
+ assert.strictEqual(removeCalls[0].fn, addCalls[0].fn);
41
+ assert.strictEqual(removeCalls[0].event, addCalls[0].event);
42
+ });
43
+ });
@@ -0,0 +1,43 @@
1
+ const assert = require('assert');
2
+ const proxyquire = require('proxyquire').noPreserveCache();
3
+
4
+ function load() {
5
+ const addCalls = [];
6
+ const removeCalls = [];
7
+ class TelegramClientStub {
8
+ addEventHandler(fn, event) { addCalls.push({fn, event}); }
9
+ removeEventHandler(fn, event) { removeCalls.push({fn, event}); }
10
+ }
11
+ class NewMessageStub {}
12
+
13
+ let NodeCtor;
14
+ const configNode = { client: new TelegramClientStub() };
15
+ const RED = {
16
+ nodes: {
17
+ createNode(node) {
18
+ node._events = {};
19
+ node.on = (e, fn) => { node._events[e] = fn; };
20
+ },
21
+ registerType(name, ctor) { NodeCtor = ctor; },
22
+ getNode() { return configNode; }
23
+ }
24
+ };
25
+
26
+ proxyquire('../nodes/receiver.js', {
27
+ 'telegram/events': { NewMessage: NewMessageStub }
28
+ })(RED);
29
+
30
+ return { NodeCtor, addCalls, removeCalls };
31
+ }
32
+
33
+ describe('Receiver node', function() {
34
+ it('removes event handler on close', function() {
35
+ const { NodeCtor, addCalls, removeCalls } = load();
36
+ const node = new NodeCtor({config:'c', ignore:''});
37
+ assert.strictEqual(addCalls.length, 1);
38
+ node._events.close();
39
+ assert.strictEqual(removeCalls.length, 1);
40
+ assert.strictEqual(removeCalls[0].fn, addCalls[0].fn);
41
+ assert.strictEqual(removeCalls[0].event, addCalls[0].event);
42
+ });
43
+ });