node-red-contrib-telegrambot
Version:
Telegram bot nodes for Node-RED
588 lines (508 loc) • 25.5 kB
JavaScript
const { expect } = require('chai');
const converter = require('../../telegrambot/lib/converter');
const fx = require('../fixtures/telegram-payloads');
// =============================================================================
// getUserInfo
// =============================================================================
describe('lib/converter — getUserInfo', function () {
it('extracts username, userid and chatid from a normal private chat', function () {
const info = converter.getUserInfo(fx.textMessage('hi'));
expect(info).to.deep.equal({
chatid: 123,
username: 'alice',
userid: 4242,
isAnonymous: false,
});
});
it('returns the chat-level username when chat is present', function () {
// For incoming messages, getUserInfo prefers chat.username for `username`
// (see converter.js — the `if (botMsg.chat)` branch fires first).
const msg = fx.baseMessage({ text: 'hi', chat: fx.chat({ username: 'alice-chat-name' }) });
const info = converter.getUserInfo(msg);
expect(info.username).to.equal('alice-chat-name');
expect(info.chatid).to.equal(123);
expect(info.userid).to.equal(4242);
expect(info.isAnonymous).to.equal(false);
});
it('falls back to from-only when chat is absent (event-style update)', function () {
const update = { from: fx.from(), message: { chat: fx.chat({ id: 999 }) } };
const info = converter.getUserInfo(update);
expect(info.username).to.equal('alice');
expect(info.userid).to.equal(4242);
expect(info.chatid).to.equal(999);
});
it('marks isAnonymous=true when neither chat nor from is present (poll-style)', function () {
const info = converter.getUserInfo({});
expect(info.isAnonymous).to.equal(true);
expect(info.username).to.be.undefined;
expect(info.userid).to.be.undefined;
});
it('marks isAnonymous=true for channel posts (chat present, no from.username)', function () {
const info = converter.getUserInfo(fx.channelPost());
// Channel posts have no `from` and chat.username is undefined for a channel.
expect(info.isAnonymous).to.equal(true);
expect(info.chatid).to.equal(-1001234567890);
});
});
// =============================================================================
// getMessageDetails — one case per content type
// =============================================================================
describe('lib/converter — getMessageDetails', function () {
function build(overrides) {
return fx.baseMessage(overrides);
}
it('text message', function () {
const r = converter.getMessageDetails(build({ text: 'hello' }));
expect(r).to.include({ chatId: 123, messageId: 7, type: 'message', content: 'hello', date: 1715520000 });
});
it('photo — picks the highest-resolution file_id', function () {
const r = converter.getMessageDetails(build({ photo: fx.photoArray() }));
expect(r.type).to.equal('photo');
expect(r.content).to.equal('hi');
expect(r.blob).to.equal(true);
expect(r.photos).to.have.length(2);
});
it('photo — passes through caption and media_group_id', function () {
const r = converter.getMessageDetails(build({ photo: fx.photoArray(), caption: 'cap', media_group_id: 'mg-1' }));
expect(r.caption).to.equal('cap');
expect(r.mediaGroupId).to.equal('mg-1');
});
it('photo — empty array drops the message rather than crashing', function () {
// Regression: getPhotoIndexWithHighestResolution used to return 0
// unconditionally, then botMsg.photo[0].file_id threw TypeError.
const r = converter.getMessageDetails(build({ photo: [] }));
expect(r).to.be.undefined;
});
it('photo — array with missing width/height falls back to index 0', function () {
// No entry has usable dimensions; behaviour: pick the first item.
const arr = [{ file_id: 'no-dim' }];
const r = converter.getMessageDetails(build({ photo: arr }));
expect(r.type).to.equal('photo');
expect(r.content).to.equal('no-dim');
});
it('audio', function () {
const r = converter.getMessageDetails(build({ audio: { file_id: 'a-1' }, caption: 'song' }));
expect(r.type).to.equal('audio');
expect(r.content).to.equal('a-1');
expect(r.caption).to.equal('song');
expect(r.blob).to.equal(true);
});
it('sticker', function () {
const r = converter.getMessageDetails(build({ sticker: { file_id: 's-1' } }));
expect(r.type).to.equal('sticker');
expect(r.content).to.equal('s-1');
expect(r.blob).to.equal(true);
});
it('dice', function () {
const dice = { emoji: '🎲', value: 5 };
const r = converter.getMessageDetails(build({ dice: dice }));
expect(r.type).to.equal('dice');
expect(r.content).to.deep.equal(dice);
expect(r.blob).to.equal(false);
});
it('animation', function () {
const r = converter.getMessageDetails(build({ animation: { file_id: 'g-1' }, caption: 'gif', media_group_id: 'mg-1' }));
expect(r.type).to.equal('animation');
expect(r.content).to.equal('g-1');
expect(r.caption).to.equal('gif');
expect(r.mediaGroupId).to.equal('mg-1');
expect(r.blob).to.equal(true);
});
it('video', function () {
const r = converter.getMessageDetails(build({ video: { file_id: 'v-1' }, caption: 'vid' }));
expect(r.type).to.equal('video');
expect(r.content).to.equal('v-1');
expect(r.blob).to.equal(true);
});
it('video_note', function () {
const r = converter.getMessageDetails(build({ video_note: { file_id: 'vn-1' } }));
expect(r.type).to.equal('video_note');
expect(r.content).to.equal('vn-1');
expect(r.blob).to.equal(true);
});
it('voice', function () {
const r = converter.getMessageDetails(build({ voice: { file_id: 'vo-1' }, caption: 'oh' }));
expect(r.type).to.equal('voice');
expect(r.content).to.equal('vo-1');
expect(r.caption).to.equal('oh');
expect(r.blob).to.equal(true);
});
it('location', function () {
const loc = { latitude: 48.137, longitude: 11.575 };
const r = converter.getMessageDetails(build({ location: loc }));
expect(r.type).to.equal('location');
expect(r.content).to.deep.equal(loc);
});
it('venue', function () {
const venue = { location: { latitude: 1, longitude: 2 }, title: 'X', address: 'Y' };
const r = converter.getMessageDetails(build({ venue: venue }));
expect(r.type).to.equal('venue');
expect(r.content).to.deep.equal(venue);
});
it('contact', function () {
const contact = { phone_number: '+49 123', first_name: 'Carol' };
const r = converter.getMessageDetails(build({ contact: contact }));
expect(r.type).to.equal('contact');
expect(r.content).to.deep.equal(contact);
});
it('document', function () {
const r = converter.getMessageDetails(build({ document: { file_id: 'd-1' }, caption: 'pdf' }));
expect(r.type).to.equal('document');
expect(r.content).to.equal('d-1');
expect(r.caption).to.equal('pdf');
expect(r.blob).to.equal(true);
});
it('poll', function () {
const poll = { id: 'p-1', question: 'Q?', options: [], total_voter_count: 0 };
const r = converter.getMessageDetails(build({ poll: poll }));
expect(r.type).to.equal('poll');
expect(r.content).to.deep.equal(poll);
expect(r.blob).to.equal(false);
});
it('invoice', function () {
const inv = { title: 'T', description: 'D', start_parameter: 'sp', currency: 'EUR', total_amount: 100 };
const r = converter.getMessageDetails(build({ invoice: inv }));
expect(r.type).to.equal('invoice');
expect(r.content).to.deep.equal(inv);
});
it('successful_payment', function () {
const pay = { currency: 'EUR', total_amount: 100, invoice_payload: 'p' };
const r = converter.getMessageDetails(build({ successful_payment: pay }));
expect(r.type).to.equal('successful_payment');
expect(r.content).to.deep.equal(pay);
});
// ---- subtypes added in V17.3.0 -----------------------------------------
it('refunded_payment (V17.3.0)', function () {
const pay = { currency: 'EUR', total_amount: 100, telegram_payment_charge_id: 'tg-x', provider_payment_charge_id: 'p-x' };
const r = converter.getMessageDetails(build({ refunded_payment: pay }));
expect(r.type).to.equal('refunded_payment');
expect(r.content).to.deep.equal(pay);
});
it('paid_media (V17.3.0) — passes caption through', function () {
const pm = { star_count: 50, paid_media: [{ type: 'photo' }] };
const r = converter.getMessageDetails(build({ paid_media: pm, caption: 'paid' }));
expect(r.type).to.equal('paid_media');
expect(r.content).to.deep.equal(pm);
expect(r.caption).to.equal('paid');
});
it('gift (V17.3.0)', function () {
const gift = { id: 'g-x', sticker: { file_id: 'st' } };
const r = converter.getMessageDetails(build({ gift: gift }));
expect(r.type).to.equal('gift');
expect(r.content).to.deep.equal(gift);
});
// ---- service messages --------------------------------------------------
it('new_chat_title', function () {
const r = converter.getMessageDetails(build({ new_chat_title: 'renamed' }));
expect(r.type).to.equal('new_chat_title');
expect(r.content).to.equal('renamed');
});
it('new_chat_photo — picks the highest-resolution variant', function () {
const r = converter.getMessageDetails(build({ new_chat_photo: fx.photoArray() }));
expect(r.type).to.equal('new_chat_photo');
expect(r.content).to.equal('hi');
expect(r.blob).to.equal(true);
});
it('new_chat_photo — empty array drops the message', function () {
const r = converter.getMessageDetails(build({ new_chat_photo: [] }));
expect(r).to.be.undefined;
});
it('new_chat_members — user field is populated from the array (V17.3.0 regression test)', function () {
// Before V17.3.0 this branch referenced the (long-removed) singular
// botMsg.new_chat_member, so `user` was always undefined.
const members = [
{ id: 1, first_name: 'X', is_bot: false },
{ id: 2, first_name: 'Y', is_bot: false },
];
const r = converter.getMessageDetails(build({ new_chat_members: members }));
expect(r.type).to.equal('new_chat_members');
expect(r.content).to.deep.equal(members);
expect(r.user).to.deep.equal(members[0]);
});
it('left_chat_member', function () {
const member = { id: 1, first_name: 'X', is_bot: false };
const r = converter.getMessageDetails(build({ left_chat_member: member }));
expect(r.type).to.equal('left_chat_member');
expect(r.content).to.deep.equal(member);
expect(r.user).to.deep.equal(member);
});
it('delete_chat_photo', function () {
const r = converter.getMessageDetails(build({ delete_chat_photo: true }));
expect(r.type).to.equal('delete_chat_photo');
expect(r.content).to.equal(true);
});
it('channel_chat_created', function () {
const r = converter.getMessageDetails(build({ channel_chat_created: true }));
expect(r.type).to.equal('channel_chat_created');
expect(r.content).to.equal(true);
});
it('group_chat_created', function () {
const r = converter.getMessageDetails(build({ group_chat_created: true }));
expect(r.type).to.equal('group_chat_created');
expect(r.content).to.equal(true);
});
it('supergroup_chat_created', function () {
const r = converter.getMessageDetails(build({ supergroup_chat_created: true }));
expect(r.type).to.equal('supergroup_chat_created');
expect(r.content).to.equal(true);
});
it('pinned_message', function () {
const inner = fx.baseMessage({ text: 'pinned' });
const r = converter.getMessageDetails(build({ pinned_message: inner }));
expect(r.type).to.equal('pinned_message');
expect(r.content).to.deep.equal(inner);
});
it('migrate_from_chat_id', function () {
const r = converter.getMessageDetails(build({ migrate_from_chat_id: -1001 }));
expect(r.type).to.equal('migrate_from_chat_id');
expect(r.content).to.equal(-1001);
});
it('migrate_to_chat_id', function () {
const r = converter.getMessageDetails(build({ migrate_to_chat_id: -1002 }));
expect(r.type).to.equal('migrate_to_chat_id');
expect(r.content).to.equal(-1002);
});
it('web_app_data', function () {
const data = { data: '{"a":1}', button_text: 'go' };
const r = converter.getMessageDetails(build({ web_app_data: data }));
expect(r.type).to.equal('web_app_data');
expect(r.content).to.deep.equal(data);
});
it('unknown payload returns undefined (drops to the default branch)', function () {
const r = converter.getMessageDetails(build({ some_unknown_field: 'x' }));
expect(r).to.be.undefined;
});
});
// =============================================================================
// convertMessage — one case per event type
// =============================================================================
describe('lib/converter — convertMessage', function () {
it('"message" delegates to getMessageDetails', function () {
const msg = fx.textMessage('hi');
const r = converter.convertMessage('message', 123, msg);
expect(r).to.include({ chatId: 123, type: 'message', content: 'hi' });
});
it('callback_query — projects id, data, inline_message_id, from', function () {
const cb = fx.callbackQuery({ data: 'choice-A', inline_message_id: 'inline-1' });
const r = converter.convertMessage('callback_query', 123, cb);
expect(r.type).to.equal('callback_query');
expect(r.content).to.equal('choice-A');
expect(r.callbackQueryId).to.equal('cb-1');
expect(r.inlineMessageId).to.equal('inline-1');
expect(r.messageId).to.equal(cb.message.message_id);
expect(r.from).to.deep.equal(cb.from);
});
it('callback_query — handles missing inner message (inline-only)', function () {
const cb = fx.callbackQuery();
delete cb.message;
const r = converter.convertMessage('callback_query', 123, cb);
expect(r.messageId).to.be.undefined;
expect(r.content).to.equal('choice-A');
});
it('inline_query — projects id, query, offset, from, location', function () {
const iq = fx.inlineQuery();
const r = converter.convertMessage('inline_query', 123, iq);
expect(r.type).to.equal('inline_query');
expect(r.content).to.equal('search-term');
expect(r.inlineQueryId).to.equal('iq-1');
expect(r.offset).to.equal('');
expect(r.from).to.deep.equal(iq.from);
});
it('edited_message — surfaces editDate alongside the text content', function () {
const m = fx.baseMessage({ text: 'fixed', edit_date: 1715520500 });
const r = converter.convertMessage('edited_message', 123, m);
expect(r.type).to.equal('edited_message');
expect(r.content).to.equal('fixed');
expect(r.editDate).to.equal(1715520500);
});
it('edited_message_text', function () {
const m = fx.baseMessage({ text: 't', edit_date: 1 });
const r = converter.convertMessage('edited_message_text', 123, m);
expect(r.type).to.equal('edited_message_text');
expect(r.editDate).to.equal(1);
expect(r.content).to.equal('t');
});
it('edited_message_caption — content is the caption, not the text', function () {
const m = fx.baseMessage({ caption: 'cap', edit_date: 2 });
const r = converter.convertMessage('edited_message_caption', 123, m);
expect(r.content).to.equal('cap');
expect(r.editDate).to.equal(2);
});
it('channel_post', function () {
const m = fx.channelPost();
const r = converter.convertMessage('channel_post', m.chat.id, m);
expect(r.type).to.equal('channel_post');
expect(r.content).to.equal('channel announcement');
expect(r.chat).to.deep.equal(m.chat);
});
it('edited_channel_post', function () {
const m = Object.assign(fx.channelPost(), { edit_date: 99 });
const r = converter.convertMessage('edited_channel_post', m.chat.id, m);
expect(r.type).to.equal('edited_channel_post');
expect(r.editDate).to.equal(99);
});
it('edited_channel_post_text', function () {
const m = Object.assign(fx.channelPost(), { edit_date: 99 });
const r = converter.convertMessage('edited_channel_post_text', m.chat.id, m);
expect(r.content).to.equal('channel announcement');
expect(r.editDate).to.equal(99);
});
it('edited_channel_post_caption — content is caption', function () {
const m = Object.assign(fx.channelPost(), { caption: 'caption-only', edit_date: 99 });
delete m.text;
const r = converter.convertMessage('edited_channel_post_caption', m.chat.id, m);
expect(r.content).to.equal('caption-only');
});
it('business_connection', function () {
const bc = { id: 'bc-1', user: fx.from(), user_chat_id: 123, date: 1, rights: { a: true }, is_enabled: true };
const r = converter.convertMessage('business_connection', 0, bc);
expect(r.type).to.equal('business_connection');
expect(r.id).to.equal('bc-1');
expect(r.user).to.deep.equal(bc.user);
expect(r.userChatId).to.equal(123);
expect(r.isEnabled).to.equal(true);
});
it('business_message', function () {
const m = fx.baseMessage({ text: 'biz' });
const r = converter.convertMessage('business_message', 123, m);
expect(r.type).to.equal('business_message');
expect(r.content).to.equal('biz');
});
it('edited_business_message', function () {
const m = fx.baseMessage({ text: 'biz-edit' });
const r = converter.convertMessage('edited_business_message', 123, m);
expect(r.type).to.equal('edited_business_message');
expect(r.content).to.equal('biz-edit');
});
it('deleted_business_messages — array of ids', function () {
const m = { message_ids: [1, 2, 3], business_connection_id: 'bc-1', chat: fx.chat() };
const r = converter.convertMessage('deleted_business_messages', 123, m);
expect(r.type).to.equal('deleted_business_messages');
expect(r.messageIds).to.deep.equal([1, 2, 3]);
expect(r.businessConnectionId).to.equal('bc-1');
});
it('message_reaction — old/new reactions', function () {
const m = {
message_id: 1,
user: fx.from(),
actor_chat: null,
date: 1,
old_reaction: [],
new_reaction: [{ type: 'emoji', emoji: '👍' }],
};
const r = converter.convertMessage('message_reaction', 123, m);
expect(r.type).to.equal('message_reaction');
expect(r.newReaction).to.have.length(1);
});
it('message_reaction_count', function () {
const m = { message_id: 1, date: 1, chat: fx.chat(), reactions: [{ type: { type: 'emoji', emoji: '👍' }, total_count: 5 }] };
const r = converter.convertMessage('message_reaction_count', 123, m);
expect(r.type).to.equal('message_reaction_count');
expect(r.reactions).to.have.length(1);
});
it('pre_checkout_query', function () {
const q = {
id: 'pcq-1',
from: fx.from(),
currency: 'EUR',
total_amount: 100,
invoice_payload: 'p',
shipping_option_id: 's-1',
order_info: { name: 'Alice' },
};
const r = converter.convertMessage('pre_checkout_query', 123, q);
expect(r.type).to.equal('pre_checkout_query');
expect(r.preCheckoutQueryId).to.equal('pcq-1');
expect(r.content).to.equal('p');
expect(r.currency).to.equal('EUR');
});
it('shipping_query', function () {
const q = { id: 'sq-1', from: fx.from(), invoice_payload: 'p', shipping_address: { country_code: 'DE' } };
const r = converter.convertMessage('shipping_query', 123, q);
expect(r.type).to.equal('shipping_query');
expect(r.shippingQueryId).to.equal('sq-1');
expect(r.content).to.equal('p');
});
it('chosen_inline_result', function () {
const cir = { result_id: 'r-1', from: fx.from(), query: 'q', inline_message_id: 'im-1' };
const r = converter.convertMessage('chosen_inline_result', 123, cir);
expect(r.type).to.equal('chosen_inline_result');
expect(r.result_id).to.equal('r-1');
expect(r.content).to.equal('r-1');
});
it('purchased_paid_media', function () {
const p = { from: fx.from(), paid_media_payload: 'p-data' };
const r = converter.convertMessage('purchased_paid_media', 123, p);
expect(r.type).to.equal('purchased_paid_media');
expect(r.paidMediaPayload).to.equal('p-data');
});
it('poll_answer', function () {
const pa = { poll_id: 'p-1', user: fx.from(), option_ids: [0, 2], date: 1, chat: fx.chat() };
const r = converter.convertMessage('poll_answer', 123, pa);
expect(r.type).to.equal('poll_answer');
expect(r.poll_id).to.equal('p-1');
expect(r.option_ids).to.deep.equal([0, 2]);
expect(r.content).to.deep.equal(pa.user);
});
it('poll', function () {
const p = { id: 'p-1', question: 'Q', options: [{ text: 'A', voter_count: 1 }], total_voter_count: 1, is_anonymous: true, type: 'regular' };
const r = converter.convertMessage('poll', 123, p);
expect(r.type).to.equal('poll');
expect(r.id).to.equal('p-1');
expect(r.question).to.equal('Q');
expect(r.content).to.equal('Q');
expect(r.pollType).to.equal('regular');
});
it('my_chat_member', function () {
const upd = { from: fx.from(), old_chat_member: {}, new_chat_member: {}, invite_link: null, date: 1, chat: fx.chat() };
const r = converter.convertMessage('my_chat_member', 123, upd);
expect(r.type).to.equal('my_chat_member');
expect(r.from).to.deep.equal(upd.from);
});
it('chat_member', function () {
const upd = { from: fx.from(), old_chat_member: {}, new_chat_member: {}, date: 1, chat: fx.chat() };
const r = converter.convertMessage('chat_member', 123, upd);
expect(r.type).to.equal('chat_member');
});
it('chat_join_request', function () {
const upd = { from: fx.from(), bio: 'hi', invite_link: { invite_link: 'x' }, date: 1, chat: fx.chat() };
const r = converter.convertMessage('chat_join_request', 123, upd);
expect(r.type).to.equal('chat_join_request');
expect(r.bio).to.equal('hi');
});
it('chat_boost', function () {
const upd = { chat: fx.chat(), boost: { source: { source: 'premium' } } };
const r = converter.convertMessage('chat_boost', 123, upd);
expect(r.type).to.equal('chat_boost');
expect(r.boost).to.deep.equal(upd.boost);
});
it('removed_chat_boost', function () {
const upd = { chat: fx.chat(), boost_id: 'b-1', remove_date: 100, source: { source: 'premium' } };
const r = converter.convertMessage('removed_chat_boost', 123, upd);
expect(r.type).to.equal('removed_chat_boost');
expect(r.boostId).to.equal('b-1');
expect(r.removeDate).to.equal(100);
});
it('unknown type returns undefined (falls into the default branch)', function () {
const r = converter.convertMessage('completely_made_up_event', 123, {});
expect(r).to.be.undefined;
});
});
// =============================================================================
// chatId propagation — verifies that convertMessage always tags chatId from
// the second arg rather than the underlying botMsg.
// =============================================================================
describe('lib/converter — chatId propagation', function () {
it('uses the caller-supplied chatId, not botMsg.chat.id, for non-message types', function () {
const upd = { chat: fx.chat({ id: 999 }), boost: {} };
const r = converter.convertMessage('chat_boost', 12345, upd);
expect(r.chatId).to.equal(12345); // caller-supplied wins
});
it('uses botMsg.chat.id for type "message" (delegates to getMessageDetails)', function () {
const msg = fx.textMessage('hi');
const r = converter.convertMessage('message', 999999, msg);
// getMessageDetails reads botMsg.chat.id directly and ignores the
// caller's chatId arg — current documented behaviour.
expect(r.chatId).to.equal(123);
});
});