UNPKG

telegraf

Version:

📡 Modern Telegram bot framework

531 lines (466 loc) • 17.4 kB
const test = require('ava') const Telegraf = require('../') const { Composer } = Telegraf const baseMessage = { chat: { id: 1 }, from: { id: 42, username: 'telegraf' } } const baseGroupMessage = { chat: { id: 1, type: 'group' } } const topLevelUpdates = [ { type: 'message', update: { message: baseMessage } }, { type: 'edited_message', update: { edited_message: baseMessage } }, { type: 'callback_query', update: { callback_query: { message: baseMessage } } }, { type: 'inline_query', update: { inline_query: {} } }, { type: 'channel_post', update: { channel_post: {} } }, { type: 'edited_channel_post', update: { edited_channel_post: {} } }, { type: 'chosen_inline_result', update: { chosen_inline_result: {} } } ] topLevelUpdates.forEach((update) => { test.cb('should route ' + update.type, (t) => { const bot = new Telegraf() bot.on(update.type, (ctx) => t.end()) bot.handleUpdate(update.update) }) }) test.cb('should route many types', (t) => { const bot = new Telegraf() bot.on(['chosen_inline_result', 'message'], (ctx) => t.end()) bot.handleUpdate({inline_query: baseMessage}) bot.handleUpdate({message: baseMessage}) }) test.cb('should route sub types', (t) => { const bot = new Telegraf() bot.on('text', (ctx) => t.end()) bot.handleUpdate({message: Object.assign({voice: {}}, baseMessage)}) bot.handleUpdate({message: Object.assign({text: 'hello'}, baseMessage)}) }) const updateTypes = [ 'voice', 'video_note', 'video', 'venue', 'text', 'supergroup_chat_created', 'successful_payment', 'sticker', 'pinned_message', 'photo', 'new_chat_title', 'new_chat_photo', 'new_chat_members', 'migrate_to_chat_id', 'migrate_from_chat_id', 'location', 'left_chat_member', 'invoice', 'group_chat_created', 'game', 'document', 'delete_chat_photo', 'contact', 'channel_chat_created', 'audio' ] updateTypes.forEach((update) => { test.cb('should route ' + update, (t) => { const bot = new Telegraf() bot.on(update, (ctx) => { t.end() }) const message = Object.assign({}, baseMessage) message[update] = {} bot.handleUpdate({message: message}) }) }) test.cb('should route venue', (t) => { const bot = new Telegraf() bot.on('venue', (ctx) => t.end()) const message = Object.assign({location: {}, venue: {title: 'location', address: 'n/a'}}, baseMessage) bot.handleUpdate({message: message}) }) test.cb('should route venue/location', (t) => { const bot = new Telegraf() bot.on('venue', (ctx) => { t.true(ctx.updateSubTypes.includes('venue')) t.true(ctx.updateSubTypes.includes('location')) t.end() }) const message = Object.assign({location: {}, venue: {title: 'location', address: 'n/a'}}, baseMessage) bot.handleUpdate({message: message}) }) test('should throw error then called with invalid middleware', (t) => { const composer = new Composer() t.throws(() => { composer.compose(() => undefined) }) }) test.cb('should throw error then called with invalid middleware', (t) => { const bot = new Telegraf() bot.catch((e) => t.end()) bot.on('text', 'foo') bot.handleUpdate({message: Object.assign({text: 'hello'}, baseMessage)}) }) test.cb('should throw error then "next()" called twice', (t) => { const bot = new Telegraf() bot.catch((e) => t.end()) bot.use((ctx, next) => { next() return next() }) bot.handleUpdate({message: Object.assign({text: 'hello'}, baseMessage)}) }) test.cb('should throw error then "next()" called with wrong context', (t) => { const bot = new Telegraf() bot.catch((e) => t.end()) bot.use((ctx, next) => next('bad context')) bot.hears('hello', () => t.fail()) bot.handleUpdate({message: Object.assign({text: 'hello'}, baseMessage)}) }) test('should throw error then called with undefined trigger', (t) => { const bot = new Telegraf() t.throws(() => { bot.hears(['foo', null]) }) }) test.cb('should support Composer instance as middleware', (t) => { const bot = new Telegraf() const composer = new Composer() composer.on('text', (ctx) => { t.is('bar', ctx.state.foo) t.end() }) bot.use(({ state }, next) => { state.foo = 'bar' return next() }, composer) bot.handleUpdate({message: Object.assign({text: 'hello'}, baseMessage)}) }) test.cb('should support Composer instance as handler', (t) => { const bot = new Telegraf() const composer = new Composer() composer.on('text', (ctx) => t.end()) bot.on('text', composer) bot.handleUpdate({message: Object.assign({text: 'hello'}, baseMessage)}) }) test.cb('should handle text triggers', (t) => { const bot = new Telegraf() bot.hears('hello world', (ctx) => t.end()) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('should handle fork', (t) => { const bot = new Telegraf() bot.use(Telegraf.fork(() => t.end())) bot.handleUpdate({message: Object.assign({voice: {}}, baseMessage)}) }) test.cb('Composer.branch should work with value', (t) => { const bot = new Telegraf() bot.use(Composer.branch(true, () => t.end())) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.branch should work with fn', (t) => { const bot = new Telegraf() bot.use(Composer.branch((ctx) => false, null, () => t.end())) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.branch should work with async fn', (t) => { const bot = new Telegraf() bot.use(Composer.branch( (ctx) => { return new Promise((resolve) => setTimeout(resolve, 100, false)) }, () => { t.fail() t.end() }, () => t.end()) ) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.acl should work with user id', (t) => { const bot = new Telegraf() bot.use(Composer.acl(42, () => t.end())) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.acl should work with user id', (t) => { const bot = new Telegraf() bot.use(Composer.acl(42, Composer.passThru())) bot.use(() => t.end()) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.acl should work with user id', (t) => { const bot = new Telegraf() bot.use(Composer.acl(999, () => t.fail())) bot.use(() => t.end()) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.acl should work with user ids', (t) => { const bot = new Telegraf() bot.use(Composer.acl([42, 43], () => t.end())) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.acl should work with fn', (t) => { const bot = new Telegraf() bot.use(Composer.acl((ctx) => ctx.from.username === 'telegraf', () => t.end())) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.acl should work with async fn', (t) => { const bot = new Telegraf() bot.use(Composer.acl((ctx) => new Promise((resolve) => setTimeout(resolve, 100, true)), () => t.end())) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.optional should work with truthy value', (t) => { const bot = new Telegraf() bot.use(Composer.optional(true, () => t.end())) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.optional should work with false value', (t) => { const bot = new Telegraf() bot.use(Composer.optional(false, () => { t.fail() t.end() })) bot.use(() => t.end()) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.optional should work with fn', (t) => { const bot = new Telegraf() bot.use(Composer.optional((ctx) => true, () => t.end())) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.optional should work with async fn', (t) => { const bot = new Telegraf() bot.use(Composer.optional( (ctx) => { return new Promise((resolve) => { setTimeout(() => { resolve(false) }, 100) }) }, () => { t.fail() t.end() } )) bot.use(() => t.end()) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.filter should work with fn', (t) => { const bot = new Telegraf() bot.filter(({ message }) => message.text.length < 2) bot.use(() => t.end()) bot.handleUpdate({message: Object.assign({text: '-'}, baseMessage)}) bot.handleUpdate({message: Object.assign({text: 'hello'}, baseMessage)}) bot.handleUpdate({message: Object.assign({text: 'hello world '}, baseMessage)}) }) test.cb('Composer.filter should work with async fn', (t) => { const bot = new Telegraf() bot.filter(({ message }) => { return new Promise((resolve) => { setTimeout(() => { resolve(message.text.length < 2) }, 100) }) }) bot.use(() => t.end()) bot.handleUpdate({message: Object.assign({text: '-'}, baseMessage)}) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.drop should work with fn', (t) => { const bot = new Telegraf() bot.drop(({ message }) => message.text.length > 2) bot.use(() => t.end()) bot.handleUpdate({message: Object.assign({text: '-'}, baseMessage)}) bot.handleUpdate({message: Object.assign({text: 'hello'}, baseMessage)}) bot.handleUpdate({message: Object.assign({text: 'hello world '}, baseMessage)}) }) test.cb('Composer.drop should work with async fn', (t) => { const bot = new Telegraf() bot.drop(({ message }) => { return new Promise((resolve) => { setTimeout(() => { resolve(message.text.length > 2) }, 100) }) }) bot.use(() => t.end()) bot.handleUpdate({message: Object.assign({text: '-'}, baseMessage)}) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.lazy should work with fn', (t) => { const bot = new Telegraf() bot.use(Composer.lazy((ctx) => () => t.end())) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.lazy should work with fn', (t) => { const bot = new Telegraf() bot.use(Composer.lazy((ctx) => (_, next) => next())) bot.use((ctx) => t.end()) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.dispatch should work with handlers array', (t) => { const bot = new Telegraf() bot.use(Composer.dispatch(1, [ () => { t.fail() t.end() }, () => t.end() ])) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.dispatch should work', (t) => { const bot = new Telegraf() bot.use(Composer.dispatch('b', { b: () => t.end() })) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.dispatch should work with async fn', (t) => { const bot = new Telegraf() bot.use(Composer.dispatch( (ctx) => { return new Promise((resolve) => { setTimeout(() => { resolve(1) }, 300) }) }, [ () => { t.fail() t.end() }, () => t.end() ])) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.log should just work', (t) => { const bot = new Telegraf() bot.use(Composer.log(() => t.end())) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('Composer.entity should work', (t) => { const bot = new Telegraf() bot.use(Composer.entity('hashtag', () => t.end())) bot.handleUpdate({message: {text: '#foo', entities: [{type: 'hashtag', offset: 0, length: 4}]}}) }) test.cb('Composer.entity should not infer', (t) => { const bot = new Telegraf() bot.use(Composer.entity('command', () => t.end())) bot.use(() => t.end()) bot.handleUpdate({message: {text: '#foo', entities: [{type: 'hashtag', offset: 0, length: 4}]}}) }) test.cb('Composer.entity should work with arrays', (t) => { const bot = new Telegraf() bot.use(Composer.entity(['command', 'hashtag'], () => t.end())) bot.handleUpdate({message: {text: '#foo', entities: [{type: 'hashtag', offset: 0, length: 4}]}}) }) test.cb('Composer.entity should work with predicate', (t) => { const bot = new Telegraf() bot.use(Composer.entity((entity, value) => entity.type === 'hashtag' && value === '#foo', () => t.end())) bot.handleUpdate({message: {text: '#foo', entities: [{type: 'hashtag', offset: 0, length: 4}]}}) }) test.cb('Composer.mention should work', (t) => { const bot = new Telegraf() bot.use(Composer.mention(() => t.end())) bot.handleUpdate({message: {text: 'bar @foo', entities: [{type: 'mention', offset: 4, length: 4}]}}) }) test.cb('Composer.mention should work with pattern', (t) => { const bot = new Telegraf() bot.use(Composer.mention('foo', () => t.end())) bot.handleUpdate({message: {text: 'bar @foo', entities: [{type: 'mention', offset: 4, length: 4}]}}) }) test.cb('Composer.hashtag should work', (t) => { const bot = new Telegraf() bot.use(Composer.hashtag(() => t.end())) bot.handleUpdate({message: {text: '#foo', entities: [{type: 'hashtag', offset: 0, length: 4}]}}) }) test.cb('Composer.hashtag should work with pattern', (t) => { const bot = new Telegraf() bot.use(Composer.hashtag('foo', () => t.end())) bot.handleUpdate({message: {text: 'bar #foo', entities: [{type: 'hashtag', offset: 4, length: 4}]}}) }) test.cb('Composer.hashtag should work with pattern', (t) => { const bot = new Telegraf() bot.use(Composer.hashtag('#foo', () => t.end())) bot.handleUpdate({message: {text: 'bar #foo', entities: [{type: 'hashtag', offset: 4, length: 4}]}}) }) test.cb('Composer.hashtag should work with patterns array', (t) => { const bot = new Telegraf() bot.use(Composer.hashtag(['news', 'foo'], () => t.end())) bot.handleUpdate({message: {text: 'bar #foo', entities: [{type: 'hashtag', offset: 4, length: 4}]}}) }) test.cb('should handle text triggers via functions', (t) => { const bot = new Telegraf() bot.hears((text) => text.startsWith('Hi'), (ctx) => t.end()) bot.handleUpdate({message: Object.assign({text: 'Hi there!'}, baseMessage)}) }) test.cb('should handle regex triggers', (t) => { const bot = new Telegraf() bot.hears(/hello (.+)/, (ctx) => { t.is('world', ctx.match[1]) t.end() }) bot.handleUpdate({message: Object.assign({text: 'Ola!'}, baseMessage)}) bot.handleUpdate({message: Object.assign({text: 'hello world'}, baseMessage)}) }) test.cb('should handle command', (t) => { const bot = new Telegraf() bot.command('/start', (ctx) => t.end()) bot.handleUpdate({message: Object.assign({text: '/start', entities: [{type: 'bot_command', offset: 0, length: 6}]}, baseMessage)}) }) test.cb('should handle start command', (t) => { const bot = new Telegraf() bot.start((ctx) => t.end()) bot.handleUpdate({ message: Object.assign({ text: '/start', entities: [{ type: 'bot_command', offset: 0, length: 6 }] }, baseMessage) }) }) test.cb('should handle short command', (t) => { const bot = new Telegraf() bot.command('start', (ctx) => t.end()) bot.handleUpdate({message: Object.assign({text: '/start', entities: [{type: 'bot_command', offset: 0, length: 6}]}, baseMessage)}) }) test.cb('should handle group command', (t) => { const bot = new Telegraf(null, {username: 'bot'}) bot.command('start', (ctx) => t.end()) bot.handleUpdate({message: Object.assign({text: '/start@bot', entities: [{type: 'bot_command', offset: 0, length: 10}]}, baseGroupMessage)}) }) test.cb('should handle game query', (t) => { const bot = new Telegraf() bot.gameQuery((ctx) => t.end()) bot.handleUpdate({callback_query: {game_short_name: 'foo'}}) }) test.cb('should handle action', (t) => { const bot = new Telegraf() bot.action('foo', (ctx) => t.end()) bot.handleUpdate({callback_query: {data: 'foo'}}) }) test.cb('should handle regex action', (t) => { const bot = new Telegraf() bot.action(/foo (\d+)/, (ctx) => { t.true('match' in ctx) t.is('42', ctx.match[1]) t.end() }) bot.handleUpdate({callback_query: {data: 'foo 42'}}) }) test.cb('should handle action', (t) => { const bot = new Telegraf() bot.action('bar', (ctx) => { t.fail() }) bot.use((ctx) => t.end()) bot.handleUpdate({callback_query: {data: 'foo'}}) }) test.cb('should handle short command', (t) => { const bot = new Telegraf() bot.command('start', (ctx) => t.end()) bot.handleUpdate({message: Object.assign({text: '/start', entities: [{type: 'bot_command', offset: 0, length: 6}]}, baseMessage)}) }) test.cb('should handle command in group', (t) => { const bot = new Telegraf('---', {username: 'bot'}) bot.command('start', (ctx) => t.end()) bot.handleUpdate({message: {text: '/start@bot', entities: [{type: 'bot_command', offset: 0, length: 10}], chat: {id: 2, type: 'group'}}}) }) test.cb('should handle command in supergroup', (t) => { const bot = new Telegraf() bot.options.username = 'bot' bot.command('start', (ctx) => t.end()) bot.handleUpdate({message: {text: '/start@bot', entities: [{type: 'bot_command', offset: 0, length: 10}], chat: {id: 2, type: 'supergroup'}}}) })