Skip to content

Instantly share code, notes, and snippets.

@klim2020
Created September 19, 2021 20:12
Show Gist options
  • Save klim2020/5cddd66e7f2aa5592c420a2f9b6c70e4 to your computer and use it in GitHub Desktop.
Save klim2020/5cddd66e7f2aa5592c420a2f9b6c70e4 to your computer and use it in GitHub Desktop.
My homebrew TelegramBOT PHP class. It isn't perfect but for my needs it's good enough.
<?php
/**
* Class TelegramBot
* @author Lava Git
* @version 2.2
*/
class TelegramBot {
private $botKey;
private $hookKey;
private $hookURL;
private $botAgent;
private $telegramUpdate;
private $rootKey = 'message';
/**
* TelegramBot constructor.
* @param string $botKey The bot key (longest random character string).
* @param string $hookKey The hook key (shortest random character string).
* @param string $hookURL Where the bots index/root directory is. E.g. https://mybot.ninja/telegrambot/.
* @param string $botAgent The user agent used when connecting to Telegram servers.
*/
function __construct($botKey, $hookKey = "", $hookURL = "", $botAgent = "Lavas PHP Telegram BOT") {
$this->botKey = $botKey;
$this->hookKey = $hookKey;
$this->hookURL = $hookURL;
$this->botAgent = $botAgent;
}
/**
* @return string The bot key.
*/
private function getBotKey() {
return $this->botKey;
}
/**
* @return string The hook key.
*/
private function getHookKey() {
return $this->hookKey;
}
/**
* @return string The hook URL.
*/
private function getHookURL() {
return $this->hookURL;
}
/**
* @return string The bot user agent used when connecting to the Telegram BOT API.
*/
private function getBotAgent() {
return $this->botAgent;
}
/**
* This is used to check if the hook key presented by the client is valid. Use this before responding to any requests.
* @param string $string The hook string that is being used to communicate with the bot (only Telegram should know this).
* @return bool|null
*/
function isHookValid($string) {
if ("$string" == $this->getHookKey()) {
return true;
}
return null;
}
/**
* Was the bot called by Telegram?
* @return bool|null true if the bot was called by Telegram, using the correct hook.
*/
function fromTelegram() {
if (isset($_GET['telegramUpdate']) && $_GET['telegramUpdate'] == $this->isHookValid($_GET['telegramUpdate'])) {
return true;
}
return null;
}
/**
* Downloads a file from telegram servers and returns the contents. Use this with file_put_contents or a similar function.
* @param $file_path
* @return bool|string
*/
function getFile($file_path) {
$curl = curl_init("https://api.telegram.org/file/bot" . $this->getBotKey() . "/$file_path");
curl_setopt($curl , CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_USERAGENT, $this->getBotAgent());
return curl_exec($curl);
}
/**
* Lets the user know that the bot is processing their request.
* @param string $action typing, upload_photo, record_video, upload_video, record_voice, upload_voice, upload_document, find_location, record_video_note, upload_video_note
*/
function sendChatAction($action) {
$this->sendPOST("sendChatAction", array(
"chat_id" => $this->getChatID(),
"action" => "$action"));
}
/**
* Send a GET request to the Telegram bot API.
* @param string $type getFile, etc.
* @param array $paramArray arguments used with the API method/type.
* @return bool|string Returns Telegram's response.
*/
function sendGET($type, $paramArray = array()) {
/*
$type: getFile etc
$paramArray: An array of the fields to put in the GET request to Telegram
*/
$fields = "$type?";
foreach ($paramArray as $param => $value) {
$fields .= "$param=$value&";
}
$curl = curl_init("https://api.telegram.org/bot" . $this->getBotKey() . "/$fields");
curl_setopt($curl , CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_USERAGENT, $this->getBotAgent());
return curl_exec($curl);
}
/**
* Send a POST request to the Telegram BOT API.
* @param string $type sendMessage, sendVoice, sendVideo etc.
* @param array $postArray An array of the fields to post to Telegram
* @return array|string|null Returns Telegrams response. Null is returned if a file is to be posted but cannot be found.
*/
function sendPOST($type, $postArray = array()) {
$curl = curl_init("https://api.telegram.org/bot" . $this->getBotKey() . "/$type");
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
"Content-Type:multipart/form-data"
));
foreach(array("photo", "video", "document", "audio", "voice") as $mediaType) {
if (isset($postArray[$mediaType])) {
if (file_exists($postArray[$mediaType])) {
$postArray[$mediaType] = new CURLFile(realpath($postArray[$mediaType]));
} else {
error_log("sendPOST: Cannot find the file \"".$postArray[$mediaType]."\"");
return null; // File not found
}
}
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_USERAGENT, $this->getBotAgent());
//curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $postArray);
return curl_exec($curl);
}
/**
* Get the current webhook status from the Telegram API.
* @return array Returns an array of Telegram's response message.
*/
function getWebhookInfo() {
return json_decode($this->sendGET("getWebhookInfo"), true);
}
/**
* Update Telegram with a new webhook location. Webhooks are used so Telegram can directly send new updates to the bot.
* @param int $max_connections Maximum connections Telegram can make to the bot (optional, default is 2).
* @return mixed Returns an array of Telegram's response message.
*/
function updateWebHook($max_connections = 2) {
return json_decode($this->sendPOST("setWebhook", array(
"max_connections" => "$max_connections",
"url" => $this->getHookURL() . "?telegramUpdate=" . $this->getHookKey()
)), true);
}
/**
* Automatically update Telegram with the correct webhook URL, if it doesn't match the one defined in the construct.
* @param int $max_connections Maximum connections Telegram can make to the bot (optional, default is 2).
* @return array|null Returns an array of Telegram's response message, if the hook was updated.
*/
function updateWebHookAuto($max_connections = 2) {
if ($this->getHookURL() . "?telegramUpdate=" . $this->getHookKey() != $this->getWebhookInfo()['result']['url']) {
return $this->updateWebHook($max_connections);
}
return null;
}
/**
* Decodes Telegram's message and stores it in $telegramUpdate.
*/
function parseTelegramUpdate() {
$this->telegramUpdate = json_decode(file_get_contents("php://input"), true);
if ($this->isMessageEdited()) {
$this->rootKey = 'edited_message';
}
}
/**
* @return mixed Returns the contents of the $telegramUpdate array.
*/
function getTelegramUpdate() {
return $this->telegramUpdate;
}
/**
* @return mixed|null Returns the unique identifier of the account that is communicating with the bot.
*/
function getUserID() {
if (isset($this->telegramUpdate[$this->rootKey]['from']['id'])) {
return $this->telegramUpdate[$this->rootKey]['from']['id'];
}
return null;
}
/**
* @return mixed|null Returns the locale of the account that is communicating with the bot.
*/
function getUserLang() {
if (isset($this->telegramUpdate[$this->rootKey]['from']['language_code'])) {
return $this->telegramUpdate[$this->rootKey]['from']['language_code'];
}
return null;
}
/**
* @return mixed|null Returns the username of the account that is communicating with the bot.
*/
function getUsername() {
if (isset($this->telegramUpdate[$this->rootKey]['from']['username'])) {
return $this->telegramUpdate[$this->rootKey]['from']['username'];
}
return null;
}
/**
* @return mixed|null Returns if it is a private or group chat.
*/
function getChatType() {
if (isset($this->telegramUpdate[$this->rootKey]['from']['type'])) {
return $this->telegramUpdate[$this->rootKey]['from']['type'];
}
return null;
}
/**
* @return mixed|null Returns the chat unique identifier. Used to replying.
*/
function getChatID() {
if (isset($this->telegramUpdate[$this->rootKey]['chat']['id'])) {
return $this->telegramUpdate[$this->rootKey]['chat']['id'];
}
return null;
}
/**
* Get the ID of the received message. Used for quoting messages.
* @return string|null Returns the ID of the message.
*/
function getMessageID() {
if (isset($this->telegramUpdate[$this->rootKey]['message_id'])) {
return $this->telegramUpdate[$this->rootKey]['message_id'];
}
return null;
}
/**
* Checks if a message has been edited.
* @return bool|null true if the message has been edited, otherwise return null.
*/
function isMessageEdited() {
if (isset($this->telegramUpdate['edited_message'])) {
return true;
}
return null;
}
/**
* Get the date of which the message was sent.
* @return mixed|null Returns the date that the message was received.
*/
function getDate() {
if (isset($this->telegramUpdate[$this->rootKey]['date'])) {
return $this->telegramUpdate[$this->rootKey]['date'];
}
return null;
}
/**
* Check if the received message contains a picture.
* @return bool|null true if the message contains a picture.
*/
function isPicture() {
if (isset($this->telegramUpdate[$this->rootKey]['photo'])) {
return true;
}
if (isset($this->telegramUpdate[$this->rootKey]['document']['mime_type'])) {
if (strpos($this->telegramUpdate[$this->rootKey]['document']['mime_type'], "image") !== false) {
return true;
}
}
return null;
}
/**
* Downloads a picture from Telegram.
* @return string|null Returns the file path if successful.
*/
function downloadPicture() {
if ($this->isPicture()) {
if (isset($this->telegramUpdate[$this->rootKey]['photo'])) {
$photo_count = count($this->telegramUpdate[$this->rootKey]['photo']) - 1;
$file_id = $this->telegramUpdate[$this->rootKey]['photo'][$photo_count]['file_id'];
$file_info = json_decode($this->sendGET("getFile", array("file_id" => $file_id)), true);
$file_path = $file_info['result']['file_path'];
$file_ext = "jpg";
}
if (isset($this->telegramUpdate[$this->rootKey]['document'])) {
$file_id = $this->telegramUpdate[$this->rootKey]['document']['file_id'];
$file_info = json_decode($this->sendGET("getFile", array("file_id" => $file_id)), true);
$file_path = $file_info['result']['file_path'];
$file_ext = pathinfo($this->telegramUpdate[$this->rootKey]['document']['file_name'])['extension'];
}
if (!file_exists(".tmp/")) {
if (!mkdir(".tmp", 0777, true)) {
error_log("Failed to create directory '.tmp'!", 0);
return null;
}
}
if (!isset($file_id) || !isset($file_ext) || !isset($file_path)) {
return null;
}
file_put_contents(".tmp/$file_id.$file_ext", $this->getFile("$file_path"));
if (file_exists(".tmp/$file_id.$file_ext")) {
return ".tmp/$file_id.$file_ext";
} else {
error_log("Failed to write \".tmp/$file_id.$file_ext\"!");
}
}
return null;
}
/**
* Get the text contents of the received message.
* @return string|null Returns message text.
*/
function getText() {
if (isset($this->telegramUpdate[$this->rootKey]['text'])) {
return $this->telegramUpdate[$this->rootKey]['text'];
}
if (isset($this->telegramUpdate[$this->rootKey]['caption'])) {
return $this->telegramUpdate[$this->rootKey]['caption'];
}
return null;
}
/**
* Search the message from the user for specific key words.
* @param string|array $string The word or word(s) to search for in the received message. String or array can be given.
* @return array|null Returns an array of words found in the users message.
*/
function textContains($string) {
$return = array();
$toSearch = strtolower($this->getText());
/**
* The given parameter is an array rather than a single search query/string
*/
if (is_array($string)) {
error_log("Array search query given.", 0);
foreach($string as $searchKey) {
$searchKey = strtolower($searchKey);
if (preg_match('/(?<=[\s,.:;"\']|^)' . $searchKey . '(?=[\s,.:;"\']|$)/', $toSearch)) {
array_push($return, $searchKey);
}
}
return $return;
}
$string = strtolower($string);
if (strpos($toSearch, $string)) {
array_push($return, $string);
return $return;
}
return null;
}
/**
* Were we sent a bot command?
* @return bool|null true if a command was issued.
*/
function isCommand() {
if (isset($this->telegramUpdate[$this->rootKey]['entities'][0]['type'])) {
if ($this->telegramUpdate[$this->rootKey]['entities'][0]['type'] == "bot_command") {
return true;
}
}
if (isset($this->telegramUpdate[$this->rootKey]['caption_entities'][0]['type'])) {
if ($this->telegramUpdate[$this->rootKey]['caption_entities'][0]['type'] == "bot_command") {
return true;
}
}
return null;
}
/**
* Parses the user's message and returns the requested command.
* @return false|string|null
*/
function getCommand() {
if ($this->isCommand()) {
return trim(explode("@", substr($this->getText(), $this->getCommandOffset()['offset'], $this->getCommandOffset()['length']))[0]);
}
return null;
}
/**
* Called by getCommand() to get the offsets of the /command parameter.
* @return array Returns an array of the offsets: { "offset" => int, "length" => int }
*/
function getCommandOffset() {
$offsetArray = array();
if (isset($this->telegramUpdate[$this->rootKey]['entities'])) {
$offsetArray['offset'] = $this->telegramUpdate[$this->rootKey]['entities'][0]['offset'] + 1;
$offsetArray['length'] = $this->telegramUpdate[$this->rootKey]['entities'][0]['length'];
} else {
$offsetArray['offset'] = $this->telegramUpdate[$this->rootKey]['caption_entities'][0]['offset'] + 1;
$offsetArray['length'] = $this->telegramUpdate[$this->rootKey]['caption_entities'][0]['length'];
}
return $offsetArray;
}
/**
* Returns the arguments passed when calling a command.
* @return array|null An array of all command arguments. Null if no command was called.
*/
function getCommandArgs() {
if ($this->isCommand()) {
return explode(" ", trim(substr_replace($this->getText(), '',
$this->getCommandOffset()['offset'] - 1,
$this->getCommandOffset()['offset'] + $this->getCommandOffset()['length'] - 1)));
}
return null;
}
/**
* Are we talking to another bot?
* @return mixed|null
*/
function isBot() {
if (isset($this->telegramUpdate[$this->rootKey]['from']['is_bot'])) {
return $this->telegramUpdate[$this->rootKey]['from']['is_bot'];
}
return null;
}
/**
* Allows for sending a message directly to a specific chat that isn't the same as the one that called the bot.
* @param string $string The message to send.
* @param string $chatID The ID of the chat to send the message to.
* @param string $file Path to the file we wish to send. (optional)
* @param string $fileSendAs Send the file as a photo, video, document, audio, voice? (optional)
* @return array|null On success, an array is returned, containing Telegram's response.
*/
function sendMessage($string, $chatID, $file = "", $fileSendAs = "") {
$messageContents = array(
'chat_id' => $chatID,
'text' => $string
);
if ($file != "") {
$fileAttach = $this->attachFile($file, $string, $fileSendAs);
$messageType = $fileAttach['messageType'];
$messageContents = array_merge($messageContents, $fileAttach['messageContents']);
} else {
$messageType = "sendMessage";
}
return $this->sendPOST($messageType, $messageContents);
}
/**
* Make the bot leave the chat (groups only)
* @param string $chatID The ID of the chat we wish to leave.
* @return array|string|null Returns the Telegram API response.
*/
function leaveChat($chatID) {
if ($chatID != "") {
$messageContents['chat_id'] = $chatID;
return $this->sendPOST("leaveChat", $messageContents);
}
return null;
}
/**
* Get some info about the chat we are in.
* @param string $chatID The ID of the chat we wish to get some info about.
* @return array|string|null Returns the Telegram API response.
*/
function getChat($chatID) {
if ($chatID != "") {
$messageContents['chat_id'] = $chatID;
return $this->sendPOST("getChat", $messageContents);
}
return null;
}
/**
* Get who is an administrator in this group/chat.
* @param string $chatID The ID of the chat we wish to get a list of admins from.
* @return array|string|null Returns the Telegram API response.
*/
function getChatAdmin($chatID) {
if ($chatID != "") {
$messageContents['chat_id'] = $chatID;
return $this->sendPOST("getChatAdministrators", $messageContents);
}
return null;
}
/**
* Generate an invite link for a group chat. Bot requires admin privileges.
* @param $chatID The ID of the chat we wish to have an invite for.
* @return array|string|null Returns the Telegram API response.
*/
function getChatInviteLink($chatID) {
if ($chatID != "") {
$messageContents['chat_id'] = $chatID;
return $this->sendPOST("exportChatInviteLink", $messageContents);
}
return null;
}
/**
* Used to attach a file to a prepared message.
* @param string $file The path to the file we wish to send.
* @param string $caption The caption of the file (optional).
* @param string $fileSendAs Send the file as a photo, video, document, audio, voice? (optional)
* @return array Returns a multidimensional array: { "messageContents" => array(), "messageType" => "document, audio, etc" }
*/
function attachFile($file, $caption = "", $fileSendAs = "") {
$fileExt = pathinfo($file, PATHINFO_EXTENSION);
$messageContents = array();
if ($fileSendAs != "") {
switch($fileSendAs){
case "photo":
$fileExt = "jpg"; // "Spoof" file extension to force sending as a photo.
break;
case "video":
$fileExt = "mp4";
break;
case "audio":
$fileExt = "mp3";
break;
case "voice":
$fileExt = "wav";
break;
case "document":
$fileExt = "document";
break;
}
}
switch($fileExt) {
case "jpg":
case "png":
case "bmp":
case "webp":
$messageContents['photo'] = $file;
$messageContents["caption"] = $caption;
$messageType = "sendPhoto";
$this->sendChatAction("upload_photo");
break;
case "mp4":
case "gif":
case "mkv":
case "webm":
case "ogv":
case "wmv":
$messageContents['video'] = $file;
$messageContents["caption"] = $caption;
$messageType = "sendVideo";
$this->sendChatAction("upload_video");
break;
case "mp3":
case "mp2":
case "ogg":
case "opus":
case "m4a":
case "ape":
case "wma":
case "flac":
$messageContents['audio'] = $file;
$messageContents["caption"] = $caption;
$messageType = "sendAudio";
$this->sendChatAction("upload_document");
break;
case "wav":
$messageContents['voice'] = $file;
$messageContents["caption"] = $caption;
$messageType = "sendVoice";
$this->sendChatAction("upload_voice");
break;
default:
$messageContents['document'] = $file;
$messageContents["caption"] = $caption;
$messageType = "sendDocument";
$this->sendChatAction("upload_document");
}
return array(
"messageContents" => $messageContents,
"messageType" => "$messageType"
);
}
/**
* A method used to easily respond to messages.
* @param string $string The message to send.
* @param bool $quote Quote the message we're replying to. (optional)
* @param string $file Path to the file we wish to send. (optional)
* @param string $fileSendAs Send the file as a photo, video, document, audio, voice? (optional)
* @return array On success, an array is returned, containing Telegram's response.
*/
function sendReply($string, $quote = true, $file = "", $fileSendAs = "") {
$messageContents = array();
$messageContents['chat_id'] = $this->getChatID();
if ($quote == true) {
$messageContents['reply_to_message_id'] = $this->getMessageID();
}
if ($file != "") {
$fileAttach = $this->attachFile($file, $string, $fileSendAs);
$messageType = $fileAttach['messageType'];
$messageContents = array_merge($messageContents, $fileAttach['messageContents']);
} else {
$messageType = "sendMessage";
}
$messageContents["text"] = $string;
return $this->sendPOST($messageType, $messageContents);
}
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment