Bug Fixes

This commit is contained in:
Luis Fernando Molina committed 2020-02-09 16:35:17 +01:00
1 parent 82bc086e92
commit b1af6e078c
131 files changed
+8597 -1930

No files matched your search

+23 -5
View File
@@ -1,4 +1,4 @@
const TelegrafContext = require('./core/context')
const Context = require('./context')
class Composer {
constructor (...fns) {
@@ -100,9 +100,24 @@ class Composer {
return (ctx) => ctx.reply(...args)
}
static catchAll (...fns) {
return Composer.catch((err) => {
console.error()
console.error((err.stack || err.toString()).replace(/^/gm, ' '))
console.error()
}, ...fns)
}
static catch (errorHandler, ...fns) {
const handler = Composer.compose(fns)
return (ctx, next) => Promise.resolve(handler(ctx, next))
.catch((err) => errorHandler(err, ctx))
}
static fork (middleware) {
const handler = Composer.unwrap(middleware)
return (ctx, next) => {
setImmediate(Composer.unwrap(middleware), ctx, Composer.safePassThru())
setImmediate(handler, ctx, Composer.safePassThru())
return next(ctx)
}
}
@@ -317,7 +332,10 @@ class Composer {
}
static unwrap (handler) {
return handler && typeof handler.middleware === 'function'
if (!handler) {
throw new Error('Handler is undefined')
}
return typeof handler.middleware === 'function'
? handler.middleware()
: handler
}
@@ -336,14 +354,14 @@ class Composer {
let index = -1
return execute(0, ctx)
function execute (i, context) {
if (!(context instanceof TelegrafContext)) {
if (!(context instanceof Context)) {
return Promise.reject(new Error('next(ctx) called with invalid context'))
}
if (i <= index) {
return Promise.reject(new Error('next() called multiple times'))
}
index = i
const handler = Composer.unwrap(middlewares[i]) || next
const handler = middlewares[i] ? Composer.unwrap(middlewares[i]) : next
if (!handler) {
return Promise.resolve()
}
+568
View File
@@ -0,0 +1,568 @@
const UpdateTypes = [
'callback_query',
'channel_post',
'chosen_inline_result',
'edited_channel_post',
'edited_message',
'inline_query',
'shipping_query',
'pre_checkout_query',
'message',
'poll',
'poll_answer'
]
const MessageSubTypes = [
'voice',
'video_note',
'video',
'animation',
'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',
'connected_website',
'passport_data',
'poll',
'forward_date'
]
const MessageSubTypesMapping = {
forward_date: 'forward'
}
class TelegrafContext {
constructor (update, telegram, options) {
this.tg = telegram
this.update = update
this.options = options
this.updateType = UpdateTypes.find((key) => key in this.update)
if (this.updateType === 'message' || (this.options.channelMode && this.updateType === 'channel_post')) {
this.updateSubTypes = MessageSubTypes
.filter((key) => key in this.update[this.updateType])
.map((type) => MessageSubTypesMapping[type] || type)
} else {
this.updateSubTypes = []
}
Object.getOwnPropertyNames(TelegrafContext.prototype)
.filter((key) => key !== 'constructor' && typeof this[key] === 'function')
.forEach((key) => (this[key] = this[key].bind(this)))
}
get me () {
return this.options && this.options.username
}
get telegram () {
return this.tg
}
get message () {
return this.update.message
}
get editedMessage () {
return this.update.edited_message
}
get inlineQuery () {
return this.update.inline_query
}
get shippingQuery () {
return this.update.shipping_query
}
get preCheckoutQuery () {
return this.update.pre_checkout_query
}
get chosenInlineResult () {
return this.update.chosen_inline_result
}
get channelPost () {
return this.update.channel_post
}
get editedChannelPost () {
return this.update.edited_channel_post
}
get callbackQuery () {
return this.update.callback_query
}
get poll () {
return this.update.poll
}
get pollAnswer () {
return this.update.poll_answer
}
get chat () {
return (this.message && this.message.chat) ||
(this.editedMessage && this.editedMessage.chat) ||
(this.callbackQuery && this.callbackQuery.message && this.callbackQuery.message.chat) ||
(this.channelPost && this.channelPost.chat) ||
(this.editedChannelPost && this.editedChannelPost.chat)
}
get from () {
return (this.message && this.message.from) ||
(this.editedMessage && this.editedMessage.from) ||
(this.callbackQuery && this.callbackQuery.from) ||
(this.inlineQuery && this.inlineQuery.from) ||
(this.channelPost && this.channelPost.from) ||
(this.editedChannelPost && this.editedChannelPost.from) ||
(this.shippingQuery && this.shippingQuery.from) ||
(this.preCheckoutQuery && this.preCheckoutQuery.from) ||
(this.chosenInlineResult && this.chosenInlineResult.from)
}
get inlineMessageId () {
return (this.callbackQuery && this.callbackQuery.inline_message_id) || (this.chosenInlineResult && this.chosenInlineResult.inline_message_id)
}
get passportData () {
return this.message && this.message.passport_data
}
get state () {
if (!this.contextState) {
this.contextState = {}
}
return this.contextState
}
set state (value) {
this.contextState = { ...value }
}
get webhookReply () {
return this.tg.webhookReply
}
set webhookReply (enable) {
this.tg.webhookReply = enable
}
assert (value, method) {
if (!value) {
throw new Error(`Telegraf: "${method}" isn't available for "${this.updateType}::${this.updateSubTypes}"`)
}
}
answerInlineQuery (...args) {
this.assert(this.inlineQuery, 'answerInlineQuery')
return this.telegram.answerInlineQuery(this.inlineQuery.id, ...args)
}
answerCbQuery (...args) {
this.assert(this.callbackQuery, 'answerCbQuery')
return this.telegram.answerCbQuery(this.callbackQuery.id, ...args)
}
answerGameQuery (...args) {
this.assert(this.callbackQuery, 'answerGameQuery')
return this.telegram.answerGameQuery(this.callbackQuery.id, ...args)
}
answerShippingQuery (...args) {
this.assert(this.shippingQuery, 'answerShippingQuery')
return this.telegram.answerShippingQuery(this.shippingQuery.id, ...args)
}
answerPreCheckoutQuery (...args) {
this.assert(this.preCheckoutQuery, 'answerPreCheckoutQuery')
return this.telegram.answerPreCheckoutQuery(this.preCheckoutQuery.id, ...args)
}
editMessageText (text, extra) {
this.assert(this.callbackQuery || this.inlineMessageId, 'editMessageText')
return this.inlineMessageId
? this.telegram.editMessageText(
undefined,
undefined,
this.inlineMessageId,
text,
extra
)
: this.telegram.editMessageText(
this.chat.id,
this.callbackQuery.message.message_id,
undefined,
text,
extra
)
}
editMessageCaption (caption, extra) {
this.assert(this.callbackQuery || this.inlineMessageId, 'editMessageCaption')
return this.inlineMessageId
? this.telegram.editMessageCaption(
undefined,
undefined,
this.inlineMessageId,
caption,
extra
)
: this.telegram.editMessageCaption(
this.chat.id,
this.callbackQuery.message.message_id,
undefined,
caption,
extra
)
}
editMessageMedia (media, extra) {
this.assert(this.callbackQuery || this.inlineMessageId, 'editMessageMedia')
return this.inlineMessageId
? this.telegram.editMessageMedia(
undefined,
undefined,
this.inlineMessageId,
media,
extra
)
: this.telegram.editMessageMedia(
this.chat.id,
this.callbackQuery.message.message_id,
undefined,
media,
extra
)
}
editMessageReplyMarkup (markup) {
this.assert(this.callbackQuery || this.inlineMessageId, 'editMessageReplyMarkup')
return this.inlineMessageId
? this.telegram.editMessageReplyMarkup(
undefined,
undefined,
this.inlineMessageId,
markup
)
: this.telegram.editMessageReplyMarkup(
this.chat.id,
this.callbackQuery.message.message_id,
undefined,
markup
)
}
editMessageLiveLocation (latitude, longitude, markup) {
this.assert(this.callbackQuery || this.inlineMessageId, 'editMessageLiveLocation')
return this.inlineMessageId
? this.telegram.editMessageLiveLocation(
latitude,
longitude,
undefined,
undefined,
this.inlineMessageId,
markup
)
: this.telegram.editMessageLiveLocation(
latitude,
longitude,
this.chat.id,
this.callbackQuery.message.message_id,
undefined,
markup
)
}
stopMessageLiveLocation (markup) {
this.assert(this.callbackQuery || this.inlineMessageId, 'stopMessageLiveLocation')
return this.inlineMessageId
? this.telegram.stopMessageLiveLocation(
undefined,
undefined,
this.inlineMessageId,
markup
)
: this.telegram.stopMessageLiveLocation(
this.chat.id,
this.callbackQuery.message.message_id,
undefined,
markup
)
}
reply (...args) {
this.assert(this.chat, 'reply')
return this.telegram.sendMessage(this.chat.id, ...args)
}
getChat (...args) {
this.assert(this.chat, 'getChat')
return this.telegram.getChat(this.chat.id, ...args)
}
exportChatInviteLink (...args) {
this.assert(this.chat, 'exportChatInviteLink')
return this.telegram.exportChatInviteLink(this.chat.id, ...args)
}
kickChatMember (...args) {
this.assert(this.chat, 'kickChatMember')
return this.telegram.kickChatMember(this.chat.id, ...args)
}
restrictChatMember (...args) {
this.assert(this.chat, 'restrictChatMember')
return this.telegram.restrictChatMember(this.chat.id, ...args)
}
promoteChatMember (...args) {
this.assert(this.chat, 'promoteChatMember')
return this.telegram.promoteChatMember(this.chat.id, ...args)
}
setChatAdministratorCustomTitle (...args) {
this.assert(this.chat, 'setChatAdministratorCustomTitle')
return this.telegram.setChatAdministratorCustomTitle(this.chat.id, ...args)
}
setChatPhoto (...args) {
this.assert(this.chat, 'setChatPhoto')
return this.telegram.setChatPhoto(this.chat.id, ...args)
}
deleteChatPhoto (...args) {
this.assert(this.chat, 'deleteChatPhoto')
return this.telegram.deleteChatPhoto(this.chat.id, ...args)
}
setChatTitle (...args) {
this.assert(this.chat, 'setChatTitle')
return this.telegram.setChatTitle(this.chat.id, ...args)
}
setChatDescription (...args) {
this.assert(this.chat, 'setChatDescription')
return this.telegram.setChatDescription(this.chat.id, ...args)
}
pinChatMessage (...args) {
this.assert(this.chat, 'pinChatMessage')
return this.telegram.pinChatMessage(this.chat.id, ...args)
}
unpinChatMessage (...args) {
this.assert(this.chat, 'unpinChatMessage')
return this.telegram.unpinChatMessage(this.chat.id, ...args)
}
leaveChat (...args) {
this.assert(this.chat, 'leaveChat')
return this.telegram.leaveChat(this.chat.id, ...args)
}
setChatPermissions (...args) {
this.assert(this.chat, 'setChatPermissions')
return this.telegram.setChatPermissions(this.chat.id, ...args)
}
getChatAdministrators (...args) {
this.assert(this.chat, 'getChatAdministrators')
return this.telegram.getChatAdministrators(this.chat.id, ...args)
}
getChatMember (...args) {
this.assert(this.chat, 'getChatMember')
return this.telegram.getChatMember(this.chat.id, ...args)
}
getChatMembersCount (...args) {
this.assert(this.chat, 'getChatMembersCount')
return this.telegram.getChatMembersCount(this.chat.id, ...args)
}
setPassportDataErrors (errors) {
this.assert(this.chat, 'setPassportDataErrors')
return this.telegram.setPassportDataErrors(this.from.id, errors)
}
replyWithPhoto (...args) {
this.assert(this.chat, 'replyWithPhoto')
return this.telegram.sendPhoto(this.chat.id, ...args)
}
replyWithMediaGroup (...args) {
this.assert(this.chat, 'replyWithMediaGroup')
return this.telegram.sendMediaGroup(this.chat.id, ...args)
}
replyWithAudio (...args) {
this.assert(this.chat, 'replyWithAudio')
return this.telegram.sendAudio(this.chat.id, ...args)
}
replyWithDocument (...args) {
this.assert(this.chat, 'replyWithDocument')
return this.telegram.sendDocument(this.chat.id, ...args)
}
replyWithSticker (...args) {
this.assert(this.chat, 'replyWithSticker')
return this.telegram.sendSticker(this.chat.id, ...args)
}
replyWithVideo (...args) {
this.assert(this.chat, 'replyWithVideo')
return this.telegram.sendVideo(this.chat.id, ...args)
}
replyWithAnimation (...args) {
this.assert(this.chat, 'replyWithAnimation')
return this.telegram.sendAnimation(this.chat.id, ...args)
}
replyWithVideoNote (...args) {
this.assert(this.chat, 'replyWithVideoNote')
return this.telegram.sendVideoNote(this.chat.id, ...args)
}
replyWithInvoice (...args) {
this.assert(this.chat, 'replyWithInvoice')
return this.telegram.sendInvoice(this.chat.id, ...args)
}
replyWithGame (...args) {
this.assert(this.chat, 'replyWithGame')
return this.telegram.sendGame(this.chat.id, ...args)
}
replyWithVoice (...args) {
this.assert(this.chat, 'replyWithVoice')
return this.telegram.sendVoice(this.chat.id, ...args)
}
replyWithPoll (...args) {
this.assert(this.chat, 'replyWithPoll')
return this.telegram.sendPoll(this.chat.id, ...args)
}
replyWithQuiz (...args) {
this.assert(this.chat, 'replyWithQuiz')
return this.telegram.sendQuiz(this.chat.id, ...args)
}
stopPoll (...args) {
this.assert(this.chat, 'stopPoll')
return this.telegram.stopPoll(this.chat.id, ...args)
}
replyWithChatAction (...args) {
this.assert(this.chat, 'replyWithChatAction')
return this.telegram.sendChatAction(this.chat.id, ...args)
}
replyWithLocation (...args) {
this.assert(this.chat, 'replyWithLocation')
return this.telegram.sendLocation(this.chat.id, ...args)
}
replyWithVenue (...args) {
this.assert(this.chat, 'replyWithVenue')
return this.telegram.sendVenue(this.chat.id, ...args)
}
replyWithContact (...args) {
this.assert(this.from, 'replyWithContact')
return this.telegram.sendContact(this.chat.id, ...args)
}
getStickerSet (setName) {
return this.telegram.getStickerSet(setName)
}
setChatStickerSet (setName) {
this.assert(this.chat, 'setChatStickerSet')
return this.telegram.setChatStickerSet(this.chat.id, setName)
}
deleteChatStickerSet () {
this.assert(this.chat, 'deleteChatStickerSet')
return this.telegram.deleteChatStickerSet(this.chat.id)
}
setStickerPositionInSet (sticker, position) {
return this.telegram.setStickerPositionInSet(sticker, position)
}
deleteStickerFromSet (sticker) {
return this.telegram.deleteStickerFromSet(sticker)
}
uploadStickerFile (...args) {
this.assert(this.from, 'uploadStickerFile')
return this.telegram.uploadStickerFile(this.from.id, ...args)
}
createNewStickerSet (...args) {
this.assert(this.from, 'createNewStickerSet')
return this.telegram.createNewStickerSet(this.from.id, ...args)
}
addStickerToSet (...args) {
this.assert(this.from, 'addStickerToSet')
return this.telegram.addStickerToSet(this.from.id, ...args)
}
replyWithMarkdown (markdown, extra) {
return this.reply(markdown, { parse_mode: 'Markdown', ...extra })
}
replyWithHTML (html, extra) {
return this.reply(html, { parse_mode: 'HTML', ...extra })
}
deleteMessage (messageId) {
this.assert(this.chat, 'deleteMessage')
if (typeof messageId !== 'undefined') {
return this.telegram.deleteMessage(this.chat.id, messageId)
}
const message = this.message ||
this.editedMessage ||
this.channelPost ||
this.editedChannelPost ||
(this.callbackQuery && this.callbackQuery.message)
this.assert(message, 'deleteMessage')
return this.telegram.deleteMessage(this.chat.id, message.message_id)
}
forwardMessage (chatId, extra) {
this.assert(this.chat, 'forwardMessage')
const message = this.message ||
this.editedMessage ||
this.channelPost ||
this.editedChannelPost ||
(this.callbackQuery && this.callbackQuery.message)
this.assert(message, 'forwardMessage')
return this.telegram.forwardMessage(chatId, this.chat.id, message.message_id, extra)
}
}
module.exports = TelegrafContext
+50 -32
View File
@@ -8,7 +8,7 @@ const TelegramError = require('./error')
const MultipartStream = require('./multipart-stream')
const { isStream } = MultipartStream
const WebhookBlacklist = [
const WEBHOOK_BLACKLIST = [
'getChat',
'getChatAdministrators',
'getChatMember',
@@ -22,7 +22,7 @@ const WebhookBlacklist = [
'exportChatInviteLink'
]
const DefaultExtensions = {
const DEFAULT_EXTENSIONS = {
audio: 'mp3',
photo: 'jpg',
sticker: 'webp',
@@ -32,7 +32,7 @@ const DefaultExtensions = {
voice: 'ogg'
}
const DefaultOptions = {
const DEFAULT_OPTIONS = {
apiRoot: 'https://api.telegram.org',
webhookReply: true,
agent: new https.Agent({
@@ -41,7 +41,7 @@ const DefaultOptions = {
})
}
const WebhookReplyStub = {
const WEBHOOK_REPLY_STUB = {
webhook: true,
details: 'https://core.telegram.org/bots/api#making-requests-when-getting-updates'
}
@@ -79,13 +79,23 @@ function buildJSONConfig (payload) {
})
}
function buildFormDataConfig (payload) {
if (payload.reply_markup && typeof payload.reply_markup !== 'string') {
payload.reply_markup = JSON.stringify(payload.reply_markup)
const FORM_DATA_JSON_FIELDS = [
'results',
'reply_markup',
'mask_position',
'shipping_options',
'errors'
]
function buildFormDataConfig (payload, agent) {
for (const field of FORM_DATA_JSON_FIELDS) {
if (field in payload && typeof payload[field] !== 'string') {
payload[field] = JSON.stringify(payload[field])
}
}
const boundary = crypto.randomBytes(32).toString('hex')
const formData = new MultipartStream(boundary)
const tasks = Object.keys(payload).map((key) => attachFormValue(formData, key, payload[key]))
const tasks = Object.keys(payload).map((key) => attachFormValue(formData, key, payload[key], agent))
return Promise.all(tasks).then(() => {
return {
method: 'POST',
@@ -96,7 +106,7 @@ function buildFormDataConfig (payload) {
})
}
function attachFormValue (form, id, value) {
function attachFormValue (form, id, value, agent) {
if (!value) {
return Promise.resolve()
}
@@ -110,7 +120,7 @@ function attachFormValue (form, id, value) {
}
if (id === 'thumb') {
const attachmentId = crypto.randomBytes(16).toString('hex')
return attachFormMedia(form, value, attachmentId)
return attachFormMedia(form, value, attachmentId, agent)
.then(() => form.addPart({
headers: { 'content-disposition': `form-data; name="${id}"` },
body: `attach://${attachmentId}`
@@ -123,8 +133,8 @@ function attachFormValue (form, id, value) {
return Promise.resolve(item)
}
const attachmentId = crypto.randomBytes(16).toString('hex')
return attachFormMedia(form, item.media, attachmentId)
.then(() => Object.assign({}, item, { media: `attach://${attachmentId}` }))
return attachFormMedia(form, item.media, attachmentId, agent)
.then(() => ({ ...item, media: `attach://${attachmentId}` }))
})
).then((items) => form.addPart({
headers: { 'content-disposition': `form-data; name="${id}"` },
@@ -133,21 +143,22 @@ function attachFormValue (form, id, value) {
}
if (typeof value.media !== 'undefined' && typeof value.type !== 'undefined') {
const attachmentId = crypto.randomBytes(16).toString('hex')
return attachFormMedia(form, value.media, attachmentId)
return attachFormMedia(form, value.media, attachmentId, agent)
.then(() => form.addPart({
headers: { 'content-disposition': `form-data; name="${id}"` },
body: JSON.stringify(Object.assign(value, {
body: JSON.stringify({
...value,
media: `attach://${attachmentId}`
}))
})
}))
}
return attachFormMedia(form, value, id)
return attachFormMedia(form, value, id, agent)
}
function attachFormMedia (form, media, id) {
let fileName = media.filename || `${id}.${DefaultExtensions[id] || 'dat'}`
function attachFormMedia (form, media, id, agent) {
let fileName = media.filename || `${id}.${DEFAULT_EXTENSIONS[id] || 'dat'}`
if (media.url) {
return fetch(media.url).then((res) =>
return fetch(media.url, { agent }).then((res) =>
form.addPart({
headers: { 'content-disposition': `form-data; name="${id}"; filename="${fileName}"` },
body: res.body
@@ -173,32 +184,36 @@ function isKoaResponse (response) {
return typeof response.set === 'function' && typeof response.header === 'object'
}
function answerToWebhook (response, payload = {}) {
function answerToWebhook (response, payload = {}, options) {
if (!includesMedia(payload)) {
if (isKoaResponse(response)) {
response.body = payload
return Promise.resolve(WebhookReplyStub)
return Promise.resolve(WEBHOOK_REPLY_STUB)
}
if (!response.headersSent) {
response.setHeader('content-type', 'application/json')
}
return new Promise((resolve) =>
response.end(JSON.stringify(payload), 'utf-8', () => resolve(WebhookReplyStub))
)
return new Promise((resolve) => {
if (response.end.length === 2) {
response.end(JSON.stringify(payload), 'utf-8')
return resolve(WEBHOOK_REPLY_STUB)
}
response.end(JSON.stringify(payload), 'utf-8', () => resolve(WEBHOOK_REPLY_STUB))
})
}
return buildFormDataConfig(payload)
return buildFormDataConfig(payload, options.agent)
.then(({ headers, body }) => {
if (isKoaResponse(response)) {
Object.keys(headers).forEach(key => response.set(key, headers[key]))
response.body = body
return Promise.resolve(WebhookReplyStub)
return Promise.resolve(WEBHOOK_REPLY_STUB)
}
if (!response.headersSent) {
Object.keys(headers).forEach(key => response.setHeader(key, headers[key]))
}
return new Promise((resolve) => {
response.on('finish', () => resolve(WebhookReplyStub))
response.on('finish', () => resolve(WEBHOOK_REPLY_STUB))
body.pipe(response)
})
})
@@ -207,7 +222,10 @@ function answerToWebhook (response, payload = {}) {
class ApiClient {
constructor (token, options, webhookResponse) {
this.token = token
this.options = Object.assign({}, DefaultOptions, options)
this.options = {
...DEFAULT_OPTIONS,
...options
}
if (this.options.apiRoot.startsWith('http://')) {
this.options.agent = null
}
@@ -227,12 +245,12 @@ class ApiClient {
const payload = Object.keys(data)
.filter((key) => typeof data[key] !== 'undefined' && data[key] !== null)
.reduce((acc, key) => Object.assign(acc, { [key]: data[key] }), {})
.reduce((acc, key) => ({ ...acc, [key]: data[key] }), {})
if (options.webhookReply && response && !responseEnd && !WebhookBlacklist.includes(method)) {
if (options.webhookReply && response && !responseEnd && !WEBHOOK_BLACKLIST.includes(method)) {
debug('Call via webhook', method, payload)
this.responseEnd = true
return answerToWebhook(response, Object.assign({ method }, payload))
return answerToWebhook(response, { method, ...payload }, options)
}
if (!token) {
@@ -241,7 +259,7 @@ class ApiClient {
debug('HTTP call', method, payload)
const buildConfig = includesMedia(payload)
? buildFormDataConfig(Object.assign({ method }, payload))
? buildFormDataConfig({ method, ...payload }, options.agent)
: buildJSONConfig(payload)
return buildConfig
.then((config) => {
+31 -30
View File
@@ -1,26 +1,4 @@
function wrapEntity (content, entity) {
switch (entity.type) {
case 'bold':
return `<b>${content}</b>`
case 'italic':
return `<i>${content}</i>`
case 'code':
return `<code>${content}</code>`
case 'pre':
return `<pre>${content}</pre>`
case 'text_link':
return `<a href="${entity.url}">${content}</a>`
default:
return content
}
}
function applyEntity (text, entity) {
const head = text.substring(0, entity.offset)
const tail = text.substring(entity.offset + entity.length)
const content = wrapEntity(text.substring(entity.offset, entity.offset + entity.length), entity)
return `${head}${content}${tail}`
}
const { formatHTML } = require('../markup')
module.exports = {
copyMethods: {
@@ -35,17 +13,20 @@ module.exports = {
video: 'sendVideo',
video_note: 'sendVideoNote',
animation: 'sendAnimation',
voice: 'sendVoice'
voice: 'sendVoice',
poll: 'sendPoll'
},
text: (message) => {
const entities = message.entities || []
return {
reply_markup: message.reply_markup,
parse_mode: entities.length > 0 ? 'HTML' : '',
text: entities.reduceRight(applyEntity, message.text)
text: formatHTML(message.text, entities)
}
},
contact: (message) => {
return {
reply_markup: message.reply_markup,
phone_number: message.contact.phone_number,
first_name: message.contact.first_name,
last_name: message.contact.last_name
@@ -53,12 +34,14 @@ module.exports = {
},
location: (message) => {
return {
reply_markup: message.reply_markup,
latitude: message.location.latitude,
longitude: message.location.longitude
}
},
venue: (message) => {
return {
reply_markup: message.reply_markup,
latitude: message.venue.location.latitude,
longitude: message.venue.location.longitude,
title: message.venue.title,
@@ -69,30 +52,33 @@ module.exports = {
voice: (message) => {
const entities = message.caption_entities || []
return {
reply_markup: message.reply_markup,
voice: message.voice.file_id,
duration: message.voice.duration,
caption: entities.reduceRight(applyEntity, message.caption),
caption: formatHTML(message.caption, entities),
parse_mode: entities.length > 0 ? 'HTML' : ''
}
},
audio: (message) => {
const entities = message.caption_entities || []
return {
reply_markup: message.reply_markup,
audio: message.audio.file_id,
thumb: message.audio.thumb,
duration: message.audio.duration,
performer: message.audio.performer,
title: message.audio.title,
caption: entities.reduceRight(applyEntity, message.caption),
caption: formatHTML(message.caption, entities),
parse_mode: entities.length > 0 ? 'HTML' : ''
}
},
video: (message) => {
const entities = message.caption_entities || []
return {
reply_markup: message.reply_markup,
video: message.video.file_id,
thumb: message.video.thumb,
caption: entities.reduceRight(applyEntity, message.caption),
caption: formatHTML(message.caption, entities),
parse_mode: entities.length > 0 ? 'HTML' : '',
duration: message.video.duration,
width: message.video.width,
@@ -103,26 +89,30 @@ module.exports = {
document: (message) => {
const entities = message.caption_entities || []
return {
reply_markup: message.reply_markup,
document: message.document.file_id,
caption: entities.reduceRight(applyEntity, message.caption),
caption: formatHTML(message.caption, entities),
parse_mode: entities.length > 0 ? 'HTML' : ''
}
},
sticker: (message) => {
return {
reply_markup: message.reply_markup,
sticker: message.sticker.file_id
}
},
photo: (message) => {
const entities = message.caption_entities || []
return {
reply_markup: message.reply_markup,
photo: message.photo[message.photo.length - 1].file_id,
parse_mode: entities.length > 0 ? 'HTML' : '',
caption: entities.reduceRight(applyEntity, message.caption)
caption: formatHTML(message.caption, entities)
}
},
video_note: (message) => {
return {
reply_markup: message.reply_markup,
video_note: message.video_note.file_id,
thumb: message.video_note.thumb,
length: message.video_note.length,
@@ -131,9 +121,20 @@ module.exports = {
},
animation: (message) => {
return {
reply_markup: message.reply_markup,
animation: message.animation.file_id,
thumb: message.animation.thumb,
duration: message.animation.duration
}
},
poll: (message) => {
return {
question: message.poll.question,
type: message.poll.type,
is_anonymous: message.poll.is_anonymous,
allows_multiple_answers: message.poll.allows_multiple_answers,
correct_option_id: message.poll.correct_option_id,
options: message.poll.options.map(({ text }) => text)
}
}
}
+3 -3
View File
@@ -5,8 +5,8 @@ class Extra {
this.load(opts)
}
load (opts) {
return Object.assign(this, opts || {})
load (opts = {}) {
return Object.assign(this, opts)
}
inReplyTo (messageId) {
@@ -28,7 +28,7 @@ class Extra {
if (typeof markup === 'function') {
markup = markup(new Markup())
}
this.reply_markup = Object.assign({}, markup)
this.reply_markup = { ...markup }
return this
}
+87 -6
View File
@@ -15,13 +15,14 @@ class Markup {
}
extra (options) {
return Object.assign({
reply_markup: Object.assign({}, this)
}, options)
return {
reply_markup: { ...this },
...options
}
}
keyboard (buttons, options) {
const keyboard = buildKeyboard(buttons, Object.assign({ columns: 1 }, options))
const keyboard = buildKeyboard(buttons, { columns: 1, ...options })
if (keyboard && keyboard.length > 0) {
this.keyboard = keyboard
}
@@ -39,7 +40,7 @@ class Markup {
}
inlineKeyboard (buttons, options) {
const keyboard = buildKeyboard(buttons, Object.assign({ columns: buttons.length }, options))
const keyboard = buildKeyboard(buttons, { columns: buttons.length, ...options })
if (keyboard && keyboard.length > 0) {
this.inline_keyboard = keyboard
}
@@ -126,6 +127,10 @@ class Markup {
return { text: text, request_location: true, hide: hide }
}
static pollRequestButton (text, type, hide = false) {
return { text: text, request_poll: { type }, hide: hide }
}
static urlButton (text, url, hide = false) {
return { text: text, url: url, hide: hide }
}
@@ -153,10 +158,86 @@ class Markup {
static loginButton (text, url, opts = {}, hide = false) {
return {
text: text,
login_url: Object.assign({}, opts, { url: url }),
login_url: { ...opts, url: url },
hide: hide
}
}
static formatHTML (text, entities) {
const chars = [...text]
const available = [...entities]
const opened = []
const result = []
for (let offset = 0; offset < chars.length; offset++) {
while (true) {
const index = available.findIndex((entity) => entity.offset === offset)
if (index === -1) {
break
}
const entity = available[index]
switch (entity.type) {
case 'bold':
result.push('<b>')
break
case 'italic':
result.push('<i>')
break
case 'code':
result.push('<code>')
break
case 'pre':
result.push('<pre>')
break
case 'strikethrough':
result.push('<s>')
break
case 'underline':
result.push('<u>')
break
case 'text_link':
result.push(`<a href="${entity.url}">`)
break
}
opened.unshift(entity)
available.splice(index, 1)
}
result.push(chars[offset])
while (true) {
const index = opened.findIndex((entity) => entity.offset + entity.length - 1 === offset)
if (index === -1) {
break
}
const entity = opened[index]
switch (entity.type) {
case 'bold':
result.push('</b>')
break
case 'italic':
result.push('</i>')
break
case 'code':
result.push('</code>')
break
case 'pre':
result.push('</pre>')
break
case 'strikethrough':
result.push('</s>')
break
case 'underline':
result.push('</u>')
break
case 'text_link':
result.push('</a>')
break
}
opened.splice(index, 1)
}
}
return result.join('')
}
}
function buildKeyboard (buttons, options) {
+26 -19
View File
@@ -1,61 +1,67 @@
{
"_from": "telegraf",
"_id": "telegraf@3.32.0",
"_from": "telegraf@3.36.0",
"_id": "telegraf@3.36.0",
"_inBundle": false,
"_integrity": "sha512-5ZHiovyuG1rVLygJjaqf57wDt8e1nijAinKXCxN1tyUZ4BcrkZW/z5rVTEXA+KegpFbWxKKv3KnJwUJrKDqD0Q==",
"_integrity": "sha512-9o6AJKRiTm5vMWYI6WpTfBHzu4FMpWBNKxvnMxRds/cbMY9RnsVVjdi8i4bFFlfd+xbi73EbrnI3dybayryICA==",
"_location": "/telegraf",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"type": "version",
"registry": true,
"raw": "telegraf",
"raw": "telegraf@3.36.0",
"name": "telegraf",
"escapedName": "telegraf",
"rawSpec": "",
"rawSpec": "3.36.0",
"saveSpec": null,
"fetchSpec": "latest"
"fetchSpec": "3.36.0"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/telegraf/-/telegraf-3.32.0.tgz",
"_shasum": "70ec304b9c8645f8988924ebe90e5c00ee46ddbb",
"_spec": "telegraf",
"_resolved": "https://registry.npmjs.org/telegraf/-/telegraf-3.36.0.tgz",
"_shasum": "eab1160159df6033d3cb92ac990902523277822a",
"_spec": "telegraf@3.36.0",
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer",
"author": {
"name": "Vitaly Domnikov",
"email": "oss@vitaly.codes"
},
"bin": {
"telegraf": "./bin/telegraf"
},
"bugs": {
"url": "https://github.com/telegraf/telegraf/issues"
},
"bundleDependencies": false,
"dependencies": {
"@types/node": "^12.0.4",
"@types/node": "^13.1.0",
"debug": "^4.0.1",
"minimist": "^1.2.0",
"module-alias": "^2.2.2",
"node-fetch": "^2.2.0",
"sandwich-stream": "^2.0.1",
"telegram-typings": "^3.6.0"
},
"deprecated": false,
"description": "📡 Modern Telegram Bot Framework",
"description": "Modern Telegram Bot Framework",
"devDependencies": {
"ava": "^2.2.0",
"eslint": "^6.1.0",
"eslint-config-standard": "^13.0.1",
"eslint-plugin-ava": "^7.1.0",
"ava": "^3.0.0",
"eslint": "^6.2.2",
"eslint-config-standard": "^14.1.0",
"eslint-plugin-ava": "^10.0.0",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-node": "^9.1.0",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-promise": "^4.0.0",
"eslint-plugin-standard": "^4.0.0",
"husky": "^3.0.2",
"husky": "^4.2.0",
"typescript": "^3.0.1"
},
"engines": {
"node": ">=8"
},
"files": [
"bin/*",
"core/**/*.js",
"scenes/**/*.js",
"typings/*.d.ts",
@@ -83,6 +89,7 @@
"test": "ava test/*",
"typecheck": "tsc"
},
"type": "commonjs",
"types": "./typings/index.d.ts",
"version": "3.32.0"
"version": "3.36.0"
}
+4 -3
View File
@@ -3,11 +3,12 @@ const { compose } = Composer
class BaseScene extends Composer {
constructor (id, options) {
const opts = Object.assign({
const opts = {
handlers: [],
enterHandlers: [],
leaveHandlers: []
}, options)
leaveHandlers: [],
...options
}
super(...opts.handlers)
this.id = id
this.options = opts
+1 -1
View File
@@ -27,7 +27,7 @@ class SceneContext {
}
set state (value) {
this.session.state = Object.assign({}, value)
this.session.state = { ...value }
}
get current () {
+1 -1
View File
@@ -8,7 +8,7 @@ class WizardScene extends Composer {
this.id = id
this.options = typeof options === 'function'
? { steps: [options, ...steps], leaveHandlers: [] }
: Object.assign({ steps: steps, leaveHandlers: [] }, options)
: { steps: steps, leaveHandlers: [], ...options }
this.leaveHandler = compose(this.options.leaveHandlers)
}
+5 -4
View File
@@ -1,9 +1,10 @@
module.exports = function (opts) {
const options = Object.assign({
const options = {
property: 'session',
store: new Map(),
getSessionKey: (ctx) => ctx.from && ctx.chat && `${ctx.from.id}:${ctx.chat.id}`
}, opts)
getSessionKey: (ctx) => ctx.from && ctx.chat && `${ctx.from.id}:${ctx.chat.id}`,
...opts
}
const ttlMs = options.ttl && options.ttl * 1000
@@ -21,7 +22,7 @@ module.exports = function (opts) {
}
Object.defineProperty(ctx, options.property, {
get: function () { return session },
set: function (newValue) { session = Object.assign({}, newValue) }
set: function (newValue) { session = { ...newValue } }
})
return next(ctx).then(() => options.store.set(key, {
session,
+4 -3
View File
@@ -5,9 +5,10 @@ const { compose, optional, lazy, safePassThru } = Composer
class Stage extends Composer {
constructor (scenes = [], options) {
super()
this.options = Object.assign({
sessionName: 'session'
}, options)
this.options = {
sessionName: 'session',
...options
}
this.scenes = new Map()
scenes.forEach((scene) => this.register(scene))
}
+42 -18
View File
@@ -5,14 +5,17 @@ const Composer = require('./composer')
const Markup = require('./markup')
const session = require('./session')
const Router = require('./router')
const Context = require('./core/context')
const Stage = require('./stage')
const BaseScene = require('./scenes/base')
const Context = require('./context')
const generateCallback = require('./core/network/webhook')
const crypto = require('crypto')
const { URL } = require('url')
const DefaultOptions = {
const DEFAULT_OPTIONS = {
retryAfter: 1,
handlerTimeout: 0
handlerTimeout: 0,
contextType: Context
}
const noop = () => { }
@@ -20,7 +23,10 @@ const noop = () => { }
class Telegraf extends Composer {
constructor (token, options) {
super()
this.options = Object.assign({}, DefaultOptions, options)
this.options = {
...DEFAULT_OPTIONS,
...options
}
this.token = token
this.handleError = (err) => {
console.error()
@@ -92,6 +98,7 @@ class Telegraf extends Composer {
}
launch (config = {}) {
debug('Connecting to Telegram')
return this.telegram.getMe()
.then((botInfo) => {
debug(`Launching @${botInfo.username}`)
@@ -101,7 +108,7 @@ class Telegraf extends Composer {
const { timeout, limit, allowedUpdates, stopCallback } = config.polling || {}
return this.telegram.deleteWebhook()
.then(() => this.startPolling(timeout, limit, allowedUpdates, stopCallback))
.then(() => debug(`Bot started with long-polling`))
.then(() => debug('Bot started with long-polling'))
}
if (typeof config.webhook.domain !== 'string' && typeof config.webhook.hookPath !== 'string') {
throw new Error('Webhook domain or webhook path is required')
@@ -114,23 +121,31 @@ class Telegraf extends Composer {
const { port, host, tlsOptions, cb } = config.webhook
this.startWebhook(hookPath, tlsOptions, port, host, cb)
if (!domain) {
debug(`Bot started with webhook`)
debug('Bot started with webhook')
return
}
return this.telegram
.setWebhook(`https://${domain}${hookPath}`)
.then(() => debug(`Bot started with webhook @ https://${domain}`))
})
.catch((err) => {
console.error('Launch failed')
console.error(err.stack || err.toString())
})
}
stop (cb = noop) {
this.polling.started = false
if (this.webhookServer) {
this.webhookServer.close(cb)
} else {
cb()
}
return this
debug('Stopping bot...')
return new Promise((resolve) => {
const done = () => resolve() & cb()
if (this.webhookServer) {
return this.webhookServer.close(done)
} else if (!this.polling.started) {
return done()
}
this.polling.stopCallback = done
this.polling.started = false
})
}
handleUpdates (updates) {
@@ -150,16 +165,18 @@ class Telegraf extends Composer {
handleUpdate (update, webhookResponse) {
debug('Processing update', update.update_id)
const tg = new Telegram(this.token, this.telegram.options, webhookResponse)
const ctx = new Context(update, tg, this.options)
const TelegrafContext = this.options.contextType
const ctx = new TelegrafContext(update, tg, this.options)
Object.assign(ctx, this.context)
return this.middleware()(ctx).catch(this.handleError)
return this.middleware()(ctx).catch((err) => this.handleError(err, ctx))
}
fetchUpdates () {
const { timeout, limit, offset, started, allowedUpdates } = this.polling
if (!started) {
if (!this.polling.started) {
this.polling.stopCallback && this.polling.stopCallback()
return
}
const { timeout, limit, offset, allowedUpdates } = this.polling
this.telegram.getUpdates(timeout, limit, offset, allowedUpdates)
.catch((err) => {
if (err.code === 401 || err.code === 409) {
@@ -169,7 +186,10 @@ class Telegraf extends Composer {
console.error(`Failed to fetch updates. Waiting: ${wait}s`, err.message)
return new Promise((resolve) => setTimeout(resolve, wait * 1000, []))
})
.then((updates) => this.handleUpdates(updates).then(() => updates))
.then((updates) => this.polling.started
? this.handleUpdates(updates).then(() => updates)
: []
)
.catch((err) => {
console.error('Failed to process updates.', err)
this.polling.started = false
@@ -187,6 +207,7 @@ class Telegraf extends Composer {
}
module.exports = Object.assign(Telegraf, {
Context,
Composer,
Extra,
Markup,
@@ -196,10 +217,13 @@ module.exports = Object.assign(Telegraf, {
})
module.exports.default = Object.assign(Telegraf, {
Context,
Composer,
Extra,
Markup,
Router,
Telegram,
Stage,
BaseScene,
session
})
+96 -84
View File
@@ -1,4 +1,3 @@
const util = require('util')
const replicators = require('./core/replicators')
const ApiClient = require('./core/network/client')
@@ -31,11 +30,11 @@ class Telegram extends ApiClient {
}
getWebhookInfo () {
return this.callApi(`getWebhookInfo`)
return this.callApi('getWebhookInfo')
}
getGameHighScores (userId, inlineMessageId, chatId, messageId) {
return this.callApi(`getGameHighScores`, {
return this.callApi('getGameHighScores', {
user_id: userId,
inline_message_id: inlineMessageId,
chat_id: chatId,
@@ -44,21 +43,21 @@ class Telegram extends ApiClient {
}
setGameScore (userId, score, inlineMessageId, chatId, messageId, editMessage = true, force) {
return this.callApi(`setGameScore`, {
return this.callApi('setGameScore', {
force,
score,
user_id: userId,
score: score,
inline_message_id: inlineMessageId,
chat_id: chatId,
message_id: messageId,
disable_edit_message: !editMessage,
force: force
disable_edit_message: !editMessage
})
}
setWebhook (url, cert, maxConnections, allowedUpdates) {
setWebhook (url, certificate, maxConnections, allowedUpdates) {
return this.callApi('setWebhook', {
url: url,
certificate: cert,
url,
certificate,
max_connections: maxConnections,
allowed_updates: allowedUpdates
})
@@ -69,93 +68,99 @@ class Telegram extends ApiClient {
}
sendMessage (chatId, text, extra) {
return this.callApi('sendMessage', Object.assign({ chat_id: chatId, text: text }, extra))
return this.callApi('sendMessage', { chat_id: chatId, text, ...extra })
}
forwardMessage (chatId, fromChatId, messageId, extra) {
return this.callApi('forwardMessage', Object.assign({
return this.callApi('forwardMessage', {
chat_id: chatId,
from_chat_id: fromChatId,
message_id: messageId
}, extra))
message_id: messageId,
...extra
})
}
sendChatAction (chatId, action) {
return this.callApi('sendChatAction', { chat_id: chatId, action: action })
return this.callApi('sendChatAction', { chat_id: chatId, action })
}
getUserProfilePhotos (userId, offset, limit) {
return this.callApi('getUserProfilePhotos', { user_id: userId, offset: offset, limit: limit })
return this.callApi('getUserProfilePhotos', { user_id: userId, offset, limit })
}
sendLocation (chatId, latitude, longitude, extra) {
return this.callApi('sendLocation', Object.assign({ chat_id: chatId, latitude: latitude, longitude: longitude }, extra))
return this.callApi('sendLocation', { chat_id: chatId, latitude, longitude, ...extra })
}
sendVenue (chatId, latitude, longitude, title, address, extra) {
return this.callApi('sendVenue', Object.assign({
return this.callApi('sendVenue', {
latitude,
longitude,
title,
address,
chat_id: chatId,
latitude: latitude,
longitude: longitude,
title: title,
address: address
}, extra))
...extra
})
}
sendInvoice (chatId, invoice, extra) {
return this.callApi('sendInvoice', Object.assign({ chat_id: chatId }, invoice, extra))
return this.callApi('sendInvoice', { chat_id: chatId, ...invoice, ...extra })
}
sendContact (chatId, phoneNumber, firstName, extra) {
return this.callApi('sendContact', Object.assign({ chat_id: chatId, phone_number: phoneNumber, first_name: firstName }, extra))
return this.callApi('sendContact', { 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))
return this.callApi('sendPhoto', { chat_id: chatId, photo, ...extra })
}
sendDocument (chatId, doc, extra) {
return this.callApi('sendDocument', Object.assign({ chat_id: chatId, document: doc }, extra))
sendDocument (chatId, document, extra) {
return this.callApi('sendDocument', { chat_id: chatId, document, ...extra })
}
sendAudio (chatId, audio, extra) {
return this.callApi('sendAudio', Object.assign({ chat_id: chatId, audio: audio }, extra))
return this.callApi('sendAudio', { chat_id: chatId, audio, ...extra })
}
sendSticker (chatId, sticker, extra) {
return this.callApi('sendSticker', Object.assign({ chat_id: chatId, sticker: sticker }, extra))
return this.callApi('sendSticker', { chat_id: chatId, sticker, ...extra })
}
sendVideo (chatId, video, extra) {
return this.callApi('sendVideo', Object.assign({ chat_id: chatId, video: video }, extra))
return this.callApi('sendVideo', { chat_id: chatId, video, ...extra })
}
sendAnimation (chatId, animation, extra) {
return this.callApi('sendAnimation', Object.assign({ chat_id: chatId, animation: animation }, extra))
return this.callApi('sendAnimation', { chat_id: chatId, animation, ...extra })
}
sendVideoNote (chatId, videoNote, extra) {
return this.callApi('sendVideoNote', Object.assign({ chat_id: chatId, video_note: videoNote }, extra))
return this.callApi('sendVideoNote', { chat_id: chatId, video_note: videoNote, ...extra })
}
sendVoice (chatId, voice, extra) {
return this.callApi('sendVoice', Object.assign({ chat_id: chatId, voice: voice }, extra))
return this.callApi('sendVoice', { chat_id: chatId, voice, ...extra })
}
sendGame (chatId, gameName, extra) {
return this.callApi('sendGame', Object.assign({ chat_id: chatId, game_short_name: gameName }, extra))
return this.callApi('sendGame', { chat_id: chatId, game_short_name: gameName, ...extra })
}
sendMediaGroup (chatId, media, extra) {
return this.callApi('sendMediaGroup', Object.assign({ chat_id: chatId, media }, extra))
return this.callApi('sendMediaGroup', { chat_id: chatId, media, ...extra })
}
sendPoll (chatId, question, options, extra) {
return this.callApi('sendPoll', Object.assign({ chat_id: chatId, question, options }, extra))
return this.callApi('sendPoll', { chat_id: chatId, type: 'regular', question, options, ...extra })
}
sendQuiz (chatId, question, options, extra) {
return this.callApi('sendPoll', { chat_id: chatId, type: 'quiz', question, options, ...extra })
}
stopPoll (chatId, messageId, extra) {
return this.callApi('stopPoll', Object.assign({ chat_id: chatId, message_id: messageId }, extra))
return this.callApi('stopPoll', { chat_id: chatId, message_id: messageId, ...extra })
}
getChat (chatId) {
@@ -175,7 +180,7 @@ class Telegram extends ApiClient {
}
answerInlineQuery (inlineQueryId, results, extra) {
return this.callApi('answerInlineQuery', Object.assign({ inline_query_id: inlineQueryId, results: JSON.stringify(results) }, extra))
return this.callApi('answerInlineQuery', { inline_query_id: inlineQueryId, results, ...extra })
}
setChatPermissions (chatId, permissions) {
@@ -187,11 +192,15 @@ class Telegram extends ApiClient {
}
promoteChatMember (chatId, userId, extra) {
return this.callApi('promoteChatMember', Object.assign({ chat_id: chatId, user_id: userId }, extra))
return this.callApi('promoteChatMember', { chat_id: chatId, user_id: userId, ...extra })
}
restrictChatMember (chatId, userId, extra) {
return this.callApi('restrictChatMember', Object.assign({ chat_id: chatId, user_id: userId }, extra))
return this.callApi('restrictChatMember', { chat_id: chatId, user_id: userId, ...extra })
}
setChatAdministratorCustomTitle (chatId, userId, title) {
return this.callApi('setChatAdministratorCustomTitle', { chat_id: chatId, user_id: userId, custom_title: title })
}
exportChatInviteLink (chatId) {
@@ -199,7 +208,7 @@ class Telegram extends ApiClient {
}
setChatPhoto (chatId, photo) {
return this.callApi('setChatPhoto', { chat_id: chatId, photo: photo })
return this.callApi('setChatPhoto', { chat_id: chatId, photo })
}
deleteChatPhoto (chatId) {
@@ -207,15 +216,15 @@ class Telegram extends ApiClient {
}
setChatTitle (chatId, title) {
return this.callApi('setChatTitle', { chat_id: chatId, title: title })
return this.callApi('setChatTitle', { chat_id: chatId, title })
}
setChatDescription (chatId, description) {
return this.callApi('setChatDescription', { chat_id: chatId, description: description })
return this.callApi('setChatDescription', { chat_id: chatId, description })
}
pinChatMessage (chatId, messageId, extra) {
return this.callApi('pinChatMessage', Object.assign({ chat_id: chatId, message_id: messageId }, extra))
return this.callApi('pinChatMessage', { chat_id: chatId, message_id: messageId, ...extra })
}
unpinChatMessage (chatId) {
@@ -230,35 +239,26 @@ class Telegram extends ApiClient {
return this.callApi('unbanChatMember', { chat_id: chatId, user_id: userId })
}
answerCallbackQuery (callbackQueryId, text, url, showAlert, cacheTime) {
return this.callApi('answerCallbackQuery', {
callback_query_id: callbackQueryId,
text: text,
url: url,
show_alert: showAlert,
cache_time: cacheTime
})
}
answerCbQuery (callbackQueryId, text, showAlert, extra) {
return this.callApi('answerCallbackQuery', Object.assign({
return this.callApi('answerCallbackQuery', {
text,
show_alert: showAlert,
callback_query_id: callbackQueryId,
text: text,
show_alert: showAlert
}, extra))
...extra
})
}
answerGameQuery (callbackQueryId, url) {
return this.callApi('answerCallbackQuery', {
callback_query_id: callbackQueryId,
url: url
url,
callback_query_id: callbackQueryId
})
}
answerShippingQuery (shippingQueryId, ok, shippingOptions, errorMessage) {
return this.callApi('answerShippingQuery', {
ok,
shipping_query_id: shippingQueryId,
ok: ok,
shipping_options: shippingOptions,
error_message: errorMessage
})
@@ -266,27 +266,28 @@ class Telegram extends ApiClient {
answerPreCheckoutQuery (preCheckoutQueryId, ok, errorMessage) {
return this.callApi('answerPreCheckoutQuery', {
ok,
pre_checkout_query_id: preCheckoutQueryId,
ok: ok,
error_message: errorMessage
})
}
editMessageText (chatId, messageId, inlineMessageId, text, extra) {
return this.callApi('editMessageText', Object.assign({
return this.callApi('editMessageText', {
text,
chat_id: chatId,
message_id: messageId,
inline_message_id: inlineMessageId,
text: text
}, extra))
...extra
})
}
editMessageCaption (chatId, messageId, inlineMessageId, caption, extra = {}) {
return this.callApi('editMessageCaption', {
caption,
chat_id: chatId,
message_id: messageId,
inline_message_id: inlineMessageId,
caption: caption,
parse_mode: extra.parse_mode,
reply_markup: extra.parse_mode || extra.reply_markup ? extra.reply_markup : extra
})
@@ -297,7 +298,7 @@ class Telegram extends ApiClient {
chat_id: chatId,
message_id: messageId,
inline_message_id: inlineMessageId,
media: Object.assign(media, { parse_mode: extra.parse_mode }),
media: { ...media, parse_mode: extra.parse_mode },
reply_markup: extra.reply_markup ? extra.reply_markup : extra
})
}
@@ -349,8 +350,8 @@ class Telegram extends ApiClient {
return this.callApi('deleteChatStickerSet', { chat_id: chatId })
}
getStickerSet (setName) {
return this.callApi('getStickerSet', { name: setName })
getStickerSet (name) {
return this.callApi('getStickerSet', { name })
}
uploadStickerFile (ownerId, stickerFile) {
@@ -361,30 +362,39 @@ class Telegram extends ApiClient {
}
createNewStickerSet (ownerId, name, title, stickerData) {
return this.callApi('createNewStickerSet', Object.assign({
return this.callApi('createNewStickerSet', {
name,
title,
user_id: ownerId,
name: name,
title: title
}, stickerData))
...stickerData
})
}
addStickerToSet (ownerId, name, stickerData, isMasks) {
return this.callApi('addStickerToSet', Object.assign({
return this.callApi('addStickerToSet', {
name,
user_id: ownerId,
name: name,
is_masks: isMasks
}, stickerData))
is_masks: isMasks,
...stickerData
})
}
setStickerPositionInSet (sticker, position) {
return this.callApi('setStickerPositionInSet', {
sticker: sticker,
position: position
sticker,
position
})
}
deleteStickerFromSet (sticker) {
return this.callApi('deleteStickerFromSet', { sticker: sticker })
return this.callApi('deleteStickerFromSet', { sticker })
}
setPassportDataErrors (userId, errors) {
return this.callApi('setPassportDataErrors', {
user_id: userId,
errors: errors
})
}
sendCopy (chatId, message, extra) {
@@ -395,11 +405,13 @@ class Telegram extends ApiClient {
if (!type) {
throw new Error('Unsupported message type')
}
const opts = Object.assign({ chat_id: chatId }, replicators[type](message), extra)
const opts = {
chat_id: chatId,
...replicators[type](message),
...extra
}
return this.callApi(replicators.copyMethods[type], opts)
}
}
Telegram.prototype.answerCallbackQuery = util.deprecate(Telegram.prototype.answerCallbackQuery, '️⚠️ Telegraf: answerCallbackQuery() is deprecated, use answerCbQuery() instead')
module.exports = Telegram