UNPKG

tgapi

Version:

Actual Telegram bot API JS implementation

1,064 lines (903 loc) 29.8 kB
/** * tgapi v2.2.6 * Actual Telegram bot API JS implementation */ import FormData from 'form-data'; import 'core-js/modules/es.symbol.description'; import 'core-js/modules/es.promise'; import EventEmitter from 'events'; import $$observable from 'symbol-observable'; import fetch from 'isomorphic-fetch'; const getSet = keyName => { const key = Symbol(keyName); return [instance => instance[key], // eslint-disable-next-line no-param-reassign (instance, value) => instance[key] = value]; }; const [getToken, setToken] = getSet('token'); const [getUserCallMethod, setUserCallMethod] = getSet('userCallMethod'); const callMethod = (client, method, formData) => { const token = getToken(client); const url = `https://api.telegram.org/bot${token}/${method}`; const userCallMethod = getUserCallMethod(client); if (!formData) return userCallMethod({ url, token, method }); const body = new FormData(); Object.entries(formData).forEach(prop => { if (prop[1] != null) body.append(prop[0], prop[1]); }); return userCallMethod({ url, token, method, body }); }; /* :: import * as t from '../types' import * as a from './apiTypes' import * as r from '../returnTypes' */ class BotCore { /** * getUpdates * * Use this method to receive incoming updates using long polling (wiki). An * Array of Update objects is returned. */ getUpdates(props = {}) { return callMethod(this, 'getUpdates', props); } /** * setWebhook * * Use this method to specify a url and receive incoming updates via an * outgoing webhook. Whenever there is an update for the bot, we will send an * HTTPS POST request to the specified url, containing a JSON-serialized * Update. In case of an unsuccessful request, we will give up after a * reasonable amount of attempts. Returns True on success. * * If you'd like to make sure that the Webhook request comes from Telegram, * we recommend using a secret path in the URL, e.g. * https://www.example.com/<token>. Since nobody else knows your bot‘s token, * you can be pretty sure it’s us. */ setWebhook(props) { return callMethod(this, 'setWebhook', props); } /** * deleteWebhook * * Use this method to remove webhook integration if you decide to switch back * to getUpdates. Returns True on success. Requires no parameters. */ deleteWebhook() { return callMethod(this, 'deleteWebhook'); } /** * getWebhookInfo * * Use this method to get current webhook status. Requires no parameters. On * success, returns a WebhookInfo object. If the bot is using getUpdates, * will return an object with the url field empty. */ getWebhookInfo() { return callMethod(this, 'getWebhookInfo'); } /** * getMe * * A simple method for testing your bot's auth token. Requires no parameters. * Returns basic information about the bot in form of a User object. */ getMe() { return callMethod(this, 'getMe'); } /** * sendMessage * * Use this method to send text messages. On success, the sent Message is * returned. */ sendMessage(props) { return callMethod(this, 'sendMessage', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * forwardMessage * * Use this method to forward messages of any kind. On success, the sent * Message is returned. */ forwardMessage(props) { return callMethod(this, 'forwardMessage', props); } /** * sendPhoto * * Use this method to send photos. On success, the sent Message is returned. */ sendPhoto(props) { return callMethod(this, 'sendPhoto', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * sendAudio * * Use this method to send audio files, if you want Telegram clients to * display them in the music player. Your audio must be in the .mp3 format. * On success, the sent Message is returned. Bots can currently send audio * files of up to 50 MB in size, this limit may be changed in the future. * * For sending voice messages, use the sendVoice method instead. */ sendAudio(props) { return callMethod(this, 'sendAudio', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * sendDocument * * Use this method to send general files. On success, the sent Message is * returned. Bots can currently send files of any type of up to 50 MB in * size, this limit may be changed in the future. */ sendDocument(props) { return callMethod(this, 'sendDocument', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * sendVideo * * Use this method to send video files, Telegram clients support mp4 videos * (other formats may be sent as Document). On success, the sent Message is * returned. Bots can currently send video files of up to 50 MB in size, this * limit may be changed in the future. */ sendVideo(props) { return callMethod(this, 'sendVideo', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * sendAnimation * * Use this method to send animation files (GIF or H.264/MPEG-4 AVC video * without sound). On success, the sent Message is returned. Bots can * currently send animation files of up to 50 MB in size, this limit may be * changed in the future. */ sendAnimation(props) { return callMethod(this, 'sendAnimation', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * sendVoice * * Use this method to send audio files, if you want Telegram clients to * display the file as a playable voice message. For this to work, your audio * must be in an .ogg file encoded with OPUS (other formats may be sent as * Audio or Document). On success, the sent Message is returned. Bots can * currently send voice messages of up to 50 MB in size, this limit may be * changed in the future. */ sendVoice(props) { return callMethod(this, 'sendVoice', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * sendVideoNote * * As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 * minute long. Use this method to send video messages. On success, the sent * Message is returned. */ sendVideoNote(props) { return callMethod(this, 'sendVideoNote', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * sendMediaGroup * * Use this method to send a group of photos or videos as an album. On * success, an array of the sent Messages is returned. */ sendMediaGroup(props) { return callMethod(this, 'sendMediaGroup', { ...props, media: props.media && JSON.stringify(props.media) }); } /** * sendLocation * * Use this method to send point on the map. On success, the sent Message is * returned. */ sendLocation(props) { return callMethod(this, 'sendLocation', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * editMessageLiveLocation * * Use this method to edit live location messages sent by the bot or via the * bot (for inline bots). A location can be edited until its live_period * expires or editing is explicitly disabled by a call to * stopMessageLiveLocation. On success, if the edited message was sent by the * bot, the edited Message is returned, otherwise True is returned. */ editMessageLiveLocation(props) { return callMethod(this, 'editMessageLiveLocation', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * stopMessageLiveLocation * * Use this method to stop updating a live location message sent by the bot * or via the bot (for inline bots) before live_period expires. On success, * if the message was sent by the bot, the sent Message is returned, * otherwise True is returned. */ stopMessageLiveLocation(props = {}) { return callMethod(this, 'stopMessageLiveLocation', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * sendVenue * * Use this method to send information about a venue. On success, the sent * Message is returned. */ sendVenue(props) { return callMethod(this, 'sendVenue', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * sendContact * * Use this method to send phone contacts. On success, the sent Message is * returned. */ sendContact(props) { return callMethod(this, 'sendContact', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * sendChatAction * * Use this method when you need to tell the user that something is happening * on the bot's side. The status is set for 5 seconds or less (when a message * arrives from your bot, Telegram clients clear its typing status). Returns * True on success. * * We only recommend using this method when a response from the bot will take * a noticeable amount of time to arrive. */ sendChatAction(props) { return callMethod(this, 'sendChatAction', props); } /** * getUserProfilePhotos * * Use this method to get a list of profile pictures for a user. Returns a * UserProfilePhotos object. */ getUserProfilePhotos(props) { return callMethod(this, 'getUserProfilePhotos', props); } /** * getFile * * Use this method to get basic info about a file and prepare it for * downloading. For the moment, bots can download files of up to 20MB in * size. On success, a File object is returned. The file can then be * downloaded via the link * https://api.telegram.org/file/bot<token>/<file_path>, where <file_path> is * taken from the response. It is guaranteed that the link will be valid for * at least 1 hour. When the link expires, a new one can be requested by * calling getFile again. */ getFile(props) { return callMethod(this, 'getFile', props); } /** * kickChatMember * * Use this method to kick a user from a group, a supergroup or a channel. In * the case of supergroups and channels, the user will not be able to return * to the group on their own using invite links, etc., unless unbanned first. * The bot must be an administrator in the chat for this to work and must * have the appropriate admin rights. Returns True on success. */ kickChatMember(props) { return callMethod(this, 'kickChatMember', props); } /** * unbanChatMember * * Use this method to unban a previously kicked user in a supergroup or * channel. The user will not return to the group or channel automatically, * but will be able to join via link, etc. The bot must be an administrator * for this to work. Returns True on success. */ unbanChatMember(props) { return callMethod(this, 'unbanChatMember', props); } /** * restrictChatMember * * Use this method to restrict a user in a supergroup. The bot must be an * administrator in the supergroup for this to work and must have the * appropriate admin rights. Pass True for all boolean parameters to lift * restrictions from a user. Returns True on success. */ restrictChatMember(props) { return callMethod(this, 'restrictChatMember', props); } /** * promoteChatMember * * Use this method to promote or demote a user in a supergroup or a channel. * The bot must be an administrator in the chat for this to work and must * have the appropriate admin rights. Pass False for all boolean parameters * to demote a user. Returns True on success. */ promoteChatMember(props) { return callMethod(this, 'promoteChatMember', props); } /** * exportChatInviteLink * * Use this method to generate a new invite link for a chat; any previously * generated link is revoked. The bot must be an administrator in the chat * for this to work and must have the appropriate admin rights. Returns the * new invite link as String on success. */ exportChatInviteLink(props) { return callMethod(this, 'exportChatInviteLink', props); } /** * setChatPhoto * * Use this method to set a new profile photo for the chat. Photos can't be * changed for private chats. The bot must be an administrator in the chat * for this to work and must have the appropriate admin rights. Returns True * on success. */ setChatPhoto(props) { return callMethod(this, 'setChatPhoto', props); } /** * deleteChatPhoto * * Use this method to delete a chat photo. Photos can't be changed for * private chats. The bot must be an administrator in the chat for this to * work and must have the appropriate admin rights. Returns True on success. */ deleteChatPhoto(props) { return callMethod(this, 'deleteChatPhoto', props); } /** * setChatTitle * * Use this method to change the title of a chat. Titles can't be changed for * private chats. The bot must be an administrator in the chat for this to * work and must have the appropriate admin rights. Returns True on success. */ setChatTitle(props) { return callMethod(this, 'setChatTitle', props); } /** * setChatDescription * * Use this method to change the description of a supergroup or a channel. * The bot must be an administrator in the chat for this to work and must * have the appropriate admin rights. Returns True on success. */ setChatDescription(props) { return callMethod(this, 'setChatDescription', props); } /** * pinChatMessage * * Use this method to pin a message in a supergroup or a channel. The bot * must be an administrator in the chat for this to work and must have the * ‘can_pin_messages’ admin right in the supergroup or ‘can_edit_messages’ * admin right in the channel. Returns True on success. */ pinChatMessage(props) { return callMethod(this, 'pinChatMessage', props); } /** * unpinChatMessage * * Use this method to unpin a message in a supergroup or a channel. The bot * must be an administrator in the chat for this to work and must have the * ‘can_pin_messages’ admin right in the supergroup or ‘can_edit_messages’ * admin right in the channel. Returns True on success. */ unpinChatMessage(props) { return callMethod(this, 'unpinChatMessage', props); } /** * leaveChat * * Use this method for your bot to leave a group, supergroup or channel. * Returns True on success. */ leaveChat(props) { return callMethod(this, 'leaveChat', props); } /** * getChat * * Use this method to get up to date information about the chat (current name * of the user for one-on-one conversations, current username of a user, * group or channel, etc.). Returns a Chat object on success. */ getChat(props) { return callMethod(this, 'getChat', props); } /** * getChatAdministrators * * Use this method to get a list of administrators in a chat. On success, * returns an Array of ChatMember objects that contains information about all * chat administrators except other bots. If the chat is a group or a * supergroup and no administrators were appointed, only the creator will be * returned. */ getChatAdministrators(props) { return callMethod(this, 'getChatAdministrators', props); } /** * getChatMembersCount * * Use this method to get the number of members in a chat. Returns Int on * success. */ getChatMembersCount(props) { return callMethod(this, 'getChatMembersCount', props); } /** * getChatMember * * Use this method to get information about a member of a chat. Returns a * ChatMember object on success. */ getChatMember(props) { return callMethod(this, 'getChatMember', props); } /** * setChatStickerSet * * Use this method to set a new group sticker set for a supergroup. The bot * must be an administrator in the chat for this to work and must have the * appropriate admin rights. Use the field can_set_sticker_set optionally * returned in getChat requests to check if the bot can use this method. * Returns True on success. */ setChatStickerSet(props) { return callMethod(this, 'setChatStickerSet', props); } /** * deleteChatStickerSet * * Use this method to delete a group sticker set from a supergroup. The bot * must be an administrator in the chat for this to work and must have the * appropriate admin rights. Use the field can_set_sticker_set optionally * returned in getChat requests to check if the bot can use this method. * Returns True on success. */ deleteChatStickerSet(props) { return callMethod(this, 'deleteChatStickerSet', props); } /** * answerCallbackQuery * * Use this method to send answers to callback queries sent from inline * keyboards. The answer will be displayed to the user as a notification at * the top of the chat screen or as an alert. On success, True is returned. */ answerCallbackQuery(props) { return callMethod(this, 'answerCallbackQuery', props); } /** * editMessageText * * Use this method to edit text and game messages sent by the bot or via the * bot (for inline bots). On success, if edited message is sent by the bot, * the edited Message is returned, otherwise True is returned. */ editMessageText(props) { return callMethod(this, 'editMessageText', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * editMessageCaption * * Use this method to edit captions of messages sent by the bot or via the * bot (for inline bots). On success, if edited message is sent by the bot, * the edited Message is returned, otherwise True is returned. */ editMessageCaption(props = {}) { return callMethod(this, 'editMessageCaption', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * editMessageMedia * * Use this method to edit animation, audio, document, photo, or video * messages. If a message is a part of a message album, then it can be edited * only to a photo or a video. Otherwise, message type can be changed * arbitrarily. When inline message is edited, new file can't be uploaded. * Use previously uploaded file via its file_id or specify a URL. On success, * if the edited message was sent by the bot, the edited Message is returned, * otherwise True is returned. */ editMessageMedia(props) { return callMethod(this, 'editMessageMedia', { ...props, media: props.media && JSON.stringify(props.media), reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * editMessageReplyMarkup * * Use this method to edit only the reply markup of messages sent by the bot * or via the bot (for inline bots). On success, if edited message is sent * by the bot, the edited Message is returned, otherwise True is returned. */ editMessageReplyMarkup(props = {}) { return callMethod(this, 'editMessageReplyMarkup', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * deleteMessage * * Use this method to delete a message, including service messages, with the * following limitations: * * - A message can only be deleted if it was sent less than 48 hours ago. * * - Bots can delete outgoing messages in private chats, groups, and * supergroups. * * - Bots granted can_post_messages permissions can delete outgoing messages * in channels. * * - If the bot is an administrator of a group, it can delete any message * there. * * - If the bot has can_delete_messages permission in a supergroup or a * channel, it can delete any message there. * * Returns True on success. */ deleteMessage(props) { return callMethod(this, 'deleteMessage', props); } /** * sendSticker * * Use this method to send .webp stickers. On success, the sent Message is * returned. */ sendSticker(props) { return callMethod(this, 'sendSticker', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * getStickerSet * * Use this method to get a sticker set. On success, a StickerSet object is * returned. */ getStickerSet(props) { return callMethod(this, 'getStickerSet', props); } /** * uploadStickerFile * * Use this method to upload a .png file with a sticker for later use in * createNewStickerSet and addStickerToSet methods (can be used multiple * times). Returns the uploaded File on success. */ uploadStickerFile(props) { return callMethod(this, 'uploadStickerFile', props); } /** * createNewStickerSet * * Use this method to create new sticker set owned by a user. The bot will be * able to edit the created sticker set. Returns True on success. */ createNewStickerSet(props) { return callMethod(this, 'createNewStickerSet', { ...props, mask_position: props.mask_position && JSON.stringify(props.mask_position) }); } /** * addStickerToSet * * Use this method to add a new sticker to a set created by the bot. Returns * True on success. */ addStickerToSet(props) { return callMethod(this, 'addStickerToSet', { ...props, mask_position: props.mask_position && JSON.stringify(props.mask_position) }); } /** * setStickerPositionInSet * * Use this method to move a sticker in a set created by the bot to a * specific position . Returns True on success. */ setStickerPositionInSet(props) { return callMethod(this, 'setStickerPositionInSet', props); } /** * deleteStickerFromSet * * Use this method to delete a sticker from a set created by the bot. Returns * True on success. */ deleteStickerFromSet(props) { return callMethod(this, 'deleteStickerFromSet', props); } /** * answerInlineQuery * * Use this method to send answers to an inline query. On success, True is * returned. * * No more than 50 results per query are allowed. */ answerInlineQuery(props) { return callMethod(this, 'answerInlineQuery', { ...props, results: props.results && JSON.stringify(props.results) }); } /** * sendInvoice * * Use this method to send invoices. On success, the sent Message is returned. */ sendInvoice(props) { return callMethod(this, 'sendInvoice', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * answerShippingQuery * * If you sent an invoice requesting a shipping address and the parameter * is_flexible was specified, the Bot API will send an Update with a * shipping_query field to the bot. Use this method to reply to shipping * queries. On success, True is returned. */ answerShippingQuery(props) { return callMethod(this, 'answerShippingQuery', { ...props, shipping_options: props.shipping_options && JSON.stringify(props.shipping_options) }); } /** * answerPreCheckoutQuery * * Once the user has confirmed their payment and shipping details, the Bot * API sends the final confirmation in the form of an Update with the field * pre_checkout_query. Use this method to respond to such pre-checkout * queries. On success, True is returned. Note: The Bot API must receive an * answer within 10 seconds after the pre-checkout query was sent. */ answerPreCheckoutQuery(props) { return callMethod(this, 'answerPreCheckoutQuery', props); } /** * setPassportDataErrors * * Informs a user that some of the Telegram Passport elements they provided * contains errors. The user will not be able to re-submit their Passport to * you until the errors are fixed (the contents of the field for which you * returned the error must change). Returns True on success. * * Use this if the data submitted by the user doesn't satisfy the standards * your service requires for any reason. For example, if a birthday date * seems invalid, a submitted document is blurry, a scan shows evidence of * tampering, etc. Supply some details in the error message to make sure the * user knows how to correct the issues. */ setPassportDataErrors(props) { return callMethod(this, 'setPassportDataErrors', { ...props, errors: props.errors && JSON.stringify(props.errors) }); } /** * sendGame * * Use this method to send a game. On success, the sent Message is returned. */ sendGame(props) { return callMethod(this, 'sendGame', { ...props, reply_markup: props.reply_markup && JSON.stringify(props.reply_markup) }); } /** * setGameScore * * Use this method to set the score of the specified user in a game. On * success, if the message was sent by the bot, returns the edited Message, * otherwise returns True. Returns an error, if the new score is not greater * than the user's current score in the chat and force is False. */ setGameScore(props) { return callMethod(this, 'setGameScore', props); } /** * getGameHighScores * * Use this method to get data for high score tables. Will return the score * of the specified user and several of his neighbors in a game. On success, * returns an Array of GameHighScore objects. */ getGameHighScores(props) { return callMethod(this, 'getGameHighScores', props); } } const [getConnected, setConnected] = getSet('connected'); const getOnUpdate = next => { if (typeof next == 'function') return next; if (next && next.next) return value => next.next(value); }; const getOnError = (next, error) => { if (typeof next == 'object' && next.error) { return err => next.error(err); } return error; }; class Subscriber extends EventEmitter { constructor() { super(); this.connect(); } disconnect() { setConnected(this, false); return this; } connect() { setConnected(this, true); return this; } /* :: +subscribe: (( next?: (Update) => mixed, error?: (Error) => mixed, ) => Subscription) & ((observer: Observer<Update>) => Subscription) */ subscribe(next, error) { const onUpdate = getOnUpdate(next); const onError = getOnError(next, error); if (onUpdate) this.on('update', onUpdate); if (onError) this.on('error', onError); return { unsubscribe: () => { if (onUpdate) this.removeListener('update', onUpdate); if (onError) this.removeListener('error', onError); } }; } // $FlowFixMe [$$observable]() { return this; } } const [getLastId, setLastId] = getSet('lastId'); const [getOptions, setOptions] = getSet('options'); const events = ['message', 'edited_message', 'channel_post', 'edited_channel_post', 'inline_query', 'chosen_inline_result', 'callback_query', 'shipping_query', 'pre_checkout_query']; const getUpdates = emitter => { if (!getConnected(emitter)) return; const { limit, timeout, allowedUpdates } = getOptions(emitter); getOptions(emitter).bot.getUpdates({ limit, timeout, offset: getLastId(emitter) + 1, allowed_updates: allowedUpdates }).then(response => { if (!response.ok) { return Promise.reject(new Error(`${response.error_code}: ${response.description}`)); } if (!response.result.length) return; const updates = response.result; setLastId(emitter, updates[updates.length - 1].update_id); updates.forEach(update => { emitter.emit('update', update); events.forEach(eventName => update[eventName] && emitter.emit(eventName, update[eventName])); }); return getUpdates(emitter); }).catch(error => emitter.emit('error', error)); }; class PollingSubscriber extends Subscriber { constructor({ allowedUpdates, ...options }) { super(); setLastId(this, -1); setOptions(this, { ...options, allowedUpdates: allowedUpdates && [...allowedUpdates] }); } get bot() { return getOptions(this).bot; } setLimit(limit) { getOptions(this).limit = limit; return this; } setTimeout(timeout) { getOptions(this).timeout = timeout; return this; } setAllowedUpdates(allowedUpdates) { getOptions(this).allowedUpdates = allowedUpdates && [...allowedUpdates]; return this; } connect() { super.connect(); Promise.resolve(this).then(getUpdates); return this; } } const json = res => res.json(); const callMethod$1 = request => fetch(request.url, { method: 'POST', body: request.body }).then(json); class Bot extends BotCore { constructor(config) { super(); if (typeof config == 'string') { setToken(this, config); setUserCallMethod(this, callMethod$1); } else { setToken(this, config.token); setUserCallMethod(this, config.callMethod || callMethod$1); } } setToken(token) { setToken(this, token); return this; } get token() { return getToken(this); } polling(options = {}) { return new PollingSubscriber({ ...options, bot: this }); } } export { Bot };