From e3c378cbf1d502263c61d3b9c31cd270bc3ae239 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Fri, 13 Jan 2023 10:28:31 +0300 Subject: vnext: Telegram bot (WIP) --- vnext/server/common/MessageUtils.js | 16 +++++++++++ vnext/server/common/MessageUtils.spec.js | 2 +- .../common/__snapshots__/MessageUtils.spec.js.snap | 2 +- vnext/server/durov.js | 33 ++++++++++++++++++++++ vnext/server/http.js | 8 ++++-- vnext/server/index.js | 2 +- vnext/server/middleware/event.js | 33 ++++++++++++++++++---- vnext/server/sender.js | 15 +++++----- 8 files changed, 92 insertions(+), 19 deletions(-) create mode 100644 vnext/server/durov.js (limited to 'vnext/server') diff --git a/vnext/server/common/MessageUtils.js b/vnext/server/common/MessageUtils.js index f766faf2..74638c02 100644 --- a/vnext/server/common/MessageUtils.js +++ b/vnext/server/common/MessageUtils.js @@ -1,3 +1,5 @@ +import config from 'config'; + /** * check if message is PM * @@ -64,3 +66,17 @@ export function formatQuote(msg) { export function formatMessage(msg) { return msg.body || 'Sent an image'; } + +const baseURL = 'https://juick.com'; + +/** + * @param {import('../api').Message} msg message + */ +export function formatUrl(msg) { + if (isReply(msg)) { + return `${baseURL}/m/${msg.mid}#${msg.rid}`; + } else if (isPM(msg)) { + return `${baseURL}/pm/inbox`; + } + return `${baseURL}/m/${msg.mid}`; +} diff --git a/vnext/server/common/MessageUtils.spec.js b/vnext/server/common/MessageUtils.spec.js index 766b1882..5e6f64c2 100644 --- a/vnext/server/common/MessageUtils.spec.js +++ b/vnext/server/common/MessageUtils.spec.js @@ -27,7 +27,7 @@ describe('Message formatting', () => { 'uname': 'ugnich' }, 'replyQuote': '> The message', - 'body': 'The reply' + 'body': 'The #reply #bla' }; expect(formatTitle(msg)).toMatchSnapshot(); expect(formatQuote(msg)).toMatchSnapshot(); diff --git a/vnext/server/common/__snapshots__/MessageUtils.spec.js.snap b/vnext/server/common/__snapshots__/MessageUtils.spec.js.snap index ea58aebc..fb3c3513 100644 --- a/vnext/server/common/__snapshots__/MessageUtils.spec.js.snap +++ b/vnext/server/common/__snapshots__/MessageUtils.spec.js.snap @@ -14,4 +14,4 @@ exports[`Message formatting Reply message 1`] = `"Reply by ugnich:"`; exports[`Message formatting Reply message 2`] = `"> The message"`; -exports[`Message formatting Reply message 3`] = `"The reply"`; +exports[`Message formatting Reply message 3`] = `"The #reply #bla"`; diff --git a/vnext/server/durov.js b/vnext/server/durov.js new file mode 100644 index 00000000..0f70133d --- /dev/null +++ b/vnext/server/durov.js @@ -0,0 +1,33 @@ +import TelegramBot from 'node-telegram-bot-api'; +import debug from 'debug'; +var log = debug('durov'); +import config from 'config'; + + +import { formatMessage, formatQuote, formatTitle } from './common/MessageUtils'; +import { format } from '../src/utils/embed'; + +const sender = new TelegramBot(config.get('service.durov.token'), { polling: true }); +const demouser = config.get('service.durov.demouser'); + +sender.on('message', msg => { + log(`MESSAGE: ${JSON.stringify(msg)}`); +}); + +/** + * @param {import('../src/api').Message} msg + * @param {string[]} subscribers + */ +export const sendTelegramNotification = (msg, subscribers) => { + log(`Telegram event: ${JSON.stringify(msg)}, ${subscribers} ${subscribers.length}`); + if (!msg.service) { + if (subscribers && subscribers.includes(demouser)) { + const message = `${formatTitle(msg)}\n${formatQuote(msg)}\n${format(msg.body, msg.uid, false)}`; + log(message); + sender.sendMessage(demouser, message, { + parse_mode: 'HTML', + disable_web_page_preview: true + }).then(log).catch(log); + } + } +}; diff --git a/vnext/server/http.js b/vnext/server/http.js index c2557108..0ffa8bfe 100644 --- a/vnext/server/http.js +++ b/vnext/server/http.js @@ -1,6 +1,7 @@ import axios from 'axios'; import config from 'config'; -var debug = require('debug')('http'); +import debug from 'debug'; +var log = debug('http'); /** @@ -17,6 +18,7 @@ export function subscribers(params) { return new Promise((resolve, reject) => { client.get(`/notifications?${params.toString()}`) .then(response => { + log(`CODE: ${response.status}`); resolve(response.data); }) .catch(reason => { reject(reason); }); @@ -33,7 +35,7 @@ export const deleteSubscribers = async (tokens) => { return response.data; }; -const baseURL = config.get('service.baseURL') || process.env.JUICK_SERVICE_URL; +const baseURL = config.get('service.baseURL') + '/api'; const user = config.get('service.user') || process.env.JUICK_SERVICE_USER; const password = config.get('service.password') || process.env.JUICK_SERVICE_PASSWORD; @@ -44,4 +46,4 @@ let client = axios.create({ 'Authorization': 'Basic ' + Buffer.from(user + ':' + password).toString('base64') } : {} }); -debug(`HTTP client initialized with base URL ${baseURL} ${ user? `and ${user} user` : ''}`); +log(`HTTP client initialized with base URL ${baseURL} ${ user? `and ${user} user` : ''}`); diff --git a/vnext/server/index.js b/vnext/server/index.js index 95f88cd1..89122069 100644 --- a/vnext/server/index.js +++ b/vnext/server/index.js @@ -5,7 +5,6 @@ import config from 'config'; import debug from 'debug'; const log = debug('http'); -// we'll talk about this in a minute: import serverRenderer from './middleware/renderer'; import event from './middleware/event'; import oembed from './middleware/oembed'; @@ -37,6 +36,7 @@ router.use('*', serverRenderer); app.use(router); + // start the app app.listen(PORT, (error) => { if (error) { diff --git a/vnext/server/middleware/event.js b/vnext/server/middleware/event.js index 54611280..1267d1c4 100644 --- a/vnext/server/middleware/event.js +++ b/vnext/server/middleware/event.js @@ -1,8 +1,27 @@ import { simpleParser } from 'mailparser'; import { isPM, isReply, isService } from '../common/MessageUtils'; +import { sendTelegramNotification } from '../durov'; import { subscribers } from '../http'; import { sendNotification, buildNotification } from '../sender'; -var debug = require('debug')('event'); +import debug from 'debug'; +var log = debug('event'); +import config from 'config'; +import EventSource from 'eventsource'; + +const es = new EventSource(config.get('service.baseURL')+ '/api/events'); +es.addEventListener('msg', (msg) => { + log(msg.data); + processMessageEvent(JSON.parse(msg.data)); +}); +es.addEventListener('read', (msg) => { + log(msg); +}); +es.addEventListener('open', () => { + log('online'); +}); +es.onerror = (evt) => { + log(`err: ${JSON.stringify(evt)}`); +}; /** @type {number[]} */ const allSandboxIds = []; @@ -23,7 +42,7 @@ function processMessageEvent(msg) { } subscribers(new URLSearchParams(JSON.parse(JSON.stringify(params)))).then(users => { users.forEach(user => { - debug(`${user.uname}: ${user.unreadCount}`); + log(`${user.uname}: ${user.unreadCount}`); let [sandboxTokens, productionTokens] = (user.tokens || []) .filter(t => ['mpns', 'apns', 'gcm'].includes(t.type)) .map(t => t.token) @@ -34,8 +53,12 @@ function processMessageEvent(msg) { return result; }, [[], []]); sendNotification(productionTokens, sandboxTokens, buildNotification(user, msg)); + let durovIds = (user.tokens || []) + .filter(t => ['durov'].includes(t.type)) + .map(t => t.token); + sendTelegramNotification(msg, durovIds); }); - }).catch(console.error); + }).catch(log); } /** @@ -47,7 +70,7 @@ export default function handleMessage(req, res) { return simpleParser(req.body, {}) .then(parsed => { const new_version = parsed.headers.get('x-event-version') == '1.0'; - debug(`New event: ${parsed.text}, new version: ${new_version}`); + log(`New event: ${parsed.text}, new version: ${new_version}`); if (new_version) { /** @type {import('../../client').SystemEvent} */ const event = JSON.parse(parsed.text || ''); @@ -71,5 +94,5 @@ export default function handleMessage(req, res) { } res.end(); }) - .catch(err => { console.error(err); res.status(400).send('Invalid request'); }); + .catch(err => { log(err); res.status(400).send('Invalid request'); }); } diff --git a/vnext/server/sender.js b/vnext/server/sender.js index 6ece8eaa..f33eadcf 100644 --- a/vnext/server/sender.js +++ b/vnext/server/sender.js @@ -1,5 +1,6 @@ import PushNotifications from 'node-pushnotifications'; -var debug = require('debug')('sender'); +import debug from 'debug'; +const log = debug('sender'); import { deleteSubscribers } from './http'; import { formatMessage, formatTitle, formatQuote } from './common/MessageUtils'; import config from 'config'; @@ -51,7 +52,7 @@ export function sendNotification(productionIds, sandboxIds, data) { sender.send(registrationIds, data) .then((results) => { results.forEach(result => { - debug(`${result.method}: ${result.success} success, ${result.failure} failure`); + log(`${result.method}: ${result.success} success, ${result.failure} failure`); if (result.failure) { console.error(`${result.method} failure: ${JSON.stringify(result)}`); console.error(`Failed data: ${JSON.stringify(data)}`); @@ -59,13 +60,13 @@ export function sendNotification(productionIds, sandboxIds, data) { }); results.filter(r => r.method === 'apn') .forEach(async r => { - debug(`Response message: ${JSON.stringify(r.message)}`); + log(`Response message: ${JSON.stringify(r.message)}`); let badTokens = r.message.filter(m => m.errorMsg === 'BadDeviceToken').map(m => { return { 'type': 'apns', 'token': m.regId }; }); if (badTokens.length > 0) { await deleteSubscribers(badTokens); - debug(`${badTokens.length} APNS tokens deleted`); + log(`${badTokens.length} APNS tokens deleted`); } }); results.filter(r => r.method === 'gcm') @@ -75,7 +76,7 @@ export function sendNotification(productionIds, sandboxIds, data) { }); if (badTokens.length > 0) { await deleteSubscribers(badTokens); - debug(`${badTokens.length} GCM tokens deleted`); + log(`${badTokens.length} GCM tokens deleted`); } }); results.filter(r => r.method === 'mpns') @@ -85,7 +86,7 @@ export function sendNotification(productionIds, sandboxIds, data) { }); if (badTokens.length > 0) { await deleteSubscribers(badTokens); - debug(`${badTokens.length} MPNS tokens deleted`); + log(`${badTokens.length} MPNS tokens deleted`); } }); }) @@ -136,7 +137,5 @@ export function buildNotification(user, msg) { template.tag = `${tag}`; template.android_channel_id = 'default'; } - // FIXME: wrong type definition in node-pushnotifications: title and body and not required for silent pushes - // @ts-ignore return template; } -- cgit v1.2.3