Reformat and translate

This commit is contained in:
Pijus Kamandulis 2025-01-19 22:03:57 +02:00
parent beadf36b51
commit 3390e6f093
23 changed files with 2820 additions and 2224 deletions

View File

@ -1 +1,3 @@
Node-red and telegram (gramjs) integration
Node-red and telegram (gramjs) integration
Fork of `https://github.com/borovlioff/node-red-telegram-account`

144
package-lock.json generated
View File

@ -45,9 +45,9 @@
]
},
"node_modules/big-integer": {
"version": "1.6.51",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
"integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
"version": "1.6.52",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
"integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==",
"engines": {
"node": ">=0.6"
}
@ -76,9 +76,9 @@
}
},
"node_modules/bufferutil": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz",
"integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz",
"integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==",
"hasInstallScript": true,
"dependencies": {
"node-gyp-build": "^4.3.0"
@ -88,12 +88,15 @@
}
},
"node_modules/d": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz",
"integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==",
"dependencies": {
"es5-ext": "^0.10.50",
"type": "^1.0.1"
"es5-ext": "^0.10.64",
"type": "^2.7.2"
},
"engines": {
"node": ">=0.12"
}
},
"node_modules/debug": {
@ -164,13 +167,14 @@
}
},
"node_modules/es5-ext": {
"version": "0.10.62",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
"integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
"version": "0.10.64",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
"integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
"hasInstallScript": true,
"dependencies": {
"es6-iterator": "^2.0.3",
"es6-symbol": "^3.1.3",
"esniff": "^2.0.1",
"next-tick": "^1.1.0"
},
"engines": {
@ -188,12 +192,38 @@
}
},
"node_modules/es6-symbol": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz",
"integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==",
"dependencies": {
"d": "^1.0.2",
"ext": "^1.7.0"
},
"engines": {
"node": ">=0.12"
}
},
"node_modules/esniff": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
"integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
"dependencies": {
"d": "^1.0.1",
"ext": "^1.1.2"
"es5-ext": "^0.10.62",
"event-emitter": "^0.3.5",
"type": "^2.7.2"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/event-emitter": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
"integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
"dependencies": {
"d": "1",
"es5-ext": "~0.10.14"
}
},
"node_modules/ext": {
@ -204,11 +234,6 @@
"type": "^2.7.2"
}
},
"node_modules/ext/node_modules/type": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
"integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@ -259,16 +284,28 @@
"node": ">=0.8.19"
}
},
"node_modules/ip": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
"node_modules/ip-address": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
"integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
"dependencies": {
"jsbn": "1.1.0",
"sprintf-js": "^1.1.3"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
},
"node_modules/jsbn": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
},
"node_modules/mime": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
@ -291,9 +328,9 @@
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
},
"node_modules/node-gyp-build": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz",
"integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==",
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
"bin": {
"node-gyp-build": "bin.js",
"node-gyp-build-optional": "optional.js",
@ -344,27 +381,32 @@
}
},
"node_modules/socks": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
"integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz",
"integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==",
"dependencies": {
"ip": "^2.0.0",
"ip-address": "^9.0.5",
"smart-buffer": "^4.2.0"
},
"engines": {
"node": ">= 10.13.0",
"node": ">= 10.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/sprintf-js": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="
},
"node_modules/store2": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/store2/-/store2-2.14.2.tgz",
"integrity": "sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w=="
"version": "2.14.4",
"resolved": "https://registry.npmjs.org/store2/-/store2-2.14.4.tgz",
"integrity": "sha512-srTItn1GOvyvOycgxjAnPA63FZNwy0PTyUBFMHRM+hVFltAeoh0LmNBz9SZqUS9mMqGk8rfyWyXn3GH5ReJ8Zw=="
},
"node_modules/telegram": {
"version": "2.17.10",
"resolved": "https://registry.npmjs.org/telegram/-/telegram-2.17.10.tgz",
"integrity": "sha512-BeWrH6jrTjtmZtGi63mM2UWwciOivqQknhRIysULVRQkDbo+MW4I2jI6d3sPM5m8Ge9mhjeKg1aVZf2qG4vh2Q==",
"version": "2.26.16",
"resolved": "https://registry.npmjs.org/telegram/-/telegram-2.26.16.tgz",
"integrity": "sha512-5tqL9HicCxRqEi+9JjXBteVDnoZ+1ggsBFxLQTO49aXwTI2d7To7xb2vOU/ahLwgKuE+Db9ra5xDJZ76Kk0NEA==",
"dependencies": {
"@cryptography/aes": "^0.1.1",
"async-mutex": "^0.3.0",
@ -395,14 +437,14 @@
}
},
"node_modules/tslib": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
"integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA=="
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
},
"node_modules/type": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz",
"integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="
},
"node_modules/typedarray-to-buffer": {
"version": "3.1.5",
@ -425,13 +467,13 @@
}
},
"node_modules/websocket": {
"version": "1.0.34",
"resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz",
"integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==",
"version": "1.0.35",
"resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz",
"integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==",
"dependencies": {
"bufferutil": "^4.0.1",
"debug": "^2.2.0",
"es5-ext": "^0.10.50",
"es5-ext": "^0.10.63",
"typedarray-to-buffer": "^3.1.5",
"utf-8-validate": "^5.0.2",
"yaeti": "^0.0.6"

View File

@ -1,6 +1,6 @@
{
"name": "node-red-telegram-account",
"version": "1.0.4",
"name": "@random-mirrors/node-red-telegram-account",
"version": "2.0.1",
"description": "",
"main": "index.js",
"scripts": {

View File

@ -1,130 +1,167 @@
<script type="text/javascript">
RED.nodes.registerType('auth', {
category: 'telegram-account',
color: '#a6bbcf',
defaults: {
api_id: { value: "", required: true },
api_hash: { value: "", required: true },
phoneNumber: { value: "", required: true },
password: { value: "" },
},
inputs: 1,
outputs: 1,
icon: "font-awesome/fa-lock",
label: function () {
return this.name || "Telegram Auth";
},
oneditprepare: function () {
// Инициализация значений
$("#node-input-api_id").val(this.api_id || "");
$("#node-input-api_hash").val(this.api_hash || "");
$("#node-input-phoneNumber").val(this.phoneNumber || "");
$("#node-input-password").val(this.password || "");
},
oneditsave: function () {
// Сохранение значений
this.api_id = $("#node-input-api_id").val();
this.api_hash = $("#node-input-api_hash").val();
this.phoneNumber = $("#node-input-phoneNumber").val();
this.password = $("#node-input-password").val();
},
});
RED.nodes.registerType("auth", {
category: "telegram-account",
color: "#a6bbcf",
defaults: {
api_id: { value: "", required: true },
api_hash: { value: "", required: true },
phoneNumber: { value: "", required: true },
password: { value: "" },
},
inputs: 1,
outputs: 1,
icon: "font-awesome/fa-lock",
label: function () {
return this.name || "Telegram Auth";
},
oneditprepare: function () {
// Initializing Values
$("#node-input-api_id").val(this.api_id || "");
$("#node-input-api_hash").val(this.api_hash || "");
$("#node-input-phoneNumber").val(this.phoneNumber || "");
$("#node-input-password").val(this.password || "");
},
oneditsave: function () {
// Saving values
this.api_id = $("#node-input-api_id").val();
this.api_hash = $("#node-input-api_hash").val();
this.phoneNumber = $("#node-input-phoneNumber").val();
this.password = $("#node-input-password").val();
},
});
</script>
<script type="text/html" data-template-name="auth">
<div class="form-row">
<label for="node-input-api_id">
<i class="fa fa-key"></i> API ID
</label>
<input type="text" id="node-input-api_id" placeholder="Enter your API ID">
</div>
<div class="form-row">
<label for="node-input-api_id"> <i class="fa fa-key"></i> API ID </label>
<input type="text" id="node-input-api_id" placeholder="Enter your API ID" />
</div>
<div class="form-row">
<div class="form-row">
<label for="node-input-api_hash">
<i class="fa fa-lock"></i> API Hash
<i class="fa fa-lock"></i> API Hash
</label>
<input type="text" id="node-input-api_hash" placeholder="Enter your API Hash">
</div>
<input
type="text"
id="node-input-api_hash"
placeholder="Enter your API Hash"
/>
</div>
<div class="form-row">
<div class="form-row">
<label for="node-input-phoneNumber">
<i class="fa fa-phone"></i> Phone Number
<i class="fa fa-phone"></i> Phone Number
</label>
<input type="text" id="node-input-phoneNumber" placeholder="Enter your phone number">
</div>
<input
type="text"
id="node-input-phoneNumber"
placeholder="Enter your phone number"
/>
</div>
<div class="form-row">
<div class="form-row">
<label for="node-input-password">
<i class="fa fa-unlock-alt"></i> Password (optional)
<i class="fa fa-unlock-alt"></i> Password (optional)
</label>
<input type="password" id="node-input-password" placeholder="Enter your password (if any)">
</div>
<input
type="password"
id="node-input-password"
placeholder="Enter your password (if any)"
/>
</div>
<p>
<strong>Note:</strong> This configuration is required for connecting to the Telegram API and starting a session.
</p>
<p>
<strong>Note:</strong> This configuration is required for connecting to the
Telegram API and starting a session.
</p>
</script>
<script type="text/html" data-help-name="auth">
<p>The <b>auth</b> node facilitates Telegram API authentication using the <code>telegram</code> library. It allows users to authenticate a session and retrieve a stringSession for future use.</p>
<p>
The <b>auth</b> node facilitates Telegram API authentication using the
<code>telegram</code> library. It allows users to authenticate a session and
retrieve a stringSession for future use.
</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload.api_id
<span class="property-type">number | string</span>
</dt>
<dd>The Telegram API ID. Required for authentication.</dd>
<dt>payload.api_hash
<span class="property-type">string</span>
</dt>
<dd>The Telegram API hash. Required for authentication.</dd>
<dt>payload.phoneNumber
<span class="property-type">string</span>
</dt>
<dd>The phone number associated with the Telegram account.</dd>
<dt>payload.password
<span class="property-type">string</span>
</dt>
<dd>The password for two-factor authentication (if enabled on the Telegram account).</dd>
</dl>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>
payload.api_id
<span class="property-type">number | string</span>
</dt>
<dd>The Telegram API ID. Required for authentication.</dd>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>topic
<span class="property-type">string</span>
</dt>
<dd>
- <b>"auth_success"</b>: Authentication was successful.
- <b>"auth_error"</b>: An error occurred during authentication.
</dd>
<dt>
payload.api_hash
<span class="property-type">string</span>
</dt>
<dd>The Telegram API hash. Required for authentication.</dd>
<dt>payload
<span class="property-type">object</span>
</dt>
<dd>
For <b>"auth_success"</b>:
<ul>
<li><code>payload.stringSession</code>: The generated session string.</li>
<li><code>payload.message</code>: Success message.</li>
</ul>
For <b>"auth_error"</b>:
<ul>
<li><code>payload.error</code>: The error message describing the issue.</li>
</ul>
</dd>
</dl>
<dt>
payload.phoneNumber
<span class="property-type">string</span>
</dt>
<dd>The phone number associated with the Telegram account.</dd>
<h3>Details</h3>
<p>To use the <b>auth</b> node, pass the required API credentials and phone number as part of the input message payload. The node will initiate the Telegram authentication flow. If a two-factor password is required, include it in the <code>payload.password</code>.</p>
<dt>
payload.password
<span class="property-type">string</span>
</dt>
<dd>
The password for two-factor authentication (if enabled on the Telegram
account).
</dd>
</dl>
<p>The node temporarily stores the phone code resolver in the flow context under the key <code>phoneCode</code>. This allows subsequent nodes to resolve the phone code using a separate input mechanism, such as user interaction.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>
topic
<span class="property-type">string</span>
</dt>
<dd>
- <b>"auth_success"</b>: Authentication was successful. -
<b>"auth_error"</b>: An error occurred during authentication.
</dd>
<h3>Example</h3>
<pre>
<dt>
payload
<span class="property-type">object</span>
</dt>
<dd>
For <b>"auth_success"</b>:
<ul>
<li>
<code>payload.stringSession</code>: The generated session string.
</li>
<li><code>payload.message</code>: Success message.</li>
</ul>
For <b>"auth_error"</b>:
<ul>
<li>
<code>payload.error</code>: The error message describing the issue.
</li>
</ul>
</dd>
</dl>
<h3>Details</h3>
<p>
To use the <b>auth</b> node, pass the required API credentials and phone
number as part of the input message payload. The node will initiate the
Telegram authentication flow. If a two-factor password is required, include
it in the <code>payload.password</code>.
</p>
<p>
The node temporarily stores the phone code resolver in the flow context
under the key <code>phoneCode</code>. This allows subsequent nodes to
resolve the phone code using a separate input mechanism, such as user
interaction.
</p>
<h3>Example</h3>
<pre>
{
"payload": {
"api_id": 123456,
@ -133,6 +170,11 @@
"password": "your_password"
}
}
</pre>
<p>This example input payload initiates the authentication process. Upon receiving the phone code, it should be resolved using a dedicated node or interface.</p>
</pre
>
<p>
This example input payload initiates the authentication process. Upon
receiving the phone code, it should be resolved using a dedicated node or
interface.
</p>
</script>

View File

@ -2,59 +2,57 @@ const { TelegramClient } = require("telegram");
const { StringSession } = require("telegram/sessions");
module.exports = function (RED) {
function TelegramClientConfig(config) {
RED.nodes.createNode(this, config);
const node = this;
function TelegramClientConfig(config) {
RED.nodes.createNode(this, config);
const node = this;
this.on("input", async (msg) => {
const api_idString = msg.payload.api_id || config.api_id;
const api_hash = msg.payload.api_hash || config.api_hash;
const phoneNumber = msg.payload.phoneNumber || config.phoneNumber;
const password = msg.payload.password || config.password;
const api_id = parseInt(api_idString);
this.on("input", async (msg) => {
const api_idString = msg.payload.api_id || config.api_id;
const api_hash = msg.payload.api_hash || config.api_hash;
const phoneNumber = msg.payload.phoneNumber || config.phoneNumber;
const password = msg.payload.password || config.password;
const api_id = parseInt(api_idString);
const session = new StringSession("");
const client = new TelegramClient(session, api_id, api_hash, {
connectionRetries: 5,
const session = new StringSession("");
const client = new TelegramClient(session, api_id, api_hash, {
connectionRetries: 5,
});
let resolvePhoneCode;
const context = node.context().flow;
try {
await client.start({
phoneNumber: () => phoneNumber,
password: () => password,
phoneCode: async () => {
return new Promise((resolve) => {
resolvePhoneCode = resolve;
context.set("phoneCode", resolvePhoneCode);
});
let resolvePhoneCode;
const context = node.context().flow;
try {
await client.start({
phoneNumber: () => phoneNumber,
password: () => password,
phoneCode: async () =>{
return new Promise((resolve) => {
resolvePhoneCode = resolve;
context.set("phoneCode", resolvePhoneCode);
})
},
onError: (err) => node.error(`Ошибка: ${err.message}`),
});
const stringSession = client.session.save(); // Сохраняем сессию
node.send({
topic: "auth_success",
payload: {
stringSession,
message: "Авторизация прошла успешно!",
},
});
} catch (error) {
node.error(`Ошибка авторизации: ${error.message}`);
node.send({
topic: "auth_error",
payload: {
error: error.message,
},
});
}
},
onError: (err) => node.error(`Error: ${err.message}`),
});
}
RED.nodes.registerType("auth", TelegramClientConfig);
const stringSession = client.session.save(); // Save the session
node.send({
topic: "auth_success",
payload: {
stringSession,
message: "Authorization was successful!",
},
});
} catch (error) {
node.error(`Authorization error: ${error.message}`);
node.send({
topic: "auth_error",
payload: {
error: error.message,
},
});
}
});
}
RED.nodes.registerType("auth", TelegramClientConfig);
};

View File

@ -1,100 +1,126 @@
<script type="text/javascript">
RED.nodes.registerType('command', {
category: 'telegram-account',
color: '#229ED9',
icon: 'tg.png',
align:"right",
defaults: {
name: { value: '' },
config: { type: 'config', required: false },
command: { value: "", required:true },
regex:{ value:false }
},
inputs: 1,
outputs: 1,
label: function () {
return this.name || 'Command';
},
});
RED.nodes.registerType("command", {
category: "telegram-account",
color: "#229ED9",
icon: "tg.png",
align: "right",
defaults: {
name: { value: "" },
config: { type: "config", required: false },
command: { value: "", required: true },
regex: { value: false },
},
inputs: 1,
outputs: 1,
label: function () {
return this.name || "Command";
},
});
</script>
<script type="text/html" data-template-name="command">
<div class="form-row">
<label for="node-input-name">
<i class="fa fa-tag"></i> Name
</label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
/>
</div>
<div class="form-row">
<label for="node-input-config">
<i class="fa fa-tag"></i> Config
</label>
<input
type="text"
id="node-input-config"
placeholder="Config"
style="width: 60%"
/>
</div>
<div class="form-row">
<label for="node-input-name"> <i class="fa fa-tag"></i> Name </label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
/>
</div>
<div class="form-row">
<label for="node-input-config"> <i class="fa fa-tag"></i> Config </label>
<input
type="text"
id="node-input-config"
placeholder="Config"
style="width: 60%"
/>
</div>
<div class="form-row">
<label for="node-input-command">
<i class="fa fa-tag"></i> Message
</label>
<input type="text" id="node-input-command" placeholder="Message">
</div>
<div class="form-row">
<label for="node-input-command"> <i class="fa fa-tag"></i> Message </label>
<input type="text" id="node-input-command" placeholder="Message" />
</div>
<div class="form-row">
<label for="regex">
<i class="fa fa-cog"></i><span>Regular expression</span>
</label>
<input type="checkbox" style="display: inline; width: 22px;vertical-align: top;" id="node-input-regex">
</div>
</script>
<div class="form-row">
<label for="regex">
<i class="fa fa-cog"></i><span>Regular expression</span>
</label>
<input
type="checkbox"
style="display: inline; width: 22px;vertical-align: top;"
id="node-input-regex"
/>
</div>
</script>
<script type="text/html" data-help-name="command">
<p>The <b>command</b> node listens for specific commands or patterns in Telegram messages and triggers further processing when a match is found. It supports both exact matches and regex-based pattern matching.</p>
<script type="text/html" data-help-name="command">
<p>
The <b>command</b> node listens for specific commands or patterns in
Telegram messages and triggers further processing when a match is found. It
supports both exact matches and regex-based pattern matching.
</p>
<h3>Inputs</h3>
<p>This node does not take any direct inputs. Instead, it listens for incoming messages from the Telegram bot or user.</p>
<h3>Inputs</h3>
<p>
This node does not take any direct inputs. Instead, it listens for incoming
messages from the Telegram bot or user.
</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload.update
<span class="property-type">object</span>
</dt>
<dd>The raw Telegram update object, containing the details of the matched message, including text, sender, chat ID, and other metadata.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>
payload.update
<span class="property-type">object</span>
</dt>
<dd>
The raw Telegram update object, containing the details of the matched
message, including text, sender, chat ID, and other metadata.
</dd>
</dl>
<h3>Configuration</h3>
<dl class="message-properties">
<dt>Command
<span class="property-type">string</span>
</dt>
<dd>The text or pattern to match against incoming messages. For example, <code>/start</code> or <code>/help</code>.</dd>
<h3>Configuration</h3>
<dl class="message-properties">
<dt>
Command
<span class="property-type">string</span>
</dt>
<dd>
The text or pattern to match against incoming messages. For example,
<code>/start</code> or <code>/help</code>.
</dd>
<dt>Regex
<span class="property-type">boolean</span>
</dt>
<dd>If checked, treats the <b>Command</b> field as a regular expression. This allows for advanced pattern matching (e.g., <code>^/command\\s*</code>).</dd>
<dt>
Regex
<span class="property-type">boolean</span>
</dt>
<dd>
If checked, treats the <b>Command</b> field as a regular expression. This
allows for advanced pattern matching (e.g., <code>^/command\\s*</code>).
</dd>
<dt>Telegram Configuration
<span class="property-type">node</span>
</dt>
<dd>A configured Telegram client node to listen for messages. Ensure the client has the necessary permissions and is correctly authenticated.</dd>
</dl>
<dt>
Telegram Configuration
<span class="property-type">node</span>
</dt>
<dd>
A configured Telegram client node to listen for messages. Ensure the
client has the necessary permissions and is correctly authenticated.
</dd>
</dl>
<h3>Details</h3>
<p>The <b>command</b> node integrates with the Telegram API to monitor incoming messages in real time. When a message matches the configured command or regex, the node outputs the Telegram update object for further processing. This is particularly useful for creating bots that respond to specific commands.</p>
<h3>Details</h3>
<p>
The <b>command</b> node integrates with the Telegram API to monitor incoming
messages in real time. When a message matches the configured command or
regex, the node outputs the Telegram update object for further processing.
This is particularly useful for creating bots that respond to specific
commands.
</p>
<h3>Example</h3>
<pre>
<h3>Example</h3>
<pre>
{
"payload": {
"update": {
@ -112,24 +138,49 @@
}
}
}
</pre>
<p>This example output is triggered when the bot receives the <code>/start</code> command. The <code>update</code> object contains metadata about the message, sender, and chat.</p>
</pre
>
<p>
This example output is triggered when the bot receives the
<code>/start</code> command. The <code>update</code> object contains
metadata about the message, sender, and chat.
</p>
<h3>Error Handling</h3>
<p>If the Telegram client fails to authenticate or there is an error in the configuration, the node logs an error message and stops functioning.</p>
<h3>Error Handling</h3>
<p>
If the Telegram client fails to authenticate or there is an error in the
configuration, the node logs an error message and stops functioning.
</p>
<h3>Advanced Usage</h3>
<p>By enabling the <b>Regex</b> option, you can create dynamic commands or match patterns such as:</p>
<ul>
<li><code>^/command\\s*</code>: Matches the command followed by optional whitespace.</li>
<li><code>^/start|/help$</code>: Matches either <code>/start</code> or <code>/help</code>.</li>
</ul>
<p>This makes the <b>command</b> node versatile for building complex Telegram bot functionality.</p>
<h3>Advanced Usage</h3>
<p>
By enabling the <b>Regex</b> option, you can create dynamic commands or
match patterns such as:
</p>
<ul>
<li>
<code>^/command\\s*</code>: Matches the command followed by optional
whitespace.
</li>
<li>
<code>^/start|/help$</code>: Matches either <code>/start</code> or
<code>/help</code>.
</li>
</ul>
<p>
This makes the <b>command</b> node versatile for building complex Telegram
bot functionality.
</p>
<h3>Notes</h3>
<ul>
<li>Ensure the Telegram bot has sufficient permissions to receive messages in the configured chat or channel.</li>
<li>Regular expressions should be carefully tested to avoid unintended matches or errors.</li>
</ul>
<h3>Notes</h3>
<ul>
<li>
Ensure the Telegram bot has sufficient permissions to receive messages in
the configured chat or channel.
</li>
<li>
Regular expressions should be carefully tested to avoid unintended matches
or errors.
</li>
</ul>
</script>

View File

@ -1,53 +1,44 @@
const { NewMessage } = require("telegram/events");
module.exports = function (RED) {
function Command(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
/** @type {TelegramClient} */
const client = this.config.client;
const client = this.config.client;
try {
client.addEventHandler((update) => {
const message = update.message.message
const message = update.message.message;
if (message) {
if (config.regex) {
const regex = new RegExp(config.command);
if (config.regex) {
const regex = new RegExp(config.command);
if (regex.test(message)) {
var msg = {
payload: {
update
}
};
if (regex.test(message)) {
var msg = {
payload: {
update,
},
};
node.send(msg);
}
} else if (message === config.command) {
var msg = {
payload: {
update
}
};
node.send(msg);
node.send(msg);
}
} else if (message === config.command) {
var msg = {
payload: {
update,
},
};
node.send(msg);
}
}
}, new NewMessage());
} catch (err) {
node.error('Ошибка авторизации: ' + err.message);
node.error("Authorization error: " + err.message);
}
}
RED.nodes.registerType('command', Command);
RED.nodes.registerType("command", Command);
};

View File

@ -1,131 +1,299 @@
<script type="text/javascript">
RED.nodes.registerType('config',{
category: 'config',
defaults: {
name: { value: '' },
api_id: {value:"", required:true},
api_hash: {value:"", required:true},
session: {value:"", required:true},
useIPV6: {value: undefined},
timeout: {value: undefined},
requestRetries: {value: undefined},
connectionRetries: {value: undefined},
proxy: {value: undefined},
downloadRetries: {value: undefined},
retryDelay: {value: undefined},
autoReconnect: {value: undefined},
sequentialUpdates: {value: undefined},
floodSleepThreshold: {value: undefined},
deviceModel: {value: undefined},
systemVersion: {value: undefined},
appVersion: {value: undefined},
langCode: {value: undefined},
systemLangCode: {value: undefined},
useWSS: {value: undefined},
maxConcurrentDownloads: {value: undefined},
securityChecks: {value: undefined},
testServers: {value: undefined}
},
label: function() {
return this.name || "Telegram Client Config";
},
oneditprepare: function() {
}
RED.nodes.registerType("config", {
category: "config",
defaults: {
name: { value: "" },
api_id: { value: "", required: true },
api_hash: { value: "", required: true },
session: { value: "", required: true },
useIPV6: { value: undefined },
timeout: { value: undefined },
requestRetries: { value: undefined },
connectionRetries: { value: undefined },
proxy: { value: undefined },
downloadRetries: { value: undefined },
retryDelay: { value: undefined },
autoReconnect: { value: undefined },
sequentialUpdates: { value: undefined },
floodSleepThreshold: { value: undefined },
deviceModel: { value: undefined },
systemVersion: { value: undefined },
appVersion: { value: undefined },
langCode: { value: undefined },
systemLangCode: { value: undefined },
useWSS: { value: undefined },
maxConcurrentDownloads: { value: undefined },
securityChecks: { value: undefined },
testServers: { value: undefined },
},
label: function () {
return this.name || "Telegram Client Config";
},
oneditprepare: function () {},
});
</script>
<script type="text/html" data-template-name="config">
<div class="form-row">
<label for="node-config-input-name"><i class="icon-tasks"></i>Name</label>
<input type="text" id="node-config-input-name" placeholder="Name" style="width:70%" ng-model="node.api_id">
<label for="node-config-input-name"><i class="icon-tasks"></i>Name</label>
<input
type="text"
id="node-config-input-name"
placeholder="Name"
style="width:70%"
ng-model="node.api_id"
/>
</div>
<div class="form-row">
<label for="node-config-input-api_id"><i class="icon-tasks"></i> API ID</label>
<input type="text" id="node-config-input-api_id" placeholder="Enter API ID" style="width:70%" ng-model="node.api_id">
<label for="node-config-input-api_id"
><i class="icon-tasks"></i> API ID</label
>
<input
type="text"
id="node-config-input-api_id"
placeholder="Enter API ID"
style="width:70%"
ng-model="node.api_id"
/>
</div>
<div class="form-row">
<label for="node-config-input-api_hash"><i class="icon-tasks"></i> API Hash</label>
<input type="text" id="node-config-input-api_hash" placeholder="Enter API Hash" style="width:70%" ng-model="node.api_hash">
<label for="node-config-input-api_hash"
><i class="icon-tasks"></i> API Hash</label
>
<input
type="text"
id="node-config-input-api_hash"
placeholder="Enter API Hash"
style="width:70%"
ng-model="node.api_hash"
/>
</div>
<div class="form-row">
<label for="node-config-input-session"><i class="icon-tasks"></i> Session</label>
<input type="text" id="node-config-input-session" placeholder="Enter Session" style="width:70%" ng-model="node.session">
<label for="node-config-input-session"
><i class="icon-tasks"></i> Session</label
>
<input
type="text"
id="node-config-input-session"
placeholder="Enter Session"
style="width:70%"
ng-model="node.session"
/>
</div>
<div class="form-row">
<label for="node-config-input-useIPV6"><i class="icon-tasks"></i> Use IPV6</label>
<input type="checkbox" id="node-config-input-useIPV6" ng-model="node.useIPV6">
<label for="node-config-input-useIPV6"
><i class="icon-tasks"></i> Use IPV6</label
>
<input
type="checkbox"
id="node-config-input-useIPV6"
ng-model="node.useIPV6"
/>
</div>
<div class="form-row">
<label for="node-config-input-timeout"><i class="icon-tasks"></i> Timeout</label>
<input type="number" id="node-config-input-timeout" placeholder="Enter Timeout" style="width:70%" ng-model="node.timeout">
<label for="node-config-input-timeout"
><i class="icon-tasks"></i> Timeout</label
>
<input
type="number"
id="node-config-input-timeout"
placeholder="Enter Timeout"
style="width:70%"
ng-model="node.timeout"
/>
</div>
<div class="form-row">
<label for="node-config-input-requestRetries"><i class="icon-tasks"></i> Request Retries</label>
<input type="number" id="node-config-input-requestRetries" placeholder="Enter Request Retries" style="width:70%" ng-model="node.requestRetries">
<label for="node-config-input-requestRetries"
><i class="icon-tasks"></i> Request Retries</label
>
<input
type="number"
id="node-config-input-requestRetries"
placeholder="Enter Request Retries"
style="width:70%"
ng-model="node.requestRetries"
/>
</div>
<div class="form-row">
<label for="node-config-input-connectionRetries"><i class="icon-tasks"></i> Connection Retries</label>
<input type="number" id="node-config-input-connectionRetries" placeholder="Enter Connection Retries" style="width:70%" ng-model="node.connectionRetries">
<label for="node-config-input-connectionRetries"
><i class="icon-tasks"></i> Connection Retries</label
>
<input
type="number"
id="node-config-input-connectionRetries"
placeholder="Enter Connection Retries"
style="width:70%"
ng-model="node.connectionRetries"
/>
</div>
<div class="form-row">
<label for="node-config-input-proxy"><i class="icon-tasks"></i> Proxy</label>
<input type="text" id="node-config-input-proxy" placeholder="Enter Proxy" style="width:70%" ng-model="node.proxy">
<label for="node-config-input-proxy"
><i class="icon-tasks"></i> Proxy</label
>
<input
type="text"
id="node-config-input-proxy"
placeholder="Enter Proxy"
style="width:70%"
ng-model="node.proxy"
/>
</div>
<div class="form-row">
<label for="node-config-input-downloadRetries"><i class="icon-tasks"></i> Download Retries</label>
<input type="number" id="node-config-input-downloadRetries" placeholder="Enter Download Retries" style="width:70%" ng-model="node.downloadRetries">
<label for="node-config-input-downloadRetries"
><i class="icon-tasks"></i> Download Retries</label
>
<input
type="number"
id="node-config-input-downloadRetries"
placeholder="Enter Download Retries"
style="width:70%"
ng-model="node.downloadRetries"
/>
</div>
<div class="form-row">
<label for="node-config-input-retryDelay"><i class="icon-tasks"></i> Retry Delay</label>
<input type="number" id="node-config-input-retryDelay" placeholder="Enter Retry Delay" style="width:70%" ng-model="node.retryDelay">
<label for="node-config-input-retryDelay"
><i class="icon-tasks"></i> Retry Delay</label
>
<input
type="number"
id="node-config-input-retryDelay"
placeholder="Enter Retry Delay"
style="width:70%"
ng-model="node.retryDelay"
/>
</div>
<div class="form-row">
<label for="node-config-input-autoReconnect"><i class="icon-tasks"></i> Auto Reconnect</label>
<input type="checkbox" id="node-config-input-autoReconnect" ng-model="node.autoReconnect">
<label for="node-config-input-autoReconnect"
><i class="icon-tasks"></i> Auto Reconnect</label
>
<input
type="checkbox"
id="node-config-input-autoReconnect"
ng-model="node.autoReconnect"
/>
</div>
<div class="form-row">
<label for="node-config-input-sequentialUpdates"><i class="icon-tasks"></i> Sequential Updates</label>
<input type="checkbox" id="node-config-input-sequentialUpdates" ng-model="node.sequentialUpdates">
<label for="node-config-input-sequentialUpdates"
><i class="icon-tasks"></i> Sequential Updates</label
>
<input
type="checkbox"
id="node-config-input-sequentialUpdates"
ng-model="node.sequentialUpdates"
/>
</div>
<div class="form-row">
<label for="node-config-input-floodSleepThreshold"><i class="icon-tasks"></i> Flood Sleep Threshold</label>
<input type="number" id="node-config-input-floodSleepThreshold" placeholder="Enter Flood Sleep Threshold" style="width:70%" ng-model="node.floodSleepThreshold">
<label for="node-config-input-floodSleepThreshold"
><i class="icon-tasks"></i> Flood Sleep Threshold</label
>
<input
type="number"
id="node-config-input-floodSleepThreshold"
placeholder="Enter Flood Sleep Threshold"
style="width:70%"
ng-model="node.floodSleepThreshold"
/>
</div>
<div class="form-row">
<label for="node-config-input-deviceModel"><i class="icon-tasks"></i> Device Model</label>
<input type="text" id="node-config-input-deviceModel" placeholder="Enter Device Model" style="width:70%" ng-model="node.deviceModel">
<label for="node-config-input-deviceModel"
><i class="icon-tasks"></i> Device Model</label
>
<input
type="text"
id="node-config-input-deviceModel"
placeholder="Enter Device Model"
style="width:70%"
ng-model="node.deviceModel"
/>
</div>
<div class="form-row">
<label for="node-config-input-systemVersion"><i class="icon-tasks"></i> System Version</label>
<input type="text" id="node-config-input-systemVersion" placeholder="Enter System Version" style="width:70%" ng-model="node.systemVersion">
<label for="node-config-input-systemVersion"
><i class="icon-tasks"></i> System Version</label
>
<input
type="text"
id="node-config-input-systemVersion"
placeholder="Enter System Version"
style="width:70%"
ng-model="node.systemVersion"
/>
</div>
<div class="form-row">
<label for="node-config-input-appVersion"><i class="icon-tasks"></i> App Version</label>
<input type="text" id="node-config-input-appVersion" placeholder="Enter App Version" style="width:70%" ng-model="node.appVersion">
<label for="node-config-input-appVersion"
><i class="icon-tasks"></i> App Version</label
>
<input
type="text"
id="node-config-input-appVersion"
placeholder="Enter App Version"
style="width:70%"
ng-model="node.appVersion"
/>
</div>
<div class="form-row">
<label for="node-config-input-langCode"><i class="icon-tasks"></i> Language Code</label>
<input type="text" id="node-config-input-langCode" placeholder="Enter Language Code" style="width:70%" ng-model="node.langCode">
<label for="node-config-input-langCode"
><i class="icon-tasks"></i> Language Code</label
>
<input
type="text"
id="node-config-input-langCode"
placeholder="Enter Language Code"
style="width:70%"
ng-model="node.langCode"
/>
</div>
<div class="form-row">
<label for="node-config-input-systemLangCode"><i class="icon-tasks"></i> System Language Code</label>
<input type="text" id="node-config-input-systemLangCode" placeholder="Enter System Language Code" style="width:70%" ng-model="node.systemLangCode">
<label for="node-config-input-systemLangCode"
><i class="icon-tasks"></i> System Language Code</label
>
<input
type="text"
id="node-config-input-systemLangCode"
placeholder="Enter System Language Code"
style="width:70%"
ng-model="node.systemLangCode"
/>
</div>
<div class="form-row">
<label for="node-config-input-useWSS"><i class="icon-tasks"></i> Use WSS</label>
<input type="checkbox" id="node-config-input-useWSS" ng-model="node.useWSS">
<label for="node-config-input-useWSS"
><i class="icon-tasks"></i> Use WSS</label
>
<input
type="checkbox"
id="node-config-input-useWSS"
ng-model="node.useWSS"
/>
</div>
<div class="form-row">
<label for="node-config-input-maxConcurrentDownloads"><i class="icon-tasks"></i> Max Concurrent Downloads</label>
<input type="number" id="node-config-input-maxConcurrentDownloads" placeholder="Enter Max Concurrent Downloads" style="width:70%" ng-model="node.maxConcurrentDownloads">
<label for="node-config-input-maxConcurrentDownloads"
><i class="icon-tasks"></i> Max Concurrent Downloads</label
>
<input
type="number"
id="node-config-input-maxConcurrentDownloads"
placeholder="Enter Max Concurrent Downloads"
style="width:70%"
ng-model="node.maxConcurrentDownloads"
/>
</div>
<div class="form-row">
<label for="node-config-input-securityChecks"><i class="icon-tasks"></i> Security Checks</label>
<input type="checkbox" id="node-config-input-securityChecks" ng-model="node.securityChecks">
<label for="node-config-input-securityChecks"
><i class="icon-tasks"></i> Security Checks</label
>
<input
type="checkbox"
id="node-config-input-securityChecks"
ng-model="node.securityChecks"
/>
</div>
<div class="form-row">
<label for="node-config-input-testServers"><i class="icon-tasks"></i> Test Servers</label>
<input type="checkbox" id="node-config-input-testServers" ng-model="node.testServers">
<label for="node-config-input-testServers"
><i class="icon-tasks"></i> Test Servers</label
>
<input
type="checkbox"
id="node-config-input-testServers"
ng-model="node.testServers"
/>
</div>
</script>

View File

@ -2,57 +2,58 @@ const { TelegramClient } = require("telegram");
const { StringSession } = require("telegram/sessions");
module.exports = function (RED) {
function TelegramClientConfig(config) {
RED.nodes.createNode(this, config);
this.apiId = config.api_id;
this.apiHash = config.api_hash;
this.session = new StringSession(config.session);
this.useIPV6 = config.useIPV6;
this.timeout = config.timeout;
this.requestRetries = config.requestRetries;
this.connectionRetries = config.connectionRetries;
this.proxy = config.proxy;
this.downloadRetries = config.downloadRetries;
this.retryDelay = config.retryDelay;
this.autoReconnect = config.autoReconnect;
this.sequentialUpdates = config.sequentialUpdates;
this.floodSleepThreshold = config.floodSleepThreshold;
this.deviceModel = config.deviceModel;
this.systemVersion = config.systemVersion;
this.appVersion = config.appVersion;
this.langCode = config.langCode;
this.systemLangCode = config.systemLangCode;
this.useWSS = config.useWSS;
this.maxConcurrentDownloads = config.maxConcurrentDownloads;
this.securityChecks = config.securityChecks;
this.testServers = config.testServers;
const node = this;
function TelegramClientConfig(config) {
RED.nodes.createNode(this, config);
this.apiId = config.api_id;
this.apiHash = config.api_hash;
this.session = new StringSession(config.session);
this.useIPV6 = config.useIPV6;
this.timeout = config.timeout;
this.requestRetries = config.requestRetries;
this.connectionRetries = config.connectionRetries;
this.proxy = config.proxy;
this.downloadRetries = config.downloadRetries;
this.retryDelay = config.retryDelay;
this.autoReconnect = config.autoReconnect;
this.sequentialUpdates = config.sequentialUpdates;
this.floodSleepThreshold = config.floodSleepThreshold;
this.deviceModel = config.deviceModel;
this.systemVersion = config.systemVersion;
this.appVersion = config.appVersion;
this.langCode = config.langCode;
this.systemLangCode = config.systemLangCode;
this.useWSS = config.useWSS;
this.maxConcurrentDownloads = config.maxConcurrentDownloads;
this.securityChecks = config.securityChecks;
this.testServers = config.testServers;
const node = this;
this.client = new TelegramClient(
this.session,
parseInt(this.apiId),
this.apiHash,
{}
);
this.client = new TelegramClient(this.session, parseInt(this.apiId), this.apiHash, {
});
try {
this.client.connect().then(async () => {
let isAuthorized = await this.client.isUserAuthorized();
if (!isAuthorized) {
node.error(`Session is invalid`);
} else {
node.status({ fill: "green", shape: "dot", text: "Connected" });
}
});
} catch (err) {
node.error('Authorisation error: ' + err.message);
try {
this.client.connect().then(async () => {
let isAuthorized = await this.client.isUserAuthorized();
if (!isAuthorized) {
node.error(`Session is invalid`);
} else {
node.status({ fill: "green", shape: "dot", text: "Connected" });
}
this.on("close", () => {
if (this.client) {
this.client.disconnect();
node.status({ fill: "red", shape: "ring", text: "Disconnected" });
}
});
});
} catch (err) {
node.error("Authorization error: " + err.message);
}
RED.nodes.registerType('config', TelegramClientConfig);
this.on("close", () => {
if (this.client) {
this.client.disconnect();
node.status({ fill: "red", shape: "ring", text: "Disconnected" });
}
});
}
RED.nodes.registerType("config", TelegramClientConfig);
};

View File

@ -1,86 +1,101 @@
<script type="text/javascript">
RED.nodes.registerType('delete-message', {
category: 'telegram-account',
color: '#FF5733',
icon: 'tg.png',
align: 'right',
defaults: {
name: { value: '' },
config: { type: 'config', required: false },
},
inputs: 1,
outputs: 1,
label: function () {
return this.name || 'Delete Message';
},
});
RED.nodes.registerType("delete-message", {
category: "telegram-account",
color: "#FF5733",
icon: "tg.png",
align: "right",
defaults: {
name: { value: "" },
config: { type: "config", required: false },
},
inputs: 1,
outputs: 1,
label: function () {
return this.name || "Delete Message";
},
});
</script>
<script type="text/html" data-template-name="delete-message">
<div class="form-row">
<label for="node-input-name">
<i class="fa fa-tag"></i> Name
</label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
/>
</div>
<div class="form-row">
<label for="node-input-config">
<i class="fa fa-gear"></i> Config
</label>
<input
type="hidden"
id="node-input-config"
placeholder="Config"
style="width: 60%"
/>
</div>
</script>
<div class="form-row">
<label for="node-input-name"> <i class="fa fa-tag"></i> Name </label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
/>
</div>
<div class="form-row">
<label for="node-input-config"> <i class="fa fa-gear"></i> Config </label>
<input
type="hidden"
id="node-input-config"
placeholder="Config"
style="width: 60%"
/>
</div>
</script>
<script type="text/html" data-help-name="delete-message">
<p>
The <b>delete-message</b> node allows you to delete messages from a Telegram
chat. It supports deleting multiple messages at once and provides an option
to revoke messages for all chat participants.
</p>
<script type="text/html" data-help-name="delete-message">
<p>The <b>delete-message</b> node allows you to delete messages from a Telegram chat. It supports deleting multiple messages at once and provides an option to revoke messages for all chat participants.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>
payload.chatId
<span class="property-type">string</span>
</dt>
<dd>
The ID or username of the chat from which the messages will be deleted.
Use "me" for personal chats.
</dd>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload.chatId
<span class="property-type">string</span>
</dt>
<dd>The ID or username of the chat from which the messages will be deleted. Use "me" for personal chats.</dd>
<dt>payload.messageIds
<span class="property-type">number | array</span>
</dt>
<dd>The ID or an array of IDs of the messages to be deleted.</dd>
<dt>payload.revoke
<span class="property-type">boolean</span>
</dt>
<dd>If true, the messages will be deleted for all participants in the chat (revoke). Defaults to true.</dd>
<dt>payload.client
<span class="property-type">object</span>
</dt>
<dd>An optional Telegram client instance if not configured globally.</dd>
</dl>
<dt>
payload.messageIds
<span class="property-type">number | array</span>
</dt>
<dd>The ID or an array of IDs of the messages to be deleted.</dd>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">object</span>
</dt>
<dd>The response from the Telegram API, confirming the deletion.</dd>
</dl>
<dt>
payload.revoke
<span class="property-type">boolean</span>
</dt>
<dd>
If true, the messages will be deleted for all participants in the chat
(revoke). Defaults to true.
</dd>
<h3>Details</h3>
<p>The <b>delete-message</b> node uses the Telegram API to delete messages from a specified chat. It can delete a single message or multiple messages at once. If the <code>revoke</code> parameter is set to true, the messages will be removed for all participants, not just the sender.</p>
<dt>
payload.client
<span class="property-type">object</span>
</dt>
<dd>An optional Telegram client instance if not configured globally.</dd>
</dl>
<h3>Example</h3>
<pre>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>
payload
<span class="property-type">object</span>
</dt>
<dd>The response from the Telegram API, confirming the deletion.</dd>
</dl>
<h3>Details</h3>
<p>
The <b>delete-message</b> node uses the Telegram API to delete messages from
a specified chat. It can delete a single message or multiple messages at
once. If the <code>revoke</code> parameter is set to true, the messages will
be removed for all participants, not just the sender.
</p>
<h3>Example</h3>
<pre>
{
"payload": {
"chatId": "@example_user",
@ -88,9 +103,17 @@
"revoke": true
}
}
</pre>
<p>This input deletes the messages with IDs 12345 and 12346 from the chat with the user <code>@example_user</code>, revoking them for all participants.</p>
</pre
>
<p>
This input deletes the messages with IDs 12345 and 12346 from the chat with
the user <code>@example_user</code>, revoking them for all participants.
</p>
<h3>Configuration</h3>
<p>The node can use a globally configured Telegram client or a client instance provided in the message payload. Ensure that the client has the necessary permissions to delete messages from the specified chat.</p>
<h3>Configuration</h3>
<p>
The node can use a globally configured Telegram client or a client instance
provided in the message payload. Ensure that the client has the necessary
permissions to delete messages from the specified chat.
</p>
</script>

View File

@ -1,27 +1,33 @@
module.exports = function (RED) {
function DeleteMessage(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
function DeleteMessage(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
this.on('input', async function (msg) {
const chatId = msg.payload.chatId || config.chatId;
const messageIds = msg.payload.messageIds || config.messageIds;
const revoke = msg.payload.revoke || config.revoke || { revoke: true };
/** @type {TelegramClient} */
const client = msg.payload?.client ? msg.payload.client : this.config.client;
this.on("input", async function (msg) {
const chatId = msg.payload.chatId || config.chatId;
const messageIds = msg.payload.messageIds || config.messageIds;
const revoke = msg.payload.revoke || config.revoke || { revoke: true };
/** @type {TelegramClient} */
const client = msg.payload?.client
? msg.payload.client
: this.config.client;
try {
const response = await client.deleteMessages(chatId, messageIds, revoke);
try {
const response = await client.deleteMessages(
chatId,
messageIds,
revoke
);
node.send({
payload: response,
});
} catch (err) {
node.error('Error deleting message: ' + err.message);
}
node.send({
payload: response,
});
}
} catch (err) {
node.error("Error deleting message: " + err.message);
}
});
}
RED.nodes.registerType('delete-message', DeleteMessage);
RED.nodes.registerType("delete-message", DeleteMessage);
};

View File

@ -1,87 +1,113 @@
<script type="text/javascript">
RED.nodes.registerType('get-entity', {
category: 'telegram-account',
color: '#229ED9',
icon: 'tg.png',
align: "right",
defaults: {
name: { value: '' },
config: { type: 'config', required: false },
},
inputs: 1,
outputs: 1,
label: function () {
return this.name || 'Get Entity';
}
});
RED.nodes.registerType("get-entity", {
category: "telegram-account",
color: "#229ED9",
icon: "tg.png",
align: "right",
defaults: {
name: { value: "" },
config: { type: "config", required: false },
},
inputs: 1,
outputs: 1,
label: function () {
return this.name || "Get Entity";
},
});
</script>
<script type="text/html" data-template-name="get-entity">
<div class="form-row">
<label for="node-input-name">
<i class="fa fa-tag"></i> Name
</label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
/>
</div>
<div class="form-row">
<label for="node-input-config">
<i class="fa fa-tag"></i> Config
</label>
<input
type="text"
id="node-input-config"
placeholder="Config"
style="width: 60%"
/>
</div>
<div class="form-row">
<label for="node-input-name"> <i class="fa fa-tag"></i> Name </label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
/>
</div>
<div class="form-row">
<label for="node-input-config"> <i class="fa fa-tag"></i> Config </label>
<input
type="text"
id="node-input-config"
placeholder="Config"
style="width: 60%"
/>
</div>
</script>
<script type="text/html" data-help-name="get-entity">
<p>The <b>get-entity</b> node retrieves information about a Telegram entity (e.g., user, chat, or channel) using its identifier or URL.</p>
<p>
The <b>get-entity</b> node retrieves information about a Telegram entity
(e.g., user, chat, or channel) using its identifier or URL.
</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload.input
<span class="property-type">string</span>
</dt>
<dd>The identifier (username, user ID, chat ID, or channel ID) or URL of the Telegram entity to retrieve. For example, "123456789", "@example_user", or "https://t.me/example_channel".</dd>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>
payload.input
<span class="property-type">string</span>
</dt>
<dd>
The identifier (username, user ID, chat ID, or channel ID) or URL of the
Telegram entity to retrieve. For example, "123456789", "@example_user", or
"https://t.me/example_channel".
</dd>
<dt>payload.client
<span class="property-type">object</span>
</dt>
<dd>An optional Telegram client instance if not configured globally.</dd>
</dl>
<dt>
payload.client
<span class="property-type">object</span>
</dt>
<dd>An optional Telegram client instance if not configured globally.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload.input
<span class="property-type">object</span>
</dt>
<dd>The retrieved entity object containing details about the user, chat, or channel.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>
payload.input
<span class="property-type">object</span>
</dt>
<dd>
The retrieved entity object containing details about the user, chat, or
channel.
</dd>
</dl>
<h3>Details</h3>
<p>The <b>get-entity</b> node uses the Telegram API to fetch details about a specified entity. If the input is a Telegram URL (e.g., "https://t.me/example_user"), the node extracts the username or channel name and fetches the corresponding entity. For non-URL inputs, it directly retrieves the entity using the provided identifier.</p>
<h3>Details</h3>
<p>
The <b>get-entity</b> node uses the Telegram API to fetch details about a
specified entity. If the input is a Telegram URL (e.g.,
"https://t.me/example_user"), the node extracts the username or channel name
and fetches the corresponding entity. For non-URL inputs, it directly
retrieves the entity using the provided identifier.
</p>
<h3>Example</h3>
<pre>
<h3>Example</h3>
<pre>
{
"payload": {
"input": "https://t.me/example_user"
}
}
</pre>
<p>This input retrieves the entity information for the Telegram user <code>example_user</code>.</p>
</pre
>
<p>
This input retrieves the entity information for the Telegram user
<code>example_user</code>.
</p>
<h3>Error Handling</h3>
<p>If an error occurs while retrieving the entity (e.g., the identifier is invalid or the entity does not exist), the node logs an error and sends a message with <code>payload.input</code> set to <code>null</code>.</p>
<h3>Error Handling</h3>
<p>
If an error occurs while retrieving the entity (e.g., the identifier is
invalid or the entity does not exist), the node logs an error and sends a
message with <code>payload.input</code> set to <code>null</code>.
</p>
<h3>Configuration</h3>
<p>The node can use a globally configured Telegram client or a client instance provided in the message payload. Ensure the client has the necessary permissions to access the requested entity.</p>
<h3>Configuration</h3>
<p>
The node can use a globally configured Telegram client or a client instance
provided in the message payload. Ensure the client has the necessary
permissions to access the requested entity.
</p>
</script>

View File

@ -1,36 +1,38 @@
module.exports = function (RED) {
function GetEntity(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
function GetEntity(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
this.on('input', async function (msg) {
const input = msg.payload.input || config.input;
/** @type {TelegramClient} */
const client = msg.payload?.client ? msg.payload.client : this.config.client;
this.on("input", async function (msg) {
const input = msg.payload.input || config.input;
/** @type {TelegramClient} */
const client = msg.payload?.client
? msg.payload.client
: this.config.client;
try {
let entity;
try {
let entity;
// Check if the input is a URL
if (input.includes('https://t.me/')) {
const username = input.split('/').pop();
entity = await client.getEntity(username);
} else {
entity = await client.getEntity(input);
}
// Check if the input is a URL
if (input.includes("https://t.me/")) {
const username = input.split("/").pop();
entity = await client.getEntity(username);
} else {
entity = await client.getEntity(input);
}
node.send({
payload: {input:entity},
});
} catch (err) {
node.error('Error getting entity: ' + err.message);
node.send({
payload:{ input: null}
})
}
node.send({
payload: { input: entity },
});
}
} catch (err) {
node.error("Error getting entity: " + err.message);
node.send({
payload: { input: null },
});
}
});
}
RED.nodes.registerType('get-entity', GetEntity);
RED.nodes.registerType("get-entity", GetEntity);
};

View File

@ -1,198 +1,224 @@
<script type="text/javascript">
RED.nodes.registerType('iter-dialogs', {
category: 'telegram',
color: '#32a3e0',
defaults: {
name: { value: '' },
config: { type: 'config', required: true },
limit: { value: "" },
offsetDate: { value: ""},
offsetId: { value: "" },
ignorePinned: { value: false },
ignoreMigrated: { value: false },
folder: { value: 0 },
archived: { value: "" },
},
inputs: 1,
outputs: 1,
paletteLabel: 'Iterate Dialogs',
label: function () {
return this.name || 'Iterate Dialogs';
},
});
RED.nodes.registerType("iter-dialogs", {
category: "telegram",
color: "#32a3e0",
defaults: {
name: { value: "" },
config: { type: "config", required: true },
limit: { value: "" },
offsetDate: { value: "" },
offsetId: { value: "" },
ignorePinned: { value: false },
ignoreMigrated: { value: false },
folder: { value: 0 },
archived: { value: "" },
},
inputs: 1,
outputs: 1,
paletteLabel: "Iterate Dialogs",
label: function () {
return this.name || "Iterate Dialogs";
},
});
</script>
<script type="text/html" data-template-name="iter-dialogs">
<div class="form-row">
<label for="node-input-name">
<i class="fa fa-tag"></i> Name
</label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
ng-model="name"
/>
</div>
<div class="form-row">
<label for="node-input-config">
<i class="fa fa-tag"></i> Config
</label>
<input
type="text"
id="node-input-config"
placeholder="Config"
style="width: 60%"
ng-model="config"
/>
</div>
<div class="form-row">
<label for="node-input-limit">
<i class="fa fa-tag"></i> Limit
</label>
<input
type="text"
id="node-input-limit"
placeholder="Limit"
style="width: 60%"
ng-model="limit"
/>
</div>
<div class="form-row">
<label for="node-input-offsetDate">
<i class="fa fa-tag"></i> Offset Date
</label>
<input
type="datetime-local"
id="node-input-offsetDate"
placeholder="Offset Date"
style="width: 60%"
ng-model="offsetDate"
/>
</div>
<div class="form-row">
<label for="node-input-offsetId">
<i class="fa fa-tag"></i> Offset ID
</label>
<input
type="text"
id="node-input-offsetId"
placeholder="Offset ID"
style="width: 60%"
ng-model="offsetId"
/>
</div>
<div class="form-row">
<label for="node-input-ignorePinned">
<i class="fa fa-tag"></i> Ignore Pinned
</label>
<input
type="checkbox"
id="node-input-ignorePinned"
ng-model="ignorePinned"
/>
</div>
<div class="form-row">
<label for="node-input-ignoreMigrated">
<i class="fa fa-tag"></i> Ignore Migrated
</label>
<input
type="checkbox"
id="node-input-ignoreMigrated"
ng-model="ignoreMigrated"
/>
</div>
<div class="form-row">
<label for="node-input-folder">
<i class="fa fa-tag"></i> Folder
</label>
<input
type="text"
id="node-input-folder"
placeholder="Folder"
style="width: 60%"
ng-model="folder"
/>
</div>
<div class="form-row">
<label for="node-input-archived">
<i class="fa fa-tag"></i> Archived
</label>
<input
type="checkbox"
id="node-input-archived"
ng-model="archived"
/>
</div>
<div class="form-row">
<label for="node-input-name"> <i class="fa fa-tag"></i> Name </label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
ng-model="name"
/>
</div>
<div class="form-row">
<label for="node-input-config"> <i class="fa fa-tag"></i> Config </label>
<input
type="text"
id="node-input-config"
placeholder="Config"
style="width: 60%"
ng-model="config"
/>
</div>
<div class="form-row">
<label for="node-input-limit"> <i class="fa fa-tag"></i> Limit </label>
<input
type="text"
id="node-input-limit"
placeholder="Limit"
style="width: 60%"
ng-model="limit"
/>
</div>
<div class="form-row">
<label for="node-input-offsetDate">
<i class="fa fa-tag"></i> Offset Date
</label>
<input
type="datetime-local"
id="node-input-offsetDate"
placeholder="Offset Date"
style="width: 60%"
ng-model="offsetDate"
/>
</div>
<div class="form-row">
<label for="node-input-offsetId">
<i class="fa fa-tag"></i> Offset ID
</label>
<input
type="text"
id="node-input-offsetId"
placeholder="Offset ID"
style="width: 60%"
ng-model="offsetId"
/>
</div>
<div class="form-row">
<label for="node-input-ignorePinned">
<i class="fa fa-tag"></i> Ignore Pinned
</label>
<input
type="checkbox"
id="node-input-ignorePinned"
ng-model="ignorePinned"
/>
</div>
<div class="form-row">
<label for="node-input-ignoreMigrated">
<i class="fa fa-tag"></i> Ignore Migrated
</label>
<input
type="checkbox"
id="node-input-ignoreMigrated"
ng-model="ignoreMigrated"
/>
</div>
<div class="form-row">
<label for="node-input-folder"> <i class="fa fa-tag"></i> Folder </label>
<input
type="text"
id="node-input-folder"
placeholder="Folder"
style="width: 60%"
ng-model="folder"
/>
</div>
<div class="form-row">
<label for="node-input-archived">
<i class="fa fa-tag"></i> Archived
</label>
<input type="checkbox" id="node-input-archived" ng-model="archived" />
</div>
</script>
<script type="text/html" data-help-name="iter-dialogs">
<p>The <b>iter-dialogs</b> node retrieves a list of Telegram dialogs (chats, channels, or groups) by iterating through them using the Telegram API.</p>
<p>
The <b>iter-dialogs</b> node retrieves a list of Telegram dialogs (chats,
channels, or groups) by iterating through them using the Telegram API.
</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload.client
<span class="property-type">object</span>
</dt>
<dd>An optional Telegram client instance if not configured globally.</dd>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>
payload.client
<span class="property-type">object</span>
</dt>
<dd>An optional Telegram client instance if not configured globally.</dd>
<dt>payload.limit
<span class="property-type">number</span>
</dt>
<dd>Limits the number of dialogs to retrieve. Default is no limit.</dd>
<dt>
payload.limit
<span class="property-type">number</span>
</dt>
<dd>Limits the number of dialogs to retrieve. Default is no limit.</dd>
<dt>payload.offsetDate
<span class="property-type">string | number</span>
</dt>
<dd>Fetch dialogs starting from this date. Provide a UNIX timestamp or a date string.</dd>
<dt>
payload.offsetDate
<span class="property-type">string | number</span>
</dt>
<dd>
Fetch dialogs starting from this date. Provide a UNIX timestamp or a date
string.
</dd>
<dt>payload.offsetId
<span class="property-type">number</span>
</dt>
<dd>Fetch dialogs starting from this message ID.</dd>
<dt>
payload.offsetId
<span class="property-type">number</span>
</dt>
<dd>Fetch dialogs starting from this message ID.</dd>
<dt>payload.offsetPeer
<span class="property-type">object</span>
</dt>
<dd>The peer object to start retrieving dialogs from.</dd>
<dt>
payload.offsetPeer
<span class="property-type">object</span>
</dt>
<dd>The peer object to start retrieving dialogs from.</dd>
<dt>payload.ignorePinned
<span class="property-type">boolean</span>
</dt>
<dd>Ignores pinned dialogs if set to <code>true</code>. Default is <code>false</code>.</dd>
<dt>
payload.ignorePinned
<span class="property-type">boolean</span>
</dt>
<dd>
Ignores pinned dialogs if set to <code>true</code>. Default is
<code>false</code>.
</dd>
<dt>payload.ignoreMigrated
<span class="property-type">boolean</span>
</dt>
<dd>Ignores migrated chats if set to <code>true</code>. Default is <code>false</code>.</dd>
<dt>
payload.ignoreMigrated
<span class="property-type">boolean</span>
</dt>
<dd>
Ignores migrated chats if set to <code>true</code>. Default is
<code>false</code>.
</dd>
<dt>payload.folder
<span class="property-type">number</span>
</dt>
<dd>Retrieves dialogs from a specific folder by folder ID.</dd>
<dt>
payload.folder
<span class="property-type">number</span>
</dt>
<dd>Retrieves dialogs from a specific folder by folder ID.</dd>
<dt>payload.archived
<span class="property-type">boolean</span>
</dt>
<dd>Includes archived dialogs if set to <code>true</code>. Default is <code>false</code>.</dd>
</dl>
<dt>
payload.archived
<span class="property-type">boolean</span>
</dt>
<dd>
Includes archived dialogs if set to <code>true</code>. Default is
<code>false</code>.
</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload.dialogs
<span class="property-type">object</span>
</dt>
<dd>An object containing dialogs, where keys are dialog IDs and values are dialog details.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>
payload.dialogs
<span class="property-type">object</span>
</dt>
<dd>
An object containing dialogs, where keys are dialog IDs and values are
dialog details.
</dd>
</dl>
<h3>Details</h3>
<p>The <b>iter-dialogs</b> node uses the Telegram API to iterate over all available dialogs. It allows filtering by various parameters, such as the number of dialogs to retrieve (<code>limit</code>), starting date or message (<code>offsetDate</code>, <code>offsetId</code>), and additional flags like <code>ignorePinned</code> or <code>archived</code>.</p>
<p>It supports advanced configurations such as folder-specific retrieval and skipping migrated chats, providing granular control over the dialogs to be processed.</p>
<h3>Details</h3>
<p>
The <b>iter-dialogs</b> node uses the Telegram API to iterate over all
available dialogs. It allows filtering by various parameters, such as the
number of dialogs to retrieve (<code>limit</code>), starting date or message
(<code>offsetDate</code>, <code>offsetId</code>), and additional flags like
<code>ignorePinned</code> or <code>archived</code>.
</p>
<h3>Example</h3>
<pre>
<p>
It supports advanced configurations such as folder-specific retrieval and
skipping migrated chats, providing granular control over the dialogs to be
processed.
</p>
<h3>Example</h3>
<pre>
{
"payload": {
"limit": 10,
@ -200,13 +226,23 @@
"archived": true
}
}
</pre>
<p>This input retrieves up to 10 archived dialogs starting from December 1, 2023.</p>
</pre
>
<p>
This input retrieves up to 10 archived dialogs starting from December 1,
2023.
</p>
<h3>Error Handling</h3>
<p>If an error occurs during dialog retrieval (e.g., invalid parameters or API limitations), the node logs an error message and does not return a payload.</p>
<h3>Error Handling</h3>
<p>
If an error occurs during dialog retrieval (e.g., invalid parameters or API
limitations), the node logs an error message and does not return a payload.
</p>
<h3>Configuration</h3>
<p>The node can use a globally configured Telegram client or a client instance provided in the message payload. Ensure the client has sufficient permissions to access the dialogs.</p>
<h3>Configuration</h3>
<p>
The node can use a globally configured Telegram client or a client instance
provided in the message payload. Ensure the client has sufficient
permissions to access the dialogs.
</p>
</script>

View File

@ -1,54 +1,55 @@
const { TelegramClient } = require("telegram");
module.exports = function (RED) {
function IterDialogs(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
function IterDialogs(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
this.on('input', async function (msg) {
/** @type {TelegramClient} */
const client = msg.payload?.client ? msg.payload.client : this.config.client;
const limit = msg.payload.limit || config.limit;
const offsetDate = msg.payload.offsetDate || config.offsetDate;
const offsetId = msg.payload.offsetId || config.offsetId;
const offsetPeer = msg.payload?.offsetPeer || undefined;
const ignorePinned = msg.payload.ignorePinned || config.ignorePinned;
const ignoreMigrated = msg.payload.ignoreMigrated || config.ignoreMigrated;
const folder = msg.payload.folder || config.folder;
const archived = msg.payload.archived || config.archived;
const params = {
limit: limit !== ""? parseInt(limit) : undefined,
offsetDate: offsetDate !== "" ? offsetDate:undefined,
offsetId: offsetId !== "" ? parseInt(offsetId):undefined,
offsetPeer: offsetPeer,
ignorePinned: ignorePinned,
ignoreMigrated: ignoreMigrated,
folder: folder !== "" ? parseInt(folder):undefined,
archived: archived,
}
this.on("input", async function (msg) {
/** @type {TelegramClient} */
const client = msg.payload?.client
? msg.payload.client
: this.config.client;
const limit = msg.payload.limit || config.limit;
const offsetDate = msg.payload.offsetDate || config.offsetDate;
const offsetId = msg.payload.offsetId || config.offsetId;
const offsetPeer = msg.payload?.offsetPeer || undefined;
const ignorePinned = msg.payload.ignorePinned || config.ignorePinned;
const ignoreMigrated =
msg.payload.ignoreMigrated || config.ignoreMigrated;
const folder = msg.payload.folder || config.folder;
const archived = msg.payload.archived || config.archived;
if (offsetDate) {
params.offsetDate = new Date(offsetDate).getTime() / 1000;
}
const params = {
limit: limit !== "" ? parseInt(limit) : undefined,
offsetDate: offsetDate !== "" ? offsetDate : undefined,
offsetId: offsetId !== "" ? parseInt(offsetId) : undefined,
offsetPeer: offsetPeer,
ignorePinned: ignorePinned,
ignoreMigrated: ignoreMigrated,
folder: folder !== "" ? parseInt(folder) : undefined,
archived: archived,
};
try {
const dialogs = {};
for await (const dialog of client.iterDialogs(params)){
dialogs[dialog.id] = dialog;
console.log(`${dialog.id}: ${dialog.title}`);
}
node.send({
payload: { dialogs },
});
} catch (err) {
node.error('Error iter dialogs: ' + err.message);
}
if (offsetDate) {
params.offsetDate = new Date(offsetDate).getTime() / 1000;
}
try {
const dialogs = {};
for await (const dialog of client.iterDialogs(params)) {
dialogs[dialog.id] = dialog;
console.log(`${dialog.id}: ${dialog.title}`);
}
node.send({
payload: { dialogs },
});
}
} catch (err) {
node.error("Error iter dialogs: " + err.message);
}
});
}
RED.nodes.registerType('iter-dialogs', IterDialogs);
RED.nodes.registerType("iter-dialogs", IterDialogs);
};

View File

@ -1,351 +1,433 @@
<script type="text/javascript">
RED.nodes.registerType('iter-messages', {
category: 'telegram',
color: '#32a3e0',
defaults: {
name: { value: '' },
config: { type: 'config', required: true },
chatId: { value: '', required: false },
limit: { value: '', required: false },
offsetDate: { value: '', required: false },
offsetId: { value: '', required: false },
maxId: { value: '', required: false },
minId: { value: '', required: false },
addOffset: { value: '', required: false },
search: { value: '', required: false },
filter: { value: '', required: false },
fromUser: { value: '', required: false },
waitTime: { value: '', required: false },
ids: { value: '', required: false },
reverse: { value: '', required: false },
replyTo: { value: '', required: false },
scheduled: { value: '', required: false }
},
inputs: 1,
outputs: 1,
paletteLabel: 'Iterate Messages',
label: function () {
return this.name || 'Iterate Messages';
},
oneditprepare: function(){
const node = this;
$("#node-input-ids").val(node.ids.join(","))
},
oneditsave:function(){
const node = this;
ids = $("#node-input-ids").val();
if(ids){
node.ids = ids.split(",").map(id => parseInt(id))
}
},
});
RED.nodes.registerType("iter-messages", {
category: "telegram",
color: "#32a3e0",
defaults: {
name: { value: "" },
config: { type: "config", required: true },
chatId: { value: "", required: false },
limit: { value: "", required: false },
offsetDate: { value: "", required: false },
offsetId: { value: "", required: false },
maxId: { value: "", required: false },
minId: { value: "", required: false },
addOffset: { value: "", required: false },
search: { value: "", required: false },
filter: { value: "", required: false },
fromUser: { value: "", required: false },
waitTime: { value: "", required: false },
ids: { value: "", required: false },
reverse: { value: "", required: false },
replyTo: { value: "", required: false },
scheduled: { value: "", required: false },
},
inputs: 1,
outputs: 1,
paletteLabel: "Iterate Messages",
label: function () {
return this.name || "Iterate Messages";
},
oneditprepare: function () {
const node = this;
$("#node-input-ids").val(node.ids.join(","));
},
oneditsave: function () {
const node = this;
ids = $("#node-input-ids").val();
if (ids) {
node.ids = ids.split(",").map((id) => parseInt(id));
}
},
});
</script>
<script type="text/html" data-template-name="iter-messages">
<div class="form-row">
<label for="node-input-name">
<i class="fa fa-tag"></i> Name
</label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
ng-model="name"
/>
</div>
<div class="form-row">
<label for="node-input-config">
<i class="fa fa-tag"></i> Config
</label>
<input
type="text"
id="node-input-config"
placeholder="Config"
style="width: 60%"
ng-model="config"
/>
</div>
<div class="form-row">
<label for="node-input-chatId">
<i class="fa fa-tag"></i> Chat ID
</label>
<input
type="text"
id="node-input-chatId"
placeholder="Chat ID"
style="width: 60%"
ng-model="chatId"
/>
</div>
<div class="form-row">
<label for="node-input-limit">
<i class="fa fa-tag"></i> Limit
</label>
<input
type="number"
id="node-input-limit"
placeholder="Limit"
style="width: 60%"
ng-model="limit"
/>
</div>
<div class="form-row">
<label for="node-input-offsetDate">
<i class="fa fa-tag"></i> Offset Date
</label>
<input
type="datetime-local"
id="node-input-offsetDate"
placeholder="Offset Date"
style="width: 60%"
ng-model="offsetDate"
/>
</div>
<div class="form-row">
<label for="node-input-offsetId">
<i class="fa fa-tag"></i> Offset ID
</label>
<input
type="number"
id="node-input-offsetId"
placeholder="Offset ID"
style="width: 60%"
ng-model="offsetId"
/>
</div>
<div class="form-row">
<label for="node-input-maxId">
<i class="fa fa-tag"></i> Max ID
</label>
<input
type="number"
id="node-input-maxId"
placeholder="Max ID"
style="width: 60%"
ng-model="maxId"
/>
</div>
<div class="form-row">
<label for="node-input-minId">
<i class="fa fa-tag"></i> Min ID
</label>
<input
type="number"
id="node-input-minId"
placeholder="Min ID"
style="width: 60%"
ng-model="minId"
/>
</div>
<div class="form-row">
<label for="node-input-addOffset">
<i class="fa fa-tag"></i> Add Offset
</label>
<input
type="number"
id="node-input-addOffset"
placeholder="Add Offset"
style="width: 60%"
ng-model="addOffset"
/>
</div>
<div class="form-row">
<label for="node-input-search">
<i class="fa fa-tag"></i> Search
</label>
<input
type="text"
id="node-input-search"
placeholder="Search"
style="width: 60%"
ng-model="search"
/>
</div>
<div class="form-row">
<label for="node-input-filter">
<i class="fa fa-tag"></i> Filter
</label>
<input type="checkbox" name="filter" value="InputMessagesFilterEmpty">Empty<br>
<input type="checkbox" name="filter" value="InputMessagesFilterPhotos">Photos<br>
<input type="checkbox" name="filter" value="InputMessagesFilterVideo">Video<br>
<input type="checkbox" name="filter" value="InputMessagesFilterPhotoVideo">Photo and Video<br>
<input type="checkbox" name="filter" value="InputMessagesFilterDocument">Document<br>
<input type="checkbox" name="filter" value="InputMessagesFilterUrl">URL<br>
<input type="checkbox" name="filter" value="InputMessagesFilterGif">GIF<br>
<input type="checkbox" name="filter" value="InputMessagesFilterVoice">Voice<br>
<input type="checkbox" name="filter" value="InputMessagesFilterMusic">Music<br>
<input type="checkbox" name="filter" value="InputMessagesFilterChatPhotos">Chat Photos<br>
<input type="checkbox" name="filter" value="InputMessagesFilterPhoneCalls">Phone Calls<br>
<input type="checkbox" name="filter" value="InputMessagesFilterRoundVoice">Round Voice<br>
<input type="checkbox" name="filter" value="InputMessagesFilterRoundVideo">Round Video<br>
<input type="checkbox" name="filter" value="InputMessagesFilterMyMentions">My Mentions<br>
<input type="checkbox" name="filter" value="InputMessagesFilterGeo">Geo<br>
<input type="checkbox" name="filter" value="InputMessagesFilterContacts">Contacts<br>
<input type="checkbox" name="filter" value="InputMessagesFilterPinned">Pinned<br>
</div>
<div class="form-row">
<label for="node-input-fromUser">
<i class="fa fa-tag"></i> From User
</label>
<input
type="text"
id="node-input-fromUser"
placeholder="From User"
style="width: 60%"
ng-model="fromUser"
/>
</div>
<div class="form-row">
<label for="node-input-waitTime">
<i class="fa fa-tag"></i> Wait Time
</label>
<input
type="number"
id="node-input-waitTime"
placeholder="Wait Time"
style="width: 60%"
ng-model="waitTime"
/>
</div>
<div class="form-row">
<label for="node-input-ids">
<i class="fa fa-tag"></i> IDs
</label>
<input
type="text"
id="node-input-ids"
placeholder="123,1245"
style="width: 60%"
ng-model="ids"
/>
</div>
<div class="form-row">
<label for="node-input-reverse">
<i class="fa fa-tag"></i> Reverse
</label>
<input
type="checkbox"
id="node-input-reverse"
ng-model="reverse"
/>
</div>
<div class="form-row">
<label for="node-input-replyTo">
<i class="fa fa-tag"></i> Reply To
</label>
<input
type="number"
id="node-input-replyTo"
placeholder="Reply To"
style="width: 60%"
ng-model="replyTo"
/>
</div>
<div class="form-row">
<label for="node-input-scheduled">
<i class="fa fa-tag"></i> Scheduled
</label>
<input
type="checkbox"
id="node-input-scheduled"
ng-model="scheduled"
/>
</div>
<div class="form-row">
<label for="node-input-name"> <i class="fa fa-tag"></i> Name </label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
ng-model="name"
/>
</div>
<div class="form-row">
<label for="node-input-config"> <i class="fa fa-tag"></i> Config </label>
<input
type="text"
id="node-input-config"
placeholder="Config"
style="width: 60%"
ng-model="config"
/>
</div>
<div class="form-row">
<label for="node-input-chatId"> <i class="fa fa-tag"></i> Chat ID </label>
<input
type="text"
id="node-input-chatId"
placeholder="Chat ID"
style="width: 60%"
ng-model="chatId"
/>
</div>
<div class="form-row">
<label for="node-input-limit"> <i class="fa fa-tag"></i> Limit </label>
<input
type="number"
id="node-input-limit"
placeholder="Limit"
style="width: 60%"
ng-model="limit"
/>
</div>
<div class="form-row">
<label for="node-input-offsetDate">
<i class="fa fa-tag"></i> Offset Date
</label>
<input
type="datetime-local"
id="node-input-offsetDate"
placeholder="Offset Date"
style="width: 60%"
ng-model="offsetDate"
/>
</div>
<div class="form-row">
<label for="node-input-offsetId">
<i class="fa fa-tag"></i> Offset ID
</label>
<input
type="number"
id="node-input-offsetId"
placeholder="Offset ID"
style="width: 60%"
ng-model="offsetId"
/>
</div>
<div class="form-row">
<label for="node-input-maxId"> <i class="fa fa-tag"></i> Max ID </label>
<input
type="number"
id="node-input-maxId"
placeholder="Max ID"
style="width: 60%"
ng-model="maxId"
/>
</div>
<div class="form-row">
<label for="node-input-minId"> <i class="fa fa-tag"></i> Min ID </label>
<input
type="number"
id="node-input-minId"
placeholder="Min ID"
style="width: 60%"
ng-model="minId"
/>
</div>
<div class="form-row">
<label for="node-input-addOffset">
<i class="fa fa-tag"></i> Add Offset
</label>
<input
type="number"
id="node-input-addOffset"
placeholder="Add Offset"
style="width: 60%"
ng-model="addOffset"
/>
</div>
<div class="form-row">
<label for="node-input-search"> <i class="fa fa-tag"></i> Search </label>
<input
type="text"
id="node-input-search"
placeholder="Search"
style="width: 60%"
ng-model="search"
/>
</div>
<div class="form-row">
<label for="node-input-filter"> <i class="fa fa-tag"></i> Filter </label>
<input
type="checkbox"
name="filter"
value="InputMessagesFilterEmpty"
/>Empty<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterPhotos"
/>Photos<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterVideo"
/>Video<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterPhotoVideo"
/>Photo and Video<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterDocument"
/>Document<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterUrl"
/>URL<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterGif"
/>GIF<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterVoice"
/>Voice<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterMusic"
/>Music<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterChatPhotos"
/>Chat Photos<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterPhoneCalls"
/>Phone Calls<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterRoundVoice"
/>Round Voice<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterRoundVideo"
/>Round Video<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterMyMentions"
/>My Mentions<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterGeo"
/>Geo<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterContacts"
/>Contacts<br />
<input
type="checkbox"
name="filter"
value="InputMessagesFilterPinned"
/>Pinned<br />
</div>
<div class="form-row">
<label for="node-input-fromUser">
<i class="fa fa-tag"></i> From User
</label>
<input
type="text"
id="node-input-fromUser"
placeholder="From User"
style="width: 60%"
ng-model="fromUser"
/>
</div>
<div class="form-row">
<label for="node-input-waitTime">
<i class="fa fa-tag"></i> Wait Time
</label>
<input
type="number"
id="node-input-waitTime"
placeholder="Wait Time"
style="width: 60%"
ng-model="waitTime"
/>
</div>
<div class="form-row">
<label for="node-input-ids"> <i class="fa fa-tag"></i> IDs </label>
<input
type="text"
id="node-input-ids"
placeholder="123,1245"
style="width: 60%"
ng-model="ids"
/>
</div>
<div class="form-row">
<label for="node-input-reverse"> <i class="fa fa-tag"></i> Reverse </label>
<input type="checkbox" id="node-input-reverse" ng-model="reverse" />
</div>
<div class="form-row">
<label for="node-input-replyTo"> <i class="fa fa-tag"></i> Reply To </label>
<input
type="number"
id="node-input-replyTo"
placeholder="Reply To"
style="width: 60%"
ng-model="replyTo"
/>
</div>
<div class="form-row">
<label for="node-input-scheduled">
<i class="fa fa-tag"></i> Scheduled
</label>
<input type="checkbox" id="node-input-scheduled" ng-model="scheduled" />
</div>
</script>
<script type="text/html" data-help-name="iter-messages">
<p>The <b>iter-messages</b> node retrieves messages from a specified chat or user using the Telegram API. It supports a wide range of filtering and pagination options, allowing for efficient message iteration.</p>
<p>
The <b>iter-messages</b> node retrieves messages from a specified chat or
user using the Telegram API. It supports a wide range of filtering and
pagination options, allowing for efficient message iteration.
</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload.client
<span class="property-type">object</span>
</dt>
<dd>An optional Telegram client instance if not configured globally.</dd>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>
payload.client
<span class="property-type">object</span>
</dt>
<dd>An optional Telegram client instance if not configured globally.</dd>
<dt>payload.chatId
<span class="property-type">string</span>
</dt>
<dd>The ID or username of the chat or user to retrieve messages from. Use "me" for personal messages.</dd>
<dt>
payload.chatId
<span class="property-type">string</span>
</dt>
<dd>
The ID or username of the chat or user to retrieve messages from. Use "me"
for personal messages.
</dd>
<dt>payload.limit
<span class="property-type">number</span>
</dt>
<dd>Maximum number of messages to retrieve.</dd>
<dt>
payload.limit
<span class="property-type">number</span>
</dt>
<dd>Maximum number of messages to retrieve.</dd>
<dt>payload.offsetDate
<span class="property-type">string | number</span>
</dt>
<dd>Fetch messages starting from this date. Accepts a UNIX timestamp or a date string.</dd>
<dt>
payload.offsetDate
<span class="property-type">string | number</span>
</dt>
<dd>
Fetch messages starting from this date. Accepts a UNIX timestamp or a date
string.
</dd>
<dt>payload.offsetId
<span class="property-type">number</span>
</dt>
<dd>Fetch messages starting from this message ID.</dd>
<dt>
payload.offsetId
<span class="property-type">number</span>
</dt>
<dd>Fetch messages starting from this message ID.</dd>
<dt>payload.maxId
<span class="property-type">number</span>
</dt>
<dd>Fetch messages with IDs less than or equal to this value.</dd>
<dt>
payload.maxId
<span class="property-type">number</span>
</dt>
<dd>Fetch messages with IDs less than or equal to this value.</dd>
<dt>payload.minId
<span class="property-type">number</span>
</dt>
<dd>Fetch messages with IDs greater than or equal to this value.</dd>
<dt>
payload.minId
<span class="property-type">number</span>
</dt>
<dd>Fetch messages with IDs greater than or equal to this value.</dd>
<dt>payload.addOffset
<span class="property-type">number</span>
</dt>
<dd>An additional offset to apply when retrieving messages.</dd>
<dt>
payload.addOffset
<span class="property-type">number</span>
</dt>
<dd>An additional offset to apply when retrieving messages.</dd>
<dt>payload.search
<span class="property-type">string</span>
</dt>
<dd>Filters messages containing the specified search term.</dd>
<dt>
payload.search
<span class="property-type">string</span>
</dt>
<dd>Filters messages containing the specified search term.</dd>
<dt>payload.filter
<span class="property-type">object</span>
</dt>
<dd>Applies a specific message filter (e.g., photos, videos, documents).</dd>
<dt>
payload.filter
<span class="property-type">object</span>
</dt>
<dd>
Applies a specific message filter (e.g., photos, videos, documents).
</dd>
<dt>payload.filters
<span class="property-type">array</span>
</dt>
<dd>Applies multiple message filters using Telegram API filter classes (e.g., <code>Api.InputMessagesFilterPhotos</code>).</dd>
<dt>
payload.filters
<span class="property-type">array</span>
</dt>
<dd>
Applies multiple message filters using Telegram API filter classes (e.g.,
<code>Api.InputMessagesFilterPhotos</code>).
</dd>
<dt>payload.reverse
<span class="property-type">boolean</span>
</dt>
<dd>Retrieves messages in reverse order if set to <code>true</code>. Default is <code>false</code>.</dd>
<dt>
payload.reverse
<span class="property-type">boolean</span>
</dt>
<dd>
Retrieves messages in reverse order if set to <code>true</code>. Default
is <code>false</code>.
</dd>
<dt>payload.replyTo
<span class="property-type">number</span>
</dt>
<dd>Fetches messages replying to the specified message ID.</dd>
<dt>
payload.replyTo
<span class="property-type">number</span>
</dt>
<dd>Fetches messages replying to the specified message ID.</dd>
<dt>payload.scheduled
<span class="property-type">boolean</span>
</dt>
<dd>Includes scheduled messages if set to <code>true</code>.</dd>
</dl>
<dt>
payload.scheduled
<span class="property-type">boolean</span>
</dt>
<dd>Includes scheduled messages if set to <code>true</code>.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload.messages
<span class="property-type">object</span>
</dt>
<dd>An object containing messages, where keys are message IDs and values are message details.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>
payload.messages
<span class="property-type">object</span>
</dt>
<dd>
An object containing messages, where keys are message IDs and values are
message details.
</dd>
</dl>
<h3>Details</h3>
<p>The <b>iter-messages</b> node provides a flexible way to retrieve messages from a Telegram chat or user. It allows advanced configurations such as filtering messages by type, searching for specific terms, and paginating results using offsets and IDs.</p>
<p>The node also handles cases where a chat username needs to be resolved into an entity or peer ID. It supports using multiple filters in conjunction, enabling precise control over the messages to process.</p>
<h3>Details</h3>
<p>
The <b>iter-messages</b> node provides a flexible way to retrieve messages
from a Telegram chat or user. It allows advanced configurations such as
filtering messages by type, searching for specific terms, and paginating
results using offsets and IDs.
</p>
<h3>Example</h3>
<pre>
<p>
The node also handles cases where a chat username needs to be resolved into
an entity or peer ID. It supports using multiple filters in conjunction,
enabling precise control over the messages to process.
</p>
<h3>Example</h3>
<pre>
{
"payload": {
"chatId": "@examplechannel",
@ -354,12 +436,23 @@
"filters": ["InputMessagesFilterPhotos"]
}
}
</pre>
<p>This input retrieves up to 50 messages containing the term "announcement" and filters them to include only photos from the specified channel.</p>
</pre
>
<p>
This input retrieves up to 50 messages containing the term "announcement"
and filters them to include only photos from the specified channel.
</p>
<h3>Error Handling</h3>
<p>If an error occurs during message retrieval (e.g., invalid chat ID or API errors), the node logs an error message and does not return a payload.</p>
<h3>Error Handling</h3>
<p>
If an error occurs during message retrieval (e.g., invalid chat ID or API
errors), the node logs an error message and does not return a payload.
</p>
<h3>Configuration</h3>
<p>The node can use a globally configured Telegram client or a client instance provided in the message payload. Ensure the client has access to the specified chat or user.</p>
<h3>Configuration</h3>
<p>
The node can use a globally configured Telegram client or a client instance
provided in the message payload. Ensure the client has access to the
specified chat or user.
</p>
</script>

View File

@ -1,94 +1,93 @@
const { TelegramClient, utils, Api } = require("telegram");
module.exports = function (RED) {
function IterMessages(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
function IterMessages(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
this.on('input', async function (msg) {
/** @type {TelegramClient} */
const client = msg.payload?.client ? msg.payload.client : this.config.client;
const chatId = msg.payload?.chatId ? msg.payload.chatId : config.chatId;
let peerId = chatId === "me" ? chatId : utils.parseID(chatId);
this.on("input", async function (msg) {
/** @type {TelegramClient} */
const client = msg.payload?.client
? msg.payload.client
: this.config.client;
const chatId = msg.payload?.chatId ? msg.payload.chatId : config.chatId;
let peerId = chatId === "me" ? chatId : utils.parseID(chatId);
// Получаем параметры из входного сообщения или из конфигурации узла
const limit = msg.payload?.limit || config.limit;
const offsetDate = msg.payload?.offsetDate || config.offsetDate;
const offsetId = msg.payload?.offsetId || config.offsetId;
const maxId = msg.payload?.maxId || config.maxId;
const minId = msg.payload?.minId || config.minId;
const addOffset = msg.payload?.addOffset || config.addOffset;
const search = msg.payload?.search || config.search;
const filter = msg.payload?.filter || config.filter;
const fromUser = msg.payload?.fromUser || config.fromUser;
const waitTime = msg.payload?.waitTime || config.waitTime;
const ids = msg.payload?.ids || config.ids;
const reverse = msg.payload?.reverse || config.reverse;
const replyTo = msg.payload?.replyTo || config.replyTo;
const scheduled = msg.payload?.scheduled || config.scheduled;
// We receive parameters from the input message or from the node configuration
const limit = msg.payload?.limit || config.limit;
const offsetDate = msg.payload?.offsetDate || config.offsetDate;
const offsetId = msg.payload?.offsetId || config.offsetId;
const maxId = msg.payload?.maxId || config.maxId;
const minId = msg.payload?.minId || config.minId;
const addOffset = msg.payload?.addOffset || config.addOffset;
const search = msg.payload?.search || config.search;
const filter = msg.payload?.filter || config.filter;
const fromUser = msg.payload?.fromUser || config.fromUser;
const waitTime = msg.payload?.waitTime || config.waitTime;
const ids = msg.payload?.ids || config.ids;
const reverse = msg.payload?.reverse || config.reverse;
const replyTo = msg.payload?.replyTo || config.replyTo;
const scheduled = msg.payload?.scheduled || config.scheduled;
try {
const params = {
limit: limit !== ""? parseInt(limit) : undefined,
offsetDate: offsetDate !== "" ? offsetDate:undefined,
offsetId: offsetId !== "" ? parseInt(offsetId):undefined,
maxId: maxId,
minId: minId,
addOffset: addOffset,
search: search !== "" ? search : undefined,
filter: filter,
// fromUser: fromUser,
waitTime: waitTime,
ids: ids,
reverse: reverse,
replyTo: replyTo,
scheduled: scheduled,
};
try {
const params = {
limit: limit !== "" ? parseInt(limit) : undefined,
offsetDate: offsetDate !== "" ? offsetDate : undefined,
offsetId: offsetId !== "" ? parseInt(offsetId) : undefined,
maxId: maxId,
minId: minId,
addOffset: addOffset,
search: search !== "" ? search : undefined,
filter: filter,
// fromUser: fromUser,
waitTime: waitTime,
ids: ids,
reverse: reverse,
replyTo: replyTo,
scheduled: scheduled,
};
if (offsetDate) {
params.offsetDate = new Date(offsetDate).getTime() / 1000;
}
if (offsetDate) {
params.offsetDate = new Date(offsetDate).getTime() / 1000;
}
if (chatId[0] === "@") {
peerId = await client.getEntity(chatId);
}
const messages = {};
if (chatId[0] === "@") {
peerId = await client.getEntity(chatId);
}
const messages = {};
const filters = msg.payload?.filters || config.filters || [];
const filters = msg.payload?.filters || config.filters || [];
// Обработка выбранных фильтров
if (filters.length > 0) {
params.filter = [];
filters.forEach((filter) => {
params.filter.push( Api[filter]);
});
}
// Processing selected filters
if (filters.length > 0) {
params.filter = [];
filters.forEach((filter) => {
params.filter.push(Api[filter]);
});
}
try {
for await (const message of client.iterMessages(peerId, params)){
messages[message.id] = message;
console.log(message.id, message.text);
}
} catch (error) {
const entity = await client.getInputEntity(peerId)
for await (const message of client.iterMessages(entity, params)){
messages[message.id] = message;
console.log(message.id, message.text);
}
}
node.send({
payload: { messages },
});
} catch (err) {
node.error('Error iter messages: ' + err.message);
}
try {
for await (const message of client.iterMessages(peerId, params)) {
messages[message.id] = message;
console.log(message.id, message.text);
}
} catch (error) {
const entity = await client.getInputEntity(peerId);
for await (const message of client.iterMessages(entity, params)) {
messages[message.id] = message;
console.log(message.id, message.text);
}
}
node.send({
payload: { messages },
});
}
} catch (err) {
node.error("Error iter messages: " + err.message);
}
});
}
RED.nodes.registerType('iter-messages', IterMessages);
RED.nodes.registerType("iter-messages", IterMessages);
};

View File

@ -1,91 +1,108 @@
<script type="text/javascript">
RED.nodes.registerType('receiver', {
category: 'telegram-account',
color: '#229ED9',
icon: 'tg.png',
align:"right",
defaults: {
name: { value: '' },
config: { type: 'config', required: false },
ignore: { value:""}
},
inputs: 1,
outputs: 1,
label: function () {
return this.name || 'Receiver';
},
});
RED.nodes.registerType("receiver", {
category: "telegram-account",
color: "#229ED9",
icon: "tg.png",
align: "right",
defaults: {
name: { value: "" },
config: { type: "config", required: false },
ignore: { value: "" },
},
inputs: 1,
outputs: 1,
label: function () {
return this.name || "Receiver";
},
});
</script>
<script type="text/html" data-template-name="receiver">
<div class="form-row">
<label for="node-input-name">
<i class="fa fa-tag"></i> Name
</label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
/>
</div>
<div class="form-row">
<label for="node-input-config">
<i class="fa fa-tag"></i> Config
</label>
<input
type="text"
id="node-input-config"
placeholder="Config"
style="width: 60%"
/>
</div>
<div class="form-row">
<label for="node-input-ignore">
<i class="fa fa-tag"></i> Config
</label>
<textarea
type="text"
id="node-input-ignore"
placeholder="Config"
style="width: 60%"
></textarea>
</div>
</script>
<div class="form-row">
<label for="node-input-name"> <i class="fa fa-tag"></i> Name </label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
/>
</div>
<div class="form-row">
<label for="node-input-config"> <i class="fa fa-tag"></i> Config </label>
<input
type="text"
id="node-input-config"
placeholder="Config"
style="width: 60%"
/>
</div>
<div class="form-row">
<label for="node-input-ignore"> <i class="fa fa-tag"></i> Config </label>
<textarea
type="text"
id="node-input-ignore"
placeholder="Config"
style="width: 60%"
></textarea>
</div>
</script>
<script type="text/html" data-help-name="receiver">
<p>The <b>receiver</b> node listens for incoming Telegram messages and forwards them as output messages in Node-RED. It supports filtering messages based on sender IDs to ignore specific users.</p>
<script type="text/html" data-help-name="receiver">
<p>
The <b>receiver</b> node listens for incoming Telegram messages and forwards
them as output messages in Node-RED. It supports filtering messages based on
sender IDs to ignore specific users.
</p>
<h3>Inputs</h3>
<p>This node does not take any direct inputs. It listens to all incoming messages from the configured Telegram client.</p>
<h3>Inputs</h3>
<p>
This node does not take any direct inputs. It listens to all incoming
messages from the configured Telegram client.
</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload.update
<span class="property-type">object</span>
</dt>
<dd>The raw Telegram update object containing details about the incoming message, sender, chat, and metadata.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>
payload.update
<span class="property-type">object</span>
</dt>
<dd>
The raw Telegram update object containing details about the incoming
message, sender, chat, and metadata.
</dd>
</dl>
<h3>Configuration</h3>
<dl class="message-properties">
<dt>Telegram Configuration
<span class="property-type">node</span>
</dt>
<dd>A configured Telegram client node to receive messages. Ensure the client is authenticated and has necessary permissions.</dd>
<h3>Configuration</h3>
<dl class="message-properties">
<dt>
Telegram Configuration
<span class="property-type">node</span>
</dt>
<dd>
A configured Telegram client node to receive messages. Ensure the client
is authenticated and has necessary permissions.
</dd>
<dt>Ignore List
<span class="property-type">string</span>
</dt>
<dd>A newline-separated list of user IDs to ignore. Messages from these users will not trigger the output.</dd>
</dl>
<dt>
Ignore List
<span class="property-type">string</span>
</dt>
<dd>
A newline-separated list of user IDs to ignore. Messages from these users
will not trigger the output.
</dd>
</dl>
<h3>Details</h3>
<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>
<h3>Details</h3>
<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>
<h3>Example</h3>
<pre>
<h3>Example</h3>
<pre>
{
"payload": {
"update": {
@ -103,26 +120,47 @@
}
}
}
</pre>
<p>In this example, the node outputs the raw Telegram update object when a user sends the message "Hello, bot!" to the Telegram bot.</p>
</pre
>
<p>
In this example, the node outputs the raw Telegram update object when a user
sends the message "Hello, bot!" to the Telegram bot.
</p>
<h3>Error Handling</h3>
<p>If the Telegram client encounters an authentication issue or configuration error, the node logs an error message and stops listening for messages.</p>
<h3>Error Handling</h3>
<p>
If the Telegram client encounters an authentication issue or configuration
error, the node logs an error message and stops listening for messages.
</p>
<h3>Advanced Usage</h3>
<p>By configuring the <b>Ignore List</b>, you can filter out messages from specific users. For example:</p>
<pre>
<h3>Advanced Usage</h3>
<p>
By configuring the <b>Ignore List</b>, you can filter out messages from
specific users. For example:
</p>
<pre>
123456789
987654321
</pre>
<p>In this case, messages from user IDs <code>123456789</code> and <code>987654321</code> will be ignored.</p>
</pre
>
<p>
In this case, messages from user IDs <code>123456789</code> and
<code>987654321</code> will be ignored.
</p>
<h3>Notes</h3>
<ul>
<li>Ensure the Telegram bot has sufficient permissions to receive messages in the configured chat or channel.</li>
<li>The <b>Ignore List</b> only filters messages based on the sender's user ID.</li>
<li>For advanced filtering based on message content, consider chaining this node with additional processing nodes in Node-RED.</li>
</ul>
<h3>Notes</h3>
<ul>
<li>
Ensure the Telegram bot has sufficient permissions to receive messages in
the configured chat or channel.
</li>
<li>
The <b>Ignore List</b> only filters messages based on the sender's user
ID.
</li>
<li>
For advanced filtering based on message content, consider chaining this
node with additional processing nodes in Node-RED.
</li>
</ul>
</script>

View File

@ -5,28 +5,26 @@ module.exports = function (RED) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
const client = this.config.client;
const client = this.config.client;
const ignore = config.ignore.split(/\n/);
try {
client.addEventHandler((update) => {
if(update.message.fromId != null && !ignore.includes(update.message.fromId.userId.toString())){
if (
update.message.fromId != null &&
!ignore.includes(update.message.fromId.userId.toString())
) {
node.send({
payload:{
update
}
} )
payload: {
update,
},
});
}
}, new NewMessage());
} catch (err) {
node.error('Ошибка авторизации: ' + err.message);
node.error("Authorization error: " + err.message);
}
}
RED.nodes.registerType('receiver', Receiver);
RED.nodes.registerType("receiver", Receiver);
};

View File

@ -1,444 +1,459 @@
<script type="text/javascript">
RED.nodes.registerType('send-files', {
category: 'telegram',
color: '#32a3e0',
defaults: {
name: { value: '' },
config: { type: 'config', required: true },
chatId: { value: '', required: true },
files: { value: '', required: true },
caption: { value: '' },
forceDocument: { value: false },
fileSize: { value: 0 },
clearDraft: { value: false },
progressCallback: { value: '' },
replyTo: { value: '' },
attributes: { value: '' },
thumb: { value: '' },
voiceNote: { value: false },
videoNote: { value: false },
supportsStreaming: { value: false },
parseMode: { value: '' },
formattingEntities: { value: '' },
silent: { value: false },
scheduleDate: { value: 0 },
buttons: { value: '' },
workers: { value: 0 },
noforwards: { value: false },
commentTo: { value: '' },
topMsgId: { value: '' },
},
inputs: 1,
outputs: 1,
icon: 'file.png',
paletteLabel: 'Send Files',
label: function () {
return this.name || 'Send Files';
},
});
RED.nodes.registerType("send-files", {
category: "telegram",
color: "#32a3e0",
defaults: {
name: { value: "" },
config: { type: "config", required: true },
chatId: { value: "", required: true },
files: { value: "", required: true },
caption: { value: "" },
forceDocument: { value: false },
fileSize: { value: 0 },
clearDraft: { value: false },
progressCallback: { value: "" },
replyTo: { value: "" },
attributes: { value: "" },
thumb: { value: "" },
voiceNote: { value: false },
videoNote: { value: false },
supportsStreaming: { value: false },
parseMode: { value: "" },
formattingEntities: { value: "" },
silent: { value: false },
scheduleDate: { value: 0 },
buttons: { value: "" },
workers: { value: 0 },
noforwards: { value: false },
commentTo: { value: "" },
topMsgId: { value: "" },
},
inputs: 1,
outputs: 1,
icon: "file.png",
paletteLabel: "Send Files",
label: function () {
return this.name || "Send Files";
},
});
</script>
<script type="text/html" data-template-name="send-files">
<div class="form-row">
<label for="node-input-name">
<i class="fa fa-tag"></i> Name
</label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
ng-model="name"
/>
</div>
<div class="form-row">
<label for="node-input-config">
<i class="fa fa-tag"></i> Config
</label>
<input
type="text"
id="node-input-config"
placeholder="Config"
style="width: 60%"
ng-model="config"
/>
</div>
<div class="form-row">
<label for="node-input-chatId">
<i class="fa fa-tag"></i> Chat ID
</label>
<input
type="text"
id="node-input-chatId"
placeholder="Chat ID"
style="width: 60%"
ng-model="chatId"
/>
</div>
<div class="form-row">
<label for="node-input-files">
<i class="fa fa-tag"></i> Files (comma-separated)
</label>
<input
type="text"
id="node-input-files"
placeholder="url"
style="width: 60%"
ng-model="files"
/>
</div>
<div class="form-row">
<label for="node-input-caption">
<i class="fa fa-tag"></i> Caption
</label>
<input
type="text"
id="node-input-caption"
placeholder="Caption"
style="width: 60%"
ng-model="caption"
/>
</div>
<div class="form-row">
<label for="node-input-forceDocument">
<i class="fa fa-tag"></i> Force Document
</label>
<input
type="checkbox"
id="node-input-forceDocument"
ng-model="forceDocument"
/>
</div>
<div class="form-row">
<label for="node-input-fileSize">
<i class="fa fa-tag"></i> File Size
</label>
<input
type="number"
id="node-input-fileSize"
placeholder="File Size"
style="width: 60%"
ng-model="fileSize"
/>
</div>
<div class="form-row">
<label for="node-input-clearDraft">
<i class="fa fa-tag"></i> Clear Draft
</label>
<input
type="checkbox"
id="node-input-clearDraft"
ng-model="clearDraft"
/>
</div>
<div class="form-row">
<label for="node-input-progressCallback">
<i class="fa fa-tag"></i> Progress Callback
</label>
<input
type="text"
id="node-input-progressCallback"
placeholder="Progress Callback"
style="width: 60%"
ng-model="progressCallback"
/>
</div>
<div class="form-row">
<label for="node-input-replyTo">
<i class="fa fa-tag"></i> Reply To
</label>
<input
type="text"
id="node-input-replyTo"
placeholder="Reply To"
style="width: 60%"
ng-model="replyTo"
/>
</div>
<div class="form-row">
<label for="node-input-attributes">
<i class="fa fa-tag"></i> Attributes
</label>
<input
type="text"
id="node-input-attributes"
placeholder="Attributes"
style="width: 60%"
ng-model="attributes"
/>
</div>
<div class="form-row">
<label for="node-input-thumb">
<i class="fa fa-tag"></i> Thumbnail
</label>
<input
type="text"
id="node-input-thumb"
placeholder="Thumbnail"
style="width: 60%"
ng-model="thumb"
/>
</div>
<div class="form-row">
<label for="node-input-voiceNote">
<i class="fa fa-tag"></i> Voice Note
</label>
<input
type="checkbox"
id="node-input-voiceNote"
ng-model="voiceNote"
/>
</div>
<div class="form-row">
<label for="node-input-videoNote">
<i class="fa fa-tag"></i> Video Note
</label>
<input
type="checkbox"
id="node-input-videoNote"
ng-model="videoNote"
/>
</div>
<div class="form-row">
<label for="node-input-supportsStreaming">
<i class="fa fa-tag"></i> Supports Streaming
</label>
<input
type="checkbox"
id="node-input-supportsStreaming"
ng-model="supportsStreaming"
/>
</div>
<div class="form-row">
<label for="node-input-parseMode">
<i class="fa fa-tag"></i> Parse Mode
</label>
<input
type="text"
id="node-input-parseMode"
placeholder="Parse Mode"
style="width: 60%"
ng-model="parseMode"
/>
</div>
<div class="form-row">
<label for="node-input-formattingEntities">
<i class="fa fa-tag"></i> Formatting Entities
</label>
<input
type="text"
id="node-input-formattingEntities"
placeholder="Formatting Entities"
style="width: 60%"
ng-model="formattingEntities"
/>
</div>
<div class="form-row">
<label for="node-input-silent">
<i class="fa fa-tag"></i> Silent
</label>
<input
type="checkbox"
id="node-input-silent"
ng-model="silent"
/>
</div>
<div class="form-row">
<label for="node-input-scheduleDate">
<i class="fa fa-tag"></i> Schedule Date
</label>
<input
type="number"
id="node-input-scheduleDate"
placeholder="Schedule Date"
style="width: 60%"
ng-model="scheduleDate"
/>
</div>
<div class="form-row">
<label for="node-input-buttons">
<i class="fa fa-tag"></i> Buttons
</label>
<input
type="text"
id="node-input-buttons"
placeholder="Buttons"
style="width: 60%"
ng-model="buttons"
/>
</div>
<div class="form-row">
<label for="node-input-workers">
<i class="fa fa-tag"></i> Workers
</label>
<input
type="number"
id="node-input-workers"
placeholder="Workers"
style="width: 60%"
ng-model="workers"
/>
</div>
<div class="form-row">
<label for="node-input-noforwards">
<i class="fa fa-tag"></i> No Forwards
</label>
<input
type="checkbox"
id="node-input-noforwards"
ng-model="noforwards"
/>
</div>
<div class="form-row">
<label for="node-input-commentTo">
<i class="fa fa-tag"></i> Comment To
</label>
<input
type="text"
id="node-input-commentTo"
placeholder="Comment To"
style="width: 60%"
ng-model="commentTo"
/>
</div>
<div class="form-row">
<label for="node-input-topMsgId">
<i class="fa fa-tag"></i> Top Message ID
</label>
<input
type="text"
id="node-input-topMsgId"
placeholder="Top Message ID"
style="width: 60%"
ng-model="topMsgId"
/>
</div>
<div class="form-row">
<label for="node-input-name"> <i class="fa fa-tag"></i> Name </label>
<input
type="text"
id="node-input-name"
placeholder="Name"
style="width: 60%"
ng-model="name"
/>
</div>
<div class="form-row">
<label for="node-input-config"> <i class="fa fa-tag"></i> Config </label>
<input
type="text"
id="node-input-config"
placeholder="Config"
style="width: 60%"
ng-model="config"
/>
</div>
<div class="form-row">
<label for="node-input-chatId"> <i class="fa fa-tag"></i> Chat ID </label>
<input
type="text"
id="node-input-chatId"
placeholder="Chat ID"
style="width: 60%"
ng-model="chatId"
/>
</div>
<div class="form-row">
<label for="node-input-files">
<i class="fa fa-tag"></i> Files (comma-separated)
</label>
<input
type="text"
id="node-input-files"
placeholder="url"
style="width: 60%"
ng-model="files"
/>
</div>
<div class="form-row">
<label for="node-input-caption"> <i class="fa fa-tag"></i> Caption </label>
<input
type="text"
id="node-input-caption"
placeholder="Caption"
style="width: 60%"
ng-model="caption"
/>
</div>
<div class="form-row">
<label for="node-input-forceDocument">
<i class="fa fa-tag"></i> Force Document
</label>
<input
type="checkbox"
id="node-input-forceDocument"
ng-model="forceDocument"
/>
</div>
<div class="form-row">
<label for="node-input-fileSize">
<i class="fa fa-tag"></i> File Size
</label>
<input
type="number"
id="node-input-fileSize"
placeholder="File Size"
style="width: 60%"
ng-model="fileSize"
/>
</div>
<div class="form-row">
<label for="node-input-clearDraft">
<i class="fa fa-tag"></i> Clear Draft
</label>
<input type="checkbox" id="node-input-clearDraft" ng-model="clearDraft" />
</div>
<div class="form-row">
<label for="node-input-progressCallback">
<i class="fa fa-tag"></i> Progress Callback
</label>
<input
type="text"
id="node-input-progressCallback"
placeholder="Progress Callback"
style="width: 60%"
ng-model="progressCallback"
/>
</div>
<div class="form-row">
<label for="node-input-replyTo"> <i class="fa fa-tag"></i> Reply To </label>
<input
type="text"
id="node-input-replyTo"
placeholder="Reply To"
style="width: 60%"
ng-model="replyTo"
/>
</div>
<div class="form-row">
<label for="node-input-attributes">
<i class="fa fa-tag"></i> Attributes
</label>
<input
type="text"
id="node-input-attributes"
placeholder="Attributes"
style="width: 60%"
ng-model="attributes"
/>
</div>
<div class="form-row">
<label for="node-input-thumb"> <i class="fa fa-tag"></i> Thumbnail </label>
<input
type="text"
id="node-input-thumb"
placeholder="Thumbnail"
style="width: 60%"
ng-model="thumb"
/>
</div>
<div class="form-row">
<label for="node-input-voiceNote">
<i class="fa fa-tag"></i> Voice Note
</label>
<input type="checkbox" id="node-input-voiceNote" ng-model="voiceNote" />
</div>
<div class="form-row">
<label for="node-input-videoNote">
<i class="fa fa-tag"></i> Video Note
</label>
<input type="checkbox" id="node-input-videoNote" ng-model="videoNote" />
</div>
<div class="form-row">
<label for="node-input-supportsStreaming">
<i class="fa fa-tag"></i> Supports Streaming
</label>
<input
type="checkbox"
id="node-input-supportsStreaming"
ng-model="supportsStreaming"
/>
</div>
<div class="form-row">
<label for="node-input-parseMode">
<i class="fa fa-tag"></i> Parse Mode
</label>
<input
type="text"
id="node-input-parseMode"
placeholder="Parse Mode"
style="width: 60%"
ng-model="parseMode"
/>
</div>
<div class="form-row">
<label for="node-input-formattingEntities">
<i class="fa fa-tag"></i> Formatting Entities
</label>
<input
type="text"
id="node-input-formattingEntities"
placeholder="Formatting Entities"
style="width: 60%"
ng-model="formattingEntities"
/>
</div>
<div class="form-row">
<label for="node-input-silent"> <i class="fa fa-tag"></i> Silent </label>
<input type="checkbox" id="node-input-silent" ng-model="silent" />
</div>
<div class="form-row">
<label for="node-input-scheduleDate">
<i class="fa fa-tag"></i> Schedule Date
</label>
<input
type="number"
id="node-input-scheduleDate"
placeholder="Schedule Date"
style="width: 60%"
ng-model="scheduleDate"
/>
</div>
<div class="form-row">
<label for="node-input-buttons"> <i class="fa fa-tag"></i> Buttons </label>
<input
type="text"
id="node-input-buttons"
placeholder="Buttons"
style="width: 60%"
ng-model="buttons"
/>
</div>
<div class="form-row">
<label for="node-input-workers"> <i class="fa fa-tag"></i> Workers </label>
<input
type="number"
id="node-input-workers"
placeholder="Workers"
style="width: 60%"
ng-model="workers"
/>
</div>
<div class="form-row">
<label for="node-input-noforwards">
<i class="fa fa-tag"></i> No Forwards
</label>
<input type="checkbox" id="node-input-noforwards" ng-model="noforwards" />
</div>
<div class="form-row">
<label for="node-input-commentTo">
<i class="fa fa-tag"></i> Comment To
</label>
<input
type="text"
id="node-input-commentTo"
placeholder="Comment To"
style="width: 60%"
ng-model="commentTo"
/>
</div>
<div class="form-row">
<label for="node-input-topMsgId">
<i class="fa fa-tag"></i> Top Message ID
</label>
<input
type="text"
id="node-input-topMsgId"
placeholder="Top Message ID"
style="width: 60%"
ng-model="topMsgId"
/>
</div>
</script>
<script type="text/html" data-help-name="send-files">
<p>The <b>send-files</b> node allows you to send files to a Telegram chat or user using the Telegram API. It supports a wide range of parameters, including file attributes, captions, buttons, and scheduling options.</p>
<p>
The <b>send-files</b> node allows you to send files to a Telegram chat or
user using the Telegram API. It supports a wide range of parameters,
including file attributes, captions, buttons, and scheduling options.
</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload.chatId
<span class="property-type">string</span>
</dt>
<dd>The ID or username of the chat or user to send files to.</dd>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>
payload.chatId
<span class="property-type">string</span>
</dt>
<dd>The ID or username of the chat or user to send files to.</dd>
<dt>payload.files
<span class="property-type">array</span>
</dt>
<dd>A list of file paths or URLs to be sent. Multiple files can be specified.</dd>
<dt>
payload.files
<span class="property-type">array</span>
</dt>
<dd>
A list of file paths or URLs to be sent. Multiple files can be specified.
</dd>
<dt>payload.caption
<span class="property-type">string</span>
</dt>
<dd>The caption text to include with the file. Supports Markdown or HTML formatting.</dd>
<dt>
payload.caption
<span class="property-type">string</span>
</dt>
<dd>
The caption text to include with the file. Supports Markdown or HTML
formatting.
</dd>
<dt>payload.forceDocument
<span class="property-type">boolean</span>
</dt>
<dd>If <code>true</code>, forces the file to be sent as a document instead of its native media type.</dd>
<dt>
payload.forceDocument
<span class="property-type">boolean</span>
</dt>
<dd>
If <code>true</code>, forces the file to be sent as a document instead of
its native media type.
</dd>
<dt>payload.fileSize
<span class="property-type">number</span>
</dt>
<dd>Specifies the file size, if known. Useful for optimization.</dd>
<dt>
payload.fileSize
<span class="property-type">number</span>
</dt>
<dd>Specifies the file size, if known. Useful for optimization.</dd>
<dt>payload.clearDraft
<span class="property-type">boolean</span>
</dt>
<dd>If <code>true</code>, clears the draft in the chat before sending the file.</dd>
<dt>
payload.clearDraft
<span class="property-type">boolean</span>
</dt>
<dd>
If <code>true</code>, clears the draft in the chat before sending the
file.
</dd>
<dt>payload.progressCallback
<span class="property-type">function</span>
</dt>
<dd>A function to monitor the progress of file uploads.</dd>
<dt>
payload.progressCallback
<span class="property-type">function</span>
</dt>
<dd>A function to monitor the progress of file uploads.</dd>
<dt>payload.replyTo
<span class="property-type">number</span>
</dt>
<dd>The ID of the message to reply to with the file.</dd>
<dt>
payload.replyTo
<span class="property-type">number</span>
</dt>
<dd>The ID of the message to reply to with the file.</dd>
<dt>payload.attributes
<span class="property-type">object</span>
</dt>
<dd>Additional attributes for the file, such as file type or metadata.</dd>
<dt>
payload.attributes
<span class="property-type">object</span>
</dt>
<dd>Additional attributes for the file, such as file type or metadata.</dd>
<dt>payload.thumb
<span class="property-type">string</span>
</dt>
<dd>A path or URL to a thumbnail image for the file.</dd>
<dt>
payload.thumb
<span class="property-type">string</span>
</dt>
<dd>A path or URL to a thumbnail image for the file.</dd>
<dt>payload.voiceNote
<span class="property-type">boolean</span>
</dt>
<dd>If <code>true</code>, marks the file as a voice note.</dd>
<dt>
payload.voiceNote
<span class="property-type">boolean</span>
</dt>
<dd>If <code>true</code>, marks the file as a voice note.</dd>
<dt>payload.videoNote
<span class="property-type">boolean</span>
</dt>
<dd>If <code>true</code>, marks the file as a video note.</dd>
<dt>
payload.videoNote
<span class="property-type">boolean</span>
</dt>
<dd>If <code>true</code>, marks the file as a video note.</dd>
<dt>payload.supportsStreaming
<span class="property-type">boolean</span>
</dt>
<dd>If <code>true</code>, enables streaming support for video files.</dd>
<dt>
payload.supportsStreaming
<span class="property-type">boolean</span>
</dt>
<dd>If <code>true</code>, enables streaming support for video files.</dd>
<dt>payload.parseMode
<span class="property-type">string</span>
</dt>
<dd>Specifies the parse mode for captions (<code>Markdown</code> or <code>HTML</code>).</dd>
<dt>
payload.parseMode
<span class="property-type">string</span>
</dt>
<dd>
Specifies the parse mode for captions (<code>Markdown</code> or
<code>HTML</code>).
</dd>
<dt>payload.silent
<span class="property-type">boolean</span>
</dt>
<dd>If <code>true</code>, sends the message silently without a notification.</dd>
<dt>
payload.silent
<span class="property-type">boolean</span>
</dt>
<dd>
If <code>true</code>, sends the message silently without a notification.
</dd>
<dt>payload.scheduleDate
<span class="property-type">string | number</span>
</dt>
<dd>Schedules the message to be sent at a specific time. Accepts a UNIX timestamp or date string.</dd>
<dt>
payload.scheduleDate
<span class="property-type">string | number</span>
</dt>
<dd>
Schedules the message to be sent at a specific time. Accepts a UNIX
timestamp or date string.
</dd>
<dt>payload.buttons
<span class="property-type">array</span>
</dt>
<dd>An array of buttons to include with the file message.</dd>
<dt>
payload.buttons
<span class="property-type">array</span>
</dt>
<dd>An array of buttons to include with the file message.</dd>
<dt>payload.workers
<span class="property-type">number</span>
</dt>
<dd>The number of workers for parallel uploads.</dd>
<dt>
payload.workers
<span class="property-type">number</span>
</dt>
<dd>The number of workers for parallel uploads.</dd>
<dt>payload.noforwards
<span class="property-type">boolean</span>
</dt>
<dd>If <code>true</code>, prevents the message from being forwarded.</dd>
<dt>
payload.noforwards
<span class="property-type">boolean</span>
</dt>
<dd>If <code>true</code>, prevents the message from being forwarded.</dd>
<dt>payload.commentTo
<span class="property-type">number</span>
</dt>
<dd>The ID of the message to comment on in a channel.</dd>
<dt>
payload.commentTo
<span class="property-type">number</span>
</dt>
<dd>The ID of the message to comment on in a channel.</dd>
<dt>payload.topMsgId
<span class="property-type">number</span>
</dt>
<dd>The ID of the pinned message to associate with the file.</dd>
</dl>
<dt>
payload.topMsgId
<span class="property-type">number</span>
</dt>
<dd>The ID of the pinned message to associate with the file.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">object</span>
</dt>
<dd>Contains the response from the Telegram API, including details of the sent message or file.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>
payload
<span class="property-type">object</span>
</dt>
<dd>
Contains the response from the Telegram API, including details of the sent
message or file.
</dd>
</dl>
<h3>Details</h3>
<p>The <b>send-files</b> node allows advanced file-sending capabilities with full customization. It supports various Telegram features such as captions with rich text formatting, scheduled messages, silent delivery, and progress tracking.</p>
<h3>Details</h3>
<p>
The <b>send-files</b> node allows advanced file-sending capabilities with
full customization. It supports various Telegram features such as captions
with rich text formatting, scheduled messages, silent delivery, and progress
tracking.
</p>
<h3>Example</h3>
<pre>
<h3>Example</h3>
<pre>
{
"payload": {
"chatId": "@examplechannel",
@ -451,13 +466,23 @@
]
}
}
</pre>
<p>This example sends two files with a caption, using Markdown for formatting, and includes a button linking to a website. The message is sent silently.</p>
</pre
>
<p>
This example sends two files with a caption, using Markdown for formatting,
and includes a button linking to a website. The message is sent silently.
</p>
<h3>Error Handling</h3>
<p>If an error occurs (e.g., invalid file path or chat ID), the node logs an error message and does not send the file.</p>
<h3>Error Handling</h3>
<p>
If an error occurs (e.g., invalid file path or chat ID), the node logs an
error message and does not send the file.
</p>
<h3>Configuration</h3>
<p>The node can use a globally configured Telegram client or a client instance provided in the message payload. Ensure the client has permissions to send messages in the specified chat.</p>
<h3>Configuration</h3>
<p>
The node can use a globally configured Telegram client or a client instance
provided in the message payload. Ensure the client has permissions to send
messages in the specified chat.
</p>
</script>

View File

@ -1,72 +1,77 @@
module.exports = function (RED) {
function SendFile(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
function SendFile(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
this.on('input', async function (msg) {
const chatId = msg.payload.chatId || config.chatId;
const files = msg.payload.files || config.files.split(',').map(file => file.trim());
const caption = msg.payload.caption || config.caption;
const forceDocument = msg.payload.forceDocument || config.forceDocument;
const fileSize = msg.payload.fileSize || config.fileSize;
const clearDraft = msg.payload.clearDraft || config.clearDraft;
const progressCallback = msg.payload.progressCallback || config.progressCallback;
const replyTo = msg.payload.replyTo || config.replyTo;
const attributes = msg.payload.attributes || config.attributes;
const thumb = msg.payload.thumb || config.thumb;
const voiceNote = msg.payload.voiceNote || config.voiceNote;
const videoNote = msg.payload.videoNote || config.videoNote;
const supportsStreaming = msg.payload.supportsStreaming || config.supportsStreaming;
const parseMode = msg.payload.parseMode || config.parseMode;
const formattingEntities = msg.payload.formattingEntities || config.formattingEntities;
const silent = msg.payload.silent || config.silent;
const scheduleDate = msg.payload.scheduleDate || config.scheduleDate;
const buttons = msg.payload.buttons || config.buttons;
const workers = msg.payload.workers || config.workers;
const noforwards = msg.payload.noforwards || config.noforwards;
const commentTo = msg.payload.commentTo || config.commentTo;
const topMsgId = msg.payload.topMsgId || config.topMsgId;
this.on("input", async function (msg) {
const chatId = msg.payload.chatId || config.chatId;
const files =
msg.payload.files || config.files.split(",").map((file) => file.trim());
const caption = msg.payload.caption || config.caption;
const forceDocument = msg.payload.forceDocument || config.forceDocument;
const fileSize = msg.payload.fileSize || config.fileSize;
const clearDraft = msg.payload.clearDraft || config.clearDraft;
const progressCallback =
msg.payload.progressCallback || config.progressCallback;
const replyTo = msg.payload.replyTo || config.replyTo;
const attributes = msg.payload.attributes || config.attributes;
const thumb = msg.payload.thumb || config.thumb;
const voiceNote = msg.payload.voiceNote || config.voiceNote;
const videoNote = msg.payload.videoNote || config.videoNote;
const supportsStreaming =
msg.payload.supportsStreaming || config.supportsStreaming;
const parseMode = msg.payload.parseMode || config.parseMode;
const formattingEntities =
msg.payload.formattingEntities || config.formattingEntities;
const silent = msg.payload.silent || config.silent;
const scheduleDate = msg.payload.scheduleDate || config.scheduleDate;
const buttons = msg.payload.buttons || config.buttons;
const workers = msg.payload.workers || config.workers;
const noforwards = msg.payload.noforwards || config.noforwards;
const commentTo = msg.payload.commentTo || config.commentTo;
const topMsgId = msg.payload.topMsgId || config.topMsgId;
/** @type {TelegramClient} */
const client = msg.payload?.client ? msg.payload.client : this.config.client;
/** @type {TelegramClient} */
const client = msg.payload?.client
? msg.payload.client
: this.config.client;
try {
const params = {
file: files,
caption: caption,
forceDocument: forceDocument,
fileSize: fileSize,
clearDraft: clearDraft,
progressCallback: progressCallback,
replyTo: replyTo,
attributes: attributes,
thumb: thumb,
voiceNote: voiceNote,
videoNote: videoNote,
supportsStreaming: supportsStreaming,
parseMode: parseMode,
formattingEntities: formattingEntities,
silent: silent,
scheduleDate: scheduleDate,
buttons: buttons,
workers: workers,
noforwards: noforwards,
commentTo: commentTo,
topMsgId: topMsgId,
};
// Отправка файлов
const response = await client.sendFile(chatId, params);
node.send({
payload: response,
});
} catch (err) {
node.error('Error sending files: ' + err.message);
}
try {
const params = {
file: files,
caption: caption,
forceDocument: forceDocument,
fileSize: fileSize,
clearDraft: clearDraft,
progressCallback: progressCallback,
replyTo: replyTo,
attributes: attributes,
thumb: thumb,
voiceNote: voiceNote,
videoNote: videoNote,
supportsStreaming: supportsStreaming,
parseMode: parseMode,
formattingEntities: formattingEntities,
silent: silent,
scheduleDate: scheduleDate,
buttons: buttons,
workers: workers,
noforwards: noforwards,
commentTo: commentTo,
topMsgId: topMsgId,
};
// Sending files
const response = await client.sendFile(chatId, params);
node.send({
payload: response,
});
}
} catch (err) {
node.error("Error sending files: " + err.message);
}
});
}
RED.nodes.registerType('send-files', SendFile);
RED.nodes.registerType("send-files", SendFile);
};

View File

@ -1,292 +1,335 @@
<script type="text/javascript">
RED.nodes.registerType('send-message', {
category: 'telegram-account',
color: '#229ED9',
icon: 'tg.png',
align: "right",
defaults: {
name: { value: '' },
chatId: { value: '' },
message: { value: '' },
config: { type: 'config', required: false },
parseMode: { value: 'md' }, // Default parse mode is Markdown
schedule: { value: '' }, // Default schedule is empty
replyTo: { value: '' }, // Reply to a message
attributes: { value: '' }, // Message attributes
formattingEntities: { value: '' }, // Message formatting entities
linkPreview: { value: false }, // Show link preview
file: { value: [] }, // File to be sent
thumb: { value: '' }, // Thumbnail for the file
forceDocument: { value: false }, // Send the file as a document
clearDraft: { value: false }, // Clear existing draft
buttons: { value: '' }, // Buttons to be shown after sending
silent: { value: false }, // Whether to notify in a broadcast channel
supportStreaming: { value: false }, // Whether the sent video supports streaming
noforwards: { value: false }, // Disable forwarding
commentTo: { value: '' }, // Comment to a message in a broadcast channel
topMsgId: { value: '' }, // Reply to a specific thread
},
inputs: 1,
outputs: 1,
label: function () {
return this.name || 'Send Message';
},
oneditprepare: function () {
const node = this;
RED.nodes.registerType("send-message", {
category: "telegram-account",
color: "#229ED9",
icon: "tg.png",
align: "right",
defaults: {
name: { value: "" },
chatId: { value: "" },
message: { value: "" },
config: { type: "config", required: false },
parseMode: { value: "md" }, // Default parse mode is Markdown
schedule: { value: "" }, // Default schedule is empty
replyTo: { value: "" }, // Reply to a message
attributes: { value: "" }, // Message attributes
formattingEntities: { value: "" }, // Message formatting entities
linkPreview: { value: false }, // Show link preview
file: { value: [] }, // File to be sent
thumb: { value: "" }, // Thumbnail for the file
forceDocument: { value: false }, // Send the file as a document
clearDraft: { value: false }, // Clear existing draft
buttons: { value: "" }, // Buttons to be shown after sending
silent: { value: false }, // Whether to notify in a broadcast channel
supportStreaming: { value: false }, // Whether the sent video supports streaming
noforwards: { value: false }, // Disable forwarding
commentTo: { value: "" }, // Comment to a message in a broadcast channel
topMsgId: { value: "" }, // Reply to a specific thread
},
inputs: 1,
outputs: 1,
label: function () {
return this.name || "Send Message";
},
oneditprepare: function () {
const node = this;
$("#node-input-parseMode").change(function () {
if ($(this).val() === "html") {
$("#node-linkPreview-row").show();
} else {
$("#node-linkPreview-row").hide();
}
});
$("#node-input-parseMode").change(function () {
if ($(this).val() === "html") {
$("#node-linkPreview-row").show();
} else {
$("#node-linkPreview-row").hide();
}
});
// const filesContainer = $("#node-input-files-container");
// if (Array.isArray(node.file)) {
// node.file.forEach(file => {
// const fileInput = `
// <div class="node-input-file-container">
// <input type="text" class="node-input-file" style="display: block; margin-top: 5px;" value="${file}">
// <button class="node-input-remove-file editor-button editor-button-small editor-button-delete" type="button">Remove</button>
// </div>
// `;
// filesContainer.append(fileInput);
// });
// }
// const filesContainer = $("#node-input-files-container");
// if (Array.isArray(node.file)) {
// node.file.forEach(file => {
// const fileInput = `
// <div class="node-input-file-container">
// <input type="text" class="node-input-file" style="display: block; margin-top: 5px;" value="${file}">
// <button class="node-input-remove-file editor-button editor-button-small editor-button-delete" type="button">Remove</button>
// </div>
// `;
// filesContainer.append(fileInput);
// });
// }
// $("#node-input-add-file").click(function () {
// const filesContainer = $("#node-input-files-container");
// const fileInput = `
// <div class="node-input-file-container">
// <input type="text" class="node-input-file" style="display: block; margin-top: 5px;">
// <button class="node-input-remove-file editor-button editor-button-small editor-button-delete" type="button">Remove</button>
// </div>
// `;
// filesContainer.append(fileInput);
// });
// $("#node-input-add-file").click(function () {
// const filesContainer = $("#node-input-files-container");
// const fileInput = `
// <div class="node-input-file-container">
// <input type="text" class="node-input-file" style="display: block; margin-top: 5px;">
// <button class="node-input-remove-file editor-button editor-button-small editor-button-delete" type="button">Remove</button>
// </div>
// `;
// filesContainer.append(fileInput);
// });
// $("#node-input-files-container").on("click", ".node-input-remove-file", function () {
// $(this).closest(".node-input-file-container").remove();
// const removedIndex = $(this).closest(".node-input-file-container").index();
// const updatedFiles = node.file || [];
// updatedFiles.splice(removedIndex, 1);
// node.file = updatedFiles;
// });
// $("#node-input-files-container").on("click", ".node-input-remove-file", function () {
// $(this).closest(".node-input-file-container").remove();
// const removedIndex = $(this).closest(".node-input-file-container").index();
// const updatedFiles = node.file || [];
// updatedFiles.splice(removedIndex, 1);
// node.file = updatedFiles;
// });
// $("#node-input-files-container").on("change", ".node-input-file", function () {
// const fileInput = $(this);
// const updatedFiles = node.file || [];
// updatedFiles.push(fileInput.val());
// node.file = updatedFiles;
// });
},
});
// $("#node-input-files-container").on("change", ".node-input-file", function () {
// const fileInput = $(this);
// const updatedFiles = node.file || [];
// updatedFiles.push(fileInput.val());
// node.file = updatedFiles;
// });
},
});
</script>
<script type="text/html" data-template-name="send-message">
<div class="form-row">
<label for="node-input-name">
<i class="fa fa-tag"></i> Name
</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-config">
<i class="fa fa-gear"></i> Config
</label>
<input type="hidden" id="node-input-config">
</div>
<div class="form-row">
<label for="node-input-chat_id">
<i class="fa fa-user"></i> Chat ID
</label>
<input type="text" id="node-input-chatId" placeholder="Peer ID">
</div>
<div class="form-row">
<label for="node-input-message">
<i class="fa fa-pencil"></i> Message
</label>
<textarea type="text" id="node-input-message" placeholder="Message"></textarea>
</div>
<div class="form-row">
<label for="node-input-parseMode">
<i class="fa fa-code"></i> Parse Mode
</label>
<select id="node-input-parseMode">
<option value="md" selected>Markdown</option>
<option value="html">HTML</option>
</select>
</div>
<div class="form-row">
<label for="node-input-schedule">
<i class="fa fa-clock"></i> Schedule
</label>
<input type="datetime-local" id="node-input-schedule" placeholder="Schedule">
</div>
<div class="form-row" id="node-linkPreview-row">
<label for="node-input-linkPreview">
<i class="fa fa-link"></i> Show Link Preview
</label>
<input type="checkbox" id="node-input-linkPreview">
</div>
<div class="form-row">
<label>
<i class="fa fa-file"></i> Files
<label for="node-input-name"> <i class="fa fa-tag"></i> Name </label>
<input type="text" id="node-input-name" placeholder="Name" />
</div>
<div class="form-row">
<label for="node-input-config"> <i class="fa fa-gear"></i> Config </label>
<input type="hidden" id="node-input-config" />
</div>
<div class="form-row">
<label for="node-input-chat_id"> <i class="fa fa-user"></i> Chat ID </label>
<input type="text" id="node-input-chatId" placeholder="Peer ID" />
</div>
<div class="form-row">
<label for="node-input-message">
<i class="fa fa-pencil"></i> Message
</label>
<div id="node-input-files-container">
<textarea
type="text"
id="node-input-message"
placeholder="Message"
></textarea>
</div>
<div class="form-row">
<label for="node-input-parseMode">
<i class="fa fa-code"></i> Parse Mode
</label>
<select id="node-input-parseMode">
<option value="md" selected>Markdown</option>
<option value="html">HTML</option>
</select>
</div>
<div class="form-row">
<label for="node-input-schedule">
<i class="fa fa-clock"></i> Schedule
</label>
<input
type="datetime-local"
id="node-input-schedule"
placeholder="Schedule"
/>
</div>
<div class="form-row" id="node-linkPreview-row">
<label for="node-input-linkPreview">
<i class="fa fa-link"></i> Show Link Preview
</label>
<input type="checkbox" id="node-input-linkPreview" />
</div>
<div class="form-row">
<label> <i class="fa fa-file"></i> Files </label>
<div id="node-input-files-container"></div>
<button
id="node-input-add-file"
class="editor-button editor-button-large editor-button-add"
style="margin-top: 1rem;"
type="button"
>
Add File
</button>
</div>
</div>
<button id="node-input-add-file" class="editor-button editor-button-large editor-button-add" style="margin-top: 1rem;" type="button">Add File</button>
</div>
<div class="form-row" id="node-thumb-row">
<label for="node-input-thumb">
<i class="fa fa-file-image"></i> Thumbnail (Optional)
</label>
<input type="text" id="node-input-thumb">
<label for="node-input-thumb">
<i class="fa fa-file-image"></i> Thumbnail (Optional)
</label>
<input type="text" id="node-input-thumb" />
</div>
<div class="form-row">
<label for="node-input-forceDocument">
<i class="fa fa-file"></i> Force Send as Document
</label>
<input type="checkbox" id="node-input-forceDocument">
</div>
<div class="form-row">
<label for="node-input-clearDraft">
<i class="fa fa-trash"></i> Clear Existing Draft
</label>
<input type="checkbox" id="node-input-clearDraft">
</div>
<div class="form-row">
<label for="node-input-buttons">
<i class="fa fa-th"></i> Buttons
</label>
<textarea type="text" id="node-input-buttons" placeholder="Buttons"></textarea>
</div>
<div class="form-row">
<label for="node-input-silent">
<i class="fa fa-bell-slash"></i> Silent Notification
</label>
<input type="checkbox" id="node-input-silent">
</div>
<div class="form-row">
<label for="node-input-supportStreaming">
<i class="fa fa-video"></i> Support Streaming
</label>
<input type="checkbox" id="node-input-supportStreaming">
</div>
<div class="form-row">
<label for="node-input-noforwards">
<i class="fa fa-forward"></i> Disable Forwarding
</label>
<input type="checkbox" id="node-input-noforwards">
</div>
<div class="form-row">
<label for="node-input-commentTo">
<i class="fa fa-comments"></i> Comment To Message
</label>
<input type="text" id="node-input-commentTo" placeholder="Comment To">
</div>
<div class="form-row">
<label for="node-input-topMsgId">
<i class="fa fa-reply"></i> Reply To Thread
</label>
<input type="text" id="node-input-topMsgId" placeholder="Top Msg ID">
</div>
</script>
<script type="text/html" data-help-name="send-message">
<p>The <b>send-message</b> node allows sending messages via Telegram using the <code>telegram</code> library. It supports various message types, formatting options, and additional features like scheduling and silent messages.</p>
<div class="form-row">
<label for="node-input-forceDocument">
<i class="fa fa-file"></i> Force Send as Document
</label>
<input type="checkbox" id="node-input-forceDocument" />
</div>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload.chatId
<span class="property-type">string</span>
</dt>
<dd>The recipient's chat ID, username (prefixed with <code>@</code>), or "me" for personal chats.</dd>
<dt>payload.message
<span class="property-type">string</span>
</dt>
<dd>The message text to send.</dd>
<dt>payload.parseMode
<span class="property-type">string</span>
</dt>
<dd>Specifies the formatting mode, such as <code>markdown</code> or <code>html</code>.</dd>
<dt>payload.schedule
<span class="property-type">string</span>
</dt>
<dd>The timestamp (or ISO date string) to schedule the message for later sending.</dd>
<dt>payload.replyTo
<span class="property-type">number</span>
</dt>
<dd>The ID of the message to reply to, if applicable.</dd>
<div class="form-row">
<label for="node-input-clearDraft">
<i class="fa fa-trash"></i> Clear Existing Draft
</label>
<input type="checkbox" id="node-input-clearDraft" />
</div>
<dt>payload.attributes
<span class="property-type">object</span>
</dt>
<dd>Additional attributes for the message.</dd>
<div class="form-row">
<label for="node-input-buttons"> <i class="fa fa-th"></i> Buttons </label>
<textarea
type="text"
id="node-input-buttons"
placeholder="Buttons"
></textarea>
</div>
<dt>payload.linkPreview
<span class="property-type">boolean</span>
</dt>
<dd>Determines whether to show link previews in the message.</dd>
<dt>payload.file
<span class="property-type">string</span>
</dt>
<dd>The path to a file to send (e.g., images, videos, documents).</dd>
<dt>payload.thumb
<span class="property-type">string</span>
</dt>
<dd>The path to a thumbnail for the file, if applicable.</dd>
<dt>payload.buttons
<span class="property-type">array</span>
</dt>
<dd>An array of button configurations for inline or reply keyboards.</dd>
<dt>payload.silent
<span class="property-type">boolean</span>
</dt>
<dd>If true, sends the message silently (no notification sound).</dd>
</dl>
<div class="form-row">
<label for="node-input-silent">
<i class="fa fa-bell-slash"></i> Silent Notification
</label>
<input type="checkbox" id="node-input-silent" />
</div>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload.response
<span class="property-type">object</span>
</dt>
<dd>The response from the Telegram API, containing details of the sent message.</dd>
</dl>
<div class="form-row">
<label for="node-input-supportStreaming">
<i class="fa fa-video"></i> Support Streaming
</label>
<input type="checkbox" id="node-input-supportStreaming" />
</div>
<h3>Details</h3>
<p>To use the <b>send-message</b> node, you must provide the recipient's chat ID and the message content. Additional parameters like <code>file</code>, <code>thumb</code>, and <code>buttons</code> enable sending multimedia messages or interactive content. The node automatically resolves usernames prefixed with <code>@</code> to their corresponding chat entities.</p>
<div class="form-row">
<label for="node-input-noforwards">
<i class="fa fa-forward"></i> Disable Forwarding
</label>
<input type="checkbox" id="node-input-noforwards" />
</div>
<p>If scheduling is enabled, the <code>schedule</code> parameter should contain a valid timestamp or ISO date string. When sending files, ensure the <code>file</code> parameter specifies the correct path or URL.</p>
<div class="form-row">
<label for="node-input-commentTo">
<i class="fa fa-comments"></i> Comment To Message
</label>
<input type="text" id="node-input-commentTo" placeholder="Comment To" />
</div>
<h3>Example</h3>
<pre>
<div class="form-row">
<label for="node-input-topMsgId">
<i class="fa fa-reply"></i> Reply To Thread
</label>
<input type="text" id="node-input-topMsgId" placeholder="Top Msg ID" />
</div>
</script>
<script type="text/html" data-help-name="send-message">
<p>
The <b>send-message</b> node allows sending messages via Telegram using the
<code>telegram</code> library. It supports various message types, formatting
options, and additional features like scheduling and silent messages.
</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>
payload.chatId
<span class="property-type">string</span>
</dt>
<dd>
The recipient's chat ID, username (prefixed with <code>@</code>), or "me"
for personal chats.
</dd>
<dt>
payload.message
<span class="property-type">string</span>
</dt>
<dd>The message text to send.</dd>
<dt>
payload.parseMode
<span class="property-type">string</span>
</dt>
<dd>
Specifies the formatting mode, such as <code>markdown</code> or
<code>html</code>.
</dd>
<dt>
payload.schedule
<span class="property-type">string</span>
</dt>
<dd>
The timestamp (or ISO date string) to schedule the message for later
sending.
</dd>
<dt>
payload.replyTo
<span class="property-type">number</span>
</dt>
<dd>The ID of the message to reply to, if applicable.</dd>
<dt>
payload.attributes
<span class="property-type">object</span>
</dt>
<dd>Additional attributes for the message.</dd>
<dt>
payload.linkPreview
<span class="property-type">boolean</span>
</dt>
<dd>Determines whether to show link previews in the message.</dd>
<dt>
payload.file
<span class="property-type">string</span>
</dt>
<dd>The path to a file to send (e.g., images, videos, documents).</dd>
<dt>
payload.thumb
<span class="property-type">string</span>
</dt>
<dd>The path to a thumbnail for the file, if applicable.</dd>
<dt>
payload.buttons
<span class="property-type">array</span>
</dt>
<dd>An array of button configurations for inline or reply keyboards.</dd>
<dt>
payload.silent
<span class="property-type">boolean</span>
</dt>
<dd>If true, sends the message silently (no notification sound).</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>
payload.response
<span class="property-type">object</span>
</dt>
<dd>
The response from the Telegram API, containing details of the sent
message.
</dd>
</dl>
<h3>Details</h3>
<p>
To use the <b>send-message</b> node, you must provide the recipient's chat
ID and the message content. Additional parameters like <code>file</code>,
<code>thumb</code>, and <code>buttons</code> enable sending multimedia
messages or interactive content. The node automatically resolves usernames
prefixed with <code>@</code> to their corresponding chat entities.
</p>
<p>
If scheduling is enabled, the <code>schedule</code> parameter should contain
a valid timestamp or ISO date string. When sending files, ensure the
<code>file</code> parameter specifies the correct path or URL.
</p>
<h3>Example</h3>
<pre>
{
"payload": {
"chatId": "@example_user",
@ -295,6 +338,10 @@
"silent": true
}
}
</pre>
<p>This input sends a silent message in Markdown format to the user <code>@example_user</code>.</p>
</pre
>
<p>
This input sends a silent message in Markdown format to the user
<code>@example_user</code>.
</p>
</script>

View File

@ -2,82 +2,84 @@ const { TelegramClient } = require("telegram");
const { parseID } = require("telegram/Utils");
module.exports = function (RED) {
function SendMessage(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
function SendMessage(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
this.on('input', async function (msg) {
let chatId = msg.payload.chatId || config.chatId;
const message = msg.payload.message || config.message;
const parseMode = msg.payload.parseMode || config.parseMode;
const schedule = msg.payload.schedule || config.schedule;
const replyTo = msg.payload.replyTo || config.replyTo;
const attributes = msg.payload.attributes || config.attributes;
const formattingEntities = msg.payload.formattingEntities || config.formattingEntities;
const linkPreview = msg.payload.linkPreview || config.linkPreview;
const file = msg.payload.file || config.file;
const thumb = msg.payload.thumb || config.thumb;
const forceDocument = msg.payload.forceDocument || config.forceDocument;
const clearDraft = msg.payload.clearDraft || config.clearDraft;
const buttons = msg.payload.buttons || config.buttons;
const silent = msg.payload.silent || config.silent;
const supportStreaming = msg.payload.supportStreaming || config.supportStreaming;
const noforwards = msg.payload.noforwards || config.noforwards;
const commentTo = msg.payload.commentTo || config.commentTo;
const topMsgId = msg.payload.topMsgId || config.topMsgId;
this.on("input", async function (msg) {
let chatId = msg.payload.chatId || config.chatId;
const message = msg.payload.message || config.message;
const parseMode = msg.payload.parseMode || config.parseMode;
const schedule = msg.payload.schedule || config.schedule;
const replyTo = msg.payload.replyTo || config.replyTo;
const attributes = msg.payload.attributes || config.attributes;
const formattingEntities =
msg.payload.formattingEntities || config.formattingEntities;
const linkPreview = msg.payload.linkPreview || config.linkPreview;
const file = msg.payload.file || config.file;
const thumb = msg.payload.thumb || config.thumb;
const forceDocument = msg.payload.forceDocument || config.forceDocument;
const clearDraft = msg.payload.clearDraft || config.clearDraft;
const buttons = msg.payload.buttons || config.buttons;
const silent = msg.payload.silent || config.silent;
const supportStreaming =
msg.payload.supportStreaming || config.supportStreaming;
const noforwards = msg.payload.noforwards || config.noforwards;
const commentTo = msg.payload.commentTo || config.commentTo;
const topMsgId = msg.payload.topMsgId || config.topMsgId;
/** @type {TelegramClient} */
const client = msg.payload?.client ? msg.payload.client : this.config.client;
let peerId = chatId === "me" ? chatId : parseID(chatId);
/** @type {TelegramClient} */
const client = msg.payload?.client
? msg.payload.client
: this.config.client;
let peerId = chatId === "me" ? chatId : parseID(chatId);
try {
const params = {
message: message,
parseMode: parseMode,
replyTo: replyTo !== ""? replyTo:undefined,
attributes: attributes,
formattingEntities: formattingEntities !== ""? formattingEntities:undefined,
linkPreview: linkPreview,
file: file !== "" && file.length > 1? file:undefined,
thumb: thumb,
forceDocument: forceDocument,
clearDraft: clearDraft,
buttons: buttons !== "" ? buttons : undefined,
silent: silent,
supportStreaming: supportStreaming,
noforwards: noforwards,
commentTo: commentTo !== "" ? commentTo : undefined,
topMsgId: topMsgId !== topMsgId ? commentTo : undefined,
};
try {
const params = {
message: message,
parseMode: parseMode,
replyTo: replyTo !== "" ? replyTo : undefined,
attributes: attributes,
formattingEntities:
formattingEntities !== "" ? formattingEntities : undefined,
linkPreview: linkPreview,
file: file !== "" && file.length > 1 ? file : undefined,
thumb: thumb,
forceDocument: forceDocument,
clearDraft: clearDraft,
buttons: buttons !== "" ? buttons : undefined,
silent: silent,
supportStreaming: supportStreaming,
noforwards: noforwards,
commentTo: commentTo !== "" ? commentTo : undefined,
topMsgId: topMsgId !== topMsgId ? commentTo : undefined,
};
if (schedule) {
params.schedule = new Date(schedule).getTime() / 1000;
}
if (schedule) {
params.schedule = new Date(schedule).getTime() / 1000;
}
let response;
if (chatId[0] === "@") {
peerId = await client.getEntity(chatId);
}
let response;
if (chatId[0] === "@") {
peerId = await client.getEntity(chatId);
}
try {
response = await client.sendMessage(peerId, params);
} catch (error) {
const entity = await client.getInputEntity(peerId)
await client.sendMessage(entity, params);
}
node.send({
payload: { response },
});
} catch (err) {
node.error('Error send message: ' + err.message);
}
try {
response = await client.sendMessage(peerId, params);
} catch (error) {
const entity = await client.getInputEntity(peerId);
await client.sendMessage(entity, params);
}
node.send({
payload: { response },
});
} catch (err) {
node.error("Error send message: " + err.message);
}
});
}
}
RED.nodes.registerType('send-message', SendMessage);
RED.nodes.registerType("send-message", SendMessage);
};