@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 +4 -0
- package/README.md +2 -2
- package/docs/NODES.md +2 -2
- package/nodes/command.js +15 -13
- package/nodes/receiver.js +20 -13
- package/package.json +1 -1
- package/test/command.test.js +43 -0
- package/test/receiver.test.js +43 -0
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
@@ -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
|
+
});
|