vvlad1973-telegram-framework
Version:
Current version: *7.9.5*
604 lines (540 loc) • 23.9 kB
JavaScript
/**
* @module inline_keyboard_markup
*/
'use strict';
import { ValidationError } from '@vvlad1973/error-definitions';
/**
* @typedef {Object} module:inline_keyboard_markup.InlineKeyboardButton
* @property {string} text - Text of the button. If none of the optional fields
* are used, it will be sent as a message when the button is pressed
* @property {string} [url] - HTTP or tg:// url to be opened when the button is
* pressed. Links tg://user?id=<user_id> can be used to mention a user by
* their ID without using a username, if this is allowed by their privacy
* settings
* @property {string} [callback_data] - Data to be sent in a callback query to
* the bot when button is pressed, 1-64 bytes
* @property {module:inline_keyboard_markup.LoginUrl} [login_url] - An HTTP URL
* used to automatically authorize the user. Can be used as a replacement
* for the Telegram Login Widget
* @property {string} [switch_inline_query] - If set, pressing the button will
* prompt the user to select one of their chats, open that chat and insert
* the bot's username and the specified inline query in the input field.
* Can be empty, in which case just the bot's username will be inserted
* @property {string} [switch_inline_query_current_chat] - If set, pressing the
* button will insert the bot's username and the specified inline query in
* the current chat's input field. Can be empty, in which case only the
* bot's username will be inserted
* @property {CallbackGame} [callback_game] - Description of the game that will
* be launched when the user presses the button.
* NOTE: This type of button must always be the first button in the first
* row
* @property {boolean} [pay] - Specify True, to send a Pay button.
* NOTE: This type of button must always be the first button in the first
* row and can only be used in invoice messages
*/
/**
* @typedef {Object} module:inline_keyboard_markup.LoginUrl
* @property {string} url - An HTTP URL to be opened with user authorization
* data added to the query string when the button is pressed. If the user
* refuses to provide authorization data, the original URL without
* information about the user will be opened. The data added is the same
* as described in Receiving authorization data.
* NOTE: You must always check the hash of the received data to verify the
* authentication and the integrity of the data as described in Checking
* authorization
* @property {string} [forward_text] - New text of the button in forwarded
* messages
* @property {string} [bot_username] - Username of a bot, which will be used
* for user authorization. See Setting up a bot for more details. If not
* specified, the current bot's username will be assumed. The url's
* domain must be the same as the domain linked with the bot. See Linking
* your domain to the bot for more details
* @property {boolean} [request_write_access] - Pass True to request the
* permission for your bot to send messages to the user
*/
/**
* @class Implements structure for operations with Telegram bot's commands.
*
* @description This object represents an inline keyboard that appears right
* next to the message it belongs to
*/
export class InlineKeyboardMarkup {
/**
* @property {Array.<module:inline_keyboard_markup.InlineKeyboardButton[]>} - Array of
* button rows, each represented by an Array of InlineKeyboardButton
* objects
*/
inline_keyboard;
constructor(inlineKeyboard = []) {
this.inline_keyboard = inlineKeyboard;
}
/**
* @returns {module:inline_keyboard_markup.InlineKeyboardMarkup} Instance of the InlineKeyboardMarkup class
*/
/**
* Append one or more rows to the keyboard
*
* @param {number} [number] - Number of rows to append. If not specified,
* wlll append one row
*/
appendRows(number = 1) {
for (let i = 0; i < number; i++) {
this.inline_keyboard.push([]);
}
}
/**
* Insert one or more rows to the keyboard
*
* @param {number} [index] - Number of position to insert rows (0-based).
* If not specified, rows will insert at the first position
* @param {number} [number] - Number of rows to append. If not specified,
* wlll insert one row
*/
insertRows(index = 0, number = 1) {
for (let i = 0; i < number; i++) {
this.inline_keyboard.splice(index, 0, []);
}
}
/**
* Delete a row from the keyboard
*
* @param {number} [index] - Index of row to delete (0-based). If not
* specified, will delete the last row
*/
deleteRow(index = -1) {
if (index == -1) {
this.inline_keyboard.splice(this.inline_keyboard.length - 1);
} else if (index < this.inline_keyboard.length) {
this.inline_keyboard.splice(index, 1);
} else throw new ValidationError('Keyboard row does not exist');
}
/**
* Remove a button from a specific row
*
* @param {number} [row] - Number of row of button to delete (0-based).
* If not specified, will delete a button from the last row
* @param {number} [column] - Number of column of button to delete
* (0-based). If not specified, will delete the last button from the
* row
*/
deleteButton(row = -1, column = -1) {
if (row === -1) row = this.inline_keyboard.length - 1;
if (column === -1) column = this.inline_keyboard[row].length - 1;
if (row < this.inline_keyboard.length) {
if (column < this.inline_keyboard[row].length)
this.keyboard[row].splice(column, 1);
else throw new ValidationError('The button does not exist');
} else throw new ValidationError('Keyboard row does not exist');
}
/**
* Insert a text button to inline keyboard
*
* @param {string} text - Text label for the button
* @param {string} callbackData - Data to be sent in a callback query to
* the bot when button is pressed, 1-64 bytes
* @param {number} [row] - Row number to insert button (0-based). If
* not specified, the button will insert to the last row
* @param {number} [column] - Number of column of button to insert
* (0-based). If not specified, the button will insert to the first
* position of the row
*/
insertTextButton(text, callbackData, row = 0, column = 0) {
if (this.inline_keyboard.length === 0) this.inline_keyboard.push([]);
if (row === -1) row = this.inline_keyboard.length - 1;
if (row < this.inline_keyboard.length) {
this.inline_keyboard[row].splice(column, 0, {
text: text,
callback_data: callbackData,
});
} else throw new ValidationError('Keyboard row does not exist');
}
/**
* Append a text button to inline keyboard
*
* @param {string} text - Text label for the button
* @param {string} callbackData - Data to be sent in a callback query to
* the bot when button is pressed, 1-64 bytes
* @param {number} [row] - Row number to append button (0-based). If
* not specified, the button will append to the last row
*/
appendTextButton(text, callbackData, row = -1) {
if (this.inline_keyboard.length === 0) this.inline_keyboard.push([]);
if (row === -1) row = this.inline_keyboard.length - 1;
if (row < this.inline_keyboard.length)
this.inline_keyboard[row].push({
text: text,
callback_data: callbackData,
});
else throw new ValidationError('Keyboard row does not exist');
}
/**
* Insert an url button to inline keyboard
*
* @param {string} text - Text label for the button
* @param {string} url - HTTP or tg:// url to be opened when the button is
* pressed. Links tg://user?id=<user_id> can be used to mention a user
* by their ID without using a username, if this is allowed by their
* privacy settings
* @param {number} [row] - Row number to insert button (0-based). If
* not specified, the button will insert to the last row
* @param {number} [column] - Number of column of button to insert
* (0-based). If not specified, the button will insert to the first
* position of the row
*/
insertUrlButton(text, url, row = 0, column = 0) {
if (this.inline_keyboard.length === 0) this.inline_keyboard.push([]);
if (row === -1) row = this.inline_keyboard.length - 1;
if (row < this.inline_keyboard.length) {
this.inline_keyboard[row].splice(column, 0, { text: text, url: url });
} else throw new ValidationError('Keyboard row does not exist');
}
/**
* Append an url button to inline keyboard
*
* @param {string} text - Text label for the button
* @param {string} url - HTTP or tg:// url to be opened when the button is
* pressed. Links tg://user?id=<user_id> can be used to mention a user
* by their ID without using a username, if this is allowed by their
* privacy settings
* @param {number} [row] - Row number to append button (0-based). If
* not specified, the button will append to the last row
*/
appendUrlButton(text, url, row = -1) {
if (this.inline_keyboard.length === 0) this.inline_keyboard.push([]);
if (row === -1) row = this.inline_keyboard.length - 1;
if (row < this.inline_keyboard.length)
this.inline_keyboard[row].push({ text: text, url: url });
else throw new ValidationError('Keyboard row does not exist');
}
/**
* Add a pay button to inline keyboard
*
* @param {string} text - Text label for the button
*/
addPayButton(text) {
if (
typeof this.inline_keyboard[0][0] === 'object' &&
typeof this.inline_keyboard[0][0].pay
) {
this.inline_keyboard[0][0].text = text;
} else if (
typeof this.inline_keyboard[0][0] === 'object' &&
typeof this.inline_keyboard[0][0].callback_game === 'object'
) {
throw new ValidationError('There is the CallbackGame button added');
} else {
this.inline_keyboard[0].splice(0, 0, { text: text, pay: true });
}
}
/**
* Add a callback game button to inline keyboard
*
* @param {string} text - Text label for the button
*/
addCallbackGameButton(text) {
if (
typeof this.inline_keyboard[0][0] === 'object' &&
typeof this.inline_keyboard[0][0].callback_game === 'object'
) {
this.inline_keyboard[0][0].text = text;
} else if (
typeof this.inline_keyboard[0][0] === 'object' &&
typeof this.inline_keyboard[0][0].pay === 'boolean'
) {
throw new ValidationError('There is the Pay button added');
} else {
this.inline_keyboard[0].splice(0, 0, { text: text, callback_game: {} });
}
}
/**
* Insert a SwitchInlineQuery button to inline keyboard
*
* @param {string} text - Text label for the button
* @param {string} [switchInlineQuery = ""] - If set, pressing the button will
* prompt the user to select one of their chats, open that chat and
* insert the bot's username and the specified inline query in the
* input field. Can be empty, in which case just the bot's username
* will be inserted
* @param {number} [row] - Row number to insert button (0-based). If
* not specified, the button will insert to the last row
* @param {number} [column] - Number of column of button to insert
* (0-based). If not specified, the button will insert to the first
* position of the row
*/
insertSwitchInlineQueryButton(
text,
switchInlineQuery = '',
row = 0,
column = 0
) {
if (this.inline_keyboard.length === 0) this.inline_keyboard.push([]);
if (row == -1) row = this.inline_keyboard.length - 1;
if (row < this.inline_keyboard.length) {
this.inline_keyboard[row].splice(column, 0, {
text: text,
switch_inline_query: switchInlineQuery,
});
} else throw new ValidationError('Keyboard row does not exist');
}
/**
* Append a SwitchInlineQuery button to inline keyboard
*
* @param {string} text - Text label for the button
* @param {string} [switchInlineQuery = ""] - If set, pressing the button will
* prompt the user to select one of their chats, open that chat and
* insert the bot's username and the specified inline query in the
* input field. Can be empty, in which case just the bot's username
* will be inserted
* @param {number} [row] - Row number to append button (0-based). If
* not specified, the button will append to the last row
*/
appendSwitchInlineQueryButton(text, switchInlineQuery = '', row = -1) {
if (this.inline_keyboard.length === 0) this.inline_keyboard.push([]);
if (row == -1) row = this.inline_keyboard.length - 1;
if (row < this.inline_keyboard.length)
this.inline_keyboard[row].push({
text: text,
switch_inline_query: switchInlineQuery,
});
else throw new ValidationError('Keyboard row does not exist');
}
/**
* Insert a SwitchInlineQueryCurrentChat button to inline keyboard
*
* @param {string} text - Text label for the button
* @param {string} [switchInlineQueryCurrentChat = ""] - If set, pressing
* the button will insert the bot's username and the specified inline
* query in the current chat's input field. Can be empty, in which
* case only the bot's username will be inserted.
* This offers a quick way for the user to open your bot in inline
* mode in the same chat – good for selecting something from multiple
* options
* @param {number} [row] - Row number to insert button (0-based). If
* not specified, the button will insert to the last row
* @param {number} [column] - Number of column of button to insert
* (0-based). If not specified, the button will insert to the first
* position of the row
*/
insertSwitchInlineQueryCurrentChatButton(
text,
switchInlineQueryCurrentChat = '',
row = 0,
column = 0
) {
if (this.inline_keyboard.length === 0) this.inline_keyboard.push([]);
if (row == -1) row = this.inline_keyboard.length - 1;
if (row < this.inline_keyboard.length) {
this.inline_keyboard[row].splice(column, 0, {
text: text,
switch_inline_query_current_chat: switchInlineQueryCurrentChat,
});
} else throw new ValidationError('Keyboard row does not exist');
}
/**
* Append a SwitchInlineQueryCurrentChat button to inline keyboard
*
* @param {string} text - Text label for the button
* @param {string} [switchInlineQueryCurrentChat = ""] - If set, pressing
* the button will insert the bot's username and the specified inline
* query in the current chat's input field. Can be empty, in which
* case only the bot's username will be inserted.
* This offers a quick way for the user to open your bot in inline
* mode in the same chat – good for selecting something from multiple
* options
* @param {number} [row] - Row number to append button (0-based). If
* not specified, the button will append to the last row
*/
appendSwitchInlineQueryCurrentChatButton(
text,
switchInlineQueryCurrentChat = '',
row = -1
) {
if (this.inline_keyboard.length === 0) this.inline_keyboard.push([]);
if (row == -1) row = this.inline_keyboard.length - 1;
if (row < this.inline_keyboard.length)
this.inline_keyboard[row].push({
text: text,
switch_inline_query_current_chat: switchInlineQueryCurrentChat,
});
else throw new ValidationError('Keyboard row does not exist');
}
/**
* Insert a LoginUrl button to inline keyboard
*
* @param {string} text - Text label for the button
* @param {string} url - An HTTP URL to be opened with user authorization
* data added to the query string when the button is pressed. If the user
* refuses to provide authorization data, the original URL without
* information about the user will be opened. The data added is the same
* as described in Receiving authorization data.
* NOTE: You must always check the hash of the received data to verify the
* authentication and the integrity of the data as described in Checking
* authorization
* @param {string} [forwardText] - New text of the button in forwarded
* messages
* @param {string} [botUsername] - Username of a bot, which will be used
* for user authorization. See Setting up a bot for more details. If not
* specified, the current bot's username will be assumed. The url's
* domain must be the same as the domain linked with the bot. See Linking
* your domain to the bot for more details
* @param {boolean} [requestWriteAccess] - Pass True to request the
* permission for your bot to send messages to the user
* @param {number} [row] - Row number to insert button (0-based). If
* not specified, the button will insert to the last row
* @param {number} [column] - Number of column of button to insert
* (0-based). If not specified, the button will insert to the first
* position of the row
*/
insertLoginUrlButton(
text,
url,
forwardText = '',
botUsername = '',
requestWriteAccess = false,
row = 0,
column = 0
) {
if (this.inline_keyboard.length === 0) this.inline_keyboard.push([]);
if (row == -1) row = this.inline_keyboard.length - 1;
if (row < this.inline_keyboard.length) {
let data = { url: url };
if (forwardText) data['forward_text'] = forwardText;
if (botUsername) data['bot_user_name'] = botUsername;
if (requestWriteAccess) data['request_write_access'] = requestWriteAccess;
this.inline_keyboard[row].splice(column, 0, {
text: text,
login_url: data,
});
} else throw new ValidationError('Keyboard row does not exist');
}
/**
* Append a LoginUrl button to inline keyboard
*
* @param {string} text - Text label for the button
* @param {string} url - An HTTP URL to be opened with user authorization
* data added to the query string when the button is pressed. If the user
* refuses to provide authorization data, the original URL without
* information about the user will be opened. The data added is the same
* as described in Receiving authorization data.
* NOTE: You must always check the hash of the received data to verify the
* authentication and the integrity of the data as described in Checking
* authorization
* @param {string} [forwardText] - New text of the button in forwarded
* messages
* @param {string} [botUsername] - Username of a bot, which will be used
* for user authorization. See Setting up a bot for more details. If not
* specified, the current bot's username will be assumed. The url's
* domain must be the same as the domain linked with the bot. See Linking
* your domain to the bot for more details
* @param {boolean} [requestWriteAccess] - Pass True to request the
* permission for your bot to send messages to the user
* @param {number} [row] - Row number to append button (0-based). If
* not specified, the button will append to the last row
*/
appendLoginUrlButton(
text,
url,
forwardText = '',
botUsername = '',
requestWriteAccess = false,
row = -1
) {
if (this.inline_keyboard.length === 0) this.inline_keyboard.push([]);
if (row == -1) row = this.inline_keyboard.length - 1;
if (row < this.inline_keyboard.length) {
let data = { url: url };
if (forwardText) data['forward_text'] = forwardText;
if (botUsername) data['bot_user_name'] = botUsername;
if (requestWriteAccess) data['request_write_access'] = requestWriteAccess;
this.inline_keyboard[row].push({
text: text,
login_url: data,
});
} else throw new ValidationError('Keyboard row does not exist');
}
deleteButtonWithText(text) {
if (!Array.isArray(this.inline_keyboard)) return;
for (let i = 0; i < this.inline_keyboard.length; i++) {
for (let j = 0; j < this.inline_keyboard[i].length; ) {
if (this.inline_keyboard[i][j].text === text) {
this.inline_keyboard[i].splice(j, 1);
} else {
j++;
}
}
if (this.inline_keyboard[i].length === 0) {
this.inline_keyboard.splice(i, 1);
i--;
}
}
}
deleteButtonWithCallbackData(callbackData) {
if (!Array.isArray(this.inline_keyboard)) return;
for (let i = 0; i < this.inline_keyboard.length; i++) {
for (let j = 0; j < this.inline_keyboard[i].length; ) {
if (this.inline_keyboard[i][j].callback_data === callbackData) {
this.inline_keyboard[i].splice(j, 1);
} else {
j++;
}
}
if (this.inline_keyboard[i].length === 0) {
this.inline_keyboard.splice(i, 1);
i--;
}
}
}
/**
* Insert a WebApp button to inline keyboard
*
* @param {string} text - Text label for the button
* @param {string} url - An HTTPS URL of a Web App to be opened with
* additional data as specified in Initializing Web Apps
* @param {number} [row] - Row number to insert button (0-based). If
* not specified, the button will insert to the last row
* @param {number} [column] - Number of column of button to insert
* (0-based). If not specified, the button will insert to the first
* position of the row
*/
insertWebAppButton(text, url, row = 0, column = 0) {
if (this.inline_keyboard.length === 0) this.inline_keyboard.push([]);
if (row === -1) row = this.inline_keyboard.length - 1;
if (row < this.inline_keyboard.length) {
this.inline_keyboard[row].splice(column, 0, {
text: text,
web_app: { url: url },
});
} else throw new ValidationError('Keyboard row does not exist');
}
/**
* Append an WebApp button to inline keyboard
*
* @param {string} text - Text label for the button
* @param {string} url - An HTTPS URL of a Web App to be opened with
* additional data as specified in Initializing Web Apps
* @param {number} [row] - Row number to append button (0-based). If
* not specified, the button will append to the last row
*/
appendWebAppButton(text, url, row = -1) {
if (this.inline_keyboard.length === 0) this.inline_keyboard.push([]);
if (row === -1) row = this.inline_keyboard.length - 1;
if (row < this.inline_keyboard.length)
this.inline_keyboard[row].push({ text: text, web_app: { url: url } });
else throw new ValidationError('Keyboard row does not exist');
}
/**
* Checks if the keyboard contains a button with the specified text.
*
* @param {string} text - The text to search for in the keyboard buttons
* @return {boolean} True if a button with the specified text is found, false otherwise
*/
hasButtonWithText(text) {
if (Array.isArray(this.inline_keyboard)) {
return this.inline_keyboard
.flat()
.some((item) => item && typeof item === 'object' && item.text === text);
}
return false;
}
}
export default InlineKeyboardMarkup;