September 15, 2020

Собираем базу пользователей (подписчиков) Телеграм бота + мультиязычный интерфейс

Собрать базу подписчиков бота не сложно, потому что при "старте" бота вместе с запросом приходят нужные данные. Нам необходимо только организовать сбор этих данных. Они нам могу пригодиться, например для хранения настроек пользователя, или при формировании рассылки информационных сообщений. Без самостоятельного ведения базы или использования сторонних ресурсов получить список тех, кто начал диалог с вашим ботом невозможно, то есть Телеграм вам такой информации нигде не выведет, по крайней мере на момент написания статьи, я такого функционала в мессенджере не встречал.

Для примера сделаем возможность пользователю выбирать язык интерфейса (мультиязычность), и соответствующую настройку будем хранить вместе с данными пользователя.

Как организовать сбор данных?

Для начала создадим таблицу users в базе MySQL (используйте кодировку utf8mb4unicodeci)

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `telegram_id` bigint(20) DEFAULT NULL,
  `first_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `last_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `lang` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Класс User по работе с таблицей, к нему мы будем обращаться для создания, получения или обновления данных о пользователях.

<?php // Подключаем класс для работы с БД require_once("Db.php");

/** * Class User * @property int $id * @property int $telegram_id * @property string $first_name * @property string $last_name * @property string $username * @property string $lang * @property User|bool $identity */ class User extends Db { private $id; private $telegram_id; private $first_name; private $last_name; private $username; private $lang;

private $identity;

/** * User constructor. * @param $userData array */ public function __construct($userData) { // проверяем наличие пользователя в базе $this->identity = $this->getUserByTelegramId($userData['telegram_id']); if ($this->identity) { // если есть то отправляем на обновление данных $this->updateUserData($userData); } else { // если не нашли то отправляем на регистрацию $this->identity = $this->insertUserData($userData); } }

/** Добавляем пользователя * @param $userData array * @return bool */ public function insertUserData($userData) { // готовим запрос $insert = $this->connect() ->prepare("INSERT INTO users SET " . $this->pdoSet(array_keys($userData), $values, $userData)); return $insert->execute($values) ? $this->getUserByTelegramId($userData['telegram_id']) : false; }

/** Обновляем данные пользователя * @param $userData array * @return bool */ public function updateUserData($userData) { return $this->setParam($userData); }

/** Проверяем наличие данных пользователя * @param $telegram_id int * @return bool|self */ public function getUserByTelegramId($telegram_id) { $order = $this->connect() ->prepare("SELECT * FROM users WHERE telegram_id = :telegram_id LIMIT 1"); $order->execute([ 'telegram_id' => (int)$telegram_id ]); // если пользователь найден то возвращаем объект return $order->rowCount() > 0 ? $order->fetch(PDO::FETCH_OBJ) : false; }

/** Получаем id * @return int */ public function getId() { return $this->identity->id; }

/** Получаем настройку lang * @return string */ public function getLang() { return $this->identity->lang; }

/** Меняем настройку lang * @param $lang * @return bool */ public function setLang($lang) { $this->identity->lang = $lang;

return $this->setParam([ 'lang' => $lang, 'telegram_id' => $this->identity->telegram_id, ]); }

/** Получаем first_name * @return string */ public function getFirstName() { return $this->identity->first_name; }

/** Получаем last_name * @return string */ public function getLastName() { return $this->identity->last_name; }

/** Получаем username * @return string */ public function getUserName() { return $this->identity->username; }

/** Получаем полное имя пользователя * @return string */ public function getFullName() { return trim($this->getFirstName() . " " . $this->getLastName()); }

/** Обновляем данные пользователя * @param array $userData * @param bool $type * @return bool */ private function setParam($userData = [], $type = true) { if (count($userData) > 0) { $update = $this->connect() ->prepare("UPDATE users SET " . $this->pdoSet(arraykeys($userData), $values, $userData, $type) . " WHERE telegramid = :telegram_id"); return $update->execute($values); } else { return false; } } }

Добавим новый метод setPdo в класс по организации соединения с СУБД MySQL, сам класс Db вы можете посмотреть в предыдущей статье "Авторизация на сайт через Телеграм без использования официального виджета", также в прикрепленном к статье файле будут все приведенные скрипты.

<? /** Готовим данные для запроса * @param $allowed * @param $values * @param array $source * @param bool $type * @return bool|string */ public function pdoSet($allowed, &$values, $source, $type = false) { $set = ''; $values = []; foreach ($allowed as $field) { if (isset($source[$field])) { $values[$field] = $source[$field]; if ($field == 'telegramid' && $type) { continue; } else { $set .= "`" . strreplace("", "`", $field) . "`" . "=:" . $field . ", "; } } } return substr($set, 0, -2); } ?>

Вынесем все необходимые в рамках этой статьи методы взаимодействия с Telegram Bot API в отдельный класс Bot, не забудьте заменить ___TOKEN__ВАШЕГО__БОТА___ на токен от своего бота.

Класс при необходимости можно расширить, а методы дополнить, но это на ваше усмотрение.

<

<?php /** * Class Bot * @property string $token * @property array $data / class Bot { // токен бота public $token = "__TOKEN_ВАШЕГО__ББОТА__ // для хранения массива данных от Телеграм public $data; /* * Bot constructor. *// public function _construct() { // записываем в свойство данные $this->data = jsondecode(file_ggetntents('php://input'), true); } /*олучаем id * @param $data * @return mixed */ p/ public function getChatId() { if ($this->getType() == "callbackquery") { return $this->data['callbackquery']['message']['chat']['id']; } return $this->data['message']['chat']['id']; } /*учаем first_name * @param $data * @return mixed */ pub/ public function getChatFirstName() { if ($this->getType() == "callbackquery") { return $this->data['callbackquery']['message']['chat']['first_nname']; } return $this->data['message']['chat']['firstme']; } /*аем last_name * @param $data * @return mixed */ publi/ public function getChatLastName() { if ($this->getType() == "callbackquery") { return $this->data['callbackquery']['message']['chat']['last_nname']; } return $this->data['message']['chat']['lastme']; } /*м username * @param $data * @return mixed */ public / public function getChatUserName() { if ($this->getType() == "callbackquery") { return $this->data['callbackquery']['message']['chat']['username']; } return $this->data['message']['chat']['username']; } /*id сообщения * @param $data * @return mixed */ public fu/ public function getMessageId() { if ($this->getType() == "callbackquery") { return $this->data['callbackquery']['message']['message_iid']; } return $this->data['message']['message']; } /*чение текст * @return mixed */ public func/ public function getText() { if ($this->getType() == "callbackquery") { return $this->data['callbackquery']['data']; } return $this->data['message']['text']; } /*тип данных пришел * @param $data * @return bool|string */ public functi/ public function getType() { if (isset($this->data['callbackquery'])) { return "callbackquery"; } elseif (isset($this->data['message']['text'])) { return "message"; } else { return false; } } /*лиенте * @param $cbq_id * @param $text * @param bool $type */ public function/ public function notice($text = "") { $data = [ 'callbackqueryid' => $this->data['callback_query']['id'], ]; if (!empty($text)) { $data['text'] = $text; } $this->botApiQuery("answerCallbackQuery", $data); } /* * @param $chatid * @param $messageid * @return mixed */ public function d/ public function deleteMessage() { return $this->botApiQuery("deleteMessage", [ "chatid" => $this->getChatId(), "messageid" => $this->getMessageId() ] ); } /*ram $text * @param string $callback_data * @param string $url * @return array */ public function bui/ public function buildInlineKeyboardButton($text, $callbackdata = '', $url = '') { // рисуем кнопке текст $replyMarkup = [ 'text' => $text, ]; // пишем одно из обязательных дополнений кнопке if ($url != '') { $replyMarkup['url'] = $url; } elseif ($callbackdata != '') { $replyMarkup['callback_ddata'] = $callbackta; } // возвращаем кнопку return $replyMarkup; } /* @param array $options * @return string */ public function build/ public function buildInlineKeyBoard(array $options) { // собираем кнопки $replyMarkup = [ 'inlinekeyboard' => $options, ]; // преобразуем в JSON объект $encodedMarkup = jsonencode($replyMarkup, true); // возвращаем клавиатуру return $encodedMarkup; } /*общение с inline кнопками * @param $user_id * @param $text * @param null $buttons * @return mixed */ public function sendMes/ public function sendMessage($userid, $text, $buttons = NULL) { // готовим массив данных $datasend = [ 'chat_iid' => $user, 'text' => $text, 'parse_modmode' => 'html', ]; // если переданны кнопки то добавляем их к сообщению if (!is($buttons) && is_arrayarray($buttons)) { $datareply_markup'markup'] = $this->buildInlineKeyBoard($buttons); } // отправляем текстовое сообщение return $this->botApiQuery("sendMessage", $data /*m $method * @param array $fields * @return mixed */ public function botApiQuery($method, $fields = array()) { $ch = curl_init('https://api.telegram.org/bot' . $this->token . '/' . $method); curlsetoptarray($ch, array( CURLOPT_PPOST => count($fields), CURLOPTSTFIELDS => http_buibuildy($fields), CURLOPTSSLVSSLPEER => 0, CURLOPT_RETURNTRETURNTRANSFER => 1, CURLOPT=> 10 )); $r = json_decode(cudecode(curl true); curl_close($ch); return $r; } } ?>