@patricktobias86/node-red-telegram-account 1.1.12 → 1.1.15
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 -1
- package/README.md +1 -1
- package/docs/NODES.md +1 -2
- package/nodes/receiver.html +22 -5
- package/nodes/receiver.js +107 -2
- package/package.json +1 -1
- package/test/receiver.test.js +24 -0
- package/AGENTS.md +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.1.15] - 2025-09-21
|
|
6
|
+
### Added
|
|
7
|
+
- Receiver node option to drop updates when media exceeds a configurable size threshold, preventing large downloads.
|
|
8
|
+
|
|
5
9
|
## [1.1.7] - 2025-07-22
|
|
6
10
|
### Added
|
|
7
11
|
- Mocha tests for the configuration node ensure sessions are reused correctly.
|
|
@@ -26,4 +30,3 @@ All notable changes to this project will be documented in this file.
|
|
|
26
30
|
## [1.1.12] - 2025-08-06
|
|
27
31
|
### Fixed
|
|
28
32
|
- Auth node now emits the generated `stringSession` so it can be used by other nodes.
|
|
29
|
-
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ 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` (also set on `msg.stringSession`).
|
|
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.
|
|
21
|
+
- **receiver** – emits messages for every incoming update (with optional ignore list and media size limit). Event listeners are cleaned up on node close so redeploys won't duplicate messages.
|
|
22
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.
|
package/docs/NODES.md
CHANGED
|
@@ -8,7 +8,7 @@ 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` (available in both <code>msg.payload.stringSession</code> and <code>msg.stringSession</code>) that can be reused with the `config` node. |
|
|
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. |
|
|
11
|
+
| **receiver** | Emits an output message for every incoming Telegram message. Can ignore specific user IDs and optionally skip media above a configurable size. Event handlers are automatically removed when the node is closed. |
|
|
12
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. |
|
|
@@ -22,4 +22,3 @@ Below is a short description of each node. For a full list of configuration opti
|
|
|
22
22
|
All nodes forward any properties on the incoming `msg` outside of `msg.payload` unchanged.
|
|
23
23
|
|
|
24
24
|
All nodes provide a **Debug** checkbox. When enabled the node will log its input and output messages to aid troubleshooting.
|
|
25
|
-
|
package/nodes/receiver.html
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
name: { value: '' },
|
|
8
8
|
config: { type: 'config', required: false },
|
|
9
9
|
ignore: { value:""},
|
|
10
|
-
debug: { value: false }
|
|
10
|
+
debug: { value: false },
|
|
11
|
+
maxFileSizeMb: { value: "" }
|
|
11
12
|
},
|
|
12
13
|
inputs: 1,
|
|
13
14
|
outputs: 1,
|
|
@@ -49,15 +50,28 @@
|
|
|
49
50
|
</div>
|
|
50
51
|
<div class="form-row">
|
|
51
52
|
<label for="node-input-ignore">
|
|
52
|
-
<i class="fa fa-
|
|
53
|
+
<i class="fa fa-user-times"></i> Ignore list
|
|
53
54
|
</label>
|
|
54
55
|
<textarea
|
|
55
56
|
type="text"
|
|
56
57
|
id="node-input-ignore"
|
|
57
|
-
placeholder="
|
|
58
|
+
placeholder="Enter user IDs, one per line"
|
|
58
59
|
style="width: 60%"
|
|
59
60
|
></textarea>
|
|
60
61
|
</div>
|
|
62
|
+
<div class="form-row">
|
|
63
|
+
<label for="node-input-maxFileSizeMb">
|
|
64
|
+
<i class="fa fa-download"></i> Max media size (MB)
|
|
65
|
+
</label>
|
|
66
|
+
<input
|
|
67
|
+
type="number"
|
|
68
|
+
id="node-input-maxFileSizeMb"
|
|
69
|
+
placeholder="Leave blank to disable"
|
|
70
|
+
min="0"
|
|
71
|
+
step="0.1"
|
|
72
|
+
style="width: 60%"
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
61
75
|
</script>
|
|
62
76
|
|
|
63
77
|
<script type="text/html" data-help-name="receiver">
|
|
@@ -86,6 +100,11 @@
|
|
|
86
100
|
<span class="property-type">string</span>
|
|
87
101
|
</dt>
|
|
88
102
|
<dd>A newline-separated list of user IDs to ignore. Messages from these users will not trigger the output.</dd>
|
|
103
|
+
|
|
104
|
+
<dt>Max media size (MB)
|
|
105
|
+
<span class="property-type">number</span>
|
|
106
|
+
</dt>
|
|
107
|
+
<dd>Skip updates that include media exceeding this size. Leave blank to process all media.</dd>
|
|
89
108
|
</dl>
|
|
90
109
|
|
|
91
110
|
<h3>Details</h3>
|
|
@@ -131,5 +150,3 @@
|
|
|
131
150
|
<li>For advanced filtering based on message content, consider chaining this node with additional processing nodes in Node-RED.</li>
|
|
132
151
|
</ul>
|
|
133
152
|
</script>
|
|
134
|
-
|
|
135
|
-
|
package/nodes/receiver.js
CHANGED
|
@@ -8,7 +8,92 @@ module.exports = function (RED) {
|
|
|
8
8
|
this.debugEnabled = config.debug;
|
|
9
9
|
var node = this;
|
|
10
10
|
const client = this.config.client;
|
|
11
|
-
const ignore = config.ignore
|
|
11
|
+
const ignore = (config.ignore || "")
|
|
12
|
+
.split(/\n/)
|
|
13
|
+
.map((entry) => entry.trim())
|
|
14
|
+
.filter(Boolean);
|
|
15
|
+
const maxFileSizeMb = Number(config.maxFileSizeMb);
|
|
16
|
+
const maxFileSizeBytes = Number.isFinite(maxFileSizeMb) && maxFileSizeMb > 0
|
|
17
|
+
? maxFileSizeMb * 1024 * 1024
|
|
18
|
+
: null;
|
|
19
|
+
|
|
20
|
+
const toNumber = (value) => {
|
|
21
|
+
if (typeof value === 'number') {
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
if (typeof value === 'bigint') {
|
|
25
|
+
const result = Number(value);
|
|
26
|
+
return Number.isFinite(result) ? result : Number.MAX_SAFE_INTEGER;
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const extractPhotoSize = (photo) => {
|
|
32
|
+
if (!photo || !Array.isArray(photo.sizes)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
let max = 0;
|
|
36
|
+
for (const size of photo.sizes) {
|
|
37
|
+
if (size == null) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (Array.isArray(size.sizes)) {
|
|
41
|
+
for (const nested of size.sizes) {
|
|
42
|
+
const nestedValue = toNumber(nested);
|
|
43
|
+
if (nestedValue != null && nestedValue > max) {
|
|
44
|
+
max = nestedValue;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const value = toNumber(size.size ?? size.length ?? size.bytes);
|
|
49
|
+
if (value != null && value > max) {
|
|
50
|
+
max = value;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return max > 0 ? max : null;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const extractMediaSize = (media) => {
|
|
57
|
+
if (!media) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (media.document && media.document.size != null) {
|
|
62
|
+
const value = toNumber(media.document.size);
|
|
63
|
+
if (value != null) {
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (media.photo) {
|
|
69
|
+
const photoSize = extractPhotoSize(media.photo);
|
|
70
|
+
if (photoSize != null) {
|
|
71
|
+
return photoSize;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (media.webpage) {
|
|
76
|
+
const { document, photo } = media.webpage;
|
|
77
|
+
const docSize = document && toNumber(document.size);
|
|
78
|
+
if (docSize != null) {
|
|
79
|
+
return docSize;
|
|
80
|
+
}
|
|
81
|
+
const photoSize = extractPhotoSize(photo);
|
|
82
|
+
if (photoSize != null) {
|
|
83
|
+
return photoSize;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const className = media.className || media._;
|
|
88
|
+
if ((className === 'MessageMediaDocument' || className === 'MessageMediaPhoto') && media.size != null) {
|
|
89
|
+
const value = toNumber(media.size);
|
|
90
|
+
if (value != null) {
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return null;
|
|
96
|
+
};
|
|
12
97
|
|
|
13
98
|
const event = new NewMessage();
|
|
14
99
|
const handler = (update) => {
|
|
@@ -16,7 +101,27 @@ module.exports = function (RED) {
|
|
|
16
101
|
if (debug) {
|
|
17
102
|
node.log('receiver update: ' + util.inspect(update, { depth: null }));
|
|
18
103
|
}
|
|
19
|
-
|
|
104
|
+
const message = update && update.message;
|
|
105
|
+
if (!message) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (maxFileSizeBytes != null) {
|
|
110
|
+
const mediaSize = extractMediaSize(message.media);
|
|
111
|
+
if (mediaSize != null && mediaSize > maxFileSizeBytes) {
|
|
112
|
+
if (debug) {
|
|
113
|
+
node.log(`receiver ignoring update with media size ${mediaSize} bytes exceeding limit ${maxFileSizeBytes}`);
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const senderId = message.fromId && message.fromId.userId;
|
|
120
|
+
if (senderId != null && ignore.includes(senderId.toString())) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (message.fromId != null && message.fromId.userId != null) {
|
|
20
125
|
const out = { payload: { update } };
|
|
21
126
|
node.send(out);
|
|
22
127
|
if (debug) {
|
package/package.json
CHANGED
package/test/receiver.test.js
CHANGED
|
@@ -40,4 +40,28 @@ describe('Receiver node', function() {
|
|
|
40
40
|
assert.strictEqual(removeCalls[0].fn, addCalls[0].fn);
|
|
41
41
|
assert.strictEqual(removeCalls[0].event, addCalls[0].event);
|
|
42
42
|
});
|
|
43
|
+
|
|
44
|
+
it('skips media updates when size exceeds threshold', function() {
|
|
45
|
+
const { NodeCtor, addCalls } = load();
|
|
46
|
+
const sent = [];
|
|
47
|
+
const node = new NodeCtor({config:'c', ignore:'', maxFileSizeMb:'5'});
|
|
48
|
+
node.send = (msg) => sent.push(msg);
|
|
49
|
+
const handler = addCalls[0].fn;
|
|
50
|
+
|
|
51
|
+
handler({ message: { fromId: { userId: 123 }, media: { document: { size: 6 * 1024 * 1024 } } } });
|
|
52
|
+
|
|
53
|
+
assert.strictEqual(sent.length, 0);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('delivers media updates when size is below threshold', function() {
|
|
57
|
+
const { NodeCtor, addCalls } = load();
|
|
58
|
+
const sent = [];
|
|
59
|
+
const node = new NodeCtor({config:'c', ignore:'', maxFileSizeMb:'5'});
|
|
60
|
+
node.send = (msg) => sent.push(msg);
|
|
61
|
+
const handler = addCalls[0].fn;
|
|
62
|
+
|
|
63
|
+
handler({ message: { fromId: { userId: 123 }, media: { document: { size: 3 * 1024 * 1024 } } } });
|
|
64
|
+
|
|
65
|
+
assert.strictEqual(sent.length, 1);
|
|
66
|
+
});
|
|
43
67
|
});
|