Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
128 KB
Referenced Files
None
Subscribers
None
diff --git a/src/DB.php b/src/DB.php
index 0ce49d8..badadc3 100644
--- a/src/DB.php
+++ b/src/DB.php
@@ -1,1625 +1,1626 @@
<?php
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
* Written by Marco Boretto <marco.bore@gmail.com>
*/
namespace Longman\TelegramBot;
use Longman\TelegramBot\Entities\CallbackQuery;
use Longman\TelegramBot\Entities\Chat;
use Longman\TelegramBot\Entities\ChatJoinRequest;
use Longman\TelegramBot\Entities\ChatMemberUpdated;
use Longman\TelegramBot\Entities\ChosenInlineResult;
use Longman\TelegramBot\Entities\InlineQuery;
use Longman\TelegramBot\Entities\Message;
use Longman\TelegramBot\Entities\MessageReactionCountUpdated;
use Longman\TelegramBot\Entities\MessageReactionUpdated;
use Longman\TelegramBot\Entities\Payments\PreCheckoutQuery;
use Longman\TelegramBot\Entities\Payments\ShippingQuery;
use Longman\TelegramBot\Entities\Poll;
use Longman\TelegramBot\Entities\PollAnswer;
use Longman\TelegramBot\Entities\Update;
use Longman\TelegramBot\Entities\User;
use Longman\TelegramBot\Exception\TelegramException;
use PDO;
use PDOException;
class DB
{
/**
* MySQL credentials
*
* @var array
*/
protected static $mysql_credentials = [];
/**
* PDO object
*
* @var PDO
*/
protected static $pdo;
/**
* Table prefix
*
* @var string
*/
protected static $table_prefix;
/**
* Telegram class object
*
* @var Telegram
*/
protected static $telegram;
/**
* Initialize
*
* @param array $credentials Database connection details
* @param Telegram $telegram Telegram object to connect with this object
* @param string $table_prefix Table prefix
* @param string $encoding Database character encoding
*
* @return PDO PDO database object
* @throws TelegramException
*/
public static function initialize(
array $credentials,
Telegram $telegram,
$table_prefix = '',
$encoding = 'utf8mb4'
): PDO {
if (empty($credentials)) {
throw new TelegramException('MySQL credentials not provided!');
}
if (isset($credentials['unix_socket'])) {
$dsn = 'mysql:unix_socket=' . $credentials['unix_socket'];
} else {
$dsn = 'mysql:host=' . $credentials['host'];
}
$dsn .= ';dbname=' . $credentials['database'];
if (!empty($credentials['port'])) {
$dsn .= ';port=' . $credentials['port'];
}
$options = [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . $encoding];
try {
$pdo = new PDO($dsn, $credentials['user'], $credentials['password'], $options);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
self::$pdo = $pdo;
self::$telegram = $telegram;
self::$mysql_credentials = $credentials;
self::$table_prefix = $table_prefix;
self::defineTables();
return self::$pdo;
}
/**
* External Initialize
*
* Let you use the class with an external already existing Pdo Mysql connection.
*
* @param PDO $external_pdo_connection PDO database object
* @param Telegram $telegram Telegram object to connect with this object
* @param string $table_prefix Table prefix
*
* @return PDO PDO database object
* @throws TelegramException
*/
public static function externalInitialize(
PDO $external_pdo_connection,
Telegram $telegram,
string $table_prefix = ''
): PDO {
if ($external_pdo_connection === null) {
throw new TelegramException('MySQL external connection not provided!');
}
self::$pdo = $external_pdo_connection;
self::$telegram = $telegram;
self::$mysql_credentials = [];
self::$table_prefix = $table_prefix;
self::defineTables();
return self::$pdo;
}
/**
* Define all the tables with the proper prefix
*/
protected static function defineTables(): void
{
$tables = [
'callback_query',
'chat',
'chat_join_request',
'chat_member_updated',
'chosen_inline_result',
'edited_message',
'inline_query',
'message',
'message_reaction',
'message_reaction_count',
'poll',
'poll_answer',
'pre_checkout_query',
'request_limiter',
'shipping_query',
'telegram_update',
'user',
'user_chat',
];
foreach ($tables as $table) {
$table_name = 'TB_' . strtoupper($table);
if (!defined($table_name)) {
define($table_name, self::$table_prefix . $table);
}
}
}
/**
* Check if database connection has been created
*
* @return bool
*/
public static function isDbConnected(): bool
{
return self::$pdo !== null;
}
/**
* Get the PDO object of the connected database
*
* @return PDO|null
*/
public static function getPdo(): ?PDO
{
return self::$pdo;
}
/**
* Fetch update(s) from DB
*
* @param int $limit Limit the number of updates to fetch
* @param string $id Check for unique update id
*
* @return array|bool Fetched data or false if not connected
* @throws TelegramException
*/
public static function selectTelegramUpdate(int $limit = 0, string $id = '')
{
if (!self::isDbConnected()) {
return false;
}
try {
$sql = '
SELECT `id`
FROM `' . TB_TELEGRAM_UPDATE . '`
';
if ($id !== '') {
$sql .= ' WHERE `id` = :id';
} else {
$sql .= ' ORDER BY `id` DESC';
}
if ($limit > 0) {
$sql .= ' LIMIT :limit';
}
$sth = self::$pdo->prepare($sql);
if ($limit > 0) {
$sth->bindValue(':limit', $limit, PDO::PARAM_INT);
}
if ($id !== '') {
$sth->bindValue(':id', $id);
}
$sth->execute();
return $sth->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Fetch message(s) from DB
*
* @param int $limit Limit the number of messages to fetch
*
* @return array|bool Fetched data or false if not connected
* @throws TelegramException
*/
public static function selectMessages(int $limit = 0)
{
if (!self::isDbConnected()) {
return false;
}
try {
$sql = '
SELECT *
FROM `' . TB_MESSAGE . '`
ORDER BY `id` DESC
';
if ($limit > 0) {
$sql .= ' LIMIT :limit';
}
$sth = self::$pdo->prepare($sql);
if ($limit > 0) {
$sth->bindValue(':limit', $limit, PDO::PARAM_INT);
}
$sth->execute();
return $sth->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Convert from unix timestamp to timestamp
*
* @param ?int $unixtime Unix timestamp (if empty, current timestamp is used)
*
* @return string
*/
protected static function getTimestamp(?int $unixtime = null): string
{
return date('Y-m-d H:i:s', $unixtime ?? time());
}
/**
* Convert array of Entity items to a JSON array
*
* @todo Find a better way, as json_* functions are very heavy
*
* @param array $entities
* @param mixed $default
*
* @return mixed
*/
public static function entitiesArrayToJson(array $entities, $default = null)
{
if (empty($entities)) {
return $default;
}
// Convert each Entity item into an object based on its JSON reflection
$json_entities = array_map(function ($entity) {
return json_decode($entity, true);
}, $entities);
return json_encode($json_entities);
}
/**
* Insert entry to telegram_update table
*
* @throws TelegramException
*/
protected static function insertTelegramUpdate(
int $update_id,
?int $chat_id = null,
?int $message_id = null,
?int $edited_message_id = null,
?int $channel_post_id = null,
?int $edited_channel_post_id = null,
?string $message_reaction_id = null,
?string $message_reaction_count_id = null,
?string $inline_query_id = null,
?string $chosen_inline_result_id = null,
?string $callback_query_id = null,
?string $shipping_query_id = null,
?string $pre_checkout_query_id = null,
?string $poll_id = null,
?string $poll_answer_poll_id = null,
?string $my_chat_member_updated_id = null,
?string $chat_member_updated_id = null,
?string $chat_join_request_id = null
): ?bool {
if ($message_id === null && $edited_message_id === null && $channel_post_id === null && $edited_channel_post_id === null && $message_reaction_id === null && $message_reaction_count_id === null && $inline_query_id === null && $chosen_inline_result_id === null && $callback_query_id === null && $shipping_query_id === null && $pre_checkout_query_id === null && $poll_id === null && $poll_answer_poll_id === null && $my_chat_member_updated_id === null && $chat_member_updated_id === null && $chat_join_request_id === null) {
throw new TelegramException('message_id, edited_message_id, channel_post_id, edited_channel_post_id, message_reaction_id, message_reaction_count_id, inline_query_id, chosen_inline_result_id, callback_query_id, shipping_query_id, pre_checkout_query_id, poll_id, poll_answer_poll_id, my_chat_member_updated_id, chat_member_updated_id are all null');
}
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT IGNORE INTO `' . TB_TELEGRAM_UPDATE . '`
(
`id`, `chat_id`, `message_id`, `edited_message_id`,
`channel_post_id`, `edited_channel_post_id`, `message_reaction_id`, `message_reaction_count_id`,
`inline_query_id`, `chosen_inline_result_id`,
`callback_query_id`, `shipping_query_id`, `pre_checkout_query_id`,
`poll_id`, `poll_answer_poll_id`, `my_chat_member_updated_id`, `chat_member_updated_id`,
`chat_join_request_id`
) VALUES (
:id, :chat_id, :message_id, :edited_message_id,
:channel_post_id, :edited_channel_post_id, :message_reaction_id, :message_reaction_count_id,
:inline_query_id, :chosen_inline_result_id,
:callback_query_id, :shipping_query_id, :pre_checkout_query_id,
:poll_id, :poll_answer_poll_id, :my_chat_member_updated_id, :chat_member_updated_id,
:chat_join_request_id
)
');
$sth->bindValue(':id', $update_id);
$sth->bindValue(':chat_id', $chat_id);
$sth->bindValue(':message_id', $message_id);
$sth->bindValue(':edited_message_id', $edited_message_id);
$sth->bindValue(':channel_post_id', $channel_post_id);
$sth->bindValue(':edited_channel_post_id', $edited_channel_post_id);
$sth->bindValue(':message_reaction_id', $message_reaction_id);
$sth->bindValue(':message_reaction_count_id', $message_reaction_count_id);
$sth->bindValue(':inline_query_id', $inline_query_id);
$sth->bindValue(':chosen_inline_result_id', $chosen_inline_result_id);
$sth->bindValue(':callback_query_id', $callback_query_id);
$sth->bindValue(':shipping_query_id', $shipping_query_id);
$sth->bindValue(':pre_checkout_query_id', $pre_checkout_query_id);
$sth->bindValue(':poll_id', $poll_id);
$sth->bindValue(':poll_answer_poll_id', $poll_answer_poll_id);
$sth->bindValue(':my_chat_member_updated_id', $my_chat_member_updated_id);
$sth->bindValue(':chat_member_updated_id', $chat_member_updated_id);
$sth->bindValue(':chat_join_request_id', $chat_join_request_id);
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert users and save their connection to chats
*
* @param User $user
* @param string|null $date
* @param Chat|null $chat
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertUser(User $user, ?string $date = null, ?Chat $chat = null): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT INTO `' . TB_USER . '`
(`id`, `is_bot`, `username`, `first_name`, `last_name`, `language_code`, `is_premium`, `added_to_attachment_menu`, `created_at`, `updated_at`)
VALUES
(:id, :is_bot, :username, :first_name, :last_name, :language_code, :is_premium, :added_to_attachment_menu, :created_at, :updated_at)
ON DUPLICATE KEY UPDATE
`is_bot` = VALUES(`is_bot`),
`username` = VALUES(`username`),
`first_name` = VALUES(`first_name`),
`last_name` = VALUES(`last_name`),
`language_code` = VALUES(`language_code`),
`is_premium` = VALUES(`is_premium`),
`added_to_attachment_menu` = VALUES(`added_to_attachment_menu`),
`updated_at` = VALUES(`updated_at`)
');
$sth->bindValue(':id', $user->getId());
$sth->bindValue(':is_bot', $user->getIsBot(), PDO::PARAM_INT);
$sth->bindValue(':username', $user->getUsername());
$sth->bindValue(':first_name', $user->getFirstName());
$sth->bindValue(':last_name', $user->getLastName());
$sth->bindValue(':language_code', $user->getLanguageCode());
$sth->bindValue(':is_premium', $user->getIsPremium(), PDO::PARAM_INT);
$sth->bindValue(':added_to_attachment_menu', $user->getAddedToAttachmentMenu(), PDO::PARAM_INT);
$date = $date ?: self::getTimestamp();
$sth->bindValue(':created_at', $date);
$sth->bindValue(':updated_at', $date);
$status = $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
// Also insert the relationship to the chat into the user_chat table
if ($chat) {
try {
$sth = self::$pdo->prepare('
INSERT IGNORE INTO `' . TB_USER_CHAT . '`
(`user_id`, `chat_id`)
VALUES
(:user_id, :chat_id)
');
$sth->bindValue(':user_id', $user->getId());
$sth->bindValue(':chat_id', $chat->getId());
$status = $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
return $status;
}
/**
* Insert chat
*
* @param Chat $chat
* @param string|null $date
* @param int|null $migrate_to_chat_id
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertChat(Chat $chat, ?string $date = null, ?int $migrate_to_chat_id = null): ?bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT IGNORE INTO `' . TB_CHAT . '`
(`id`, `type`, `title`, `username`, `first_name`, `last_name`, `is_forum`, `created_at` ,`updated_at`, `old_id`)
VALUES
(:id, :type, :title, :username, :first_name, :last_name, :is_forum, :created_at, :updated_at, :old_id)
ON DUPLICATE KEY UPDATE
`type` = VALUES(`type`),
`title` = VALUES(`title`),
`username` = VALUES(`username`),
`first_name` = VALUES(`first_name`),
`last_name` = VALUES(`last_name`),
`is_forum` = VALUES(`is_forum`),
`updated_at` = VALUES(`updated_at`)
');
$chat_id = $chat->getId();
$chat_type = $chat->getType();
if ($migrate_to_chat_id !== null) {
$chat_type = 'supergroup';
$sth->bindValue(':id', $migrate_to_chat_id);
$sth->bindValue(':old_id', $chat_id);
} else {
$sth->bindValue(':id', $chat_id);
$sth->bindValue(':old_id', $migrate_to_chat_id);
}
$sth->bindValue(':type', $chat_type);
$sth->bindValue(':title', $chat->getTitle());
$sth->bindValue(':username', $chat->getUsername());
$sth->bindValue(':first_name', $chat->getFirstName());
$sth->bindValue(':last_name', $chat->getLastName());
$sth->bindValue(':is_forum', $chat->getIsForum());
$date = $date ?: self::getTimestamp();
$sth->bindValue(':created_at', $date);
$sth->bindValue(':updated_at', $date);
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert request into database
*
* @todo self::$pdo->lastInsertId() - unsafe usage if expected previous insert fails?
*
* @param Update $update
*
* @return bool
* @throws TelegramException
*/
public static function insertRequest(Update $update): bool
{
if (!self::isDbConnected()) {
return false;
}
$chat_id = null;
$message_id = null;
$edited_message_id = null;
$channel_post_id = null;
$edited_channel_post_id = null;
$message_reaction_id = null;
$message_reaction_count_id = null;
$inline_query_id = null;
$chosen_inline_result_id = null;
$callback_query_id = null;
$shipping_query_id = null;
$pre_checkout_query_id = null;
$poll_id = null;
$poll_answer_poll_id = null;
$my_chat_member_updated_id = null;
$chat_member_updated_id = null;
$chat_join_request_id = null;
if (($message = $update->getMessage()) && self::insertMessageRequest($message)) {
$chat_id = $message->getChat()->getId();
$message_id = $message->getMessageId();
} elseif (($edited_message = $update->getEditedMessage()) && self::insertEditedMessageRequest($edited_message)) {
$chat_id = $edited_message->getChat()->getId();
$edited_message_id = (int) self::$pdo->lastInsertId();
} elseif (($channel_post = $update->getChannelPost()) && self::insertMessageRequest($channel_post)) {
$chat_id = $channel_post->getChat()->getId();
$channel_post_id = $channel_post->getMessageId();
} elseif (($edited_channel_post = $update->getEditedChannelPost()) && self::insertEditedMessageRequest($edited_channel_post)) {
$chat_id = $edited_channel_post->getChat()->getId();
$edited_channel_post_id = (int) self::$pdo->lastInsertId();
} elseif (($message_reaction = $update->getMessageReaction()) && self::insertMessageReaction($message_reaction)) {
$chat_id = $message_reaction->getChat()->getId();
$message_id = $message_reaction->getMessageId();
$message_reaction_id = self::$pdo->lastInsertId();
} elseif (($message_reaction_count = $update->getMessageReactionCount()) && self::insertMessageReactionCount($message_reaction_count)) {
$chat_id = $message_reaction_count->getChat()->getId();
$message_id = $message_reaction_count->getMessageId();
$message_reaction_count_id = self::$pdo->lastInsertId();
} elseif (($inline_query = $update->getInlineQuery()) && self::insertInlineQueryRequest($inline_query)) {
$inline_query_id = $inline_query->getId();
} elseif (($chosen_inline_result = $update->getChosenInlineResult()) && self::insertChosenInlineResultRequest($chosen_inline_result)) {
$chosen_inline_result_id = self::$pdo->lastInsertId();
} elseif (($callback_query = $update->getCallbackQuery()) && self::insertCallbackQueryRequest($callback_query)) {
$callback_query_id = $callback_query->getId();
} elseif (($shipping_query = $update->getShippingQuery()) && self::insertShippingQueryRequest($shipping_query)) {
$shipping_query_id = $shipping_query->getId();
} elseif (($pre_checkout_query = $update->getPreCheckoutQuery()) && self::insertPreCheckoutQueryRequest($pre_checkout_query)) {
$pre_checkout_query_id = $pre_checkout_query->getId();
} elseif (($poll = $update->getPoll()) && self::insertPollRequest($poll)) {
$poll_id = $poll->getId();
} elseif (($poll_answer = $update->getPollAnswer()) && self::insertPollAnswerRequest($poll_answer)) {
$poll_answer_poll_id = $poll_answer->getPollId();
} elseif (($my_chat_member = $update->getMyChatMember()) && self::insertChatMemberUpdatedRequest($my_chat_member)) {
$my_chat_member_updated_id = self::$pdo->lastInsertId();
} elseif (($chat_member = $update->getChatMember()) && self::insertChatMemberUpdatedRequest($chat_member)) {
$chat_member_updated_id = self::$pdo->lastInsertId();
} elseif (($chat_join_request = $update->getChatJoinRequest()) && self::insertChatJoinRequestRequest($chat_join_request)) {
$chat_join_request_id = self::$pdo->lastInsertId();
} else {
return false;
}
return self::insertTelegramUpdate(
$update->getUpdateId(),
$chat_id,
$message_id,
$edited_message_id,
$channel_post_id,
$edited_channel_post_id,
$message_reaction_id,
$message_reaction_count_id,
$inline_query_id,
$chosen_inline_result_id,
$callback_query_id,
$shipping_query_id,
$pre_checkout_query_id,
$poll_id,
$poll_answer_poll_id,
$my_chat_member_updated_id,
$chat_member_updated_id,
$chat_join_request_id
);
}
public static function insertMessageReaction(MessageReactionUpdated $message_reaction): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT IGNORE INTO `' . TB_MESSAGE_REACTION . '`
(`chat_id`, `message_id`, `user_id`, `actor_chat_id`, `old_reaction`, `new_reaction`, `created_at`)
VALUES
(:chat_id, :message_id, :user_id, :actor_chat_id, :old_reaction, :new_reaction, :created_at)
');
$date = self::getTimestamp($message_reaction->getDate());
$chat_id = null;
$user_id = null;
$actor_chat_id = null;
if ($chat = $message_reaction->getChat()) {
$chat_id = $chat->getId();
self::insertChat($chat, $date);
}
if ($user = $message_reaction->getUser()) {
$user_id = $user->getId();
self::insertUser($user, $date);
}
if ($actor_chat = $message_reaction->getActorChat()) {
$actor_chat_id = $actor_chat->getId();
self::insertChat($actor_chat, $date);
}
$sth->bindValue(':chat_id', $chat_id);
$sth->bindValue(':message_id', $message_reaction->getMessageId());
$sth->bindValue(':user_id', $user_id);
$sth->bindValue(':actor_chat_id', $actor_chat_id);
$sth->bindValue(':old_reaction', self::entitiesArrayToJson($message_reaction->getOldReaction() ?: []));
$sth->bindValue(':new_reaction', self::entitiesArrayToJson($message_reaction->getNewReaction() ?: []));
$sth->bindValue(':created_at', $date);
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
public static function insertMessageReactionCount(MessageReactionCountUpdated $message_reaction_count): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT IGNORE INTO `' . TB_MESSAGE_REACTION_COUNT . '`
(`chat_id`, `message_id`, `reactions`, `created_at`)
VALUES
(:chat_id, :message_id, :reactions, :created_at)
');
$date = self::getTimestamp($message_reaction_count->getDate());
$chat_id = null;
if ($chat = $message_reaction->getChat()) {
$chat_id = $chat->getId();
self::insertChat($chat, $date);
}
$sth->bindValue(':chat_id', $chat_id);
$sth->bindValue(':message_id', $message_reaction_count->getMessageId());
$sth->bindValue(':reactions', $message_reaction_count->getReactions());
$sth->bindValue(':created_at', $date);
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert inline query request into database
*
* @param InlineQuery $inline_query
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertInlineQueryRequest(InlineQuery $inline_query): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT IGNORE INTO `' . TB_INLINE_QUERY . '`
(`id`, `user_id`, `location`, `query`, `offset`, `chat_type`, `created_at`)
VALUES
(:id, :user_id, :location, :query, :offset, :chat_type, :created_at)
');
$date = self::getTimestamp();
$user_id = null;
if ($user = $inline_query->getFrom()) {
$user_id = $user->getId();
self::insertUser($user, $date);
}
$sth->bindValue(':id', $inline_query->getId());
$sth->bindValue(':user_id', $user_id);
$sth->bindValue(':location', $inline_query->getLocation());
$sth->bindValue(':query', $inline_query->getQuery());
$sth->bindValue(':offset', $inline_query->getOffset());
$sth->bindValue(':chat_type', $inline_query->getChatType());
$sth->bindValue(':created_at', $date);
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert chosen inline result request into database
*
* @param ChosenInlineResult $chosen_inline_result
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertChosenInlineResultRequest(ChosenInlineResult $chosen_inline_result): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT INTO `' . TB_CHOSEN_INLINE_RESULT . '`
(`result_id`, `user_id`, `location`, `inline_message_id`, `query`, `created_at`)
VALUES
(:result_id, :user_id, :location, :inline_message_id, :query, :created_at)
');
$date = self::getTimestamp();
$user_id = null;
if ($user = $chosen_inline_result->getFrom()) {
$user_id = $user->getId();
self::insertUser($user, $date);
}
$sth->bindValue(':result_id', $chosen_inline_result->getResultId());
$sth->bindValue(':user_id', $user_id);
$sth->bindValue(':location', $chosen_inline_result->getLocation());
$sth->bindValue(':inline_message_id', $chosen_inline_result->getInlineMessageId());
$sth->bindValue(':query', $chosen_inline_result->getQuery());
$sth->bindValue(':created_at', $date);
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert callback query request into database
*
* @param CallbackQuery $callback_query
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertCallbackQueryRequest(CallbackQuery $callback_query): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT IGNORE INTO `' . TB_CALLBACK_QUERY . '`
(`id`, `user_id`, `chat_id`, `message_id`, `inline_message_id`, `chat_instance`, `data`, `game_short_name`, `created_at`)
VALUES
(:id, :user_id, :chat_id, :message_id, :inline_message_id, :chat_instance, :data, :game_short_name, :created_at)
');
$date = self::getTimestamp();
$user_id = null;
if ($user = $callback_query->getFrom()) {
$user_id = $user->getId();
self::insertUser($user, $date);
}
$chat_id = null;
$message_id = null;
if ($message = $callback_query->getMessage()) {
$chat_id = $message->getChat()->getId();
$message_id = $message->getMessageId();
$is_message = self::$pdo->query('
SELECT *
FROM `' . TB_MESSAGE . '`
WHERE `id` = ' . $message_id . '
AND `chat_id` = ' . $chat_id . '
LIMIT 1
')->rowCount();
if ($is_message) {
self::insertEditedMessageRequest($message);
} else {
self::insertMessageRequest($message);
}
}
$sth->bindValue(':id', $callback_query->getId());
$sth->bindValue(':user_id', $user_id);
$sth->bindValue(':chat_id', $chat_id);
$sth->bindValue(':message_id', $message_id);
$sth->bindValue(':inline_message_id', $callback_query->getInlineMessageId());
$sth->bindValue(':chat_instance', $callback_query->getChatInstance());
$sth->bindValue(':data', $callback_query->getData());
$sth->bindValue(':game_short_name', $callback_query->getGameShortName());
$sth->bindValue(':created_at', $date);
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert shipping query request into database
*
* @param ShippingQuery $shipping_query
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertShippingQueryRequest(ShippingQuery $shipping_query): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT IGNORE INTO `' . TB_SHIPPING_QUERY . '`
(`id`, `user_id`, `invoice_payload`, `shipping_address`, `created_at`)
VALUES
(:id, :user_id, :invoice_payload, :shipping_address, :created_at)
');
$date = self::getTimestamp();
$user_id = null;
if ($user = $shipping_query->getFrom()) {
$user_id = $user->getId();
self::insertUser($user, $date);
}
$sth->bindValue(':id', $shipping_query->getId());
$sth->bindValue(':user_id', $user_id);
$sth->bindValue(':invoice_payload', $shipping_query->getInvoicePayload());
$sth->bindValue(':shipping_address', $shipping_query->getShippingAddress());
$sth->bindValue(':created_at', $date);
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert pre checkout query request into database
*
* @param PreCheckoutQuery $pre_checkout_query
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertPreCheckoutQueryRequest(PreCheckoutQuery $pre_checkout_query): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT IGNORE INTO `' . TB_PRE_CHECKOUT_QUERY . '`
(`id`, `user_id`, `currency`, `total_amount`, `invoice_payload`, `shipping_option_id`, `order_info`, `created_at`)
VALUES
(:id, :user_id, :currency, :total_amount, :invoice_payload, :shipping_option_id, :order_info, :created_at)
');
$date = self::getTimestamp();
$user_id = null;
if ($user = $pre_checkout_query->getFrom()) {
$user_id = $user->getId();
self::insertUser($user, $date);
}
$sth->bindValue(':id', $pre_checkout_query->getId());
$sth->bindValue(':user_id', $user_id);
$sth->bindValue(':currency', $pre_checkout_query->getCurrency());
$sth->bindValue(':total_amount', $pre_checkout_query->getTotalAmount());
$sth->bindValue(':invoice_payload', $pre_checkout_query->getInvoicePayload());
$sth->bindValue(':shipping_option_id', $pre_checkout_query->getShippingOptionId());
$sth->bindValue(':order_info', $pre_checkout_query->getOrderInfo());
$sth->bindValue(':created_at', $date);
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert poll request into database
*
* @param Poll $poll
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertPollRequest(Poll $poll): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT INTO `' . TB_POLL . '`
(`id`, `question`, `options`, `total_voter_count`, `is_closed`, `is_anonymous`, `type`, `allows_multiple_answers`, `correct_option_id`, `explanation`, `explanation_entities`, `open_period`, `close_date`, `created_at`)
VALUES
(:id, :question, :options, :total_voter_count, :is_closed, :is_anonymous, :type, :allows_multiple_answers, :correct_option_id, :explanation, :explanation_entities, :open_period, :close_date, :created_at)
ON DUPLICATE KEY UPDATE
`options` = VALUES(`options`),
`total_voter_count` = VALUES(`total_voter_count`),
`is_closed` = VALUES(`is_closed`),
`is_anonymous` = VALUES(`is_anonymous`),
`type` = VALUES(`type`),
`allows_multiple_answers` = VALUES(`allows_multiple_answers`),
`correct_option_id` = VALUES(`correct_option_id`),
`explanation` = VALUES(`explanation`),
`explanation_entities` = VALUES(`explanation_entities`),
`open_period` = VALUES(`open_period`),
`close_date` = VALUES(`close_date`)
');
$sth->bindValue(':id', $poll->getId());
$sth->bindValue(':question', $poll->getQuestion());
$sth->bindValue(':options', self::entitiesArrayToJson($poll->getOptions() ?: []));
$sth->bindValue(':total_voter_count', $poll->getTotalVoterCount());
$sth->bindValue(':is_closed', $poll->getIsClosed(), PDO::PARAM_INT);
$sth->bindValue(':is_anonymous', $poll->getIsAnonymous(), PDO::PARAM_INT);
$sth->bindValue(':type', $poll->getType());
$sth->bindValue(':allows_multiple_answers', $poll->getAllowsMultipleAnswers(), PDO::PARAM_INT);
$sth->bindValue(':correct_option_id', $poll->getCorrectOptionId());
$sth->bindValue(':explanation', $poll->getExplanation());
$sth->bindValue(':explanation_entities', self::entitiesArrayToJson($poll->getExplanationEntities() ?: []));
$sth->bindValue(':open_period', $poll->getOpenPeriod());
$sth->bindValue(':close_date', self::getTimestamp($poll->getCloseDate()));
$sth->bindValue(':created_at', self::getTimestamp());
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert poll answer request into database
*
* @param PollAnswer $poll_answer
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertPollAnswerRequest(PollAnswer $poll_answer): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT INTO `' . TB_POLL_ANSWER . '`
(`poll_id`, `user_id`, `option_ids`, `created_at`)
VALUES
(:poll_id, :user_id, :option_ids, :created_at)
ON DUPLICATE KEY UPDATE
`option_ids` = VALUES(`option_ids`)
');
$date = self::getTimestamp();
$user_id = null;
if ($user = $poll_answer->getUser()) {
$user_id = $user->getId();
self::insertUser($user, $date);
}
$sth->bindValue(':poll_id', $poll_answer->getPollId());
$sth->bindValue(':user_id', $user_id);
$sth->bindValue(':option_ids', json_encode($poll_answer->getOptionIds()));
$sth->bindValue(':created_at', $date);
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert chat member updated request into database
*
* @param ChatMemberUpdated $chat_member_updated
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertChatMemberUpdatedRequest(ChatMemberUpdated $chat_member_updated): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT INTO `' . TB_CHAT_MEMBER_UPDATED . '`
(`chat_id`, `user_id`, `date`, `old_chat_member`, `new_chat_member`, `invite_link`, `created_at`)
VALUES
(:chat_id, :user_id, :date, :old_chat_member, :new_chat_member, :invite_link, :created_at)
');
$date = self::getTimestamp();
$chat_id = null;
$user_id = null;
if ($chat = $chat_member_updated->getChat()) {
$chat_id = $chat->getId();
self::insertChat($chat, $date);
}
if ($user = $chat_member_updated->getFrom()) {
$user_id = $user->getId();
self::insertUser($user, $date);
}
$sth->bindValue(':chat_id', $chat_id);
$sth->bindValue(':user_id', $user_id);
$sth->bindValue(':date', self::getTimestamp($chat_member_updated->getDate()));
$sth->bindValue(':old_chat_member', $chat_member_updated->getOldChatMember());
$sth->bindValue(':new_chat_member', $chat_member_updated->getNewChatMember());
$sth->bindValue(':invite_link', $chat_member_updated->getInviteLink());
$sth->bindValue(':created_at', $date);
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert chat join request into database
*
* @param ChatJoinRequest $chat_join_request
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertChatJoinRequestRequest(ChatJoinRequest $chat_join_request): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('
INSERT INTO `' . TB_CHAT_JOIN_REQUEST . '`
(`chat_id`, `user_id`, `date`, `bio`, `invite_link`, `created_at`)
VALUES
(:chat_id, :user_id, :date, :bio, :invite_link, :created_at)
');
$date = self::getTimestamp();
$chat_id = null;
$user_id = null;
if ($chat = $chat_join_request->getChat()) {
$chat_id = $chat->getId();
self::insertChat($chat, $date);
}
if ($user = $chat_join_request->getFrom()) {
$user_id = $user->getId();
self::insertUser($user, $date);
}
$sth->bindValue(':chat_id', $chat_id);
$sth->bindValue(':user_id', $user_id);
$sth->bindValue(':date', self::getTimestamp($chat_join_request->getDate()));
$sth->bindValue(':bio', $chat_join_request->getBio());
$sth->bindValue(':invite_link', $chat_join_request->getInviteLink());
$sth->bindValue(':created_at', $date);
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert Message request in db
*
* @param Message $message
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertMessageRequest(Message $message): bool
{
if (!self::isDbConnected()) {
return false;
}
$date = self::getTimestamp($message->getDate());
// Insert chat, update chat id in case it migrated
$chat = $message->getChat();
self::insertChat($chat, $date, $message->getMigrateToChatId());
$sender_chat_id = null;
if ($sender_chat = $message->getSenderChat()) {
self::insertChat($sender_chat);
$sender_chat_id = $sender_chat->getId();
}
// Insert user and the relation with the chat
if ($user = $message->getFrom()) {
self::insertUser($user, $date, $chat);
}
// Insert the forwarded message user in users table
$forward_date = $message->getForwardDate() ? self::getTimestamp($message->getForwardDate()) : null;
if ($forward_from = $message->getForwardFrom()) {
self::insertUser($forward_from);
$forward_from = $forward_from->getId();
}
if ($forward_from_chat = $message->getForwardFromChat()) {
self::insertChat($forward_from_chat);
$forward_from_chat = $forward_from_chat->getId();
}
$via_bot_id = null;
if ($via_bot = $message->getViaBot()) {
self::insertUser($via_bot);
$via_bot_id = $via_bot->getId();
}
// New and left chat member
$new_chat_members_ids = null;
$left_chat_member_id = null;
$new_chat_members = $message->getNewChatMembers();
$left_chat_member = $message->getLeftChatMember();
if (!empty($new_chat_members)) {
foreach ($new_chat_members as $new_chat_member) {
if ($new_chat_member instanceof User) {
// Insert the new chat user
self::insertUser($new_chat_member, $date, $chat);
$new_chat_members_ids[] = $new_chat_member->getId();
}
}
$new_chat_members_ids = implode(',', $new_chat_members_ids);
} elseif ($left_chat_member) {
// Insert the left chat user
self::insertUser($left_chat_member, $date, $chat);
$left_chat_member_id = $left_chat_member->getId();
}
try {
$sth = self::$pdo->prepare('
INSERT IGNORE INTO `' . TB_MESSAGE . '`
(
`id`, `user_id`, `chat_id`, `message_thread_id`, `sender_chat_id`, `date`, `forward_from`, `forward_from_chat`, `forward_from_message_id`,
`forward_signature`, `forward_sender_name`, `forward_date`, `is_topic_message`,
- `reply_to_chat`, `reply_to_message`, `via_bot`, `edit_date`, `media_group_id`, `author_signature`, `text`, `entities`, `caption_entities`,
+ `reply_to_chat`, `reply_to_message`, `external_reply`, `via_bot`, `edit_date`, `media_group_id`, `author_signature`, `text`, `entities`, `caption_entities`,
`audio`, `document`, `animation`, `game`, `photo`, `sticker`, `story`, `video`, `voice`, `video_note`, `caption`, `has_media_spoiler`, `contact`,
`location`, `venue`, `poll`, `dice`, `new_chat_members`, `left_chat_member`,
`new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`,
`supergroup_chat_created`, `channel_chat_created`, `message_auto_delete_timer_changed`, `migrate_to_chat_id`, `migrate_from_chat_id`,
`pinned_message`, `invoice`, `successful_payment`, `user_shared`, `chat_shared`, `connected_website`, `write_access_allowed`, `passport_data`, `proximity_alert_triggered`,
`forum_topic_created`, `forum_topic_edited`, `forum_topic_closed`, `forum_topic_reopened`, `general_forum_topic_hidden`, `general_forum_topic_unhidden`,
`video_chat_scheduled`, `video_chat_started`, `video_chat_ended`, `video_chat_participants_invited`, `web_app_data`, `reply_markup`
) VALUES (
:message_id, :user_id, :chat_id, :message_thread_id, :sender_chat_id, :date, :forward_from, :forward_from_chat, :forward_from_message_id,
:forward_signature, :forward_sender_name, :forward_date, :is_topic_message,
- :reply_to_chat, :reply_to_message, :via_bot, :edit_date, :media_group_id, :author_signature, :text, :entities, :caption_entities,
+ :reply_to_chat, :reply_to_message, :external_reply, :via_bot, :edit_date, :media_group_id, :author_signature, :text, :entities, :caption_entities,
:audio, :document, :animation, :game, :photo, :sticker, :story, :video, :voice, :video_note, :caption, :has_media_spoiler, :contact,
:location, :venue, :poll, :dice, :new_chat_members, :left_chat_member,
:new_chat_title, :new_chat_photo, :delete_chat_photo, :group_chat_created,
:supergroup_chat_created, :channel_chat_created, :message_auto_delete_timer_changed, :migrate_to_chat_id, :migrate_from_chat_id,
:pinned_message, :invoice, :successful_payment, :user_shared, :chat_shared, :connected_website, :write_access_allowed, :passport_data, :proximity_alert_triggered,
:forum_topic_created, :forum_topic_edited, :forum_topic_closed, :forum_topic_reopened, :general_forum_topic_hidden, :general_forum_topic_unhidden,
:video_chat_scheduled, :video_chat_started, :video_chat_ended, :video_chat_participants_invited, :web_app_data, :reply_markup
)
');
$user_id = $user ? $user->getId() : null;
$chat_id = $chat->getId();
$reply_to_message_id = null;
if ($reply_to_message = $message->getReplyToMessage()) {
$reply_to_message_id = $reply_to_message->getMessageId();
// please notice that, as explained in the documentation, reply_to_message don't contain other
// reply_to_message field so recursion deep is 1
self::insertMessageRequest($reply_to_message);
}
$sth->bindValue(':message_id', $message->getMessageId());
$sth->bindValue(':chat_id', $chat_id);
$sth->bindValue(':sender_chat_id', $sender_chat_id);
$sth->bindValue(':message_thread_id', $message->getMessageThreadId());
$sth->bindValue(':user_id', $user_id);
$sth->bindValue(':date', $date);
$sth->bindValue(':forward_from', $forward_from);
$sth->bindValue(':forward_from_chat', $forward_from_chat);
$sth->bindValue(':forward_from_message_id', $message->getForwardFromMessageId());
$sth->bindValue(':forward_signature', $message->getForwardSignature());
$sth->bindValue(':forward_sender_name', $message->getForwardSenderName());
$sth->bindValue(':forward_date', $forward_date);
$sth->bindValue(':is_topic_message', $message->getIsTopicMessage());
$reply_to_chat_id = null;
if ($reply_to_message_id !== null) {
$reply_to_chat_id = $chat_id;
}
$sth->bindValue(':reply_to_chat', $reply_to_chat_id);
$sth->bindValue(':reply_to_message', $reply_to_message_id);
+ $sth->bindValue(':external_reply', $message->getExternalReply());
$sth->bindValue(':via_bot', $via_bot_id);
$sth->bindValue(':edit_date', self::getTimestamp($message->getEditDate()));
$sth->bindValue(':media_group_id', $message->getMediaGroupId());
$sth->bindValue(':author_signature', $message->getAuthorSignature());
$sth->bindValue(':text', $message->getText());
$sth->bindValue(':entities', self::entitiesArrayToJson($message->getEntities() ?: []));
$sth->bindValue(':caption_entities', self::entitiesArrayToJson($message->getCaptionEntities() ?: []));
$sth->bindValue(':audio', $message->getAudio());
$sth->bindValue(':document', $message->getDocument());
$sth->bindValue(':animation', $message->getAnimation());
$sth->bindValue(':game', $message->getGame());
$sth->bindValue(':photo', self::entitiesArrayToJson($message->getPhoto() ?: []));
$sth->bindValue(':sticker', $message->getSticker());
$sth->bindValue(':story', $message->getStory());
$sth->bindValue(':video', $message->getVideo());
$sth->bindValue(':voice', $message->getVoice());
$sth->bindValue(':video_note', $message->getVideoNote());
$sth->bindValue(':caption', $message->getCaption());
$sth->bindValue(':has_media_spoiler', $message->getHasMediaSpoiler());
$sth->bindValue(':contact', $message->getContact());
$sth->bindValue(':location', $message->getLocation());
$sth->bindValue(':venue', $message->getVenue());
$sth->bindValue(':poll', $message->getPoll());
$sth->bindValue(':dice', $message->getDice());
$sth->bindValue(':new_chat_members', $new_chat_members_ids);
$sth->bindValue(':left_chat_member', $left_chat_member_id);
$sth->bindValue(':new_chat_title', $message->getNewChatTitle());
$sth->bindValue(':new_chat_photo', self::entitiesArrayToJson($message->getNewChatPhoto() ?: []));
$sth->bindValue(':delete_chat_photo', $message->getDeleteChatPhoto());
$sth->bindValue(':group_chat_created', $message->getGroupChatCreated());
$sth->bindValue(':supergroup_chat_created', $message->getSupergroupChatCreated());
$sth->bindValue(':channel_chat_created', $message->getChannelChatCreated());
$sth->bindValue(':message_auto_delete_timer_changed', $message->getMessageAutoDeleteTimerChanged());
$sth->bindValue(':migrate_to_chat_id', $message->getMigrateToChatId());
$sth->bindValue(':migrate_from_chat_id', $message->getMigrateFromChatId());
$sth->bindValue(':pinned_message', $message->getPinnedMessage());
$sth->bindValue(':invoice', $message->getInvoice());
$sth->bindValue(':successful_payment', $message->getSuccessfulPayment());
$sth->bindValue(':user_shared', $message->getUserShared());
$sth->bindValue(':chat_shared', $message->getChatShared());
$sth->bindValue(':connected_website', $message->getConnectedWebsite());
$sth->bindValue(':write_access_allowed', $message->getWriteAccessAllowed());
$sth->bindValue(':passport_data', $message->getPassportData());
$sth->bindValue(':proximity_alert_triggered', $message->getProximityAlertTriggered());
$sth->bindValue(':forum_topic_created', $message->getForumTopicCreated());
$sth->bindValue(':forum_topic_edited', $message->getForumTopicEdited());
$sth->bindValue(':forum_topic_closed', $message->getForumTopicClosed());
$sth->bindValue(':forum_topic_reopened', $message->getForumTopicReopened());
$sth->bindValue(':general_forum_topic_hidden', $message->getGeneralForumTopicHidden());
$sth->bindValue(':general_forum_topic_unhidden', $message->getGeneralForumTopicUnhidden());
$sth->bindValue(':video_chat_scheduled', $message->getVideoChatScheduled());
$sth->bindValue(':video_chat_started', $message->getVideoChatStarted());
$sth->bindValue(':video_chat_ended', $message->getVideoChatEnded());
$sth->bindValue(':video_chat_participants_invited', $message->getVideoChatParticipantsInvited());
$sth->bindValue(':web_app_data', $message->getWebAppData());
$sth->bindValue(':reply_markup', $message->getReplyMarkup());
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert Edited Message request in db
*
* @param Message $edited_message
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertEditedMessageRequest(Message $edited_message): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$edit_date = self::getTimestamp($edited_message->getEditDate());
// Insert chat
$chat = $edited_message->getChat();
self::insertChat($chat, $edit_date);
// Insert user and the relation with the chat
if ($user = $edited_message->getFrom()) {
self::insertUser($user, $edit_date, $chat);
}
$sth = self::$pdo->prepare('
INSERT IGNORE INTO `' . TB_EDITED_MESSAGE . '`
(`chat_id`, `message_id`, `user_id`, `edit_date`, `text`, `entities`, `caption`)
VALUES
(:chat_id, :message_id, :user_id, :edit_date, :text, :entities, :caption)
');
$user_id = $user ? $user->getId() : null;
$sth->bindValue(':chat_id', $chat->getId());
$sth->bindValue(':message_id', $edited_message->getMessageId());
$sth->bindValue(':user_id', $user_id);
$sth->bindValue(':edit_date', $edit_date);
$sth->bindValue(':text', $edited_message->getText());
$sth->bindValue(':entities', self::entitiesArrayToJson($edited_message->getEntities() ?: []));
$sth->bindValue(':caption', $edited_message->getCaption());
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Select Groups, Supergroups, Channels and/or single user Chats (also by ID or text)
*
* @param $select_chats_params
*
* @return array|bool
* @throws TelegramException
*/
public static function selectChats($select_chats_params)
{
if (!self::isDbConnected()) {
return false;
}
// Set defaults for omitted values.
$select = array_merge([
'groups' => true,
'supergroups' => true,
'channels' => true,
'users' => true,
'date_from' => null,
'date_to' => null,
'chat_id' => null,
'text' => null,
'language' => null,
], $select_chats_params);
if (!$select['groups'] && !$select['users'] && !$select['supergroups'] && !$select['channels']) {
return false;
}
try {
$query = '
SELECT * ,
' . TB_CHAT . '.`id` AS `chat_id`,
' . TB_CHAT . '.`username` AS `chat_username`,
' . TB_CHAT . '.`created_at` AS `chat_created_at`,
' . TB_CHAT . '.`updated_at` AS `chat_updated_at`
';
if ($select['users']) {
$query .= '
, ' . TB_USER . '.`id` AS `user_id`
FROM `' . TB_CHAT . '`
LEFT JOIN `' . TB_USER . '`
ON ' . TB_CHAT . '.`id`=' . TB_USER . '.`id`
';
} else {
$query .= 'FROM `' . TB_CHAT . '`';
}
// Building parts of query
$where = [];
$tokens = [];
if (!$select['groups'] || !$select['users'] || !$select['supergroups'] || !$select['channels']) {
$chat_or_user = [];
$select['groups'] && $chat_or_user[] = TB_CHAT . '.`type` = "group"';
$select['supergroups'] && $chat_or_user[] = TB_CHAT . '.`type` = "supergroup"';
$select['channels'] && $chat_or_user[] = TB_CHAT . '.`type` = "channel"';
$select['users'] && $chat_or_user[] = TB_CHAT . '.`type` = "private"';
$where[] = '(' . implode(' OR ', $chat_or_user) . ')';
}
if (null !== $select['date_from']) {
$where[] = TB_CHAT . '.`updated_at` >= :date_from';
$tokens[':date_from'] = $select['date_from'];
}
if (null !== $select['date_to']) {
$where[] = TB_CHAT . '.`updated_at` <= :date_to';
$tokens[':date_to'] = $select['date_to'];
}
if (null !== $select['chat_id']) {
$where[] = TB_CHAT . '.`id` = :chat_id';
$tokens[':chat_id'] = $select['chat_id'];
}
if ($select['users'] && null !== $select['language']) {
$where[] = TB_USER . '.`language_code` = :language';
$tokens[':language'] = $select['language'];
}
if (null !== $select['text']) {
$text_like = '%' . strtolower($select['text']) . '%';
if ($select['users']) {
$where[] = '(
LOWER(' . TB_CHAT . '.`title`) LIKE :text1
OR LOWER(' . TB_USER . '.`first_name`) LIKE :text2
OR LOWER(' . TB_USER . '.`last_name`) LIKE :text3
OR LOWER(' . TB_USER . '.`username`) LIKE :text4
)';
$tokens[':text1'] = $text_like;
$tokens[':text2'] = $text_like;
$tokens[':text3'] = $text_like;
$tokens[':text4'] = $text_like;
} else {
$where[] = 'LOWER(' . TB_CHAT . '.`title`) LIKE :text';
$tokens[':text'] = $text_like;
}
}
if (!empty($where)) {
$query .= ' WHERE ' . implode(' AND ', $where);
}
$query .= ' ORDER BY ' . TB_CHAT . '.`updated_at` ASC';
$sth = self::$pdo->prepare($query);
$sth->execute($tokens);
return $sth->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Get Telegram API request count for current chat / message
*
* @param int|string|null $chat_id
* @param string|null $inline_message_id
*
* @return array|bool Array containing TOTAL and CURRENT fields or false on invalid arguments
* @throws TelegramException
*/
public static function getTelegramRequestCount($chat_id = null, string $inline_message_id = null)
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('SELECT
(SELECT COUNT(DISTINCT `chat_id`) FROM `' . TB_REQUEST_LIMITER . '` WHERE `created_at` >= :created_at_1) AS LIMIT_PER_SEC_ALL,
(SELECT COUNT(*) FROM `' . TB_REQUEST_LIMITER . '` WHERE `created_at` >= :created_at_2 AND ((`chat_id` = :chat_id_1 AND `inline_message_id` IS NULL) OR (`inline_message_id` = :inline_message_id AND `chat_id` IS NULL))) AS LIMIT_PER_SEC,
(SELECT COUNT(*) FROM `' . TB_REQUEST_LIMITER . '` WHERE `created_at` >= :created_at_minute AND `chat_id` = :chat_id_2) AS LIMIT_PER_MINUTE
');
$date = self::getTimestamp();
$date_minute = self::getTimestamp(strtotime('-1 minute'));
$sth->bindValue(':chat_id_1', $chat_id);
$sth->bindValue(':chat_id_2', $chat_id);
$sth->bindValue(':inline_message_id', $inline_message_id);
$sth->bindValue(':created_at_1', $date);
$sth->bindValue(':created_at_2', $date);
$sth->bindValue(':created_at_minute', $date_minute);
$sth->execute();
return $sth->fetch();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Insert Telegram API request in db
*
* @param string $method
* @param array $data
*
* @return bool If the insert was successful
* @throws TelegramException
*/
public static function insertTelegramRequest(string $method, array $data): bool
{
if (!self::isDbConnected()) {
return false;
}
try {
$sth = self::$pdo->prepare('INSERT INTO `' . TB_REQUEST_LIMITER . '`
(`method`, `chat_id`, `inline_message_id`, `created_at`)
VALUES
(:method, :chat_id, :inline_message_id, :created_at);
');
$chat_id = $data['chat_id'] ?? null;
$inline_message_id = $data['inline_message_id'] ?? null;
$sth->bindValue(':chat_id', $chat_id);
$sth->bindValue(':inline_message_id', $inline_message_id);
$sth->bindValue(':method', $method);
$sth->bindValue(':created_at', self::getTimestamp());
return $sth->execute();
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
/**
* Bulk update the entries of any table
*
* @param string $table
* @param array $fields_values
* @param array $where_fields_values
*
* @return bool
* @throws TelegramException
*/
public static function update(string $table, array $fields_values, array $where_fields_values): bool
{
if (empty($fields_values) || !self::isDbConnected()) {
return false;
}
try {
// Building parts of query
$tokens = $fields = $where = [];
// Fields with values to update
foreach ($fields_values as $field => $value) {
$token = ':' . count($tokens);
$fields[] = "`{$field}` = {$token}";
$tokens[$token] = $value;
}
// Where conditions
foreach ($where_fields_values as $field => $value) {
$token = ':' . count($tokens);
$where[] = "`{$field}` = {$token}";
$tokens[$token] = $value;
}
$sql = 'UPDATE `' . $table . '` SET ' . implode(', ', $fields);
$sql .= count($where) > 0 ? ' WHERE ' . implode(' AND ', $where) : '';
return self::$pdo->prepare($sql)->execute($tokens);
} catch (PDOException $e) {
throw new TelegramException($e->getMessage());
}
}
}
diff --git a/src/Entities/ExternalReplyInfo.php b/src/Entities/ExternalReplyInfo.php
new file mode 100644
index 0000000..e879973
--- /dev/null
+++ b/src/Entities/ExternalReplyInfo.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * This file is part of the TelegramBot package.
+ *
+ * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Longman\TelegramBot\Entities;
+
+/**
+ * This object contains information about a message that is being replied to, which may come from another chat or forum topic.
+ *
+ * @link https://core.telegram.org/bots/api#externalreplyinfo
+ *
+ * @method MessageOrigin getOrigin() Origin of the message replied to by the given message
+ * @method Chat getChat() Optional. Chat the original message belongs to. Available only if the chat is a supergroup or a channel.
+ * @method int getMessageId() Optional. Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel.
+ * @method LinkPreviewOptions getLinkPreviewOptions() Optional. Options used for link preview generation for the original message, if it is a text message
+ * @method Animation getAnimation() Optional. Message is an animation, information about the animation
+ * @method Audio getAudio() Optional. Message is an audio file, information about the file
+ * @method Document getDocument() Optional. Message is a general file, information about the file
+ * @method PhotoSize[] getPhoto() Optional. Message is a photo, available sizes of the photo
+ * @method Sticker getSticker() Optional. Message is a sticker, information about the sticker
+ * @method Story getStory() Optional. Message is a forwarded story
+ * @method Video getVideo() Optional. Message is a video, information about the video
+ * @method VideoNote getVideoNote() Optional. Message is a video note, information about the video message
+ * @method Voice getVoice() Optional. Message is a voice message, information about the file
+ * @method bool getHasMediaSpoiler() Optional. True, if the message media is covered by a spoiler animation
+ * @method Contact getContact() Optional. Message is a shared contact, information about the contact
+ * @method Dice getDice() Optional. Message is a dice with random value
+ * @method Game getGame() Optional. Message is a game, information about the game. More about games »
+ * @method Giveaway getGiveaway() Optional. Message is a scheduled giveaway, information about the giveaway
+ * @method GiveawayWinners getGiveawayWinners() Optional. A giveaway with public winners was completed
+ * @method Invoice getInvoice() Optional. Message is an invoice for a payment, information about the invoice. More about payments »
+ * @method Location getLocation() Optional. Message is a shared location, information about the location
+ * @method Poll getPoll() Optional. Message is a native poll, information about the poll
+ * @method Venue getVenue() Optional. Message is a venue, information about the venue
+ */
+class ExternalReplyInfo extends Entity
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function subEntities(): array
+ {
+ return [
+ 'origin' => MessageOrigin::class,
+ 'chat' => Chat::class,
+ 'link_preview_options' => LinkPreviewOptions::class,
+ 'animation' => Animation::class,
+ 'audio' => Audio::class,
+ 'document' => Document::class,
+ 'photo' => [PhotoSize::class],
+ 'sticker' => Sticker::class,
+ 'story' => Story::class,
+ 'video' => Video::class,
+ 'video_note' => VideoNote::class,
+ 'voice' => Voice::class,
+ 'contact' => Contact::class,
+ 'dice' => Dice::class,
+ 'game' => Game::class,
+ 'giveaway' => Giveaway::class,
+ 'giveaway_winners' => GiveawayWinners::class,
+ 'invoice' => Invoice::class,
+ 'location' => Location::class,
+ 'poll' => Poll::class,
+ 'venue' => Venue::class,
+ ];
+ }
+}
diff --git a/src/Entities/Message.php b/src/Entities/Message.php
index 58f36b4..bdf0f98 100644
--- a/src/Entities/Message.php
+++ b/src/Entities/Message.php
@@ -1,321 +1,325 @@
<?php
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Longman\TelegramBot\Entities;
use Longman\TelegramBot\Entities\Games\Game;
use Longman\TelegramBot\Entities\Payments\Invoice;
use Longman\TelegramBot\Entities\Payments\SuccessfulPayment;
use Longman\TelegramBot\Entities\TelegramPassport\PassportData;
use Longman\TelegramBot\Entities\Topics\ForumTopicClosed;
use Longman\TelegramBot\Entities\Topics\ForumTopicCreated;
use Longman\TelegramBot\Entities\Topics\ForumTopicEdited;
use Longman\TelegramBot\Entities\Topics\ForumTopicReopened;
use Longman\TelegramBot\Entities\Topics\GeneralForumTopicHidden;
use Longman\TelegramBot\Entities\Topics\GeneralForumTopicUnhidden;
/**
* Class Message
*
* Represents a message
*
* @link https://core.telegram.org/bots/api#message
*
* @method int getMessageId() Unique message identifier
* @method int getMessageThreadId() Optional. Unique identifier of a message thread to which the message belongs; for supergroups only
* @method User getFrom() Optional. Sender, can be empty for messages sent to channels
* @method Chat getSenderChat() Optional. Sender of the message, sent on behalf of a chat. The channel itself for channel messages. The supergroup itself for messages from anonymous group administrators. The linked channel for messages automatically forwarded to the discussion group
* @method int getDate() Date the message was sent in Unix time
* @method Chat getChat() Conversation the message belongs to
* @method User getForwardFrom() Optional. For forwarded messages, sender of the original message
* @method Chat getForwardFromChat() Optional. For messages forwarded from a channel, information about the original channel
* @method int getForwardFromMessageId() Optional. For forwarded channel posts, identifier of the original message in the channel
* @method string getForwardSignature() Optional. For messages forwarded from channels, signature of the post author if present
* @method string getForwardSenderName() Optional. Sender's name for messages forwarded from users who disallow adding a link to their account in forwarded messages
* @method int getForwardDate() Optional. For forwarded messages, date the original message was sent in Unix time
* @method bool getIsTopicMessage() Optional. True, if the message is sent to a forum topic
* @method bool getIsAutomaticForward() Optional. True, if the message is a channel post that was automatically forwarded to the connected discussion group
* @method ReplyToMessage getReplyToMessage() Optional. For replies, the original message. Note that the Message object in this field will not contain further reply_to_message fields even if it itself is a reply.
+ * @method ExternalReplyInfo getExternalReply() Optional. Information about the message that is being replied to, which may come from another chat or forum topic
+ * @method TextQuote getQuote() Optional. For replies that quote part of the original message, the quoted part of the message
* @method User getViaBot() Optional. Bot through which the message was sent
* @method int getEditDate() Optional. Date the message was last edited in Unix time
* @method bool getHasProtectedContent() Optional. True, if the message can't be forwarded
* @method string getMediaGroupId() Optional. The unique identifier of a media message group this message belongs to
* @method string getAuthorSignature() Optional. Signature of the post author for messages in channels
* @method MessageEntity[] getEntities() Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text
* @method MessageEntity[] getCaptionEntities() Optional. For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption
* @method Audio getAudio() Optional. Message is an audio file, information about the file
* @method Document getDocument() Optional. Message is a general file, information about the file
* @method Animation getAnimation() Optional. Message is an animation, information about the animation. For backward compatibility, when this field is set, the document field will also be set
* @method Game getGame() Optional. Message is a game, information about the game.
* @method PhotoSize[] getPhoto() Optional. Message is a photo, available sizes of the photo
* @method Sticker getSticker() Optional. Message is a sticker, information about the sticker
* @method Story getStory() Optional. Message is a forwarded story
* @method Video getVideo() Optional. Message is a video, information about the video
* @method Voice getVoice() Optional. Message is a voice message, information about the file
* @method VideoNote getVideoNote() Optional. Message is a video note message, information about the video
* @method string getCaption() Optional. Caption for the document, photo or video, 0-200 characters
* @method bool getHasMediaSpoiler() Optional. True, if the message media is covered by a spoiler animation
* @method Contact getContact() Optional. Message is a shared contact, information about the contact
* @method Location getLocation() Optional. Message is a shared location, information about the location
* @method Venue getVenue() Optional. Message is a venue, information about the venue
* @method Poll getPoll() Optional. Message is a native poll, information about the poll
* @method Dice getDice() Optional. Message is a dice with random value, 1-6 for “🎲” and “🎯” base emoji, 1-5 for “🏀” and “⚽” base emoji, 1-64 for “🎰” base emoji
* @method User[] getNewChatMembers() Optional. A new member(s) was added to the group, information about them (one of this members may be the bot itself)
* @method User getLeftChatMember() Optional. A member was removed from the group, information about them (this member may be the bot itself)
* @method string getNewChatTitle() Optional. A chat title was changed to this value
* @method PhotoSize[] getNewChatPhoto() Optional. A chat photo was changed to this value
* @method MessageAutoDeleteTimerChanged getMessageAutoDeleteTimerChanged() Optional. Service message: auto-delete timer settings changed in the chat
* @method bool getDeleteChatPhoto() Optional. Service message: the chat photo was deleted
* @method bool getGroupChatCreated() Optional. Service message: the group has been created
* @method bool getSupergroupChatCreated() Optional. Service message: the supergroup has been created. This field can't be received in a message coming through updates, because bot can’t be a member of a supergroup when it is created. It can only be found in reply_to_message if someone replies to a very first message in a directly created supergroup.
* @method bool getChannelChatCreated() Optional. Service message: the channel has been created. This field can't be received in a message coming through updates, because bot can’t be a member of a channel when it is created. It can only be found in reply_to_message if someone replies to a very first message in a channel.
* @method int getMigrateToChatId() Optional. The group has been migrated to a supergroup with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.
* @method int getMigrateFromChatId() Optional. The supergroup has been migrated from a group with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.
* @method Message getPinnedMessage() Optional. Specified message was pinned. Note that the Message object in this field will not contain further reply_to_message fields even if it is itself a reply.
* @method Invoice getInvoice() Optional. Message is an invoice for a payment, information about the invoice.
* @method SuccessfulPayment getSuccessfulPayment() Optional. Message is a service message about a successful payment, information about the payment.
* @method UserShared getUserShared() Optional. Service message: a user was shared with the bot
* @method ChatShared getChatShared() Optional. Service message: a chat was shared with the bot
* @method string getConnectedWebsite() Optional. The domain name of the website on which the user has logged in.
* @method WriteAccessAllowed getWriteAccessAllowed() Optional. Service message: the user allowed the bot added to the attachment menu to write messages
* @method PassportData getPassportData() Optional. Telegram Passport data
* @method ProximityAlertTriggered getProximityAlertTriggered() Optional. Service message. A user in the chat triggered another user's proximity alert while sharing Live Location.
* @method ForumTopicCreated getForumTopicCreated() Optional. Service message: forum topic created
* @method ForumTopicEdited getForumTopicEdited() Optional. Service message: forum topic edited
* @method ForumTopicClosed getForumTopicClosed() Optional. Service message: forum topic closed
* @method ForumTopicReopened getForumTopicReopened() Optional. Service message: forum topic reopened
* @method GeneralForumTopicHidden getGeneralForumTopicHidden() Optional. Service message: the 'General' forum topic hidden
* @method GeneralForumTopicUnhidden getGeneralForumTopicUnhidden() Optional. Service message: the 'General' forum topic unhidden
* @method VideoChatScheduled getVideoChatScheduled() Optional. Service message: voice chat scheduled
* @method VideoChatStarted getVideoChatStarted() Optional. Service message: voice chat started
* @method VideoChatEnded getVideoChatEnded() Optional. Service message: voice chat ended
* @method VideoChatParticipantsInvited getVideoChatParticipantsInvited() Optional. Service message: new participants invited to a voice chat
* @method WebAppData getWebAppData() Optional. Service message: data sent by a Web App
* @method InlineKeyboard getReplyMarkup() Optional. Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons.
*/
class Message extends Entity
{
/**
* {@inheritdoc}
*/
protected function subEntities(): array
{
return [
'from' => User::class,
'sender_chat' => Chat::class,
'chat' => Chat::class,
'forward_from' => User::class,
'forward_from_chat' => Chat::class,
'reply_to_message' => ReplyToMessage::class,
+ 'external_reply' => ExternalReplyInfo::class,
+ 'quote' => TextQuote::class,
'via_bot' => User::class,
'entities' => [MessageEntity::class],
'animation' => Animation::class,
'audio' => Audio::class,
'document' => Document::class,
'photo' => [PhotoSize::class],
'sticker' => Sticker::class,
'story' => Story::class,
'video' => Video::class,
'video_note' => VideoNote::class,
'voice' => Voice::class,
'caption_entities' => [MessageEntity::class],
'contact' => Contact::class,
'dice' => Dice::class,
'game' => Game::class,
'poll' => Poll::class,
'venue' => Venue::class,
'location' => Location::class,
'new_chat_members' => [User::class],
'left_chat_member' => User::class,
'new_chat_photo' => [PhotoSize::class],
'message_auto_delete_timer_changed' => MessageAutoDeleteTimerChanged::class,
'pinned_message' => __CLASS__,
'invoice' => Invoice::class,
'successful_payment' => SuccessfulPayment::class,
'user_shared' => UserShared::class,
'chat_shared' => ChatShared::class,
'write_access_allowed' => WriteAccessAllowed::class,
'passport_data' => PassportData::class,
'proximity_alert_triggered' => ProximityAlertTriggered::class,
'forum_topic_created' => ForumTopicCreated::class,
'forum_topic_edited' => ForumTopicEdited::class,
'forum_topic_closed' => ForumTopicClosed::class,
'forum_topic_reopened' => ForumTopicReopened::class,
'general_forum_topic_hidden' => GeneralForumTopicHidden::class,
'general_forum_topic_unhidden' => GeneralForumTopicUnhidden::class,
'video_chat_scheduled' => VideoChatScheduled::class,
'video_chat_started' => VideoChatStarted::class,
'video_chat_ended' => VideoChatEnded::class,
'video_chat_participants_invited' => VideoChatParticipantsInvited::class,
'web_app_data' => WebAppData::class,
'reply_markup' => InlineKeyboard::class,
];
}
/**
* return the entire command like /echo or /echo@bot1 if specified
*
* @return string|null
*/
public function getFullCommand(): ?string
{
$text = $this->getProperty('text') ?? '';
if (strpos($text, '/') !== 0) {
return null;
}
$no_EOL = strtok($text, PHP_EOL);
$no_space = strtok($text, ' ');
//try to understand which separator \n or space divide /command from text
return strlen($no_space) < strlen($no_EOL) ? $no_space : $no_EOL;
}
/**
* Get command
*
* @return string|null
*/
public function getCommand(): ?string
{
if ($command = $this->getProperty('command')) {
return $command;
}
$full_command = $this->getFullCommand() ?? '';
if (strpos($full_command, '/') !== 0) {
return null;
}
$full_command = substr($full_command, 1);
//check if command is followed by bot username
$split_cmd = explode('@', $full_command);
if (! isset($split_cmd[1])) {
//command is not followed by name
return $full_command;
}
if (strtolower($split_cmd[1]) === strtolower($this->getBotUsername())) {
//command is addressed to me
return $split_cmd[0];
}
return null;
}
/**
* For text messages, the actual UTF-8 text of the message, 0-4096 characters.
*
* @param bool $without_cmd
*
* @return string|null
*/
public function getText($without_cmd = false): ?string
{
$text = $this->getProperty('text');
if ($without_cmd && $command = $this->getFullCommand()) {
if (strlen($command) + 1 < strlen($text)) {
return substr($text, strlen($command) + 1);
}
return '';
}
return $text;
}
/**
* Bot added in chat
*
* @return bool
*/
public function botAddedInChat(): bool
{
foreach ($this->getNewChatMembers() as $member) {
if ($member instanceof User && $member->getUsername() === $this->getBotUsername()) {
return true;
}
}
return false;
}
/**
* Detect type based on properties.
*
* @return string
*/
public function getType(): string
{
$types = [
'text',
'animation',
'audio',
'document',
'photo',
'sticker',
'video',
'video_note',
'voice',
'contact',
'dice',
'game',
'poll',
'venue',
'location',
'new_chat_members',
'left_chat_member',
'new_chat_title',
'new_chat_photo',
'delete_chat_photo',
'group_chat_created',
'supergroup_chat_created',
'channel_chat_created',
'message_auto_delete_timer_changed',
'migrate_to_chat_id',
'migrate_from_chat_id',
'pinned_message',
'invoice',
'successful_payment',
'user_shared',
'chat_shared',
'write_access_allowed',
'passport_data',
'proximity_alert_triggered',
'forum_topic_created',
'forum_topic_edited',
'forum_topic_closed',
'forum_topic_reopened',
'general_forum_topic_hidden',
'general_forum_topic_unhidden',
'video_chat_scheduled',
'video_chat_started',
'video_chat_ended',
'video_chat_participants_invited',
'web_app_data',
'reply_markup',
];
$is_command = $this->getCommand() !== null;
foreach ($types as $type) {
if ($this->getProperty($type) !== null) {
if ($is_command && $type === 'text') {
return 'command';
}
return $type;
}
}
return 'message';
}
}
diff --git a/src/Entities/ReplyParameters.php b/src/Entities/ReplyParameters.php
new file mode 100644
index 0000000..176650d
--- /dev/null
+++ b/src/Entities/ReplyParameters.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * This file is part of the TelegramBot package.
+ *
+ * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Longman\TelegramBot\Entities;
+
+/**
+ * Describes reply parameters for the message that is being sent.
+ *
+ * @link https://core.telegram.org/bots/api#replyparameters
+ *
+ * @method int getMessageId() Identifier of the message that will be replied to in the current chat, or in the chat chat_id if it is specified
+ * @method int|string getChatId() Optional. If the message to be replied to is from a different chat, unique identifier for the chat or username of the channel (in the format @channelusername)
+ * @method bool getAllowSendingWithoutReply() Optional. Pass True if the message should be sent even if the specified message to be replied to is not found; can be used only for replies in the same chat and forum topic.
+ * @method string getQuote() Optional. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including bold, italic, underline, strikethrough, spoiler, and custom_emoji entities. The message will fail to send if the quote isn't found in the original message.
+ * @method string getQuoteParseMode() Optional. Mode for parsing entities in the quote. See formatting options for more details.
+ * @method MessageEntity[] getQuoteEntities() Optional. A JSON-serialized list of special entities that appear in the quote. It can be specified instead of quote_parse_mode.
+ * @method int getQuotePosition() Optional. Position of the quote in the original message in UTF-16 code units
+ */
+class ReplyParameters extends Entity
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function subEntities(): array
+ {
+ return [
+ 'quote_entities' => [MessageEntity::class],
+ ];
+ }
+}
diff --git a/src/Entities/TextQuote.php b/src/Entities/TextQuote.php
new file mode 100644
index 0000000..9e237d7
--- /dev/null
+++ b/src/Entities/TextQuote.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * This file is part of the TelegramBot package.
+ *
+ * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Longman\TelegramBot\Entities;
+
+/**
+ * This object contains information about the quoted part of a message that is replied to by the given message.
+ *
+ * @link https://core.telegram.org/bots/api#textquote
+ *
+ * @method string getText() Text of the quoted part of a message that is replied to by the given message
+ * @method MessageEntity[] getEntities() Optional. Special entities that appear in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are kept in quotes.
+ * @method int getPosition() Approximate quote position in the original message in UTF-16 code units as specified by the sender
+ * @method bool getIsManual() Optional. True, if the quote was chosen manually by the message sender. Otherwise, the quote was added automatically by the server.
+ */
+class TextQuote extends Entity
+{
+ /**
+ * {@inheritdoc}
+ */
+ protected function subEntities(): array
+ {
+ return [
+ 'entities' => [MessageEntity::class],
+ ];
+ }
+}
diff --git a/structure.sql b/structure.sql
index 5b34430..dcc5821 100644
--- a/structure.sql
+++ b/structure.sql
@@ -1,410 +1,411 @@
CREATE TABLE IF NOT EXISTS `user` (
`id` bigint COMMENT 'Unique identifier for this user or bot',
`is_bot` tinyint(1) DEFAULT 0 COMMENT 'True, if this user is a bot',
`first_name` CHAR(255) NOT NULL DEFAULT '' COMMENT 'User''s or bot''s first name',
`last_name` CHAR(255) DEFAULT NULL COMMENT 'User''s or bot''s last name',
`username` CHAR(191) DEFAULT NULL COMMENT 'User''s or bot''s username',
`language_code` CHAR(10) DEFAULT NULL COMMENT 'IETF language tag of the user''s language',
`is_premium` tinyint(1) DEFAULT 0 COMMENT 'True, if this user is a Telegram Premium user',
`added_to_attachment_menu` tinyint(1) DEFAULT 0 COMMENT 'True, if this user added the bot to the attachment menu',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
`updated_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date update',
PRIMARY KEY (`id`),
KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `chat` (
`id` bigint COMMENT 'Unique identifier for this chat',
`type` ENUM('private', 'group', 'supergroup', 'channel') NOT NULL COMMENT 'Type of chat, can be either private, group, supergroup or channel',
`title` CHAR(255) DEFAULT '' COMMENT 'Title, for supergroups, channels and group chats',
`username` CHAR(255) DEFAULT NULL COMMENT 'Username, for private chats, supergroups and channels if available',
`first_name` CHAR(255) DEFAULT NULL COMMENT 'First name of the other party in a private chat',
`last_name` CHAR(255) DEFAULT NULL COMMENT 'Last name of the other party in a private chat',
`is_forum` TINYINT(1) DEFAULT 0 COMMENT 'True, if the supergroup chat is a forum (has topics enabled)',
`all_members_are_administrators` tinyint(1) DEFAULT 0 COMMENT 'True if a all members of this group are admins',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
`updated_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date update',
`old_id` bigint DEFAULT NULL COMMENT 'Unique chat identifier, this is filled when a group is converted to a supergroup',
PRIMARY KEY (`id`),
KEY `old_id` (`old_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `user_chat` (
`user_id` bigint COMMENT 'Unique user identifier',
`chat_id` bigint COMMENT 'Unique user or chat identifier',
PRIMARY KEY (`user_id`, `chat_id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `message_reaction` (
`id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
`chat_id` bigint COMMENT 'The chat containing the message the user reacted to',
`message_id` bigint COMMENT 'Unique identifier of the message inside the chat',
`user_id` bigint NULL COMMENT 'Optional. The user that changed the reaction, if the user isn''t anonymous',
`actor_chat_id` bigint NULL COMMENT 'Optional. The chat on behalf of which the reaction was changed, if the user is anonymous',
`old_reaction` TEXT NOT NULL COMMENT 'Previous list of reaction types that were set by the user',
`new_reaction` TEXT NOT NULL COMMENT 'New list of reaction types that have been set by the user',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`id`),
KEY `chat_id` (`chat_id`),
KEY `user_id` (`user_id`),
KEY `actor_chat_id` (`actor_chat_id`),
FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
FOREIGN KEY (`actor_chat_id`) REFERENCES `chat` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `message_reaction_count` (
`id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
`chat_id` bigint COMMENT 'The chat containing the message',
`message_id` bigint COMMENT 'Unique message identifier inside the chat',
`reactions` TEXT NOT NULL COMMENT 'List of reactions that are present on the message',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`id`),
KEY `chat_id` (`chat_id`),
FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `inline_query` (
`id` bigint UNSIGNED COMMENT 'Unique identifier for this query',
`user_id` bigint NULL COMMENT 'Unique user identifier',
`location` CHAR(255) NULL DEFAULT NULL COMMENT 'Location of the user',
`query` TEXT NOT NULL COMMENT 'Text of the query',
`offset` CHAR(255) NULL DEFAULT NULL COMMENT 'Offset of the result',
`chat_type` CHAR(255) NULL DEFAULT NULL COMMENT 'Optional. Type of the chat, from which the inline query was sent.',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `chosen_inline_result` (
`id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
`result_id` CHAR(255) NOT NULL DEFAULT '' COMMENT 'The unique identifier for the result that was chosen',
`user_id` bigint NULL COMMENT 'The user that chose the result',
`location` CHAR(255) NULL DEFAULT NULL COMMENT 'Sender location, only for bots that require user location',
`inline_message_id` CHAR(255) NULL DEFAULT NULL COMMENT 'Identifier of the sent inline message',
`query` TEXT NOT NULL COMMENT 'The query that was used to obtain the result',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `message` (
`chat_id` bigint COMMENT 'Unique chat identifier',
`sender_chat_id` bigint COMMENT 'Sender of the message, sent on behalf of a chat',
`id` bigint UNSIGNED COMMENT 'Unique message identifier',
`message_thread_id` bigint(20) DEFAULT NULL COMMENT 'Unique identifier of a message thread to which the message belongs; for supergroups only',
`user_id` bigint NULL COMMENT 'Unique user identifier',
`date` timestamp NULL DEFAULT NULL COMMENT 'Date the message was sent in timestamp format',
`forward_from` bigint NULL DEFAULT NULL COMMENT 'Unique user identifier, sender of the original message',
`forward_from_chat` bigint NULL DEFAULT NULL COMMENT 'Unique chat identifier, chat the original message belongs to',
`forward_from_message_id` bigint NULL DEFAULT NULL COMMENT 'Unique chat identifier of the original message in the channel',
`forward_signature` TEXT NULL DEFAULT NULL COMMENT 'For messages forwarded from channels, signature of the post author if present',
`forward_sender_name` TEXT NULL DEFAULT NULL COMMENT 'Sender''s name for messages forwarded from users who disallow adding a link to their account in forwarded messages',
`forward_date` timestamp NULL DEFAULT NULL COMMENT 'date the original message was sent in timestamp format',
`is_topic_message` tinyint(1) DEFAULT 0 COMMENT 'True, if the message is sent to a forum topic',
`is_automatic_forward` tinyint(1) DEFAULT 0 COMMENT 'True, if the message is a channel post that was automatically forwarded to the connected discussion group',
`reply_to_chat` bigint NULL DEFAULT NULL COMMENT 'Unique chat identifier',
`reply_to_message` bigint UNSIGNED DEFAULT NULL COMMENT 'Message that this message is reply to',
+ `external_reply` TEXT NULL DEFAULT NULL COMMENT 'Optional. Information about the message that is being replied to, which may come from another chat or forum topic',
`via_bot` bigint NULL DEFAULT NULL COMMENT 'Optional. Bot through which the message was sent',
`edit_date` timestamp NULL DEFAULT NULL COMMENT 'Date the message was last edited in Unix time',
`has_protected_content` tinyint(1) DEFAULT 0 COMMENT 'True, if the message can''t be forwarded',
`media_group_id` TEXT COMMENT 'The unique identifier of a media message group this message belongs to',
`author_signature` TEXT COMMENT 'Signature of the post author for messages in channels',
`text` TEXT COMMENT 'For text messages, the actual UTF-8 text of the message max message length 4096 char utf8mb4',
`entities` TEXT COMMENT 'For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text',
`caption_entities` TEXT COMMENT 'For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption',
`audio` TEXT COMMENT 'Audio object. Message is an audio file, information about the file',
`document` TEXT COMMENT 'Document object. Message is a general file, information about the file',
`animation` TEXT COMMENT 'Message is an animation, information about the animation',
`game` TEXT COMMENT 'Game object. Message is a game, information about the game',
`photo` TEXT COMMENT 'Array of PhotoSize objects. Message is a photo, available sizes of the photo',
`sticker` TEXT COMMENT 'Sticker object. Message is a sticker, information about the sticker',
`story` TEXT COMMENT 'Story object. Message is a forwarded story',
`video` TEXT COMMENT 'Video object. Message is a video, information about the video',
`voice` TEXT COMMENT 'Voice Object. Message is a Voice, information about the Voice',
`video_note` TEXT COMMENT 'VoiceNote Object. Message is a Video Note, information about the Video Note',
`caption` TEXT COMMENT 'For message with caption, the actual UTF-8 text of the caption',
`has_media_spoiler` tinyint(1) DEFAULT 0 COMMENT 'True, if the message media is covered by a spoiler animation',
`contact` TEXT COMMENT 'Contact object. Message is a shared contact, information about the contact',
`location` TEXT COMMENT 'Location object. Message is a shared location, information about the location',
`venue` TEXT COMMENT 'Venue object. Message is a Venue, information about the Venue',
`poll` TEXT COMMENT 'Poll object. Message is a native poll, information about the poll',
`dice` TEXT COMMENT 'Message is a dice with random value from 1 to 6',
`new_chat_members` TEXT COMMENT 'List of unique user identifiers, new member(s) were added to the group, information about them (one of these members may be the bot itself)',
`left_chat_member` bigint NULL DEFAULT NULL COMMENT 'Unique user identifier, a member was removed from the group, information about them (this member may be the bot itself)',
`new_chat_title` CHAR(255) DEFAULT NULL COMMENT 'A chat title was changed to this value',
`new_chat_photo` TEXT COMMENT 'Array of PhotoSize objects. A chat photo was change to this value',
`delete_chat_photo` tinyint(1) DEFAULT 0 COMMENT 'Informs that the chat photo was deleted',
`group_chat_created` tinyint(1) DEFAULT 0 COMMENT 'Informs that the group has been created',
`supergroup_chat_created` tinyint(1) DEFAULT 0 COMMENT 'Informs that the supergroup has been created',
`channel_chat_created` tinyint(1) DEFAULT 0 COMMENT 'Informs that the channel chat has been created',
`message_auto_delete_timer_changed` TEXT COMMENT 'MessageAutoDeleteTimerChanged object. Message is a service message: auto-delete timer settings changed in the chat',
`migrate_to_chat_id` bigint NULL DEFAULT NULL COMMENT 'Migrate to chat identifier. The group has been migrated to a supergroup with the specified identifier',
`migrate_from_chat_id` bigint NULL DEFAULT NULL COMMENT 'Migrate from chat identifier. The supergroup has been migrated from a group with the specified identifier',
`pinned_message` TEXT NULL COMMENT 'Message object. Specified message was pinned',
`invoice` TEXT NULL COMMENT 'Message is an invoice for a payment, information about the invoice',
`successful_payment` TEXT NULL COMMENT 'Message is a service message about a successful payment, information about the payment',
`user_shared` TEXT NULL COMMENT 'Optional. Service message: a user was shared with the bot',
`chat_shared` TEXT NULL COMMENT 'Optional. Service message: a chat was shared with the bot',
`connected_website` TEXT NULL COMMENT 'The domain name of the website on which the user has logged in.',
`write_access_allowed` TEXT DEFAULT NULL COMMENT 'Service message: the user allowed the bot added to the attachment menu to write messages',
`passport_data` TEXT NULL COMMENT 'Telegram Passport data',
`proximity_alert_triggered` TEXT NULL COMMENT 'Service message. A user in the chat triggered another user''s proximity alert while sharing Live Location.',
`forum_topic_created` TEXT DEFAULT NULL COMMENT 'Service message: forum topic created',
`forum_topic_edited` TEXT DEFAULT NULL COMMENT 'Service message: forum topic edited',
`forum_topic_closed` TEXT DEFAULT NULL COMMENT 'Service message: forum topic closed',
`forum_topic_reopened` TEXT DEFAULT NULL COMMENT 'Service message: forum topic reopened',
`general_forum_topic_hidden` TEXT DEFAULT NULL COMMENT 'Service message: the General forum topic hidden',
`general_forum_topic_unhidden` TEXT DEFAULT NULL COMMENT 'Service message: the General forum topic unhidden',
`video_chat_scheduled` TEXT COMMENT 'Service message: video chat scheduled',
`video_chat_started` TEXT COMMENT 'Service message: video chat started',
`video_chat_ended` TEXT COMMENT 'Service message: video chat ended',
`video_chat_participants_invited` TEXT COMMENT 'Service message: new participants invited to a video chat',
`web_app_data` TEXT COMMENT 'Service message: data sent by a Web App',
`reply_markup` TEXT NULL COMMENT 'Inline keyboard attached to the message',
PRIMARY KEY (`chat_id`, `id`),
KEY `user_id` (`user_id`),
KEY `forward_from` (`forward_from`),
KEY `forward_from_chat` (`forward_from_chat`),
KEY `reply_to_chat` (`reply_to_chat`),
KEY `reply_to_message` (`reply_to_message`),
KEY `via_bot` (`via_bot`),
KEY `left_chat_member` (`left_chat_member`),
KEY `migrate_from_chat_id` (`migrate_from_chat_id`),
KEY `migrate_to_chat_id` (`migrate_to_chat_id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`),
FOREIGN KEY (`forward_from`) REFERENCES `user` (`id`),
FOREIGN KEY (`forward_from_chat`) REFERENCES `chat` (`id`),
FOREIGN KEY (`reply_to_chat`, `reply_to_message`) REFERENCES `message` (`chat_id`, `id`),
FOREIGN KEY (`via_bot`) REFERENCES `user` (`id`),
FOREIGN KEY (`left_chat_member`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `edited_message` (
`id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
`chat_id` bigint COMMENT 'Unique chat identifier',
`message_id` bigint UNSIGNED COMMENT 'Unique message identifier',
`user_id` bigint NULL COMMENT 'Unique user identifier',
`edit_date` timestamp NULL DEFAULT NULL COMMENT 'Date the message was edited in timestamp format',
`text` TEXT COMMENT 'For text messages, the actual UTF-8 text of the message max message length 4096 char utf8',
`entities` TEXT COMMENT 'For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text',
`caption` TEXT COMMENT 'For message with caption, the actual UTF-8 text of the caption',
PRIMARY KEY (`id`),
KEY `chat_id` (`chat_id`),
KEY `message_id` (`message_id`),
KEY `user_id` (`user_id`),
FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`),
FOREIGN KEY (`chat_id`, `message_id`) REFERENCES `message` (`chat_id`, `id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `callback_query` (
`id` bigint UNSIGNED COMMENT 'Unique identifier for this query',
`user_id` bigint NULL COMMENT 'Unique user identifier',
`chat_id` bigint NULL COMMENT 'Unique chat identifier',
`message_id` bigint UNSIGNED COMMENT 'Unique message identifier',
`inline_message_id` CHAR(255) NULL DEFAULT NULL COMMENT 'Identifier of the message sent via the bot in inline mode, that originated the query',
`chat_instance` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent',
`data` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Data associated with the callback button',
`game_short_name` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Short name of a Game to be returned, serves as the unique identifier for the game',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `chat_id` (`chat_id`),
KEY `message_id` (`message_id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
FOREIGN KEY (`chat_id`, `message_id`) REFERENCES `message` (`chat_id`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `shipping_query` (
`id` bigint UNSIGNED COMMENT 'Unique query identifier',
`user_id` bigint COMMENT 'User who sent the query',
`invoice_payload` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Bot specified invoice payload',
`shipping_address` CHAR(255) NOT NULL DEFAULT '' COMMENT 'User specified shipping address',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `pre_checkout_query` (
`id` bigint UNSIGNED COMMENT 'Unique query identifier',
`user_id` bigint COMMENT 'User who sent the query',
`currency` CHAR(3) COMMENT 'Three-letter ISO 4217 currency code',
`total_amount` bigint COMMENT 'Total price in the smallest units of the currency',
`invoice_payload` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Bot specified invoice payload',
`shipping_option_id` CHAR(255) NULL COMMENT 'Identifier of the shipping option chosen by the user',
`order_info` TEXT NULL COMMENT 'Order info provided by the user',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `poll` (
`id` bigint UNSIGNED COMMENT 'Unique poll identifier',
`question` text NOT NULL COMMENT 'Poll question',
`options` text NOT NULL COMMENT 'List of poll options',
`total_voter_count` int UNSIGNED COMMENT 'Total number of users that voted in the poll',
`is_closed` tinyint(1) DEFAULT 0 COMMENT 'True, if the poll is closed',
`is_anonymous` tinyint(1) DEFAULT 1 COMMENT 'True, if the poll is anonymous',
`type` char(255) COMMENT 'Poll type, currently can be “regular” or “quiz”',
`allows_multiple_answers` tinyint(1) DEFAULT 0 COMMENT 'True, if the poll allows multiple answers',
`correct_option_id` int UNSIGNED COMMENT '0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.',
`explanation` varchar(255) DEFAULT NULL COMMENT 'Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters',
`explanation_entities` text DEFAULT NULL COMMENT 'Special entities like usernames, URLs, bot commands, etc. that appear in the explanation',
`open_period` int UNSIGNED DEFAULT NULL COMMENT 'Amount of time in seconds the poll will be active after creation',
`close_date` timestamp NULL DEFAULT NULL COMMENT 'Point in time (Unix timestamp) when the poll will be automatically closed',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `poll_answer` (
`poll_id` bigint UNSIGNED COMMENT 'Unique poll identifier',
`user_id` bigint NOT NULL COMMENT 'The user, who changed the answer to the poll',
`option_ids` text NOT NULL COMMENT '0-based identifiers of answer options, chosen by the user. May be empty if the user retracted their vote.',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`poll_id`, `user_id`),
FOREIGN KEY (`poll_id`) REFERENCES `poll` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `chat_member_updated` (
`id` BIGINT UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
`chat_id` BIGINT NOT NULL COMMENT 'Chat the user belongs to',
`user_id` BIGINT NOT NULL COMMENT 'Performer of the action, which resulted in the change',
`date` TIMESTAMP NOT NULL COMMENT 'Date the change was done in Unix time',
`old_chat_member` TEXT NOT NULL COMMENT 'Previous information about the chat member',
`new_chat_member` TEXT NOT NULL COMMENT 'New information about the chat member',
`invite_link` TEXT NULL COMMENT 'Chat invite link, which was used by the user to join the chat; for joining by invite link events only',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`id`),
FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `chat_join_request` (
`id` BIGINT UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
`chat_id` BIGINT NOT NULL COMMENT 'Chat to which the request was sent',
`user_id` BIGINT NOT NULL COMMENT 'User that sent the join request',
`date` TIMESTAMP NOT NULL COMMENT 'Date the request was sent in Unix time',
`bio` TEXT NULL COMMENT 'Optional. Bio of the user',
`invite_link` TEXT NULL COMMENT 'Optional. Chat invite link that was used by the user to send the join request',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`id`),
FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `telegram_update` (
`id` bigint UNSIGNED COMMENT 'Update''s unique identifier',
`chat_id` bigint NULL DEFAULT NULL COMMENT 'Unique chat identifier',
`message_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming message of any kind - text, photo, sticker, etc.',
`edited_message_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New version of a message that is known to the bot and was edited',
`channel_post_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming channel post of any kind - text, photo, sticker, etc.',
`edited_channel_post_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New version of a channel post that is known to the bot and was edited',
`message_reaction_id` bigint UNSIGNED DEFAULT NULL COMMENT 'A reaction to a message was changed by a user',
`message_reaction_count_id` bigint UNSIGNED DEFAULT NULL COMMENT 'Reactions to a message with anonymous reactions were changed',
`inline_query_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming inline query',
`chosen_inline_result_id` bigint UNSIGNED DEFAULT NULL COMMENT 'The result of an inline query that was chosen by a user and sent to their chat partner',
`callback_query_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming callback query',
`shipping_query_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming shipping query. Only for invoices with flexible price',
`pre_checkout_query_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming pre-checkout query. Contains full information about checkout',
`poll_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New poll state. Bots receive only updates about polls, which are sent or stopped by the bot',
`poll_answer_poll_id` bigint UNSIGNED DEFAULT NULL COMMENT 'A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself.',
`my_chat_member_updated_id` BIGINT UNSIGNED NULL COMMENT 'The bot''s chat member status was updated in a chat. For private chats, this update is received only when the bot is blocked or unblocked by the user.',
`chat_member_updated_id` BIGINT UNSIGNED NULL COMMENT 'A chat member''s status was updated in a chat. The bot must be an administrator in the chat and must explicitly specify “chat_member” in the list of allowed_updates to receive these updates.',
`chat_join_request_id` BIGINT UNSIGNED NULL COMMENT 'A request to join the chat has been sent',
PRIMARY KEY (`id`),
KEY `message_id` (`message_id`),
KEY `chat_message_id` (`chat_id`, `message_id`),
KEY `edited_message_id` (`edited_message_id`),
KEY `channel_post_id` (`channel_post_id`),
KEY `edited_channel_post_id` (`edited_channel_post_id`),
KEY `inline_query_id` (`inline_query_id`),
KEY `chosen_inline_result_id` (`chosen_inline_result_id`),
KEY `callback_query_id` (`callback_query_id`),
KEY `shipping_query_id` (`shipping_query_id`),
KEY `pre_checkout_query_id` (`pre_checkout_query_id`),
KEY `poll_id` (`poll_id`),
KEY `poll_answer_poll_id` (`poll_answer_poll_id`),
KEY `my_chat_member_updated_id` (`my_chat_member_updated_id`),
KEY `chat_member_updated_id` (`chat_member_updated_id`),
KEY `chat_join_request_id` (`chat_join_request_id`),
FOREIGN KEY (`chat_id`, `message_id`) REFERENCES `message` (`chat_id`, `id`),
FOREIGN KEY (`edited_message_id`) REFERENCES `edited_message` (`id`),
FOREIGN KEY (`chat_id`, `channel_post_id`) REFERENCES `message` (`chat_id`, `id`),
FOREIGN KEY (`edited_channel_post_id`) REFERENCES `edited_message` (`id`),
FOREIGN KEY (`inline_query_id`) REFERENCES `inline_query` (`id`),
FOREIGN KEY (`chosen_inline_result_id`) REFERENCES `chosen_inline_result` (`id`),
FOREIGN KEY (`callback_query_id`) REFERENCES `callback_query` (`id`),
FOREIGN KEY (`shipping_query_id`) REFERENCES `shipping_query` (`id`),
FOREIGN KEY (`pre_checkout_query_id`) REFERENCES `pre_checkout_query` (`id`),
FOREIGN KEY (`poll_id`) REFERENCES `poll` (`id`),
FOREIGN KEY (`poll_answer_poll_id`) REFERENCES `poll_answer` (`poll_id`),
FOREIGN KEY (`my_chat_member_updated_id`) REFERENCES `chat_member_updated` (`id`),
FOREIGN KEY (`chat_member_updated_id`) REFERENCES `chat_member_updated` (`id`),
FOREIGN KEY (`chat_join_request_id`) REFERENCES `chat_join_request` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `conversation` (
`id` bigint(20) unsigned AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
`user_id` bigint NULL DEFAULT NULL COMMENT 'Unique user identifier',
`chat_id` bigint NULL DEFAULT NULL COMMENT 'Unique user or chat identifier',
`status` ENUM('active', 'cancelled', 'stopped') NOT NULL DEFAULT 'active' COMMENT 'Conversation state',
`command` varchar(160) DEFAULT '' COMMENT 'Default command to execute',
`notes` text DEFAULT NULL COMMENT 'Data stored from command',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
`updated_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date update',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `chat_id` (`chat_id`),
KEY `status` (`status`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `request_limiter` (
`id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
`chat_id` char(255) NULL DEFAULT NULL COMMENT 'Unique chat identifier',
`inline_message_id` char(255) NULL DEFAULT NULL COMMENT 'Identifier of the sent inline message',
`method` char(255) DEFAULT NULL COMMENT 'Request method',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
diff --git a/utils/db-schema-update/0.82.0-unreleased.sql b/utils/db-schema-update/0.82.0-unreleased.sql
index e93ffbb..adb7934 100644
--- a/utils/db-schema-update/0.82.0-unreleased.sql
+++ b/utils/db-schema-update/0.82.0-unreleased.sql
@@ -1,36 +1,39 @@
CREATE TABLE IF NOT EXISTS `message_reaction` (
`id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
`chat_id` bigint COMMENT 'The chat containing the message the user reacted to',
`message_id` bigint COMMENT 'Unique identifier of the message inside the chat',
`user_id` bigint NULL COMMENT 'Optional. The user that changed the reaction, if the user isn''t anonymous',
`actor_chat_id` bigint NULL COMMENT 'Optional. The chat on behalf of which the reaction was changed, if the user is anonymous',
`old_reaction` TEXT NOT NULL COMMENT 'Previous list of reaction types that were set by the user',
`new_reaction` TEXT NOT NULL COMMENT 'New list of reaction types that have been set by the user',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`id`),
KEY `chat_id` (`chat_id`),
KEY `user_id` (`user_id`),
KEY `actor_chat_id` (`actor_chat_id`),
FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
FOREIGN KEY (`actor_chat_id`) REFERENCES `chat` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
CREATE TABLE IF NOT EXISTS `message_reaction_count` (
`id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
`chat_id` bigint COMMENT 'The chat containing the message',
`message_id` bigint COMMENT 'Unique message identifier inside the chat',
`reactions` TEXT NOT NULL COMMENT 'List of reactions that are present on the message',
`created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
PRIMARY KEY (`id`),
KEY `chat_id` (`chat_id`),
FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
+ALTER TABLE `message`
+ ADD COLUMN `external_reply` TEXT NULL DEFAULT NULL COMMENT 'Optional. Information about the message that is being replied to, which may come from another chat or forum topic' AFTER `reply_to_message`;
+
ALTER TABLE `telegram_update`
ADD COLUMN `message_reaction_id` bigint UNSIGNED DEFAULT NULL COMMENT 'A reaction to a message was changed by a user' AFTER `edited_channel_post_id`,
ADD COLUMN `message_reaction_count_id` bigint UNSIGNED DEFAULT NULL COMMENT 'Reactions to a message with anonymous reactions were changed' AFTER `message_reaction_id`;

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 15, 6:47 AM (1 d, 22 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
148384
Default Alt Text
(128 KB)

Event Timeline