node-red-contrib-maxbot 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,26 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - uses: actions/setup-node@v4
16
+ with:
17
+ node-version: 20
18
+ registry-url: https://registry.npmjs.org/
19
+
20
+ - run: npm install
21
+
22
+ - run: npm run build
23
+
24
+ - run: npm publish --access public
25
+ env:
26
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # node-red-contrib-maxbot
2
+ Node-RED nodes for MAX messenger
3
+
4
+
5
+ ### Примеры узлов function и данных отправляемых в узел maxbot-send
6
+
7
+ Отправить сообщение пользователю:
8
+ ```
9
+ const data = `Привет!`;
10
+
11
+ return {
12
+ payload: data,
13
+ userId: msg.payload.userId, // пользователь, кому написать
14
+ };
15
+ ```
16
+
17
+
18
+ Отправить сообщение в чат:
19
+ ```
20
+ const data = `Привет, ${msg.payload.data.sender.name}!`;
21
+
22
+ return {
23
+ payload: data,
24
+ chatId: msg.payload.chatId, // чат, куда написать
25
+ };
26
+ ```
27
+
28
+ Отправить сообщение в чат в ответ на исходное сообщение:
29
+ ```
30
+ const data = {
31
+ text: `Привет, ${msg.payload.data.sender.name}!`,
32
+ link: { type: 'reply', mid: msg.payload.data.body.mid}, // ссылка на сообщение. на которое идет ответ
33
+ };
34
+
35
+ return {
36
+ payload: data,
37
+ chatId: msg.payload.chatId, // чат, куда написать
38
+ };
39
+ ```
40
+
41
+ Форматирование и клавиатура в сообщении:
42
+ ```
43
+ const data = {
44
+ text: '**Привет!** _Добро пожаловать_ в [MAX](https://dev.max.ru).',
45
+ format: 'markdown',
46
+ attachments: [{
47
+ type: 'inline_keyboard',
48
+ payload: {
49
+ buttons: [
50
+ [
51
+ {type: 'callback', text: 'Проверка', payload: 'button1'},
52
+ {type: 'callback', text: 'Еще одна', payload: 'button2'}
53
+ ],
54
+ [
55
+ {type: 'link', text: 'Ссылка далеко-далеко', url: 'https://dev.max.ru'}
56
+ ]
57
+ ]
58
+ }
59
+ }]
60
+ };
61
+
62
+ return {
63
+ payload: data,
64
+ chatId: msg.payload.chatId, // чат, куда написать
65
+ };
66
+ ```
67
+
68
+ Выставить доступные команды:
69
+ ```
70
+ const data = [
71
+ {
72
+ name: "menu",
73
+ description: "Меню"
74
+ },
75
+ {
76
+ name: "test",
77
+ description: "Тест"
78
+ },
79
+ ];
80
+
81
+ return {commands: data};
82
+ ```
83
+
84
+ Удаление сообщения по ID:
85
+ ```
86
+ return {
87
+ deleteId: msg.payload.data.body.mid,
88
+ };
89
+ ```
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const maxbot_config_1 = __importDefault(require("./nodes/maxbot-config"));
7
+ const maxbot_send_1 = __importDefault(require("./nodes/maxbot-send"));
8
+ const maxbot_receive_1 = __importDefault(require("./nodes/maxbot-receive"));
9
+ module.exports = function (RED) {
10
+ (0, maxbot_config_1.default)(RED);
11
+ (0, maxbot_send_1.default)(RED);
12
+ (0, maxbot_receive_1.default)(RED);
13
+ };
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAEA,0EAAiD;AACjD,sEAA6C;AAC7C,4EAAmD;AAEnD,MAAM,CAAC,OAAO,GAAG,UAAU,GAAY;IACrC,IAAA,uBAAY,EAAC,GAAG,CAAC,CAAC;IAClB,IAAA,qBAAU,EAAC,GAAG,CAAC,CAAC;IAChB,IAAA,wBAAa,EAAC,GAAG,CAAC,CAAC;AACrB,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { NodeAPI } from "node-red";
2
+ export default function (RED: NodeAPI): void;
@@ -0,0 +1,37 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType("maxbot-config", {
3
+ category: "config",
4
+ defaults: {
5
+ name: { value: "" }
6
+ },
7
+ credentials: {
8
+ token: { type: "text" }
9
+ },
10
+ label: function() {
11
+ return this.name || "MAX Bot";
12
+ },
13
+ oneditprepare: function() {
14
+ var tokenInput = $("#node-config-input-token");
15
+ if (this.credentials && this.credentials.token) {
16
+ tokenInput.val(this.credentials.token);
17
+ } else {
18
+ tokenInput.val("");
19
+ }
20
+ },
21
+ oneditsave: function() {
22
+ var token = $("#node-config-input-token").val();
23
+ this.credentials.token = token;
24
+ }
25
+ });
26
+ </script>
27
+
28
+ <script type="text/html" data-template-name="maxbot-config">
29
+ <div class="form-row">
30
+ <label for="node-config-input-token"><i class="fa fa-key"></i> Token</label>
31
+ <input type="text" id="node-config-input-token" data-credential="token" placeholder="Enter MAX token">
32
+ </div>
33
+ <div class="form-row">
34
+ <label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
35
+ <input type="text" id="node-config-input-name" placeholder="Bot name (optional)">
36
+ </div>
37
+ </script>
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = default_1;
4
+ const max_bot_api_1 = require("@maxhub/max-bot-api");
5
+ function default_1(RED) {
6
+ function MaxBotConfig(config) {
7
+ RED.nodes.createNode(this, config);
8
+ this.token = this.credentials?.token || '';
9
+ if (this.token) {
10
+ try {
11
+ const bot = new max_bot_api_1.Bot(this.token);
12
+ this.status({ fill: "green", shape: "dot", text: "configured" });
13
+ }
14
+ catch (e) {
15
+ this.status({ fill: "red", shape: "ring", text: "invalid token" });
16
+ }
17
+ }
18
+ else {
19
+ this.status({ fill: "red", shape: "ring", text: "missing token" });
20
+ }
21
+ }
22
+ RED.nodes.registerType("maxbot-config", MaxBotConfig, {
23
+ credentials: {
24
+ token: { type: "text" }
25
+ }
26
+ });
27
+ }
28
+ //# sourceMappingURL=maxbot-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maxbot-config.js","sourceRoot":"","sources":["../../src/nodes/maxbot-config.ts"],"names":[],"mappings":";;AAGA,4BAsBC;AAxBD,qDAA0C;AAE1C,mBAAyB,GAAY;IACnC,SAAS,YAAY,CAAY,MAAW;QAC1C,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC;QAE3C,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,iBAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAChC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;YACnE,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;YACrE,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,YAAY,EAAE;QACpD,WAAW,EAAE;YACX,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;SACxB;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { NodeAPI } from "node-red";
2
+ export default function (RED: NodeAPI): void;
@@ -0,0 +1,33 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType("maxbot-receive", {
3
+ category: "MAX Messenger",
4
+ defaults: {
5
+ name: { value: "" },
6
+ bot: { type: "maxbot-config", required: true },
7
+ pollInterval: { value: 3000, validate: RED.validators.number() }
8
+ },
9
+ inputs: 0,
10
+ outputs: 1,
11
+ icon: "font-awesome/fa-inbox",
12
+ label: function() {
13
+ return this.name || "MAX Receive";
14
+ },
15
+ oneditprepare: function() {
16
+ }
17
+ });
18
+ </script>
19
+
20
+ <script type="text/html" data-template-name="maxbot-receive">
21
+ <div class="form-row">
22
+ <label for="node-input-bot"><i class="fa fa-user"></i> Bot configuration</label>
23
+ <input type="text" id="node-input-bot" data-type="maxbot-config">
24
+ </div>
25
+ <div class="form-row">
26
+ <label for="node-input-pollInterval"><i class="fa fa-clock-o"></i> Poll interval (ms)</label>
27
+ <input type="number" id="node-input-pollInterval" min="500" step="100">
28
+ </div>
29
+ <div class="form-row">
30
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
31
+ <input type="text" id="node-input-name" placeholder="Name (optional)">
32
+ </div>
33
+ </script>
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = default_1;
4
+ const max_bot_api_1 = require("@maxhub/max-bot-api");
5
+ function default_1(RED) {
6
+ function MaxBotReceive(config) {
7
+ RED.nodes.createNode(this, config);
8
+ const configId = config.bot;
9
+ if (!configId) {
10
+ this.error("No MAX bot configuration selected");
11
+ this.status({ fill: "red", shape: "ring", text: "no config" });
12
+ return;
13
+ }
14
+ const configNode = RED.nodes.getNode(configId);
15
+ if (!configNode || !configNode.token) {
16
+ this.error("MAX bot token is not configured");
17
+ this.status({ fill: "red", shape: "ring", text: "invalid config" });
18
+ return;
19
+ }
20
+ const bot = new max_bot_api_1.Bot(String(configNode.token));
21
+ const handlers = [];
22
+ // Обработчик всех сообщений
23
+ const messageHandler = (ctx) => {
24
+ this.send({
25
+ payload: {
26
+ chatId: ctx.message?.recipient.chat_id,
27
+ userId: ctx.message?.recipient.user_id,
28
+ text: ctx.message?.body?.text,
29
+ data: ctx.message,
30
+ reply: (text) => ctx.reply(text) // Передаём возможность ответа
31
+ },
32
+ topic: "max/message",
33
+ });
34
+ };
35
+ // Обработчик команд
36
+ const commandHandler = (ctx) => {
37
+ this.send({
38
+ payload: {
39
+ chatId: ctx.message?.recipient.chat_id,
40
+ userId: ctx.message?.recipient.user_id,
41
+ command: ctx.match[0],
42
+ data: ctx.message,
43
+ reply: (text) => ctx.reply(text)
44
+ },
45
+ topic: "max/command",
46
+ });
47
+ };
48
+ // Обработчик действий
49
+ const actionHandler = (ctx) => {
50
+ this.send({
51
+ payload: {
52
+ chatId: ctx.message?.recipient.chat_id,
53
+ userId: ctx.message?.recipient.user_id,
54
+ action: ctx.match[0],
55
+ data: ctx.message,
56
+ reply: (text) => ctx.reply(text)
57
+ },
58
+ topic: "max/action",
59
+ });
60
+ };
61
+ // Подписываемся на события
62
+ bot.on('message_created', messageHandler);
63
+ bot.command(/.*/, commandHandler);
64
+ bot.action(/.*/, actionHandler);
65
+ handlers.push({ event: 'message_created', handler: messageHandler });
66
+ // Запускаем бота
67
+ bot.start().then(() => {
68
+ this.log("MAX Bot started listening");
69
+ this.status({ fill: "green", shape: "dot", text: "listening" });
70
+ }).catch((err) => {
71
+ this.error("Failed to start bot: " + err.message);
72
+ this.status({ fill: "red", shape: "ring", text: "start failed" });
73
+ });
74
+ this.on("close", (done) => {
75
+ // Остановка бота, если есть метод stop
76
+ if (typeof bot.stop === 'function') {
77
+ bot.stop();
78
+ }
79
+ // В библиотеке может не быть off, поэтому просто сбрасываем статус
80
+ this.status({});
81
+ done();
82
+ });
83
+ }
84
+ RED.nodes.registerType("maxbot-receive", MaxBotReceive);
85
+ }
86
+ //# sourceMappingURL=maxbot-receive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maxbot-receive.js","sourceRoot":"","sources":["../../src/nodes/maxbot-receive.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { NodeAPI } from "node-red";
2
+ export default function (RED: NodeAPI): void;
@@ -0,0 +1,33 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType("maxbot-send", {
3
+ category: "MAX Messenger",
4
+ defaults: {
5
+ name: { value: "" },
6
+ bot: { type: "maxbot-config", required: true },
7
+ defaultChatId: { value: "" }
8
+ },
9
+ inputs: 1,
10
+ outputs: 2,
11
+ icon: "font-awesome/fa-send",
12
+ label: function() {
13
+ return this.name || "MAX Send";
14
+ },
15
+ oneditprepare: function() {
16
+ }
17
+ });
18
+ </script>
19
+
20
+ <script type="text/html" data-template-name="maxbot-send">
21
+ <div class="form-row">
22
+ <label for="node-input-bot"><i class="fa fa-user"></i> Bot configuration</label>
23
+ <input type="text" id="node-input-bot" data-type="maxbot-config">
24
+ </div>
25
+ <div class="form-row">
26
+ <label for="node-input-defaultChatId"><i class="fa fa-comment"></i> Default Chat ID</label>
27
+ <input type="text" id="node-input-defaultChatId" placeholder="e.g. 123456789">
28
+ </div>
29
+ <div class="form-row">
30
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
31
+ <input type="text" id="node-input-name" placeholder="Name (optional)">
32
+ </div>
33
+ </script>
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = default_1;
4
+ const max_bot_api_1 = require("@maxhub/max-bot-api");
5
+ function default_1(RED) {
6
+ function MaxBotSend(config) {
7
+ RED.nodes.createNode(this, config);
8
+ const configId = config.bot;
9
+ if (!configId) {
10
+ this.error("No MAX bot configuration selected");
11
+ this.status({ fill: "red", shape: "ring", text: "no config" });
12
+ return;
13
+ }
14
+ const configNode = RED.nodes.getNode(configId);
15
+ if (!configNode || !configNode.token) {
16
+ this.error("MAX bot token is not configured");
17
+ this.status({ fill: "red", shape: "ring", text: "invalid config" });
18
+ return;
19
+ }
20
+ const bot = new max_bot_api_1.Bot(String(configNode.token));
21
+ this.on("input", async (msg, send, done) => {
22
+ try {
23
+ const commands = msg.commands;
24
+ const deleteId = msg.deleteId || msg.payload?.deleteId;
25
+ const chatId = msg.chatId || config.defaultChatId;
26
+ const userId = msg.userId;
27
+ if (!chatId && !userId && !deleteId && !commands) {
28
+ throw new Error("chatId/userId/deleteId is not provided (set in msg.chatId/msg.userId/msg.deleteId, node config)");
29
+ }
30
+ let text = '';
31
+ if (typeof msg.payload === 'object') {
32
+ text = msg.payload.text;
33
+ }
34
+ else {
35
+ text = msg.payload?.toString() || '';
36
+ }
37
+ if (!text && !deleteId && !commands) {
38
+ throw new Error("message text is empty (msg.payload)");
39
+ }
40
+ this.status({ fill: "blue", shape: "ring", text: "sending..." });
41
+ let result;
42
+ if (deleteId) {
43
+ result = await bot.api.deleteMessage(deleteId);
44
+ }
45
+ else if (commands) {
46
+ result = await bot.api.setMyCommands(commands);
47
+ }
48
+ else {
49
+ const extra = (typeof msg.payload === 'object') ? msg.payload : undefined;
50
+ result = (!chatId && userId) ?
51
+ await bot.api.sendMessageToUser(userId, text, extra) :
52
+ await bot.api.sendMessageToChat(chatId, text, extra);
53
+ }
54
+ this.status({ fill: "green", shape: "dot", text: "sent" });
55
+ msg.payload = result;
56
+ if (this.outputs === 2) {
57
+ send([msg, null]);
58
+ }
59
+ else {
60
+ send(msg);
61
+ }
62
+ done();
63
+ }
64
+ catch (err) {
65
+ this.status({ fill: "red", shape: "ring", text: "error" });
66
+ if (this.outputs === 2) {
67
+ msg.error = err;
68
+ send([null, msg]);
69
+ }
70
+ else {
71
+ this.error(err, msg);
72
+ }
73
+ done();
74
+ }
75
+ });
76
+ this.on("close", () => {
77
+ this.status({});
78
+ });
79
+ }
80
+ RED.nodes.registerType("maxbot-send", MaxBotSend);
81
+ }
82
+ //# sourceMappingURL=maxbot-send.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maxbot-send.js","sourceRoot":"","sources":["../../src/nodes/maxbot-send.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "node-red-contrib-maxbot",
3
+ "version": "0.5.2",
4
+ "description": "Node-RED nodes for MAX messenger",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc -p . && cp -r src/nodes/*.html dist/nodes/",
9
+ "watch": "tsc -w",
10
+ "lint": "eslint . --ext .ts",
11
+ "prepublishOnly": "npm run build"
12
+ },
13
+ "keywords": [
14
+ "node-red",
15
+ "max",
16
+ "messenger",
17
+ "maxbot",
18
+ "node-red-node"
19
+ ],
20
+ "author": "Kirov Ilya",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "@maxhub/max-bot-api": "^0.2.2",
24
+ "@types/node-red": "^1.3.5"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^18.0.0",
28
+ "typescript": "^5.0.0",
29
+ "eslint": "^8.0.0",
30
+ "eslint-config-airbnb-base": "^15.0.0",
31
+ "eslint-plugin-import": "^2.0.0"
32
+ },
33
+ "node-red": {
34
+ "nodes": {
35
+ "maxbot-config": "dist/nodes/maxbot-config.js",
36
+ "maxbot-send": "dist/nodes/maxbot-send.js",
37
+ "maxbot-receive": "dist/nodes/maxbot-receive.js"
38
+ }
39
+ }
40
+ }
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { NodeAPI } from "node-red";
2
+
3
+ import maxbotConfig from "./nodes/maxbot-config";
4
+ import maxbotSend from "./nodes/maxbot-send";
5
+ import maxbotReceive from "./nodes/maxbot-receive";
6
+
7
+ module.exports = function (RED: NodeAPI) {
8
+ maxbotConfig(RED);
9
+ maxbotSend(RED);
10
+ maxbotReceive(RED);
11
+ };
@@ -0,0 +1,37 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType("maxbot-config", {
3
+ category: "config",
4
+ defaults: {
5
+ name: { value: "" }
6
+ },
7
+ credentials: {
8
+ token: { type: "text" }
9
+ },
10
+ label: function() {
11
+ return this.name || "MAX Bot";
12
+ },
13
+ oneditprepare: function() {
14
+ var tokenInput = $("#node-config-input-token");
15
+ if (this.credentials && this.credentials.token) {
16
+ tokenInput.val(this.credentials.token);
17
+ } else {
18
+ tokenInput.val("");
19
+ }
20
+ },
21
+ oneditsave: function() {
22
+ var token = $("#node-config-input-token").val();
23
+ this.credentials.token = token;
24
+ }
25
+ });
26
+ </script>
27
+
28
+ <script type="text/html" data-template-name="maxbot-config">
29
+ <div class="form-row">
30
+ <label for="node-config-input-token"><i class="fa fa-key"></i> Token</label>
31
+ <input type="text" id="node-config-input-token" data-credential="token" placeholder="Enter MAX token">
32
+ </div>
33
+ <div class="form-row">
34
+ <label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
35
+ <input type="text" id="node-config-input-name" placeholder="Bot name (optional)">
36
+ </div>
37
+ </script>
@@ -0,0 +1,26 @@
1
+ import { NodeAPI } from "node-red";
2
+ import { Bot } from "@maxhub/max-bot-api";
3
+
4
+ export default function (RED: NodeAPI) {
5
+ function MaxBotConfig(this: any, config: any) {
6
+ RED.nodes.createNode(this, config);
7
+ this.token = this.credentials?.token || '';
8
+
9
+ if (this.token) {
10
+ try {
11
+ const bot = new Bot(this.token);
12
+ this.status({ fill: "green", shape: "dot", text: "configured" });
13
+ } catch (e) {
14
+ this.status({ fill: "red", shape: "ring", text: "invalid token" });
15
+ }
16
+ } else {
17
+ this.status({ fill: "red", shape: "ring", text: "missing token" });
18
+ }
19
+ }
20
+
21
+ RED.nodes.registerType("maxbot-config", MaxBotConfig, {
22
+ credentials: {
23
+ token: { type: "text" }
24
+ }
25
+ });
26
+ }
@@ -0,0 +1,33 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType("maxbot-receive", {
3
+ category: "MAX Messenger",
4
+ defaults: {
5
+ name: { value: "" },
6
+ bot: { type: "maxbot-config", required: true },
7
+ pollInterval: { value: 3000, validate: RED.validators.number() }
8
+ },
9
+ inputs: 0,
10
+ outputs: 1,
11
+ icon: "font-awesome/fa-inbox",
12
+ label: function() {
13
+ return this.name || "MAX Receive";
14
+ },
15
+ oneditprepare: function() {
16
+ }
17
+ });
18
+ </script>
19
+
20
+ <script type="text/html" data-template-name="maxbot-receive">
21
+ <div class="form-row">
22
+ <label for="node-input-bot"><i class="fa fa-user"></i> Bot configuration</label>
23
+ <input type="text" id="node-input-bot" data-type="maxbot-config">
24
+ </div>
25
+ <div class="form-row">
26
+ <label for="node-input-pollInterval"><i class="fa fa-clock-o"></i> Poll interval (ms)</label>
27
+ <input type="number" id="node-input-pollInterval" min="500" step="100">
28
+ </div>
29
+ <div class="form-row">
30
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
31
+ <input type="text" id="node-input-name" placeholder="Name (optional)">
32
+ </div>
33
+ </script>
@@ -0,0 +1,101 @@
1
+ import { NodeAPI, Node } from "node-red";
2
+ import { Bot } from "@maxhub/max-bot-api";
3
+
4
+ interface MaxBotConfigNode extends Node {
5
+ token: string;
6
+ }
7
+
8
+ export default function (RED: NodeAPI) {
9
+ function MaxBotReceive(this: any, config: any) {
10
+ RED.nodes.createNode(this, config);
11
+
12
+ const configId = config.bot;
13
+ if (!configId) {
14
+ this.error("No MAX bot configuration selected");
15
+ this.status({ fill: "red", shape: "ring", text: "no config" });
16
+ return;
17
+ }
18
+
19
+ const configNode = RED.nodes.getNode(configId) as MaxBotConfigNode | null;
20
+ if (!configNode || !configNode.token) {
21
+ this.error("MAX bot token is not configured");
22
+ this.status({ fill: "red", shape: "ring", text: "invalid config" });
23
+ return;
24
+ }
25
+
26
+ const bot = new Bot(String(configNode.token));
27
+ const handlers: Array<{ event: string; handler: (...args: any[]) => void }> = [];
28
+
29
+ // Обработчик всех сообщений
30
+ const messageHandler = (ctx: any) => {
31
+ this.send({
32
+ payload: {
33
+ chatId: ctx.message?.recipient.chat_id,
34
+ userId: ctx.message?.recipient.user_id,
35
+ text: ctx.message?.body?.text,
36
+ data: ctx.message,
37
+ reply: (text: string) => ctx.reply(text) // Передаём возможность ответа
38
+ },
39
+ topic: "max/message",
40
+ });
41
+ };
42
+
43
+ // Обработчик команд
44
+ const commandHandler = (ctx: any) => {
45
+ this.send({
46
+ payload: {
47
+ chatId: ctx.message?.recipient.chat_id,
48
+ userId: ctx.message?.recipient.user_id,
49
+ command: ctx.match[0],
50
+ data: ctx.message,
51
+ reply: (text: string) => ctx.reply(text)
52
+ },
53
+ topic: "max/command",
54
+ });
55
+ };
56
+
57
+ // Обработчик действий
58
+ const actionHandler = (ctx: any) => {
59
+ this.send({
60
+ payload: {
61
+ chatId: ctx.message?.recipient.chat_id,
62
+ userId: ctx.message?.recipient.user_id,
63
+ action: ctx.match[0],
64
+ data: ctx.message,
65
+ reply: (text: string) => ctx.reply(text)
66
+ },
67
+ topic: "max/action",
68
+ });
69
+ };
70
+
71
+ // Подписываемся на события
72
+ bot.on('message_created', messageHandler);
73
+ bot.command(/.*/, commandHandler);
74
+ bot.action(/.*/, actionHandler);
75
+
76
+ handlers.push(
77
+ { event: 'message_created', handler: messageHandler }
78
+ );
79
+
80
+ // Запускаем бота
81
+ bot.start().then(() => {
82
+ this.log("MAX Bot started listening");
83
+ this.status({ fill: "green", shape: "dot", text: "listening" });
84
+ }).catch((err: any) => {
85
+ this.error("Failed to start bot: " + err.message);
86
+ this.status({ fill: "red", shape: "ring", text: "start failed" });
87
+ });
88
+
89
+ this.on("close", (done: () => void) => {
90
+ // Остановка бота, если есть метод stop
91
+ if (typeof bot.stop === 'function') {
92
+ bot.stop();
93
+ }
94
+ // В библиотеке может не быть off, поэтому просто сбрасываем статус
95
+ this.status({});
96
+ done();
97
+ });
98
+ }
99
+
100
+ RED.nodes.registerType("maxbot-receive", MaxBotReceive);
101
+ }
@@ -0,0 +1,33 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType("maxbot-send", {
3
+ category: "MAX Messenger",
4
+ defaults: {
5
+ name: { value: "" },
6
+ bot: { type: "maxbot-config", required: true },
7
+ defaultChatId: { value: "" }
8
+ },
9
+ inputs: 1,
10
+ outputs: 2,
11
+ icon: "font-awesome/fa-send",
12
+ label: function() {
13
+ return this.name || "MAX Send";
14
+ },
15
+ oneditprepare: function() {
16
+ }
17
+ });
18
+ </script>
19
+
20
+ <script type="text/html" data-template-name="maxbot-send">
21
+ <div class="form-row">
22
+ <label for="node-input-bot"><i class="fa fa-user"></i> Bot configuration</label>
23
+ <input type="text" id="node-input-bot" data-type="maxbot-config">
24
+ </div>
25
+ <div class="form-row">
26
+ <label for="node-input-defaultChatId"><i class="fa fa-comment"></i> Default Chat ID</label>
27
+ <input type="text" id="node-input-defaultChatId" placeholder="e.g. 123456789">
28
+ </div>
29
+ <div class="form-row">
30
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
31
+ <input type="text" id="node-input-name" placeholder="Name (optional)">
32
+ </div>
33
+ </script>
@@ -0,0 +1,89 @@
1
+ import { NodeAPI, Node } from "node-red";
2
+ import { Bot, Keyboard } from "@maxhub/max-bot-api";
3
+
4
+ interface MaxBotConfigNode extends Node {
5
+ token: string;
6
+ }
7
+
8
+ export default function (RED: NodeAPI) {
9
+ function MaxBotSend(this: any, config: any) {
10
+ RED.nodes.createNode(this, config);
11
+
12
+ const configId = config.bot;
13
+ if (!configId) {
14
+ this.error("No MAX bot configuration selected");
15
+ this.status({ fill: "red", shape: "ring", text: "no config" });
16
+ return;
17
+ }
18
+
19
+ const configNode = RED.nodes.getNode(configId) as MaxBotConfigNode | null;
20
+ if (!configNode || !configNode.token) {
21
+ this.error("MAX bot token is not configured");
22
+ this.status({ fill: "red", shape: "ring", text: "invalid config" });
23
+ return;
24
+ }
25
+
26
+ const bot = new Bot(String(configNode.token));
27
+
28
+ this.on("input", async (msg: any, send: any, done: any) => {
29
+ try {
30
+ const commands = msg.commands;
31
+ const deleteId = msg.deleteId || msg.payload?.deleteId;
32
+ const chatId = msg.chatId || config.defaultChatId;
33
+ const userId = msg.userId;
34
+ if (!chatId && !userId && !deleteId && !commands) {
35
+ throw new Error("chatId/userId/deleteId is not provided (set in msg.chatId/msg.userId/msg.deleteId, node config)");
36
+ }
37
+
38
+ let text = '';
39
+ if (typeof msg.payload === 'object') {
40
+ text = msg.payload.text;
41
+ } else {
42
+ text = msg.payload?.toString() || '';
43
+ }
44
+ if (!text && !deleteId && !commands) {
45
+ throw new Error("message text is empty (msg.payload)");
46
+ }
47
+
48
+ this.status({ fill: "blue", shape: "ring", text: "sending..." });
49
+
50
+ let result;
51
+ if (deleteId) {
52
+ result = await bot.api.deleteMessage(deleteId);
53
+ } else if (commands) {
54
+ result = await bot.api.setMyCommands(commands);
55
+ } else {
56
+ const extra = (typeof msg.payload === 'object') ? msg.payload : undefined;
57
+ result = (!chatId && userId) ?
58
+ await bot.api.sendMessageToUser(userId, text, extra) :
59
+ await bot.api.sendMessageToChat(chatId, text, extra);
60
+ }
61
+
62
+ this.status({ fill: "green", shape: "dot", text: "sent" });
63
+ msg.payload = result;
64
+
65
+ if (this.outputs === 2) {
66
+ send([msg, null]);
67
+ } else {
68
+ send(msg);
69
+ }
70
+ done();
71
+ } catch (err: any) {
72
+ this.status({ fill: "red", shape: "ring", text: "error" });
73
+ if (this.outputs === 2) {
74
+ msg.error = err;
75
+ send([null, msg]);
76
+ } else {
77
+ this.error(err, msg);
78
+ }
79
+ done();
80
+ }
81
+ });
82
+
83
+ this.on("close", () => {
84
+ this.status({});
85
+ });
86
+ }
87
+
88
+ RED.nodes.registerType("maxbot-send", MaxBotSend);
89
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "rootDir": "src",
6
+ "outDir": "dist",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "resolveJsonModule": true,
10
+ "declaration": true,
11
+ "sourceMap": true,
12
+ "skipLibCheck": true
13
+ },
14
+ "include": ["src/**/*.ts"]
15
+ }