posiflow-telegram-connector 1.0.3

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,304 @@
1
+ const axios = require('axios').default;
2
+ const FormData = require('form-data');
3
+ const winston = require('../winston');
4
+
5
+ class TiledeskTelegram {
6
+
7
+ /**
8
+ * Constructor for TiledeskTelegram
9
+ *
10
+ * @example
11
+ * const { TiledeskTelegram } = require('tiledesk-telegram');
12
+ * const twclient = new TiledeskTelegram({ BASE_URL: BASE_URL, telegram_token: telegram_token, TELEGRAM_API_URL: TELEGRAM_API_URL });
13
+ *
14
+ * @param {Object} config JSON configuration.
15
+ * @param {string} config BASE_URL Mandatory. The base url of this application.
16
+ * @param {string} config.token Mandatory. Token required for authentication.
17
+ * @param {string} config.TELEGRAM_API_URL Mandatory. Url for telegram api.
18
+ * @param {boolean} options.log Optional. If true HTTP requests are logged.
19
+ */
20
+
21
+ constructor(config) {
22
+ if (!config) {
23
+ throw new Error('config is mandatory');
24
+ }
25
+
26
+ if (!config.BASE_URL) {
27
+ throw new Error('config.BASE_URL is mandatory');
28
+ }
29
+
30
+ if (!config.TELEGRAM_API_URL) {
31
+ throw new Error('config.TELEGRAM_API_URL is mandatory');
32
+ }
33
+
34
+ this.TELEGRAM_API_URL = config.TELEGRAM_API_URL;
35
+ this.BASE_URL = config.BASE_URL;
36
+ // others?
37
+
38
+ this.log = false;
39
+ if (config.log) {
40
+ this.log = config.log;
41
+ }
42
+
43
+ }
44
+
45
+ async send(telegram_token, message) {
46
+ return new Promise((resolve, reject) => {
47
+ if (message.photo) {
48
+ this.sendPhoto(telegram_token, message).then((response) => {
49
+ resolve(response);
50
+ }).catch((err) => {
51
+ reject(err);
52
+ })
53
+ }
54
+ else if (message.video) {
55
+ this.sendVideo(telegram_token, message).then((response) => {
56
+ resolve(response);
57
+ }).catch((err) => {
58
+ reject(err);
59
+ })
60
+ }
61
+ else if (message.document) {
62
+ this.sendDocument(telegram_token, message).then((response) => {
63
+ resolve(response);
64
+ }).catch((err) => {
65
+ reject(err);
66
+ })
67
+ } else {
68
+ this.sendMessage(telegram_token, message).then((response) => {
69
+ resolve(response);
70
+ }).catch((err) => {
71
+ reject(err);
72
+ })
73
+ }
74
+ })
75
+ }
76
+
77
+ async sendMessage(telegram_token, message) {
78
+ winston.verbose("(tgm) [TiledeskTelegram] Sending message...");
79
+ winston.debug("(tgm) [TiledeskTelegram] Sending message...", message);
80
+
81
+ return await axios({
82
+ url: this.TELEGRAM_API_URL + `${telegram_token}/sendMessage`,
83
+ headers: {
84
+ "Content-Type": "application/json"
85
+ },
86
+ data: message,
87
+ method: 'POST'
88
+ }).then((response) => {
89
+ return response;
90
+ }).catch((err) => {
91
+ winston.error("(tgm) [TiledeskTelegram] send message error: ", err.response.data);
92
+ return err;
93
+ })
94
+ }
95
+
96
+
97
+ async sendPhoto(telegram_token, message) {
98
+ winston.verbose("(tgm) [TiledeskTelegram] Sending message...");
99
+ winston.debug("(tgm) [TiledeskTelegram] Sending message...", message);
100
+
101
+ return await axios({
102
+ url: this.TELEGRAM_API_URL + `${telegram_token}/sendPhoto`,
103
+ headers: {
104
+ "Content-Type": "application/json"
105
+ },
106
+ data: message,
107
+ method: 'POST'
108
+ }).then((response) => {
109
+ return response;
110
+ }).catch((err) => {
111
+ winston.error("(tgm) [TiledeskTelegram] send photo message error: " + err);
112
+ throw err;
113
+ })
114
+ }
115
+
116
+ async sendVideo(telegram_token, message) {
117
+ winston.verbose("(tgm) [TiledeskTelegram] Seding message...");
118
+ winston.debug("(tgm) [TiledeskTelegram] Sending message...", message);
119
+
120
+ return await axios({
121
+ url: this.TELEGRAM_API_URL + `${telegram_token}/sendVideo`,
122
+ headers: {
123
+ "Content-Type": "application/json"
124
+ },
125
+ data: message,
126
+ method: 'POST'
127
+ }).then((response) => {
128
+ return response;
129
+ }).catch((err) => {
130
+ winston.error("(tgm) [TiledeskTelegram] send video message error: " + err);
131
+ return err;
132
+ })
133
+ }
134
+
135
+ async sendDocument(telegram_token, message) {
136
+ winston.verbose("(tgm) [TiledeskTelegram] Seding message...");
137
+ winston.debug("(tgm) [TiledeskTelegram] Sending message...", message);
138
+
139
+ return await axios({
140
+ url: this.TELEGRAM_API_URL + `${telegram_token}/sendDocument`,
141
+ headers: {
142
+ "Content-Type": "application/json"
143
+ },
144
+ data: message,
145
+ method: 'POST'
146
+ }).then((response) => {
147
+ return response;
148
+ }).catch((err) => {
149
+ winston.error("(tgm) [TiledeskTelegram] send document message error: " + err);
150
+ return err;
151
+ })
152
+ }
153
+
154
+
155
+ async setWebhookEndpoint(projectId, telegram_token, callback) {
156
+ const URL = this.TELEGRAM_API_URL + `${telegram_token}/setwebhook`;
157
+ const data = {
158
+ "url": this.BASE_URL + `/telegram/?project_id=${projectId}`
159
+ }
160
+ console.log("data: ", data)
161
+ const HTTPREQUEST = {
162
+ url: URL,
163
+ headers: {
164
+ 'Content-Type': 'application/json'
165
+ },
166
+ json: data,
167
+ method: 'POST'
168
+ };
169
+ let promise = new Promise((resolve, reject) => {
170
+ TiledeskTelegram.request(
171
+ HTTPREQUEST,
172
+ function(err, resbody) {
173
+ if (err) {
174
+ if (callback) {
175
+ callback(err);
176
+ }
177
+ reject(err);
178
+ }
179
+ else {
180
+ if (callback) {
181
+ callback(null, resbody);
182
+ }
183
+ resolve(resbody);
184
+ }
185
+ }, true)
186
+ })
187
+ return promise;
188
+ }
189
+
190
+ async deleteWebhookEndpoint(projectId, telegram_token, callback) {
191
+ const URL = this.TELEGRAM_API_URL + `${telegram_token}/deleteWebhook`;
192
+ const HTTPREQUEST = {
193
+ url: URL,
194
+ headers: {
195
+ 'Content-Type': 'application/json'
196
+ },
197
+ method: 'POST'
198
+ };
199
+ let promise = new Promise((resolve, reject) => {
200
+ TiledeskTelegram.request(
201
+ HTTPREQUEST,
202
+ function(err, resbody) {
203
+ if (err) {
204
+ if (callback) {
205
+ callback(err);
206
+ }
207
+ reject(err);
208
+ }
209
+ else {
210
+ if (callback) {
211
+ callback(null, resbody);
212
+ }
213
+ resolve(resbody);
214
+ }
215
+ }, true)
216
+ })
217
+ return promise;
218
+ }
219
+
220
+
221
+ async editMessageReplyMarkup(telegram_token, reply_markup_data, callback) {
222
+ const URL = this.TELEGRAM_API_URL + `${telegram_token}/editMessageReplyMarkup`;
223
+ const HTTPREQUEST = {
224
+ url: URL,
225
+ headers: {
226
+ 'Content-Type': 'application/json'
227
+ },
228
+ json: reply_markup_data,
229
+ method: 'GET'
230
+ };
231
+
232
+ let promise = new Promise((resolve, reject) => {
233
+ TiledeskTelegram.request(
234
+ HTTPREQUEST,
235
+ function(err, resbody) {
236
+ if (err) {
237
+ if (callback) {
238
+ callback(err);
239
+ }
240
+ reject(err);
241
+ }
242
+ else {
243
+ if (callback) {
244
+ callback(null, resbody);
245
+ }
246
+ resolve(resbody);
247
+ }
248
+ }, true)
249
+ })
250
+ return promise;
251
+ }
252
+
253
+
254
+ async downloadMedia(telegram_token, mediaId) {
255
+ winston.debug("(tgm) [TiledeskTelegram] Downloading media: ", mediaId)
256
+
257
+ return await axios({
258
+ url: this.TELEGRAM_API_URL + `${telegram_token}/getFile?file_id=${mediaId}`,
259
+ headers: {
260
+ "Content-Type": "application/json",
261
+ },
262
+ method: "GET"
263
+ }).then((response) => {
264
+ winston.verbose("(tgm) [TiledeskTelegram] Download complete");
265
+ return response.data
266
+ }).catch((err) => {
267
+ winston.error("(tgm) [TiledeskTelegram] Download failed!: " + err);
268
+ return err;
269
+ })
270
+ }
271
+
272
+
273
+ // HTTP REQUEST
274
+ static async request(options, callback, log) {
275
+
276
+ return await axios({
277
+ url: options.url,
278
+ method: options.method,
279
+ data: options.json,
280
+ params: options.params,
281
+ headers: options.headers
282
+ }).then((res) => {
283
+ if (res && res.status == 200 && res.data) {
284
+ if (callback) {
285
+ callback(null, res.data);
286
+ }
287
+ }
288
+ else {
289
+ if (callback) {
290
+ callback(TiledeskClient.getErr({ message: "Response status not 200" }, options, res), null, null);
291
+ }
292
+ }
293
+ }).catch((err) => {
294
+ winston.error("(tgm) [TiledeskTelegram] An error occured: ", err);
295
+ if (callback) {
296
+ callback("an error occured", null, null);
297
+ }
298
+ })
299
+ }
300
+
301
+
302
+ }
303
+
304
+ module.exports = { TiledeskTelegram }
@@ -0,0 +1,300 @@
1
+ const winston = require('../winston')
2
+
3
+ class TiledeskTelegramTranslator {
4
+
5
+ /**
6
+ * Constructor for TiledeskTelegramTranslator
7
+ *
8
+ * @example
9
+ * const { TiledeskTelegramTranslator } = require('tiledesk-telegram-translator');
10
+ * const tlr = new TiledeskTelegramTranslator({ TELEGRAM_FILE_URL: TELEGRAM_FILE_URL});
11
+ *
12
+ * @param {Object} config JSON configuration.
13
+ * @param {string} config.TELEGRAM_FILE_URL Mandatory. Url for telegram file.
14
+ */
15
+
16
+ static CHANNEL_NAME = "telegram";
17
+ static TELEGRAM_FILE_BASE_URL = "https://api.telegram.org/file/bot"
18
+
19
+ constructor() {
20
+
21
+ this.log = false;
22
+
23
+ }
24
+
25
+ async toTelegram(tiledeskChannelMessage, telegram_receiver) {
26
+
27
+ winston.debug("(tgm) [Translator] Tiledesk message: ", tiledeskChannelMessage);
28
+
29
+ let text = '';
30
+ if (tiledeskChannelMessage.text) {
31
+ text = tiledeskChannelMessage.text;
32
+ }
33
+
34
+ const telegram_message = {
35
+ chat_id: telegram_receiver,
36
+ parse_mode: "markdown"
37
+ }
38
+
39
+ if (tiledeskChannelMessage.type === 'frame') {
40
+ text = text + "\n\n👉 " + tiledeskChannelMessage.metadata.src
41
+ telegram_message.text = text;
42
+ return telegram_message;
43
+ }
44
+
45
+ // Metadata
46
+ if (tiledeskChannelMessage.metadata) {
47
+
48
+ if ((tiledeskChannelMessage.metadata.type && tiledeskChannelMessage.metadata.type.startsWith('image')) || tiledeskChannelMessage.type.startsWith('image')) {
49
+
50
+ telegram_message.photo = tiledeskChannelMessage.metadata.src;
51
+ telegram_message.caption = text;
52
+
53
+ }
54
+
55
+ else if ((tiledeskChannelMessage.metadata.type && tiledeskChannelMessage.metadata.type.startsWith('video')) || tiledeskChannelMessage.type.startsWith('video')) {
56
+
57
+ telegram_message.video = tiledeskChannelMessage.metadata.src
58
+ //telegram_message.caption = tiledeskChannelMessage.metadata.name || text;
59
+ telegram_message.caption = text;
60
+ }
61
+
62
+ else if ((tiledeskChannelMessage.metadata.type && tiledeskChannelMessage.metadata.type.startsWith('application')) || tiledeskChannelMessage.type.startsWith('application')) {
63
+ //else if (tiledeskChannelMessage.metadata.type.startsWith('application')) {
64
+
65
+ telegram_message.document = tiledeskChannelMessage.metadata.src;
66
+ //telegram_message.caption = tiledeskChannelMessage.text.substr(tiledeskChannelMessage.text.indexOf(')') + 1);
67
+ telegram_message.caption = tiledeskChannelMessage.text
68
+
69
+ }
70
+
71
+ else {
72
+ winston.verbose("(tgm) [Translator] file type not supported");
73
+ return null;
74
+ }
75
+
76
+ return telegram_message;
77
+
78
+ }
79
+
80
+
81
+ else if (tiledeskChannelMessage.attributes) {
82
+ if (tiledeskChannelMessage.attributes.attachment) {
83
+ if (tiledeskChannelMessage.attributes.attachment.buttons) {
84
+
85
+ let buttons = tiledeskChannelMessage.attributes.attachment.buttons;
86
+
87
+ // Using Reply Keyboard instead of Inline Keyboard
88
+ // This makes button clicks appear in chat history
89
+
90
+ let keyboard_buttons = [];
91
+
92
+ // Loop on buttons - process only 'text' and 'action' types for Reply Keyboard
93
+ for (let btn of buttons) {
94
+
95
+ if (btn.type == 'text') {
96
+ // Reply keyboard uses simple text buttons
97
+ // Each button in its own row (vertical layout)
98
+ keyboard_buttons.push([{ text: btn.value }]);
99
+ }
100
+
101
+ if (btn.type == 'action') {
102
+ // Action buttons also appear as text in Reply Keyboard
103
+ // The action will be triggered by the text message
104
+ let text_value = (btn.value.length > 36) ? btn.value.substr(0, 34) + '..' : btn.value;
105
+ keyboard_buttons.push([{ text: text_value }]);
106
+ }
107
+
108
+ // Note: URL buttons are not supported in Reply Keyboard
109
+ // They would need to remain as inline_keyboard if needed
110
+ }
111
+
112
+ telegram_message.text = tiledeskChannelMessage.text;
113
+ if (keyboard_buttons.length > 0) {
114
+ telegram_message.reply_markup = {
115
+ keyboard: keyboard_buttons, // Each button in its own row
116
+ resize_keyboard: true,
117
+ one_time_keyboard: true // Keyboard disappears after selection
118
+ }
119
+ }
120
+
121
+ return telegram_message;
122
+
123
+ // For Solution 2
124
+ // Loop on button of type = 'text' --> quick replies
125
+
126
+ /*
127
+ for (let btn of buttons) {
128
+ if (btn.type == 'text') {
129
+ quick_replies.push({ text: btn.value });
130
+ }
131
+ }
132
+
133
+ const data3 = {
134
+ chat_id: chatId,
135
+ }
136
+
137
+ if (quick_replies.length > 0) {
138
+ if (inline_buttons.length == 0) {
139
+ data3.text = payload.text;
140
+ } else {
141
+ data3.text = 'Here the problem'
142
+ }
143
+
144
+ data3.reply_markup = {
145
+ keyboard: [
146
+ quick_replies
147
+ ],
148
+ resize_keyboard: true,
149
+ one_time_keyboard: true
150
+ }
151
+ console.log("Data (Quick Replies): ", data3)
152
+ console.log("Quick Replies: ", data3.reply_markup.keyboard)
153
+
154
+ sendTelegramMessage(tg_token, data3).then((response) => {
155
+ console.log("SendTelegramMessage Response (quick replies): ", response)
156
+ }).catch((err) => {
157
+ console.log("Error SendTelegramMessage (quick replies): ", err)
158
+ })
159
+ }
160
+ */
161
+
162
+ } else {
163
+ winston.verbose("(tgm) [Translator] attributes attachment not supported");
164
+ return null;
165
+ }
166
+
167
+ } else {
168
+ telegram_message.text = tiledeskChannelMessage.text;
169
+ return telegram_message;
170
+ }
171
+ }
172
+
173
+ else {
174
+ telegram_message.text = tiledeskChannelMessage.text;
175
+ return telegram_message;
176
+ }
177
+
178
+
179
+
180
+ }
181
+
182
+ async toTiledesk(telegramChannelMessage, telegram_token, media_url) {
183
+
184
+ // Reply Keyboard sends regular messages, no callback_query handling needed
185
+
186
+ if (telegramChannelMessage.message) {
187
+
188
+ let message = telegramChannelMessage.message;
189
+
190
+ // Photo
191
+ if (message.photo) {
192
+ let image_url = TiledeskTelegramTranslator.TELEGRAM_FILE_BASE_URL + `${telegram_token}/${media_url}`;
193
+ let index = telegramChannelMessage.message.photo.length - 1;
194
+
195
+ var msg = {
196
+ text: message.caption || "Attached image",
197
+ senderFullname: message.from.first_name + " " + message.from.last_name,
198
+ channel: { name: TiledeskTelegramTranslator.CHANNEL_NAME },
199
+ type: "image",
200
+ metadata: {
201
+ src: image_url,
202
+ width: message.photo[index].width,
203
+ height: message.photo[index].height,
204
+ }
205
+ }
206
+ return msg;
207
+ }
208
+
209
+ // Video
210
+ else if (message.video) {
211
+ let video_url = TiledeskTelegramTranslator.TELEGRAM_FILE_BASE_URL + `${telegram_token}/${media_url}`;
212
+ winston.debug("(tgm) [Translator] video url: ", video_url)
213
+
214
+ var msg = {
215
+ text: "[" + message.video.file_name + "](" + video_url + ")",
216
+ senderFullname: message.from.first_name + " " + message.from.last_name,
217
+ channel: { name: TiledeskTelegramTranslator.CHANNEL_NAME },
218
+ type: "video",
219
+ metadata: {
220
+ src: video_url,
221
+ name: message.video.file_name,
222
+ type: message.video.mime_type
223
+ }
224
+ }
225
+ return msg;
226
+
227
+ }
228
+
229
+ // Document
230
+ else if (message.document) {
231
+ let document_url = TiledeskTelegramTranslator.TELEGRAM_FILE_BASE_URL + `${telegram_token}/${media_url}`;
232
+
233
+ var msg = {
234
+ text: "[" + message.document.file_name + "](" + document_url + ")",
235
+ senderFullname: message.from.first_name + " " + message.from.last_name,
236
+ channel: { name: TiledeskTelegramTranslator.CHANNEL_NAME },
237
+ type: "file",
238
+ metadata: {
239
+ name: message.document.file_name,
240
+ type: message.document.mime_type,
241
+ src: document_url
242
+ }
243
+ }
244
+ return msg;
245
+ }
246
+
247
+ // Text message
248
+ else {
249
+
250
+ var msg = {
251
+ text: telegramChannelMessage.message.text,
252
+ senderFullname: message.from.first_name + " " + message.from.last_name,
253
+ channel: { name: TiledeskTelegramTranslator.CHANNEL_NAME }
254
+ }
255
+ return msg;
256
+ }
257
+
258
+ }
259
+ else {
260
+ winston.verbose("(tgm) [Translator] Format not supported!")
261
+ return null;
262
+ }
263
+
264
+ }
265
+
266
+
267
+
268
+ // HTTP REQUEST
269
+
270
+ static async myrequest(options, callback, log) {
271
+
272
+ return await axios({
273
+ url: options.url,
274
+ method: options.method,
275
+ data: options.json,
276
+ params: options.params,
277
+ headers: options.headers
278
+ }).then((res) => {
279
+
280
+ if (res && res.status == 200 && res.data) {
281
+ if (callback) {
282
+ callback(null, res.data);
283
+ }
284
+ }
285
+ else {
286
+ if (callback) {
287
+ callback(TiledeskClient.getErr({ message: "Response status not 200" }, options, res), null, null);
288
+ }
289
+ }
290
+ }).catch((err) => {
291
+ winston.error("(tgm) [Translator] An error occured: ", err);
292
+ if (callback) {
293
+ callback("An error occurred", null, null);
294
+ }
295
+ })
296
+ }
297
+
298
+ }
299
+
300
+ module.exports = { TiledeskTelegramTranslator }
package/winston.js ADDED
@@ -0,0 +1,41 @@
1
+ var appRoot = require('app-root-path');
2
+ var winston = require('winston');
3
+ var level = process.env.TELEGRAM_LOG || 'info';
4
+
5
+ var options = {
6
+ file: {
7
+ level: level,
8
+ filename: `${appRoot}/logs/app.log`,
9
+ handleExceptions: true,
10
+ json: false,
11
+ maxsize: 5242880, // 5MB
12
+ maxFiles: 5,
13
+ colorize: false,
14
+ format: winston.format.simple()
15
+ },
16
+ console: {
17
+ level: level,
18
+ handleExceptions: true,
19
+ json: true,
20
+ colorize: true,
21
+ // timestamp: true,
22
+ format: winston.format.simple()
23
+ },
24
+ };
25
+
26
+ let logger = winston.createLogger({
27
+ transports: [
28
+ new (winston.transports.Console)(options.console),
29
+ new (winston.transports.File)(options.file),
30
+ ],
31
+ exitOnError: false, // do not exit on handled exceptions
32
+ });
33
+
34
+ logger.stream = {
35
+ write: function(message, encoding) {
36
+ logger.info(message);
37
+ },
38
+ };
39
+
40
+
41
+ module.exports = logger;