first commit

This commit is contained in:
borovlioff
2023-09-09 00:11:17 +03:00
commit 9c23f9ba86
22 changed files with 2547 additions and 0 deletions

60
sample/command.html Normal file
View File

@@ -0,0 +1,60 @@
<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';
},
});
</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-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>

55
sample/command.js Normal file
View File

@@ -0,0 +1,55 @@
const { NewMessage } = require("telegram/events");
// event.js
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;
try {
client.addEventHandler((update) => {
const message = update.message.message
if (message) {
if (config.regex) {
const regex = new RegExp(config.command);
if (regex.test(message)) {
// Создаем объект сообщения для отправки в следующий узел
var msg = {
payload: {
update
// Другие поля сообщения, которые вы хотите передать
}
};
// Отправляем объект сообщения в следующий узел
node.send(msg);
}
} else if (message === config.command) {
// Создаем объект сообщения для отправки в следующий узел
var msg = {
payload: {
update
// Другие поля сообщения, которые вы хотите передать
}
};
// Отправляем объект сообщения в следующий узел
node.send(msg);
}
}
}, new NewMessage());
} catch (err) {
node.error('Ошибка авторизации: ' + err.message);
}
}
RED.nodes.registerType('command', Command);
};

131
sample/config.html Normal file
View File

@@ -0,0 +1,131 @@
<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() {
}
});
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</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">
</div>
</script>

58
sample/config.js Normal file
View File

@@ -0,0 +1,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;
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);
}
this.on("close", () => {
if (this.client) {
this.client.disconnect();
node.status({ fill: "red", shape: "ring", text: "Disconnected" });
}
});
}
RED.nodes.registerType('config', TelegramClientConfig);
};

View File

@@ -0,0 +1,47 @@
<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';
},
});
</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>
<script type="text/html" data-help-name="delete-message">
input config, chatId, messageIds
</script>

27
sample/delete-message.js Normal file
View File

@@ -0,0 +1,27 @@
module.exports = function (RED) {
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;
try {
const response = await client.deleteMessages(chatId, messageIds, revoke);
node.send({
payload: response,
});
} catch (err) {
node.error('Error deleting message: ' + err.message);
}
});
}
RED.nodes.registerType('delete-message', DeleteMessage);
};

42
sample/get-entity.html Normal file
View File

@@ -0,0 +1,42 @@
<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';
}
});
</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>
</script>

36
sample/get-entity.js Normal file
View File

@@ -0,0 +1,36 @@
module.exports = function (RED) {
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;
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);
}
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);
};

BIN
sample/icons/tg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

128
sample/iter-dialogs.html Normal file
View File

@@ -0,0 +1,128 @@
<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';
},
});
</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>
</script>

54
sample/iter-dialogs.js Normal file
View File

@@ -0,0 +1,54 @@
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;
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,
}
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);
};

256
sample/iter-messages.html Normal file
View File

@@ -0,0 +1,256 @@
<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))
}
},
});
</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>
</script>

96
sample/iter-messages.js Normal file
View File

@@ -0,0 +1,96 @@
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;
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;
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;
}
console.log("🚀 ~ file: iter-messages.js:58 ~ params:", params)
console.log(chatId)
if (chatId[0] === "@") {
peerId = await client.getEntity(chatId);
}
const messages = {};
const filters = msg.payload?.filters || config.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);
}
});
}
RED.nodes.registerType('iter-messages', IterMessages);
};

56
sample/receiver.html Normal file
View File

@@ -0,0 +1,56 @@
<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';
},
});
</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>

33
sample/receiver.js Normal file
View File

@@ -0,0 +1,33 @@
const { NewMessage } = require("telegram/events");
// event.js
module.exports = function (RED) {
function Receiver(config) {
RED.nodes.createNode(this, config);
this.config = RED.nodes.getNode(config.config);
var node = this;
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())){
node.send({
payload:{
update
}
} )
}
}, new NewMessage());
} catch (err) {
node.error('Ошибка авторизации: ' + err.message);
}
}
RED.nodes.registerType('receiver', Receiver);
};

316
sample/send-file.html Normal file
View File

@@ -0,0 +1,316 @@
<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';
},
});
</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>
</script>

72
sample/send-file.js Normal file
View File

@@ -0,0 +1,72 @@
module.exports = function (RED) {
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;
/** @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);
}
});
}
RED.nodes.registerType('send-files', SendFile);
};

212
sample/send-message.html Normal file
View File

@@ -0,0 +1,212 @@
<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;
$("#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);
// });
// }
// $("#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("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>
<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 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">
</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>

83
sample/send-message.js Normal file
View File

@@ -0,0 +1,83 @@
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;
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);
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,
};
console.log("🚀 ~ file: send-message.js:60 ~ params:", params)
if (schedule) {
params.schedule = new Date(schedule).getTime() / 1000;
}
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);
}
});
}
RED.nodes.registerType('send-message', SendMessage);
};