class Telegraf extends Composer {
constructor (token, options) {
super()
this.options = Object.assign({
retryAfter: 1,
handlerTimeout: 0
}, options)
this.token = token
this.handleError = (err) => {
console.error()
console.error((err.stack || err.toString()).replace(/^/gm, ' '))
console.error()
throw err
}
this.context = {}
this.state = {
offset: 0,
started: false
}
}
get token () {
return this.options.token
}
set token (token) {
this.options.token = token
this.telegram = new Telegram(this.options.token, this.options.telegram)
}/* eslint brace-style: 0 */
catch (handler) {
this.handleError = handler
return this
}
webhookCallback (path = '/') {
return generateCallback(path, (update, res) => this.handleUpdate(update, res), debug)
}
startPolling (timeout = 30, limit = 100, allowedUpdates) {
this.state.timeout = timeout
this.state.limit = limit
this.state.allowedUpdates = allowedUpdates
? Array.isArray(allowedUpdates) ? allowedUpdates : [`${allowedUpdates}`]
: null
if (!this.state.started) {
this.state.started = true
this.fetchUpdates()
}
return this
}
startWebhook (path, tlsOptions, port, host, cb) {
const webhookCb = this.webhookCallback(path)
const callback = cb && typeof cb === 'function'
? (req, res) => webhookCb(req, res, () => cb(req, res))
: webhookCb
this.webhookServer = tlsOptions
? require('https').createServer(tlsOptions, callback)
: require('http').createServer(callback)
this.webhookServer.listen(port, host, () => {
debug('Webhook listening on port: %s', port)
})
return this
}
stop () {
this.state.started = false
if (this.webhookServer) {
this.webhookServer.close()
}
return this
}
handleUpdates (updates) {
if (!Array.isArray(updates)) {
return Promise.reject(new Error('Updates must be an array'))
}
const processAll = Promise.all(updates.map((update) => this.handleUpdate(update)))
if (this.options.handlerTimeout === 0) {
return processAll
}
return Promise.race([
processAll,
new Promise((resolve) => setTimeout(resolve, this.options.handlerTimeout))
])
}
handleUpdate (update, webhookResponse) {
debug('âš¡ update', update.update_id)
const telegram = webhookResponse
? new Telegram(this.token, this.options.telegram, webhookResponse)
: this.telegram
const ctx = new Context(update, telegram, this.options)
Object.assign(ctx, this.context)
return this.middleware()(ctx).catch(this.handleError)
}
fetchUpdates () {
const { timeout, limit, offset, started, allowedUpdates } = this.state
if (!started) {
return
}
this.telegram.getUpdates(timeout, limit, offset, allowedUpdates)
.catch((err) => {
const wait = err.retryAfter || this.options.retryAfter
console.error(`Failed to get updates. Waiting: ${wait}s`, err)
return new Promise((resolve) => setTimeout(resolve, wait * 1000, []))
})
.then((updates) => this.handleUpdates(updates).then(() => updates))
.catch((err) => {
console.error('Failed to process updates.', err)
this.state.offset = 0
this.state.started = false
return []
})
.then((updates) => {
if (updates.length > 0) {
this.state.offset = updates[updates.length - 1].update_id + 1
}
this.fetchUpdates()
})
}
}n/a
class Composer {
constructor (...handlers) {
this.handler = Composer.compose(handlers)
}
use (...fns) {
this.handler = Composer.compose([this.handler, ...fns])
return this
}
on (updateTypes, ...fns) {
return this.use(Composer.mount(updateTypes, Composer.compose(fns)))
}
hears (triggers, ...fns) {
return this.use(Composer.hears(triggers, Composer.compose(fns)))
}
command (commands, ...fns) {
return this.use(Composer.command(commands, Composer.compose(fns)))
}
action (triggers, ...fns) {
return this.use(Composer.action(triggers, Composer.compose(fns)))
}
gameQuery (...fns) {
return this.use(Composer.gameQuery(Composer.compose(fns)))
}
middleware () {
return this.handler
}
static reply (...args) {
return (ctx) => ctx.reply(...args)
}
static fork (middleware) {
return (ctx, next) => {
setImmediate(unwrap(middleware), ctx)
return next()
}
}
static passThru () {
return (ctx, next) => next()
}
static lazy (fn) {
if (typeof fn !== 'function') {
throw new Error('Argument must be a function')
}
return (ctx, next) => Promise.resolve(fn(ctx))
.then((middleware) => {
const handler = unwrap(middleware)
return handler(ctx, next)
})
}
static log (logFn = console.log) {
return Composer.fork((ctx) => logFn(JSON.stringify(ctx.update, null, 2)))
}
static branch (test, trueMiddleware, falseMiddleware) {
if (typeof test !== 'function') {
return test ? trueMiddleware : falseMiddleware
}
return Composer.lazy((ctx) => Promise.resolve(test(ctx)).then((value) => value ? trueMiddleware : falseMiddleware))
}
static optional (test, ...fns) {
return Composer.branch(test, Composer.compose(fns), Composer.passThru())
}
static dispatch (test, handlers) {
if (typeof test !== 'function') {
return handlers[test] || Composer.passThru()
}
return Composer.lazy((ctx) => Promise.resolve(test(ctx)).then((value) => handlers[value]))
}
static mount (updateType, middleware) {
let test = Array.isArray(updateType)
? (ctx) => updateType.includes(ctx.updateType) || updateType.includes(ctx.updateSubType)
: (ctx) => updateType === ctx.updateType || updateType === ctx.updateSubType
return Composer.optional(test, middleware)
}
static hears (triggers, middleware) {
const tests = makeTests(triggers)
return Composer.mount('text', Composer.match(tests, middleware))
}
static action (triggers, middleware) {
const tests = makeTests(triggers)
return Composer.mount('callback_query', Composer.match(tests, middleware))
}
static match (tests, middleware) {
return Composer.lazy((ctx) => {
const text = (ctx.message && (ctx.message.caption || ctx.message.text)) || (ctx.callbackQuery && ctx.callbackQuery.data)
for (let test of tests) {
const result = test(text, ctx)
if (!result) {
continue
}
ctx.match = result
return middleware
}
return Composer.passThru()
})
}
static acl (userId, middleware) {
let whitelistFn = userId
if (typeof whitelistFn !== 'function') {
const allowed = Array.isArray(userId) ? userId : [userId]
whitelistFn = (ctx) => allowed.includes(ctx.from.id) || (ctx.from.username && allowed.includes(ctx.from.username))
}
return Composer.optional(whitelistFn, middleware)
}
static gameQuery (middleware) {
return Composer.mount('callback_query', Composer.optional((ctx) => ctx.callbackQuery.game_short_name, middleware))
}
static command (command, middleware) {
let commands = Array.isArray(command) ? command : [command]
commands = commands.map((cmd) => cmd.startsWith('/') ? cmd : `/${cmd}`)
return Composer.mount('text', Composer.lazy((ctx) => {
const text = ctx.message.text
const groupCommands = ctx.me && (ctx.chat.type === 'group' || ctx.chat.type === 'supergroup')
? commands.map((command) => `${command}@${ctx.me}`)
: []
const hasM ...n/a
class Extra {
constructor (opts) {
this.load(opts)
}
load (opts) {
if (opts) {
Object.assign(this, opts)
}
return this
}
inReplyTo (messageId) {
this.reply_to_message_id = messageId
return this
}
notifications (value = true) {
this.disable_notification = !value
return this
}
webPreview (value = true) {
this.disable_web_page_preview = !value
return this
}
markup (markup) {
if (typeof markup === 'function') {
markup = markup(new ReplyMarkup())
}
this.reply_markup = Object.assign({}, markup)
return this
}
HTML (value = true) {
this.parse_mode = value ? 'HTML' : undefined
return this
}
markdown (value = true) {
this.parse_mode = value ? 'Markdown' : undefined
return this
}
static inReplyTo (messageId) {
return new Extra().inReplyTo(messageId)
}
static notifications (value) {
return new Extra().notifications(value)
}
static webPreview (value) {
return new Extra().webPreview(value)
}
static load (opts) {
return new Extra(opts)
}
static markup (markup) {
return new Extra().markup(markup)
}
static HTML (value) {
return new Extra().HTML(value)
}
static markdown (value) {
return new Extra().markdown(value)
}
}n/a
class Markup {
forceReply (value = true) {
this.force_reply = value
return this
}
removeKeyboard (value = true) {
this.remove_keyboard = value
return this
}
selective (value = true) {
this.selective = value
return this
}
extra () {
return { reply_markup: Object.assign({}, this) }
}
keyboard (buttons, options) {
const keyboard = buildKeyboard(buttons, Object.assign({columns: 1}, options))
if (keyboard && keyboard.length > 0) {
this.keyboard = keyboard
}
return this
}
resize (value = true) {
this.resize_keyboard = value
return this
}
oneTime (value = true) {
this.one_time_keyboard = value
return this
}
inlineKeyboard (buttons, options) {
const keyboard = buildKeyboard(buttons, Object.assign({columns: buttons.length}, options))
if (keyboard && keyboard.length > 0) {
this.inline_keyboard = keyboard
}
return this
}
button (text, hide) {
return Markup.button(text, hide)
}
contactRequestButton (text, hide) {
return Markup.contactRequestButton(text, hide)
}
locationRequestButton (text, hide) {
return Markup.locationRequestButton(text, hide)
}
urlButton (text, url, hide) {
return Markup.urlButton(text, url, hide)
}
callbackButton (text, data, hide) {
return Markup.callbackButton(text, data, hide)
}
gameButton (text, hide) {
return Markup.gameButton(text, hide)
}
static removeKeyboard (value) {
return new Markup().removeKeyboard(value)
}
static forceReply (value) {
return new Markup().forceReply(value)
}
static keyboard (buttons, options) {
return new Markup().keyboard(buttons, options)
}
static inlineKeyboard (buttons, options) {
return new Markup().inlineKeyboard(buttons, options)
}
static resize (value = true) {
return new Markup().resize(value)
}
static oneTime (value = true) {
return new Markup().oneTime(value)
}
static button (text, hide) {
return { text: text, hide: hide }
}
static contactRequestButton (text, hide = false) {
return { text: text, request_contact: true, hide: hide }
}
static locationRequestButton (text, hide = false) {
return { text: text, request_location: true, hide: hide }
}
static urlButton (text, url, hide = false) {
return { text: text, url: url, hide: hide }
}
static callbackButton (text, data, hide = false) {
return { text: text, callback_data: data, hide: hide }
}
static switchToChatButton (text, value, hide = false) {
return { text: text, switch_inline_query: value, hide: hide }
}
static switchToCurrentChatButton (text, value, hide = false) {
return { text: text, switch_inline_query_current_chat: value, hide: hide }
}
static gameButton (text, hide = false) {
return { text: text, callback_game: {}, hide: hide }
}
}n/a
class Router {
constructor (routeFn) {
if (!routeFn) {
throw new Error('Missing routing function')
}
this.routeFn = routeFn
this.handlers = new Map()
this.otherwiseHandler = passThru()
}
on (route, ...fns) {
if (fns.length === 0) {
throw new TypeError('At least one handler must be provided')
}
this.handlers.set(route, compose(fns))
return this
}
otherwise (...fns) {
if (fns.length === 0) {
throw new TypeError('At least one otherwise handler must be provided')
}
this.otherwiseHandler = compose(fns)
return this
}
middleware () {
return lazy((ctx) => {
return this.routeFn(ctx).then((result) => {
if (!result || !result.route || !this.handlers.has(result.route)) {
return this.otherwiseHandler
}
Object.assign(ctx, result.context)
Object.assign(ctx.state, result.state)
return this.handlers.get(result.route)
})
})
}
}n/a
class Telegram extends ApiClient {
getMe () {
return this.callApi('getMe')
}
getFile (fileId) {
return this.callApi('getFile', {file_id: fileId})
}
getFileLink (fileId) {
return Promise.resolve(fileId)
.then((fileId) => {
if (fileId && fileId.file_path) {
return fileId
}
const id = fileId && fileId.file_id ? fileId.file_id : fileId
return this.getFile(id)
})
.then((file) => `${this.options.apiRoot}/file/bot${this.token}/${file.file_path}`)
}
getUpdates (timeout, limit, offset, allowedUpdates) {
let url = `getUpdates?offset=${offset}&limit=${limit}&timeout=${timeout}`
return this.callApi(url, {
allowed_updates: allowedUpdates
})
}
getWebhookInfo () {
return this.callApi(`getWebhookInfo`)
}
getGameHighScores (userId, inlineMessageId, chatId, messageId) {
return this.callApi(`getGameHighScores`, {
user_id: userId,
inline_message_id: inlineMessageId,
chat_id: chatId,
message_id: messageId
})
}
setGameScore (userId, score, inlineMessageId, chatId, messageId, editMessage = true, force) {
return this.callApi(`setGameScore`, {
user_id: userId,
score: score,
inline_message_id: inlineMessageId,
chat_id: chatId,
message_id: messageId,
disable_edit_message: !editMessage,
force: force
})
}
setWebhook (url, cert, maxConnections, allowedUpdates) {
return this.callApi('setWebhook', {
url: url,
certificate: cert,
max_connections: maxConnections,
allowed_updates: allowedUpdates
})
}
deleteWebhook () {
return this.callApi('deleteWebhook')
}
sendMessage (chatId, text, extra) {
return this.callApi('sendMessage', Object.assign({ chat_id: chatId, text: text }, extra))
}
forwardMessage (chatId, fromChatId, messageId, extra) {
return this.callApi('forwardMessage', Object.assign({
chat_id: chatId,
from_chat_id: fromChatId,
message_id: messageId
}, extra))
}
sendChatAction (chatId, action) {
return this.callApi('sendChatAction', { chat_id: chatId, action: action })
}
getUserProfilePhotos (userId, offset, limit) {
return this.callApi('getUserProfilePhotos', { user_id: userId, offset: offset, limit: limit })
}
sendLocation (chatId, latitude, longitude, extra) {
return this.callApi('sendLocation', Object.assign({ chat_id: chatId, latitude: latitude, longitude: longitude }, extra))
}
sendVenue (chatId, latitude, longitude, title, address, extra) {
return this.callApi('sendVenue', Object.assign({
chat_id: chatId,
latitude: latitude,
longitude: longitude,
title: title,
address: address
}, extra))
}
sendContact (chatId, phoneNumber, firstName, extra) {
return this.callApi('sendContact', Object.assign({ chat_id: chatId, phone_number: phoneNumber, first_name: firstName }, extra
))
}
sendPhoto (chatId, photo, extra) {
return this.callApi('sendPhoto', Object.assign({ chat_id: chatId, photo: photo }, extra))
}
sendDocument (chatId, doc, extra) {
return this.callApi('sendDocument', Object.assign({ chat_id: chatId, document: doc }, extra))
}
sendAudio (chatId, audio, extra) {
return this.callApi('sendAudio', Object.assign({ chat_id: chatId, audio: audio }, extra))
}
sendSticker (chatId, sticker, extra) {
return this.callApi('sendSticker', Object.assign({ chat_id: chatId, sticker: sticker }, extra))
}
sendVideo (chatId, video, extra) {
return this.callApi('sendVideo', Object.assign({ chat_id: chatId, video: video }, extra))
}
sendVoice (chatId, voice, extra) {
return this.callApi('sendVoice', Object.assign({ chat_id: chatId, voice: voice }, extra))
}
sendGame (chatId, gameName, extra) {
return this.callApi('sendGame', Object.assign({ chat_id: chatId, game_short_name: gameName }, extra))
}
getChat (chatId) {
return this.callApi('getChat', {chat_id: chatId})
}
getChatAdministrators (chatId) {
return this.callApi('getCh ...n/a
class TelegramError extends Error {
constructor (payload = {}) {
super(`${payload.error_code}: ${payload.description}`)
this.code = payload.error_code
this.description = payload.description
this.retryAfter = payload.retry_after
this.migrateToChatId = payload.migrate_to_chat_id
}
}n/a
memorySession = function (opts) {
opts = Object.assign({
sessionName: 'session',
getSessionKey: (ctx) => ctx.from && ctx.chat && `${ctx.from.id}:${ctx.chat.id}`
}, opts)
const store = new Map()
return (ctx, next) => {
const key = opts.getSessionKey(ctx)
if (!key) {
return next()
}
let session = store.get(key) || {}
Object.defineProperty(ctx, opts.sessionName, {
get: function () { return session },
set: function (newValue) { session = Object.assign({}, newValue) }
})
try {
return next()
} finally {
store.set(key, session)
}
}
}n/a
class Extra {
constructor (opts) {
this.load(opts)
}
load (opts) {
if (opts) {
Object.assign(this, opts)
}
return this
}
inReplyTo (messageId) {
this.reply_to_message_id = messageId
return this
}
notifications (value = true) {
this.disable_notification = !value
return this
}
webPreview (value = true) {
this.disable_web_page_preview = !value
return this
}
markup (markup) {
if (typeof markup === 'function') {
markup = markup(new ReplyMarkup())
}
this.reply_markup = Object.assign({}, markup)
return this
}
HTML (value = true) {
this.parse_mode = value ? 'HTML' : undefined
return this
}
markdown (value = true) {
this.parse_mode = value ? 'Markdown' : undefined
return this
}
static inReplyTo (messageId) {
return new Extra().inReplyTo(messageId)
}
static notifications (value) {
return new Extra().notifications(value)
}
static webPreview (value) {
return new Extra().webPreview(value)
}
static load (opts) {
return new Extra(opts)
}
static markup (markup) {
return new Extra().markup(markup)
}
static HTML (value) {
return new Extra().HTML(value)
}
static markdown (value) {
return new Extra().markdown(value)
}
}n/a
class Markup {
forceReply (value = true) {
this.force_reply = value
return this
}
removeKeyboard (value = true) {
this.remove_keyboard = value
return this
}
selective (value = true) {
this.selective = value
return this
}
extra () {
return { reply_markup: Object.assign({}, this) }
}
keyboard (buttons, options) {
const keyboard = buildKeyboard(buttons, Object.assign({columns: 1}, options))
if (keyboard && keyboard.length > 0) {
this.keyboard = keyboard
}
return this
}
resize (value = true) {
this.resize_keyboard = value
return this
}
oneTime (value = true) {
this.one_time_keyboard = value
return this
}
inlineKeyboard (buttons, options) {
const keyboard = buildKeyboard(buttons, Object.assign({columns: buttons.length}, options))
if (keyboard && keyboard.length > 0) {
this.inline_keyboard = keyboard
}
return this
}
button (text, hide) {
return Markup.button(text, hide)
}
contactRequestButton (text, hide) {
return Markup.contactRequestButton(text, hide)
}
locationRequestButton (text, hide) {
return Markup.locationRequestButton(text, hide)
}
urlButton (text, url, hide) {
return Markup.urlButton(text, url, hide)
}
callbackButton (text, data, hide) {
return Markup.callbackButton(text, data, hide)
}
gameButton (text, hide) {
return Markup.gameButton(text, hide)
}
static removeKeyboard (value) {
return new Markup().removeKeyboard(value)
}
static forceReply (value) {
return new Markup().forceReply(value)
}
static keyboard (buttons, options) {
return new Markup().keyboard(buttons, options)
}
static inlineKeyboard (buttons, options) {
return new Markup().inlineKeyboard(buttons, options)
}
static resize (value = true) {
return new Markup().resize(value)
}
static oneTime (value = true) {
return new Markup().oneTime(value)
}
static button (text, hide) {
return { text: text, hide: hide }
}
static contactRequestButton (text, hide = false) {
return { text: text, request_contact: true, hide: hide }
}
static locationRequestButton (text, hide = false) {
return { text: text, request_location: true, hide: hide }
}
static urlButton (text, url, hide = false) {
return { text: text, url: url, hide: hide }
}
static callbackButton (text, data, hide = false) {
return { text: text, callback_data: data, hide: hide }
}
static switchToChatButton (text, value, hide = false) {
return { text: text, switch_inline_query: value, hide: hide }
}
static switchToCurrentChatButton (text, value, hide = false) {
return { text: text, switch_inline_query_current_chat: value, hide: hide }
}
static gameButton (text, hide = false) {
return { text: text, callback_game: {}, hide: hide }
}
}n/a
class Markup {
forceReply (value = true) {
this.force_reply = value
return this
}
removeKeyboard (value = true) {
this.remove_keyboard = value
return this
}
selective (value = true) {
this.selective = value
return this
}
extra () {
return { reply_markup: Object.assign({}, this) }
}
keyboard (buttons, options) {
const keyboard = buildKeyboard(buttons, Object.assign({columns: 1}, options))
if (keyboard && keyboard.length > 0) {
this.keyboard = keyboard
}
return this
}
resize (value = true) {
this.resize_keyboard = value
return this
}
oneTime (value = true) {
this.one_time_keyboard = value
return this
}
inlineKeyboard (buttons, options) {
const keyboard = buildKeyboard(buttons, Object.assign({columns: buttons.length}, options))
if (keyboard && keyboard.length > 0) {
this.inline_keyboard = keyboard
}
return this
}
button (text, hide) {
return Markup.button(text, hide)
}
contactRequestButton (text, hide) {
return Markup.contactRequestButton(text, hide)
}
locationRequestButton (text, hide) {
return Markup.locationRequestButton(text, hide)
}
urlButton (text, url, hide) {
return Markup.urlButton(text, url, hide)
}
callbackButton (text, data, hide) {
return Markup.callbackButton(text, data, hide)
}
gameButton (text, hide) {
return Markup.gameButton(text, hide)
}
static removeKeyboard (value) {
return new Markup().removeKeyboard(value)
}
static forceReply (value) {
return new Markup().forceReply(value)
}
static keyboard (buttons, options) {
return new Markup().keyboard(buttons, options)
}
static inlineKeyboard (buttons, options) {
return new Markup().inlineKeyboard(buttons, options)
}
static resize (value = true) {
return new Markup().resize(value)
}
static oneTime (value = true) {
return new Markup().oneTime(value)
}
static button (text, hide) {
return { text: text, hide: hide }
}
static contactRequestButton (text, hide = false) {
return { text: text, request_contact: true, hide: hide }
}
static locationRequestButton (text, hide = false) {
return { text: text, request_location: true, hide: hide }
}
static urlButton (text, url, hide = false) {
return { text: text, url: url, hide: hide }
}
static callbackButton (text, data, hide = false) {
return { text: text, callback_data: data, hide: hide }
}
static switchToChatButton (text, value, hide = false) {
return { text: text, switch_inline_query: value, hide: hide }
}
static switchToCurrentChatButton (text, value, hide = false) {
return { text: text, switch_inline_query_current_chat: value, hide: hide }
}
static gameButton (text, hide = false) {
return { text: text, callback_game: {}, hide: hide }
}
}n/a
function deprecated() {
warned = exports.printDeprecationMessage(msg, warned, deprecated);
if (new.target) {
return Reflect.construct(fn, arguments, new.target);
}
return fn.apply(this, arguments);
}n/a
function deprecated() {
warned = exports.printDeprecationMessage(msg, warned, deprecated);
if (new.target) {
return Reflect.construct(fn, arguments, new.target);
}
return fn.apply(this, arguments);
}n/a
class Telegram extends ApiClient {
getMe () {
return this.callApi('getMe')
}
getFile (fileId) {
return this.callApi('getFile', {file_id: fileId})
}
getFileLink (fileId) {
return Promise.resolve(fileId)
.then((fileId) => {
if (fileId && fileId.file_path) {
return fileId
}
const id = fileId && fileId.file_id ? fileId.file_id : fileId
return this.getFile(id)
})
.then((file) => `${this.options.apiRoot}/file/bot${this.token}/${file.file_path}`)
}
getUpdates (timeout, limit, offset, allowedUpdates) {
let url = `getUpdates?offset=${offset}&limit=${limit}&timeout=${timeout}`
return this.callApi(url, {
allowed_updates: allowedUpdates
})
}
getWebhookInfo () {
return this.callApi(`getWebhookInfo`)
}
getGameHighScores (userId, inlineMessageId, chatId, messageId) {
return this.callApi(`getGameHighScores`, {
user_id: userId,
inline_message_id: inlineMessageId,
chat_id: chatId,
message_id: messageId
})
}
setGameScore (userId, score, inlineMessageId, chatId, messageId, editMessage = true, force) {
return this.callApi(`setGameScore`, {
user_id: userId,
score: score,
inline_message_id: inlineMessageId,
chat_id: chatId,
message_id: messageId,
disable_edit_message: !editMessage,
force: force
})
}
setWebhook (url, cert, maxConnections, allowedUpdates) {
return this.callApi('setWebhook', {
url: url,
certificate: cert,
max_connections: maxConnections,
allowed_updates: allowedUpdates
})
}
deleteWebhook () {
return this.callApi('deleteWebhook')
}
sendMessage (chatId, text, extra) {
return this.callApi('sendMessage', Object.assign({ chat_id: chatId, text: text }, extra))
}
forwardMessage (chatId, fromChatId, messageId, extra) {
return this.callApi('forwardMessage', Object.assign({
chat_id: chatId,
from_chat_id: fromChatId,
message_id: messageId
}, extra))
}
sendChatAction (chatId, action) {
return this.callApi('sendChatAction', { chat_id: chatId, action: action })
}
getUserProfilePhotos (userId, offset, limit) {
return this.callApi('getUserProfilePhotos', { user_id: userId, offset: offset, limit: limit })
}
sendLocation (chatId, latitude, longitude, extra) {
return this.callApi('sendLocation', Object.assign({ chat_id: chatId, latitude: latitude, longitude: longitude }, extra))
}
sendVenue (chatId, latitude, longitude, title, address, extra) {
return this.callApi('sendVenue', Object.assign({
chat_id: chatId,
latitude: latitude,
longitude: longitude,
title: title,
address: address
}, extra))
}
sendContact (chatId, phoneNumber, firstName, extra) {
return this.callApi('sendContact', Object.assign({ chat_id: chatId, phone_number: phoneNumber, first_name: firstName }, extra
))
}
sendPhoto (chatId, photo, extra) {
return this.callApi('sendPhoto', Object.assign({ chat_id: chatId, photo: photo }, extra))
}
sendDocument (chatId, doc, extra) {
return this.callApi('sendDocument', Object.assign({ chat_id: chatId, document: doc }, extra))
}
sendAudio (chatId, audio, extra) {
return this.callApi('sendAudio', Object.assign({ chat_id: chatId, audio: audio }, extra))
}
sendSticker (chatId, sticker, extra) {
return this.callApi('sendSticker', Object.assign({ chat_id: chatId, sticker: sticker }, extra))
}
sendVideo (chatId, video, extra) {
return this.callApi('sendVideo', Object.assign({ chat_id: chatId, video: video }, extra))
}
sendVoice (chatId, voice, extra) {
return this.callApi('sendVoice', Object.assign({ chat_id: chatId, voice: voice }, extra))
}
sendGame (chatId, gameName, extra) {
return this.callApi('sendGame', Object.assign({ chat_id: chatId, game_short_name: gameName }, extra))
}
getChat (chatId) {
return this.callApi('getChat', {chat_id: chatId})
}
getChatAdministrators (chatId) {
return this.callApi('getCh ...n/a
function deprecated() {
warned = exports.printDeprecationMessage(msg, warned, deprecated);
if (new.target) {
return Reflect.construct(fn, arguments, new.target);
}
return fn.apply(this, arguments);
}n/a
function deprecated() {
warned = exports.printDeprecationMessage(msg, warned, deprecated);
if (new.target) {
return Reflect.construct(fn, arguments, new.target);
}
return fn.apply(this, arguments);
}n/a
(message) => {
return {
audio: message.audio.file_id,
duration: message.audio.duration,
performer: message.audio.performer,
title: message.audio.title,
caption: message.caption
}
}n/a
(message) => {
return {
phone_number: message.contact.phone_number,
first_name: message.contact.first_name,
last_name: message.contact.last_name
}
}n/a
(message) => {
return {
document: message.document.file_id,
caption: message.caption
}
}n/a
(message) => {
return {
latitude: message.location.latitude,
longitude: message.location.longitude
}
}n/a
(message) => {
return {
photo: message.photo[message.photo.length - 1].file_id,
caption: message.caption
}
}n/a
(message) => {
return {
sticker: message.sticker.file_id
}
}n/a
(message) => {
const result = {
text: message.text
}
if (!message.entities) {
return result
}
message.entities.reverse().forEach((entity) => {
switch (entity.type) {
case 'bold':
result.text = insert(result.text, entity.offset + entity.length, '</b>')
result.text = insert(result.text, entity.offset, '<b>')
break
case 'italic':
result.text = insert(result.text, entity.offset + entity.length, '</i>')
result.text = insert(result.text, entity.offset, '<i>')
break
case 'code':
result.text = insert(result.text, entity.offset + entity.length, '</code>')
result.text = insert(result.text, entity.offset, '<code>')
break
case 'pre':
result.text = insert(result.text, entity.offset + entity.length, '</pre>')
result.text = insert(result.text, entity.offset, '<pre>')
break
case 'text_link':
result.text = insert(result.text, entity.offset + entity.length, '</a>')
result.text = insert(result.text, entity.offset, `<a href="${entity.url}">`)
break
default:
return
}
result.parse_mode = 'HTML'
})
return result
}...
debug('▶︎ http', method)
const buildPayload = isMultipart ? this.buildFormDataPayload(extra) : this.buildJSONPayload(extra)
return buildPayload
.then((payload) => {
payload.agent = this.options.agent
return fetch(`${this.options.apiRoot}/bot${this.token}/${method}`, payload)
})
.then((res) => res.text())
.then((text) => {
try {
return JSON.parse(text)
} catch (err) {
throw new TelegramError({
error_code: 500,
description: 'Unsupported message received from Telegram',
...(message) => {
return {
latitude: message.venue.location.latitude,
longitude: message.venue.location.longitude,
title: message.venue.title,
address: message.venue.address,
foursquare_id: message.venue.foursquare_id
}
}n/a
(message) => {
return {
video: message.video.file_id,
caption: message.caption,
duration: message.video.duration,
width: message.video.width,
height: message.video.height
}
}n/a
(message) => {
return {
voice: message.voice.file_id,
duration: message.voice.duration,
caption: message.caption
}
}n/a
class Telegraf extends Composer {
constructor (token, options) {
super()
this.options = Object.assign({
retryAfter: 1,
handlerTimeout: 0
}, options)
this.token = token
this.handleError = (err) => {
console.error()
console.error((err.stack || err.toString()).replace(/^/gm, ' '))
console.error()
throw err
}
this.context = {}
this.state = {
offset: 0,
started: false
}
}
get token () {
return this.options.token
}
set token (token) {
this.options.token = token
this.telegram = new Telegram(this.options.token, this.options.telegram)
}/* eslint brace-style: 0 */
catch (handler) {
this.handleError = handler
return this
}
webhookCallback (path = '/') {
return generateCallback(path, (update, res) => this.handleUpdate(update, res), debug)
}
startPolling (timeout = 30, limit = 100, allowedUpdates) {
this.state.timeout = timeout
this.state.limit = limit
this.state.allowedUpdates = allowedUpdates
? Array.isArray(allowedUpdates) ? allowedUpdates : [`${allowedUpdates}`]
: null
if (!this.state.started) {
this.state.started = true
this.fetchUpdates()
}
return this
}
startWebhook (path, tlsOptions, port, host, cb) {
const webhookCb = this.webhookCallback(path)
const callback = cb && typeof cb === 'function'
? (req, res) => webhookCb(req, res, () => cb(req, res))
: webhookCb
this.webhookServer = tlsOptions
? require('https').createServer(tlsOptions, callback)
: require('http').createServer(callback)
this.webhookServer.listen(port, host, () => {
debug('Webhook listening on port: %s', port)
})
return this
}
stop () {
this.state.started = false
if (this.webhookServer) {
this.webhookServer.close()
}
return this
}
handleUpdates (updates) {
if (!Array.isArray(updates)) {
return Promise.reject(new Error('Updates must be an array'))
}
const processAll = Promise.all(updates.map((update) => this.handleUpdate(update)))
if (this.options.handlerTimeout === 0) {
return processAll
}
return Promise.race([
processAll,
new Promise((resolve) => setTimeout(resolve, this.options.handlerTimeout))
])
}
handleUpdate (update, webhookResponse) {
debug('âš¡ update', update.update_id)
const telegram = webhookResponse
? new Telegram(this.token, this.options.telegram, webhookResponse)
: this.telegram
const ctx = new Context(update, telegram, this.options)
Object.assign(ctx, this.context)
return this.middleware()(ctx).catch(this.handleError)
}
fetchUpdates () {
const { timeout, limit, offset, started, allowedUpdates } = this.state
if (!started) {
return
}
this.telegram.getUpdates(timeout, limit, offset, allowedUpdates)
.catch((err) => {
const wait = err.retryAfter || this.options.retryAfter
console.error(`Failed to get updates. Waiting: ${wait}s`, err)
return new Promise((resolve) => setTimeout(resolve, wait * 1000, []))
})
.then((updates) => this.handleUpdates(updates).then(() => updates))
.catch((err) => {
console.error('Failed to process updates.', err)
this.state.offset = 0
this.state.started = false
return []
})
.then((updates) => {
if (updates.length > 0) {
this.state.offset = updates[updates.length - 1].update_id + 1
}
this.fetchUpdates()
})
}
}n/a
class Composer {
constructor (...handlers) {
this.handler = Composer.compose(handlers)
}
use (...fns) {
this.handler = Composer.compose([this.handler, ...fns])
return this
}
on (updateTypes, ...fns) {
return this.use(Composer.mount(updateTypes, Composer.compose(fns)))
}
hears (triggers, ...fns) {
return this.use(Composer.hears(triggers, Composer.compose(fns)))
}
command (commands, ...fns) {
return this.use(Composer.command(commands, Composer.compose(fns)))
}
action (triggers, ...fns) {
return this.use(Composer.action(triggers, Composer.compose(fns)))
}
gameQuery (...fns) {
return this.use(Composer.gameQuery(Composer.compose(fns)))
}
middleware () {
return this.handler
}
static reply (...args) {
return (ctx) => ctx.reply(...args)
}
static fork (middleware) {
return (ctx, next) => {
setImmediate(unwrap(middleware), ctx)
return next()
}
}
static passThru () {
return (ctx, next) => next()
}
static lazy (fn) {
if (typeof fn !== 'function') {
throw new Error('Argument must be a function')
}
return (ctx, next) => Promise.resolve(fn(ctx))
.then((middleware) => {
const handler = unwrap(middleware)
return handler(ctx, next)
})
}
static log (logFn = console.log) {
return Composer.fork((ctx) => logFn(JSON.stringify(ctx.update, null, 2)))
}
static branch (test, trueMiddleware, falseMiddleware) {
if (typeof test !== 'function') {
return test ? trueMiddleware : falseMiddleware
}
return Composer.lazy((ctx) => Promise.resolve(test(ctx)).then((value) => value ? trueMiddleware : falseMiddleware))
}
static optional (test, ...fns) {
return Composer.branch(test, Composer.compose(fns), Composer.passThru())
}
static dispatch (test, handlers) {
if (typeof test !== 'function') {
return handlers[test] || Composer.passThru()
}
return Composer.lazy((ctx) => Promise.resolve(test(ctx)).then((value) => handlers[value]))
}
static mount (updateType, middleware) {
let test = Array.isArray(updateType)
? (ctx) => updateType.includes(ctx.updateType) || updateType.includes(ctx.updateSubType)
: (ctx) => updateType === ctx.updateType || updateType === ctx.updateSubType
return Composer.optional(test, middleware)
}
static hears (triggers, middleware) {
const tests = makeTests(triggers)
return Composer.mount('text', Composer.match(tests, middleware))
}
static action (triggers, middleware) {
const tests = makeTests(triggers)
return Composer.mount('callback_query', Composer.match(tests, middleware))
}
static match (tests, middleware) {
return Composer.lazy((ctx) => {
const text = (ctx.message && (ctx.message.caption || ctx.message.text)) || (ctx.callbackQuery && ctx.callbackQuery.data)
for (let test of tests) {
const result = test(text, ctx)
if (!result) {
continue
}
ctx.match = result
return middleware
}
return Composer.passThru()
})
}
static acl (userId, middleware) {
let whitelistFn = userId
if (typeof whitelistFn !== 'function') {
const allowed = Array.isArray(userId) ? userId : [userId]
whitelistFn = (ctx) => allowed.includes(ctx.from.id) || (ctx.from.username && allowed.includes(ctx.from.username))
}
return Composer.optional(whitelistFn, middleware)
}
static gameQuery (middleware) {
return Composer.mount('callback_query', Composer.optional((ctx) => ctx.callbackQuery.game_short_name, middleware))
}
static command (command, middleware) {
let commands = Array.isArray(command) ? command : [command]
commands = commands.map((cmd) => cmd.startsWith('/') ? cmd : `/${cmd}`)
return Composer.mount('text', Composer.lazy((ctx) => {
const text = ctx.message.text
const groupCommands = ctx.me && (ctx.chat.type === 'group' || ctx.chat.type === 'supergroup')
? commands.map((command) => `${command}@${ctx.me}`)
: []
const hasM ...n/a
class Extra {
constructor (opts) {
this.load(opts)
}
load (opts) {
if (opts) {
Object.assign(this, opts)
}
return this
}
inReplyTo (messageId) {
this.reply_to_message_id = messageId
return this
}
notifications (value = true) {
this.disable_notification = !value
return this
}
webPreview (value = true) {
this.disable_web_page_preview = !value
return this
}
markup (markup) {
if (typeof markup === 'function') {
markup = markup(new ReplyMarkup())
}
this.reply_markup = Object.assign({}, markup)
return this
}
HTML (value = true) {
this.parse_mode = value ? 'HTML' : undefined
return this
}
markdown (value = true) {
this.parse_mode = value ? 'Markdown' : undefined
return this
}
static inReplyTo (messageId) {
return new Extra().inReplyTo(messageId)
}
static notifications (value) {
return new Extra().notifications(value)
}
static webPreview (value) {
return new Extra().webPreview(value)
}
static load (opts) {
return new Extra(opts)
}
static markup (markup) {
return new Extra().markup(markup)
}
static HTML (value) {
return new Extra().HTML(value)
}
static markdown (value) {
return new Extra().markdown(value)
}
}n/a
class Markup {
forceReply (value = true) {
this.force_reply = value
return this
}
removeKeyboard (value = true) {
this.remove_keyboard = value
return this
}
selective (value = true) {
this.selective = value
return this
}
extra () {
return { reply_markup: Object.assign({}, this) }
}
keyboard (buttons, options) {
const keyboard = buildKeyboard(buttons, Object.assign({columns: 1}, options))
if (keyboard && keyboard.length > 0) {
this.keyboard = keyboard
}
return this
}
resize (value = true) {
this.resize_keyboard = value
return this
}
oneTime (value = true) {
this.one_time_keyboard = value
return this
}
inlineKeyboard (buttons, options) {
const keyboard = buildKeyboard(buttons, Object.assign({columns: buttons.length}, options))
if (keyboard && keyboard.length > 0) {
this.inline_keyboard = keyboard
}
return this
}
button (text, hide) {
return Markup.button(text, hide)
}
contactRequestButton (text, hide) {
return Markup.contactRequestButton(text, hide)
}
locationRequestButton (text, hide) {
return Markup.locationRequestButton(text, hide)
}
urlButton (text, url, hide) {
return Markup.urlButton(text, url, hide)
}
callbackButton (text, data, hide) {
return Markup.callbackButton(text, data, hide)
}
gameButton (text, hide) {
return Markup.gameButton(text, hide)
}
static removeKeyboard (value) {
return new Markup().removeKeyboard(value)
}
static forceReply (value) {
return new Markup().forceReply(value)
}
static keyboard (buttons, options) {
return new Markup().keyboard(buttons, options)
}
static inlineKeyboard (buttons, options) {
return new Markup().inlineKeyboard(buttons, options)
}
static resize (value = true) {
return new Markup().resize(value)
}
static oneTime (value = true) {
return new Markup().oneTime(value)
}
static button (text, hide) {
return { text: text, hide: hide }
}
static contactRequestButton (text, hide = false) {
return { text: text, request_contact: true, hide: hide }
}
static locationRequestButton (text, hide = false) {
return { text: text, request_location: true, hide: hide }
}
static urlButton (text, url, hide = false) {
return { text: text, url: url, hide: hide }
}
static callbackButton (text, data, hide = false) {
return { text: text, callback_data: data, hide: hide }
}
static switchToChatButton (text, value, hide = false) {
return { text: text, switch_inline_query: value, hide: hide }
}
static switchToCurrentChatButton (text, value, hide = false) {
return { text: text, switch_inline_query_current_chat: value, hide: hide }
}
static gameButton (text, hide = false) {
return { text: text, callback_game: {}, hide: hide }
}
}n/a
class Router {
constructor (routeFn) {
if (!routeFn) {
throw new Error('Missing routing function')
}
this.routeFn = routeFn
this.handlers = new Map()
this.otherwiseHandler = passThru()
}
on (route, ...fns) {
if (fns.length === 0) {
throw new TypeError('At least one handler must be provided')
}
this.handlers.set(route, compose(fns))
return this
}
otherwise (...fns) {
if (fns.length === 0) {
throw new TypeError('At least one otherwise handler must be provided')
}
this.otherwiseHandler = compose(fns)
return this
}
middleware () {
return lazy((ctx) => {
return this.routeFn(ctx).then((result) => {
if (!result || !result.route || !this.handlers.has(result.route)) {
return this.otherwiseHandler
}
Object.assign(ctx, result.context)
Object.assign(ctx.state, result.state)
return this.handlers.get(result.route)
})
})
}
}n/a
class Telegram extends ApiClient {
getMe () {
return this.callApi('getMe')
}
getFile (fileId) {
return this.callApi('getFile', {file_id: fileId})
}
getFileLink (fileId) {
return Promise.resolve(fileId)
.then((fileId) => {
if (fileId && fileId.file_path) {
return fileId
}
const id = fileId && fileId.file_id ? fileId.file_id : fileId
return this.getFile(id)
})
.then((file) => `${this.options.apiRoot}/file/bot${this.token}/${file.file_path}`)
}
getUpdates (timeout, limit, offset, allowedUpdates) {
let url = `getUpdates?offset=${offset}&limit=${limit}&timeout=${timeout}`
return this.callApi(url, {
allowed_updates: allowedUpdates
})
}
getWebhookInfo () {
return this.callApi(`getWebhookInfo`)
}
getGameHighScores (userId, inlineMessageId, chatId, messageId) {
return this.callApi(`getGameHighScores`, {
user_id: userId,
inline_message_id: inlineMessageId,
chat_id: chatId,
message_id: messageId
})
}
setGameScore (userId, score, inlineMessageId, chatId, messageId, editMessage = true, force) {
return this.callApi(`setGameScore`, {
user_id: userId,
score: score,
inline_message_id: inlineMessageId,
chat_id: chatId,
message_id: messageId,
disable_edit_message: !editMessage,
force: force
})
}
setWebhook (url, cert, maxConnections, allowedUpdates) {
return this.callApi('setWebhook', {
url: url,
certificate: cert,
max_connections: maxConnections,
allowed_updates: allowedUpdates
})
}
deleteWebhook () {
return this.callApi('deleteWebhook')
}
sendMessage (chatId, text, extra) {
return this.callApi('sendMessage', Object.assign({ chat_id: chatId, text: text }, extra))
}
forwardMessage (chatId, fromChatId, messageId, extra) {
return this.callApi('forwardMessage', Object.assign({
chat_id: chatId,
from_chat_id: fromChatId,
message_id: messageId
}, extra))
}
sendChatAction (chatId, action) {
return this.callApi('sendChatAction', { chat_id: chatId, action: action })
}
getUserProfilePhotos (userId, offset, limit) {
return this.callApi('getUserProfilePhotos', { user_id: userId, offset: offset, limit: limit })
}
sendLocation (chatId, latitude, longitude, extra) {
return this.callApi('sendLocation', Object.assign({ chat_id: chatId, latitude: latitude, longitude: longitude }, extra))
}
sendVenue (chatId, latitude, longitude, title, address, extra) {
return this.callApi('sendVenue', Object.assign({
chat_id: chatId,
latitude: latitude,
longitude: longitude,
title: title,
address: address
}, extra))
}
sendContact (chatId, phoneNumber, firstName, extra) {
return this.callApi('sendContact', Object.assign({ chat_id: chatId, phone_number: phoneNumber, first_name: firstName }, extra
))
}
sendPhoto (chatId, photo, extra) {
return this.callApi('sendPhoto', Object.assign({ chat_id: chatId, photo: photo }, extra))
}
sendDocument (chatId, doc, extra) {
return this.callApi('sendDocument', Object.assign({ chat_id: chatId, document: doc }, extra))
}
sendAudio (chatId, audio, extra) {
return this.callApi('sendAudio', Object.assign({ chat_id: chatId, audio: audio }, extra))
}
sendSticker (chatId, sticker, extra) {
return this.callApi('sendSticker', Object.assign({ chat_id: chatId, sticker: sticker }, extra))
}
sendVideo (chatId, video, extra) {
return this.callApi('sendVideo', Object.assign({ chat_id: chatId, video: video }, extra))
}
sendVoice (chatId, voice, extra) {
return this.callApi('sendVoice', Object.assign({ chat_id: chatId, voice: voice }, extra))
}
sendGame (chatId, gameName, extra) {
return this.callApi('sendGame', Object.assign({ chat_id: chatId, game_short_name: gameName }, extra))
}
getChat (chatId) {
return this.callApi('getChat', {chat_id: chatId})
}
getChatAdministrators (chatId) {
return this.callApi('getCh ...n/a
class TelegramError extends Error {
constructor (payload = {}) {
super(`${payload.error_code}: ${payload.description}`)
this.code = payload.error_code
this.description = payload.description
this.retryAfter = payload.retry_after
this.migrateToChatId = payload.migrate_to_chat_id
}
}n/a
memorySession = function (opts) {
opts = Object.assign({
sessionName: 'session',
getSessionKey: (ctx) => ctx.from && ctx.chat && `${ctx.from.id}:${ctx.chat.id}`
}, opts)
const store = new Map()
return (ctx, next) => {
const key = opts.getSessionKey(ctx)
if (!key) {
return next()
}
let session = store.get(key) || {}
Object.defineProperty(ctx, opts.sessionName, {
get: function () { return session },
set: function (newValue) { session = Object.assign({}, newValue) }
})
try {
return next()
} finally {
store.set(key, session)
}
}
}n/a
function deprecated() {
warned = exports.printDeprecationMessage(msg, warned, deprecated);
if (new.target) {
return Reflect.construct(fn, arguments, new.target);
}
return fn.apply(this, arguments);
}n/a
function deprecated() {
warned = exports.printDeprecationMessage(msg, warned, deprecated);
if (new.target) {
return Reflect.construct(fn, arguments, new.target);
}
return fn.apply(this, arguments);
}n/a