├── .gitignore ├── Commands ├── Config │ ├── DateCommand.php │ ├── README.md │ └── WeatherCommand.php ├── Conversation │ ├── CancelCommand.php │ ├── GenericmessageCommand.php │ ├── README.md │ └── SurveyCommand.php ├── GenericCommand.php ├── Group │ ├── GenericmessageCommand.php │ ├── LeftchatmemberCommand.php │ ├── NewchatmembersCommand.php │ └── README.md ├── InlineMode │ ├── ChoseninlineresultCommand.php │ ├── InlinequeryCommand.php │ └── README.md ├── Keyboard │ ├── CallbackqueryCommand.php │ ├── ForcereplyCommand.php │ ├── HidekeyboardCommand.php │ ├── InlinekeyboardCommand.php │ ├── KeyboardCommand.php │ └── README.md ├── Message │ ├── ChannelpostCommand.php │ ├── EditedchannelpostCommand.php │ ├── EditedmessageCommand.php │ ├── EditmessageCommand.php │ ├── GenericmessageCommand.php │ └── README.md ├── Other │ ├── EchoCommand.php │ ├── HelpCommand.php │ ├── ImageCommand.php │ ├── MarkdownCommand.php │ ├── README.md │ ├── SlapCommand.php │ ├── UploadCommand.php │ └── WhoamiCommand.php ├── Payments │ ├── GenericmessageCommand.php │ ├── PaymentCommand.php │ ├── PrecheckoutqueryCommand.php │ ├── README.md │ └── ShippingqueryCommand.php ├── README.MD ├── ServiceMessages │ └── GenericmessageCommand.php └── StartCommand.php ├── CustomCommands └── README.md ├── LICENSE ├── README.md ├── composer.json ├── config.example.php ├── cron.php ├── getUpdatesCLI.php ├── hook.php ├── manager.php ├── phpcs.xml.dist ├── set.php └── unset.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | composer.lock 3 | config.php 4 | vendor 5 | -------------------------------------------------------------------------------- /Commands/Config/DateCommand.php: -------------------------------------------------------------------------------- 1 | ['google_api_key' => 'your_google_api_key_here'] 21 | */ 22 | 23 | namespace Longman\TelegramBot\Commands\UserCommands; 24 | 25 | use GuzzleHttp\Client; 26 | use GuzzleHttp\Exception\RequestException; 27 | use Longman\TelegramBot\Commands\UserCommand; 28 | use Longman\TelegramBot\Entities\ServerResponse; 29 | use Longman\TelegramBot\Exception\TelegramException; 30 | use Longman\TelegramBot\Request; 31 | use Longman\TelegramBot\TelegramLog; 32 | 33 | class DateCommand extends UserCommand 34 | { 35 | /** 36 | * @var string 37 | */ 38 | protected $name = 'date'; 39 | 40 | /** 41 | * @var string 42 | */ 43 | protected $description = 'Show date/time by location'; 44 | 45 | /** 46 | * @var string 47 | */ 48 | protected $usage = '/date '; 49 | 50 | /** 51 | * @var string 52 | */ 53 | protected $version = '1.5.0'; 54 | 55 | /** 56 | * Guzzle Client object 57 | * 58 | * @var Client 59 | */ 60 | private $client; 61 | 62 | /** 63 | * Base URI for Google Maps API 64 | * 65 | * @var string 66 | */ 67 | private $google_api_base_uri = 'https://maps.googleapis.com/maps/api/'; 68 | 69 | /** 70 | * The Google API Key from the command config 71 | * 72 | * @var string 73 | */ 74 | private $google_api_key; 75 | 76 | /** 77 | * Date format 78 | * 79 | * @var string 80 | */ 81 | private $date_format = 'd-m-Y H:i:s'; 82 | 83 | /** 84 | * Get coordinates of passed location 85 | * 86 | * @param string $location 87 | * 88 | * @return array 89 | */ 90 | private function getCoordinates($location): array 91 | { 92 | $path = 'geocode/json'; 93 | $query = ['address' => urlencode($location)]; 94 | 95 | if ($this->google_api_key !== null) { 96 | $query['key'] = $this->google_api_key; 97 | } 98 | 99 | try { 100 | $response = $this->client->get($path, ['query' => $query]); 101 | } catch (RequestException $e) { 102 | TelegramLog::error($e->getMessage()); 103 | 104 | return []; 105 | } 106 | 107 | if (!($data = $this->validateResponseData($response->getBody()))) { 108 | return []; 109 | } 110 | 111 | $result = $data['results'][0]; 112 | $lat = $result['geometry']['location']['lat']; 113 | $lng = $result['geometry']['location']['lng']; 114 | $acc = $result['geometry']['location_type']; 115 | $types = $result['types']; 116 | 117 | return [$lat, $lng, $acc, $types]; 118 | } 119 | 120 | /** 121 | * Get date for location passed via coordinates 122 | * 123 | * @param string $lat 124 | * @param string $lng 125 | * 126 | * @return array 127 | * @throws \Exception 128 | */ 129 | private function getDate($lat, $lng): array 130 | { 131 | $path = 'timezone/json'; 132 | 133 | $date_utc = new \DateTimeImmutable(null, new \DateTimeZone('UTC')); 134 | $timestamp = $date_utc->format('U'); 135 | 136 | $query = [ 137 | 'location' => urlencode($lat) . ',' . urlencode($lng), 138 | 'timestamp' => urlencode($timestamp), 139 | ]; 140 | 141 | if ($this->google_api_key !== null) { 142 | $query['key'] = $this->google_api_key; 143 | } 144 | 145 | try { 146 | $response = $this->client->get($path, ['query' => $query]); 147 | } catch (RequestException $e) { 148 | TelegramLog::error($e->getMessage()); 149 | 150 | return []; 151 | } 152 | 153 | if (!($data = $this->validateResponseData($response->getBody()))) { 154 | return []; 155 | } 156 | 157 | $local_time = $timestamp + $data['rawOffset'] + $data['dstOffset']; 158 | 159 | return [$local_time, $data['timeZoneId']]; 160 | } 161 | 162 | /** 163 | * Evaluate the response data and see if the request was successful 164 | * 165 | * @param string $data 166 | * 167 | * @return array 168 | */ 169 | private function validateResponseData($data): array 170 | { 171 | if (empty($data)) { 172 | return []; 173 | } 174 | 175 | $data = json_decode($data, true); 176 | if (empty($data)) { 177 | return []; 178 | } 179 | 180 | if (isset($data['status']) && $data['status'] !== 'OK') { 181 | return []; 182 | } 183 | 184 | return $data; 185 | } 186 | 187 | /** 188 | * Get formatted date at the passed location 189 | * 190 | * @param string $location 191 | * 192 | * @return string 193 | * @throws \Exception 194 | */ 195 | private function getFormattedDate($location): string 196 | { 197 | if ($location === null || $location === '') { 198 | return 'The time in nowhere is never'; 199 | } 200 | 201 | [$lat, $lng] = $this->getCoordinates($location); 202 | if (empty($lat) || empty($lng)) { 203 | return 'It seems that in "' . $location . '" they do not have a concept of time.'; 204 | } 205 | 206 | [$local_time, $timezone_id] = $this->getDate($lat, $lng); 207 | 208 | $date_utc = new \DateTimeImmutable(gmdate('Y-m-d H:i:s', $local_time), new \DateTimeZone($timezone_id)); 209 | 210 | return 'The local time in ' . $timezone_id . ' is: ' . $date_utc->format($this->date_format); 211 | } 212 | 213 | /** 214 | * Main command execution 215 | * 216 | * @return ServerResponse 217 | * @throws TelegramException 218 | */ 219 | public function execute(): ServerResponse 220 | { 221 | // First we set up the necessary member variables. 222 | $this->client = new Client(['base_uri' => $this->google_api_base_uri]); 223 | if (($this->google_api_key = trim($this->getConfig('google_api_key'))) === '') { 224 | $this->google_api_key = null; 225 | } 226 | 227 | $message = $this->getMessage(); 228 | 229 | $chat_id = $message->getChat()->getId(); 230 | $location = $message->getText(true); 231 | 232 | $text = 'You must specify location in format: /date '; 233 | 234 | if ($location !== '') { 235 | $text = $this->getFormattedDate($location); 236 | } 237 | 238 | $data = [ 239 | 'chat_id' => $chat_id, 240 | 'text' => $text, 241 | ]; 242 | 243 | return Request::sendMessage($data); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /Commands/Config/README.md: -------------------------------------------------------------------------------- 1 | # Config 2 | 3 | Custom configurations can be passed to commands that support them. 4 | 5 | This feature is mainly used to pass secrets or special values to the commands. 6 | 7 | ## Adding configurations to your config 8 | 9 | It is very easy to add configurations to `config.php`: 10 | ```php 11 | 'commands' => [ 12 | 'configs' => [ 13 | 'yourcommand' => ['your_config_key' => 'your_config_value'], 14 | ], 15 | ], 16 | ``` 17 | 18 | Alternatively, you can set them directly via code in your `hook.php`: 19 | ```php 20 | $telegram->setCommandConfig('yourcommand', ['your_config_key' => 'your_config_value']); 21 | ``` 22 | 23 | ## Reading configurations in your command 24 | 25 | To read any command configurations, you can fetch them from within your command like this: 26 | ```php 27 | $my_config = $this->getConfig('your_config_key'); // 'your_config_value' 28 | ``` 29 | -------------------------------------------------------------------------------- /Commands/Config/WeatherCommand.php: -------------------------------------------------------------------------------- 1 | ['owm_api_key' => 'your_owm_api_key_here'] 21 | */ 22 | 23 | namespace Longman\TelegramBot\Commands\UserCommands; 24 | 25 | use Exception; 26 | use GuzzleHttp\Client; 27 | use GuzzleHttp\Exception\RequestException; 28 | use Longman\TelegramBot\Commands\UserCommand; 29 | use Longman\TelegramBot\Entities\ServerResponse; 30 | use Longman\TelegramBot\Exception\TelegramException; 31 | use Longman\TelegramBot\TelegramLog; 32 | 33 | class WeatherCommand extends UserCommand 34 | { 35 | /** 36 | * @var string 37 | */ 38 | protected $name = 'weather'; 39 | 40 | /** 41 | * @var string 42 | */ 43 | protected $description = 'Show weather by location'; 44 | 45 | /** 46 | * @var string 47 | */ 48 | protected $usage = '/weather '; 49 | 50 | /** 51 | * @var string 52 | */ 53 | protected $version = '1.3.0'; 54 | 55 | /** 56 | * Base URI for OpenWeatherMap API 57 | * 58 | * @var string 59 | */ 60 | private $owm_api_base_uri = 'http://api.openweathermap.org/data/2.5/'; 61 | 62 | /** 63 | * Get weather data using HTTP request 64 | * 65 | * @param string $location 66 | * 67 | * @return string 68 | */ 69 | private function getWeatherData($location): string 70 | { 71 | $client = new Client(['base_uri' => $this->owm_api_base_uri]); 72 | $path = 'weather'; 73 | $query = [ 74 | 'q' => $location, 75 | 'units' => 'metric', 76 | 'APPID' => trim($this->getConfig('owm_api_key')), 77 | ]; 78 | 79 | try { 80 | $response = $client->get($path, ['query' => $query]); 81 | } catch (RequestException $e) { 82 | TelegramLog::error($e->getMessage()); 83 | 84 | return ''; 85 | } 86 | 87 | return (string) $response->getBody(); 88 | } 89 | 90 | /** 91 | * Get weather string from weather data 92 | * 93 | * @param array $data 94 | * 95 | * @return string 96 | */ 97 | private function getWeatherString(array $data): string 98 | { 99 | try { 100 | if (!(isset($data['cod']) && $data['cod'] === 200)) { 101 | return ''; 102 | } 103 | 104 | //http://openweathermap.org/weather-conditions 105 | $conditions = [ 106 | 'clear' => ' ☀️', 107 | 'clouds' => ' ☁️', 108 | 'rain' => ' ☔', 109 | 'drizzle' => ' ☔', 110 | 'thunderstorm' => ' ⚡️', 111 | 'snow' => ' ❄️', 112 | ]; 113 | $conditions_now = strtolower($data['weather'][0]['main']); 114 | 115 | return sprintf( 116 | 'The temperature in %s (%s) is %s°C' . PHP_EOL . 117 | 'Current conditions are: %s%s', 118 | $data['name'], //city 119 | $data['sys']['country'], //country 120 | $data['main']['temp'], //temperature 121 | $data['weather'][0]['description'], //description of weather 122 | $conditions[$conditions_now] ?? '' 123 | ); 124 | } catch (Exception $e) { 125 | TelegramLog::error($e->getMessage()); 126 | 127 | return ''; 128 | } 129 | } 130 | 131 | /** 132 | * Main command execution 133 | * 134 | * @return ServerResponse 135 | * @throws TelegramException 136 | */ 137 | public function execute(): ServerResponse 138 | { 139 | // Check to make sure the required OWM API key has been defined. 140 | $owm_api_key = $this->getConfig('owm_api_key'); 141 | if (empty($owm_api_key)) { 142 | return $this->replyToChat('OpenWeatherMap API key not defined.'); 143 | } 144 | 145 | $location = trim($this->getMessage()->getText(true)); 146 | if ($location === '') { 147 | return $this->replyToChat('You must specify a location as: ' . $this->getUsage()); 148 | } 149 | 150 | $text = 'Cannot find weather for location: ' . $location; 151 | if ($weather_data = json_decode($this->getWeatherData($location), true)) { 152 | $text = $this->getWeatherString($weather_data); 153 | } 154 | return $this->replyToChat($text); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /Commands/Conversation/CancelCommand.php: -------------------------------------------------------------------------------- 1 | removeKeyboard('Nothing to cancel.'); 65 | } 66 | 67 | /** 68 | * Main command execution 69 | * 70 | * @return ServerResponse 71 | * @throws TelegramException 72 | */ 73 | public function execute(): ServerResponse 74 | { 75 | $text = 'No active conversation!'; 76 | 77 | // Cancel current conversation if any 78 | $conversation = new Conversation( 79 | $this->getMessage()->getFrom()->getId(), 80 | $this->getMessage()->getChat()->getId() 81 | ); 82 | 83 | if ($conversation_command = $conversation->getCommand()) { 84 | $conversation->cancel(); 85 | $text = 'Conversation "' . $conversation_command . '" cancelled!'; 86 | } 87 | 88 | return $this->removeKeyboard($text); 89 | } 90 | 91 | /** 92 | * Remove the keyboard and output a text. 93 | * 94 | * @param string $text 95 | * 96 | * @return ServerResponse 97 | * @throws TelegramException 98 | */ 99 | private function removeKeyboard(string $text): ServerResponse 100 | { 101 | return $this->replyToChat($text, [ 102 | 'reply_markup' => Keyboard::remove(['selective' => true]), 103 | ]); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Commands/Conversation/GenericmessageCommand.php: -------------------------------------------------------------------------------- 1 | getMessage(); 71 | 72 | // If a conversation is busy, execute the conversation command after handling the message. 73 | $conversation = new Conversation( 74 | $message->getFrom()->getId(), 75 | $message->getChat()->getId() 76 | ); 77 | 78 | // Fetch conversation command if it exists and execute it. 79 | if ($conversation->exists() && $command = $conversation->getCommand()) { 80 | return $this->telegram->executeCommand($command); 81 | } 82 | 83 | return Request::emptyResponse(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Commands/Conversation/README.md: -------------------------------------------------------------------------------- 1 | # Conversation 2 | 3 | Conversations can be used to create dialogues with users, to collect information in a "conversational" style. 4 | 5 | Look at the [`SurveyCommand`](SurveyCommand.php) to see how a conversation can be made. 6 | 7 | For conversations to work, you must add the code provided in [`GenericmessageCommand.php`](GenericmessageCommand.php) at the beginning of your custom `GenericmessageCommand::execute()` method. 8 | 9 | The [`CancelCommand`](CancelCommand.php) allows users to cancel any active conversation. 10 | -------------------------------------------------------------------------------- /Commands/Conversation/SurveyCommand.php: -------------------------------------------------------------------------------- 1 | getMessage(); 77 | 78 | $chat = $message->getChat(); 79 | $user = $message->getFrom(); 80 | $text = trim($message->getText(true)); 81 | $chat_id = $chat->getId(); 82 | $user_id = $user->getId(); 83 | 84 | // Preparing response 85 | $data = [ 86 | 'chat_id' => $chat_id, 87 | // Remove any keyboard by default 88 | 'reply_markup' => Keyboard::remove(['selective' => true]), 89 | ]; 90 | 91 | if ($chat->isGroupChat() || $chat->isSuperGroup()) { 92 | // Force reply is applied by default so it can work with privacy on 93 | $data['reply_markup'] = Keyboard::forceReply(['selective' => true]); 94 | } 95 | 96 | // Conversation start 97 | $this->conversation = new Conversation($user_id, $chat_id, $this->getName()); 98 | 99 | // Load any existing notes from this conversation 100 | $notes = &$this->conversation->notes; 101 | !is_array($notes) && $notes = []; 102 | 103 | // Load the current state of the conversation 104 | $state = $notes['state'] ?? 0; 105 | 106 | $result = Request::emptyResponse(); 107 | 108 | // State machine 109 | // Every time a step is achieved the state is updated 110 | switch ($state) { 111 | case 0: 112 | if ($text === '') { 113 | $notes['state'] = 0; 114 | $this->conversation->update(); 115 | 116 | $data['text'] = 'Type your name:'; 117 | 118 | $result = Request::sendMessage($data); 119 | break; 120 | } 121 | 122 | $notes['name'] = $text; 123 | $text = ''; 124 | 125 | // No break! 126 | case 1: 127 | if ($text === '') { 128 | $notes['state'] = 1; 129 | $this->conversation->update(); 130 | 131 | $data['text'] = 'Type your surname:'; 132 | 133 | $result = Request::sendMessage($data); 134 | break; 135 | } 136 | 137 | $notes['surname'] = $text; 138 | $text = ''; 139 | 140 | // No break! 141 | case 2: 142 | if ($text === '' || !is_numeric($text)) { 143 | $notes['state'] = 2; 144 | $this->conversation->update(); 145 | 146 | $data['text'] = 'Type your age:'; 147 | if ($text !== '') { 148 | $data['text'] = 'Age must be a number'; 149 | } 150 | 151 | $result = Request::sendMessage($data); 152 | break; 153 | } 154 | 155 | $notes['age'] = $text; 156 | $text = ''; 157 | 158 | // No break! 159 | case 3: 160 | if ($text === '' || !in_array($text, ['M', 'F'], true)) { 161 | $notes['state'] = 3; 162 | $this->conversation->update(); 163 | 164 | $data['reply_markup'] = (new Keyboard(['M', 'F'])) 165 | ->setResizeKeyboard(true) 166 | ->setOneTimeKeyboard(true) 167 | ->setSelective(true); 168 | 169 | $data['text'] = 'Select your gender:'; 170 | if ($text !== '') { 171 | $data['text'] = 'Choose a keyboard option to select your gender'; 172 | } 173 | 174 | $result = Request::sendMessage($data); 175 | break; 176 | } 177 | 178 | $notes['gender'] = $text; 179 | 180 | // No break! 181 | case 4: 182 | if ($message->getLocation() === null) { 183 | $notes['state'] = 4; 184 | $this->conversation->update(); 185 | 186 | $data['reply_markup'] = (new Keyboard( 187 | (new KeyboardButton('Share Location'))->setRequestLocation(true) 188 | )) 189 | ->setOneTimeKeyboard(true) 190 | ->setResizeKeyboard(true) 191 | ->setSelective(true); 192 | 193 | $data['text'] = 'Share your location:'; 194 | 195 | $result = Request::sendMessage($data); 196 | break; 197 | } 198 | 199 | $notes['longitude'] = $message->getLocation()->getLongitude(); 200 | $notes['latitude'] = $message->getLocation()->getLatitude(); 201 | 202 | // No break! 203 | case 5: 204 | if ($message->getPhoto() === null) { 205 | $notes['state'] = 5; 206 | $this->conversation->update(); 207 | 208 | $data['text'] = 'Insert your picture:'; 209 | 210 | $result = Request::sendMessage($data); 211 | break; 212 | } 213 | 214 | $photo = $message->getPhoto()[0]; 215 | $notes['photo_id'] = $photo->getFileId(); 216 | 217 | // No break! 218 | case 6: 219 | if ($message->getContact() === null) { 220 | $notes['state'] = 6; 221 | $this->conversation->update(); 222 | 223 | $data['reply_markup'] = (new Keyboard( 224 | (new KeyboardButton('Share Contact'))->setRequestContact(true) 225 | )) 226 | ->setOneTimeKeyboard(true) 227 | ->setResizeKeyboard(true) 228 | ->setSelective(true); 229 | 230 | $data['text'] = 'Share your contact information:'; 231 | 232 | $result = Request::sendMessage($data); 233 | break; 234 | } 235 | 236 | $notes['phone_number'] = $message->getContact()->getPhoneNumber(); 237 | 238 | // No break! 239 | case 7: 240 | $this->conversation->update(); 241 | $out_text = '/Survey result:' . PHP_EOL; 242 | unset($notes['state']); 243 | foreach ($notes as $k => $v) { 244 | $out_text .= PHP_EOL . ucfirst($k) . ': ' . $v; 245 | } 246 | 247 | $data['photo'] = $notes['photo_id']; 248 | $data['caption'] = $out_text; 249 | 250 | $this->conversation->stop(); 251 | 252 | $result = Request::sendPhoto($data); 253 | break; 254 | } 255 | 256 | return $result; 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /Commands/GenericCommand.php: -------------------------------------------------------------------------------- 1 | getMessage(); 50 | $user_id = $message->getFrom()->getId(); 51 | $command = $message->getCommand(); 52 | 53 | // To enable proper use of the /whois command. 54 | // If the user is an admin and the command is in the format "/whoisXYZ", call the /whois command 55 | if (stripos($command, 'whois') === 0 && $this->telegram->isAdmin($user_id)) { 56 | return $this->telegram->executeCommand('whois'); 57 | } 58 | 59 | return $this->replyToChat("Command /{$command} not found.. :("); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Commands/Group/GenericmessageCommand.php: -------------------------------------------------------------------------------- 1 | getMessage(); 54 | 55 | // Handle new chat members 56 | if ($message->getNewChatMembers()) { 57 | return $this->getTelegram()->executeCommand('newchatmembers'); 58 | } 59 | 60 | // Handle left chat members 61 | if ($message->getLeftChatMember()) { 62 | return $this->getTelegram()->executeCommand('leftchatmember'); 63 | } 64 | 65 | // The chat photo was changed 66 | if ($new_chat_photo = $message->getNewChatPhoto()) { 67 | // Whatever... 68 | } 69 | 70 | // The chat title was changed 71 | if ($new_chat_title = $message->getNewChatTitle()) { 72 | // Whatever... 73 | } 74 | 75 | // A message has been pinned 76 | if ($pinned_message = $message->getPinnedMessage()) { 77 | // Whatever... 78 | } 79 | 80 | return Request::emptyResponse(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Commands/Group/LeftchatmemberCommand.php: -------------------------------------------------------------------------------- 1 | getMessage(); 54 | $member = $message->getLeftChatMember(); 55 | 56 | return $this->replyToChat('Sorry to see you go, ' . $member->getFirstName()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Commands/Group/NewchatmembersCommand.php: -------------------------------------------------------------------------------- 1 | getMessage(); 54 | $members = $message->getNewChatMembers(); 55 | 56 | if ($message->botAddedInChat()) { 57 | return $this->replyToChat('Hi there, you BOT!'); 58 | } 59 | 60 | $member_names = []; 61 | foreach ($members as $member) { 62 | $member_names[] = $member->tryMention(); 63 | } 64 | 65 | return $this->replyToChat('Hi ' . implode(', ', $member_names) . '!'); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Commands/Group/README.md: -------------------------------------------------------------------------------- 1 | # Group or Channel 2 | 3 | Requests specific to groups and channels all get handled in [`GenericmessageCommand.php`](GenericmessageCommand.php). 4 | 5 | The two extra commands [`NewchatmembersCommand`](NewchatmembersCommand.php) and [`LeftchatmemberCommand`](LeftchatmemberCommand.php) are simply files that can be called as commands from within a command, not by a user. 6 | Have a look at [`GenericmessageCommand.php`](GenericmessageCommand.php) to understand what you can do. 7 | -------------------------------------------------------------------------------- /Commands/InlineMode/ChoseninlineresultCommand.php: -------------------------------------------------------------------------------- 1 | getChosenInlineResult(); 50 | $query = $inline_query->getQuery(); 51 | 52 | return parent::execute(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Commands/InlineMode/InlinequeryCommand.php: -------------------------------------------------------------------------------- 1 | getInlineQuery(); 54 | $query = $inline_query->getQuery(); 55 | 56 | $results = []; 57 | 58 | if ($query !== '') { 59 | // https://core.telegram.org/bots/api#inlinequeryresultarticle 60 | $results[] = new InlineQueryResultArticle([ 61 | 'id' => '001', 62 | 'title' => 'Simple text using InputTextMessageContent', 63 | 'description' => 'this will return Text', 64 | 65 | // Here you can put any other Input...MessageContent you like. 66 | // It will keep the style of an article, but post the specific message type back to the user. 67 | 'input_message_content' => new InputTextMessageContent([ 68 | 'message_text' => 'The query that got you here: ' . $query, 69 | ]), 70 | ]); 71 | 72 | // https://core.telegram.org/bots/api#inlinequeryresultcontact 73 | $results[] = new InlineQueryResultContact([ 74 | 'id' => '002', 75 | 'phone_number' => '12345678', 76 | 'first_name' => 'Best', 77 | 'last_name' => 'Friend', 78 | ]); 79 | 80 | // https://core.telegram.org/bots/api#inlinequeryresultlocation 81 | $results[] = new InlineQueryResultLocation([ 82 | 'id' => '003', 83 | 'title' => 'The center of the world!', 84 | 'latitude' => 40.866667, 85 | 'longitude' => 34.566667, 86 | ]); 87 | 88 | // https://core.telegram.org/bots/api#inlinequeryresultvenue 89 | $results[] = new InlineQueryResultVenue([ 90 | 'id' => '004', 91 | 'title' => 'No-Mans-Land', 92 | 'address' => 'In the middle of Nowhere', 93 | 'latitude' => 33, 94 | 'longitude' => -33, 95 | ]); 96 | } 97 | 98 | return $inline_query->answer($results); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Commands/InlineMode/README.md: -------------------------------------------------------------------------------- 1 | # Inline Mode 2 | 3 | The files in this folder demonstrate how to use the [inline mode](https://core.telegram.org/bots/api#inline-mode) of your bot. 4 | 5 | The [`InlinequeryCommand.php`](InlinequeryCommand.php) catches any inline queries and answers with a set of results. 6 | 7 | When a result is selected, this selection is then handled by [`ChoseninlineresultCommand.php`](ChoseninlineresultCommand.php). 8 | -------------------------------------------------------------------------------- /Commands/Keyboard/CallbackqueryCommand.php: -------------------------------------------------------------------------------- 1 | getCallbackQuery(); 53 | $callback_data = $callback_query->getData(); 54 | 55 | return $callback_query->answer([ 56 | 'text' => 'Content of the callback data: ' . $callback_data, 57 | 'show_alert' => (bool) random_int(0, 1), // Randomly show (or not) as an alert. 58 | 'cache_time' => 5, 59 | ]); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Commands/Keyboard/ForcereplyCommand.php: -------------------------------------------------------------------------------- 1 | replyToChat('Write something in reply:', [ 57 | 'reply_markup' => Keyboard::forceReply(), 58 | ]); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Commands/Keyboard/HidekeyboardCommand.php: -------------------------------------------------------------------------------- 1 | replyToChat('Keyboard Hidden', [ 58 | 'reply_markup' => Keyboard::remove(), 59 | ]); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Commands/Keyboard/InlinekeyboardCommand.php: -------------------------------------------------------------------------------- 1 | 'Inline Query (current chat)', 'switch_inline_query_current_chat' => 'inline query...'], 62 | ['text' => 'Inline Query (other chat)', 'switch_inline_query' => 'inline query...'], 63 | ], [ 64 | ['text' => 'Callback', 'callback_data' => 'identifier'], 65 | ['text' => 'Open URL', 'url' => 'https://github.com/php-telegram-bot/example-bot'], 66 | ]); 67 | 68 | return $this->replyToChat('Inline Keyboard', [ 69 | 'reply_markup' => $inline_keyboard, 70 | ]); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Commands/Keyboard/KeyboardCommand.php: -------------------------------------------------------------------------------- 1 | 'A'], 81 | 'B', 82 | ['C', 'D'] 83 | ); 84 | 85 | // Buttons to perform Contact or Location sharing 86 | $keyboards[] = new Keyboard([ 87 | ['text' => 'Send my contact', 'request_contact' => true], 88 | ['text' => 'Send my location', 'request_location' => true], 89 | ]); 90 | 91 | // Shuffle our example keyboards and return a random one 92 | shuffle($keyboards); 93 | $keyboard = end($keyboards) 94 | ->setResizeKeyboard(true) 95 | ->setOneTimeKeyboard(true) 96 | ->setSelective(false); 97 | 98 | return $this->replyToChat('Press a Button!', [ 99 | 'reply_markup' => $keyboard, 100 | ]); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Commands/Keyboard/README.md: -------------------------------------------------------------------------------- 1 | # Keyboards 2 | 3 | The files in this folder demonstrate how to create normal and inline keyboards. 4 | 5 | ## Normal Keyboard 6 | 7 | Have a look at [`KeyboardCommand.php`](KeyboardCommand.php) for usage examples. 8 | 9 | ## Inline Keyboard 10 | 11 | Have a look at [`InlinekeyboardCommand.php`](InlinekeyboardCommand.php) for usage examples. 12 | 13 | To handle inline keyboard buttons, you need to handle all callbacks inside [`CallbackqueryCommand.php`](CallbackqueryCommand.php). 14 | -------------------------------------------------------------------------------- /Commands/Message/ChannelpostCommand.php: -------------------------------------------------------------------------------- 1 | getChannelPost(); 50 | 51 | return parent::execute(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Commands/Message/EditedchannelpostCommand.php: -------------------------------------------------------------------------------- 1 | getEditedChannelPost(); 50 | 51 | return parent::execute(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Commands/Message/EditedmessageCommand.php: -------------------------------------------------------------------------------- 1 | getEditedMessage(); 49 | 50 | return parent::execute(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Commands/Message/EditmessageCommand.php: -------------------------------------------------------------------------------- 1 | getMessage(); 57 | $chat_id = $message->getChat()->getId(); 58 | $reply_to_message = $message->getReplyToMessage(); 59 | $text = $message->getText(true); 60 | 61 | if ($reply_to_message && $message_to_edit = $reply_to_message->getMessageId()) { 62 | // Try to edit the selected message. 63 | $result = Request::editMessageText([ 64 | 'chat_id' => $chat_id, 65 | 'message_id' => $message_to_edit, 66 | 'text' => $text ?: 'Edited message', 67 | ]); 68 | 69 | // If successful, delete this editing reply message. 70 | if ($result->isOk()) { 71 | Request::deleteMessage([ 72 | 'chat_id' => $chat_id, 73 | 'message_id' => $message->getMessageId(), 74 | ]); 75 | } 76 | 77 | return $result; 78 | } 79 | 80 | return $this->replyToChat(sprintf("Reply to any bots' message and use /%s to edit it.", $this->getName())); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Commands/Message/GenericmessageCommand.php: -------------------------------------------------------------------------------- 1 | getMessage(); 52 | 53 | /** 54 | * Handle any kind of message here 55 | */ 56 | 57 | $message_text = $message->getText(true); 58 | 59 | return Request::emptyResponse(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Commands/Message/README.md: -------------------------------------------------------------------------------- 1 | # Message 2 | 3 | You bot can handle all types of messages. 4 | 5 | ## Private and Group chats 6 | 7 | Messages in private and group chats get handled by [`GenericmessageCommand.php`](GenericmessageCommand.php). 8 | When a message gets edited, it gets handled by [`EditedmessageCommand.php`](EditedmessageCommand.php) 9 | 10 | (Have a look at [`EditmessageCommand.php`](EditmessageCommand.php) for an example of how to edit messages via your bot) 11 | 12 | ## Channels 13 | 14 | For channels, the messages (or posts) get handled by [`ChannelpostCommand.php`](ChannelpostCommand.php). 15 | When a channel post gets edited, it gets handled by [`EditedchannelpostCommand.php`](EditedchannelpostCommand.php) 16 | -------------------------------------------------------------------------------- /Commands/Other/EchoCommand.php: -------------------------------------------------------------------------------- 1 | '; 41 | 42 | /** 43 | * @var string 44 | */ 45 | protected $version = '1.2.0'; 46 | 47 | /** 48 | * Main command execution 49 | * 50 | * @return ServerResponse 51 | * @throws TelegramException 52 | */ 53 | public function execute(): ServerResponse 54 | { 55 | $message = $this->getMessage(); 56 | $text = $message->getText(true); 57 | 58 | if ($text === '') { 59 | return $this->replyToChat('Command usage: ' . $this->getUsage()); 60 | } 61 | 62 | return $this->replyToChat($text); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Commands/Other/HelpCommand.php: -------------------------------------------------------------------------------- 1 | '; 42 | 43 | /** 44 | * @var string 45 | */ 46 | protected $version = '1.4.0'; 47 | 48 | /** 49 | * Main command execution 50 | * 51 | * @return ServerResponse 52 | * @throws TelegramException 53 | */ 54 | public function execute(): ServerResponse 55 | { 56 | $message = $this->getMessage(); 57 | $command_str = trim($message->getText(true)); 58 | 59 | // Admin commands shouldn't be shown in group chats 60 | $safe_to_show = $message->getChat()->isPrivateChat(); 61 | 62 | [$all_commands, $user_commands, $admin_commands] = $this->getUserAndAdminCommands(); 63 | 64 | // If no command parameter is passed, show the list. 65 | if ($command_str === '') { 66 | $text = '*Commands List*:' . PHP_EOL; 67 | foreach ($user_commands as $user_command) { 68 | $text .= '/' . $user_command->getName() . ' - ' . $user_command->getDescription() . PHP_EOL; 69 | } 70 | 71 | if ($safe_to_show && count($admin_commands) > 0) { 72 | $text .= PHP_EOL . '*Admin Commands List*:' . PHP_EOL; 73 | foreach ($admin_commands as $admin_command) { 74 | $text .= '/' . $admin_command->getName() . ' - ' . $admin_command->getDescription() . PHP_EOL; 75 | } 76 | } 77 | 78 | $text .= PHP_EOL . 'For exact command help type: /help '; 79 | 80 | return $this->replyToChat($text, ['parse_mode' => 'markdown']); 81 | } 82 | 83 | $command_str = str_replace('/', '', $command_str); 84 | if (isset($all_commands[$command_str]) && ($safe_to_show || !$all_commands[$command_str]->isAdminCommand())) { 85 | $command = $all_commands[$command_str]; 86 | 87 | return $this->replyToChat(sprintf( 88 | 'Command: %s (v%s)' . PHP_EOL . 89 | 'Description: %s' . PHP_EOL . 90 | 'Usage: %s', 91 | $command->getName(), 92 | $command->getVersion(), 93 | $command->getDescription(), 94 | $command->getUsage() 95 | ), ['parse_mode' => 'markdown']); 96 | } 97 | 98 | return $this->replyToChat('No help available: Command `/' . $command_str . '` not found', ['parse_mode' => 'markdown']); 99 | } 100 | 101 | /** 102 | * Get all available User and Admin commands to display in the help list. 103 | * 104 | * @return Command[][] 105 | * @throws TelegramException 106 | */ 107 | protected function getUserAndAdminCommands(): array 108 | { 109 | /** @var Command[] $all_commands */ 110 | $all_commands = $this->telegram->getCommandsList(); 111 | 112 | // Only get enabled Admin and User commands that are allowed to be shown. 113 | $commands = array_filter($all_commands, function ($command): bool { 114 | return !$command->isSystemCommand() && $command->showInHelp() && $command->isEnabled(); 115 | }); 116 | 117 | // Filter out all User commands 118 | $user_commands = array_filter($commands, function ($command): bool { 119 | return $command->isUserCommand(); 120 | }); 121 | 122 | // Filter out all Admin commands 123 | $admin_commands = array_filter($commands, function ($command): bool { 124 | return $command->isAdminCommand(); 125 | }); 126 | 127 | ksort($commands); 128 | ksort($user_commands); 129 | ksort($admin_commands); 130 | 131 | return [$commands, $user_commands, $admin_commands]; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Commands/Other/ImageCommand.php: -------------------------------------------------------------------------------- 1 | getMessage(); 57 | 58 | // Use any extra parameters as the caption text. 59 | $caption = trim($message->getText(true)); 60 | 61 | // Make sure the Upload path has been defined and exists. 62 | $upload_path = $this->telegram->getUploadPath(); 63 | if (!is_dir($upload_path)) { 64 | return $this->replyToChat('Upload path has not been defined or does not exist.'); 65 | } 66 | 67 | // Get a random picture from the Upload path. 68 | $random_image = $this->getRandomImagePath($upload_path); 69 | if ('' === $random_image) { 70 | return $this->replyToChat('No image found!'); 71 | } 72 | 73 | // If no caption is set, use the filename. 74 | if ('' === $caption) { 75 | $caption = basename($random_image); 76 | } 77 | 78 | return Request::sendPhoto([ 79 | 'chat_id' => $message->getFrom()->getId(), 80 | 'caption' => $caption, 81 | 'photo' => $random_image, 82 | ]); 83 | } 84 | 85 | /** 86 | * Return the path to a random image in the passed directory. 87 | * 88 | * @param string $dir 89 | * 90 | * @return string 91 | */ 92 | private function getRandomImagePath($dir): string 93 | { 94 | if (!is_dir($dir)) { 95 | return ''; 96 | } 97 | 98 | // Filter the file list to only return images. 99 | $image_list = array_filter(scandir($dir), function ($file) { 100 | $extension = pathinfo($file, PATHINFO_EXTENSION); 101 | return in_array($extension, ['png', 'jpg', 'jpeg', 'gif']); 102 | }); 103 | if (!empty($image_list)) { 104 | shuffle($image_list); 105 | return $dir . '/' . $image_list[0]; 106 | } 107 | 108 | return ''; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Commands/Other/MarkdownCommand.php: -------------------------------------------------------------------------------- 1 | replyToChat(' 57 | *bold* _italic_ `inline fixed width code` 58 | 59 | ``` 60 | preformatted code block 61 | code block 62 | ``` 63 | 64 | [Best Telegram bot api!!](https://github.com/php-telegram-bot/core)', [ 65 | 'parse_mode' => 'markdown', 66 | ]); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Commands/Other/README.md: -------------------------------------------------------------------------------- 1 | # Other Commands 2 | 3 | In this folder you can find a few example commands, that demonstrate how to use different features of the library. 4 | 5 | Best just take a look at them and see what they do! 6 | -------------------------------------------------------------------------------- /Commands/Other/SlapCommand.php: -------------------------------------------------------------------------------- 1 | '; 41 | 42 | /** 43 | * @var string 44 | */ 45 | protected $version = '1.2.0'; 46 | 47 | /** 48 | * Main command execution 49 | * 50 | * @return ServerResponse 51 | * @throws TelegramException 52 | */ 53 | public function execute(): ServerResponse 54 | { 55 | $message = $this->getMessage(); 56 | $text = $message->getText(true); 57 | 58 | $sender = '@' . $message->getFrom()->getUsername(); 59 | 60 | // Username validation (simply checking for `@something` in the text) 61 | if (0 === preg_match('/@[\w_]{5,}/', $text)) { 62 | return $this->replyToChat('Sorry, no one to slap around...'); 63 | } 64 | 65 | return $this->replyToChat($sender . ' slaps ' . $text . ' around a bit with a large trout'); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Commands/Other/UploadCommand.php: -------------------------------------------------------------------------------- 1 | getMessage(); 68 | $chat = $message->getChat(); 69 | $chat_id = $chat->getId(); 70 | $user_id = $message->getFrom()->getId(); 71 | 72 | // Make sure the Download path has been defined and exists 73 | $download_path = $this->telegram->getDownloadPath(); 74 | if (!is_dir($download_path)) { 75 | return $this->replyToChat('Download path has not been defined or does not exist.'); 76 | } 77 | 78 | // Initialise the data array for the response 79 | $data = ['chat_id' => $chat_id]; 80 | 81 | if ($chat->isGroupChat() || $chat->isSuperGroup()) { 82 | // Reply to message id is applied by default 83 | $data['reply_to_message_id'] = $message->getMessageId(); 84 | // Force reply is applied by default to work with privacy on 85 | $data['reply_markup'] = Keyboard::forceReply(['selective' => true]); 86 | } 87 | 88 | // Start conversation 89 | $conversation = new Conversation($user_id, $chat_id, $this->getName()); 90 | $message_type = $message->getType(); 91 | 92 | if (in_array($message_type, ['audio', 'document', 'photo', 'video', 'voice'], true)) { 93 | $doc = $message->{'get' . ucfirst($message_type)}(); 94 | 95 | // For photos, get the best quality! 96 | ($message_type === 'photo') && $doc = end($doc); 97 | 98 | $file_id = $doc->getFileId(); 99 | $file = Request::getFile(['file_id' => $file_id]); 100 | if ($file->isOk() && Request::downloadFile($file->getResult())) { 101 | $data['text'] = $message_type . ' file is located at: ' . $download_path . '/' . $file->getResult()->getFilePath(); 102 | } else { 103 | $data['text'] = 'Failed to download.'; 104 | } 105 | 106 | $conversation->notes['file_id'] = $file_id; 107 | $conversation->update(); 108 | $conversation->stop(); 109 | } else { 110 | $data['text'] = 'Please upload the file now'; 111 | } 112 | 113 | return Request::sendMessage($data); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Commands/Other/WhoamiCommand.php: -------------------------------------------------------------------------------- 1 | getMessage(); 64 | 65 | $from = $message->getFrom(); 66 | $user_id = $from->getId(); 67 | $chat_id = $message->getChat()->getId(); 68 | $message_id = $message->getMessageId(); 69 | 70 | $data = [ 71 | 'chat_id' => $chat_id, 72 | 'reply_to_message_id' => $message_id, 73 | ]; 74 | 75 | // Send chat action "typing..." 76 | Request::sendChatAction([ 77 | 'chat_id' => $chat_id, 78 | 'action' => ChatAction::TYPING, 79 | ]); 80 | 81 | $caption = sprintf( 82 | 'Your Id: %d' . PHP_EOL . 83 | 'Name: %s %s' . PHP_EOL . 84 | 'Username: %s', 85 | $user_id, 86 | $from->getFirstName(), 87 | $from->getLastName(), 88 | $from->getUsername() 89 | ); 90 | 91 | // Fetch the most recent user profile photo 92 | $limit = 1; 93 | $offset = null; 94 | 95 | $user_profile_photos_response = Request::getUserProfilePhotos([ 96 | 'user_id' => $user_id, 97 | 'limit' => $limit, 98 | 'offset' => $offset, 99 | ]); 100 | 101 | if ($user_profile_photos_response->isOk()) { 102 | /** @var UserProfilePhotos $user_profile_photos */ 103 | $user_profile_photos = $user_profile_photos_response->getResult(); 104 | 105 | if ($user_profile_photos->getTotalCount() > 0) { 106 | $photos = $user_profile_photos->getPhotos(); 107 | 108 | // Get the best quality of the profile photo 109 | $photo = end($photos[0]); 110 | $file_id = $photo->getFileId(); 111 | 112 | $data['photo'] = $file_id; 113 | $data['caption'] = $caption; 114 | 115 | return Request::sendPhoto($data); 116 | } 117 | } 118 | 119 | // No Photo just send text 120 | $data['text'] = $caption; 121 | 122 | return Request::sendMessage($data); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Commands/Payments/GenericmessageCommand.php: -------------------------------------------------------------------------------- 1 | getMessage(); 51 | $user_id = $message->getFrom()->getId(); 52 | 53 | // Handle successful payment 54 | if ($payment = $message->getSuccessfulPayment()) { 55 | return PaymentCommand::handleSuccessfulPayment($payment, $user_id); 56 | } 57 | 58 | return Request::emptyResponse(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Commands/Payments/PaymentCommand.php: -------------------------------------------------------------------------------- 1 | ['payment_provider_token' => 'your_payment_provider_token_here'] 25 | * 26 | * You will also need to copy the `Precheckoutquerycommand.php` file. 27 | */ 28 | 29 | namespace Longman\TelegramBot\Commands\UserCommands; 30 | 31 | use Longman\TelegramBot\Commands\UserCommand; 32 | use Longman\TelegramBot\Entities\Payments\LabeledPrice; 33 | use Longman\TelegramBot\Entities\Payments\SuccessfulPayment; 34 | use Longman\TelegramBot\Entities\ServerResponse; 35 | use Longman\TelegramBot\Exception\TelegramException; 36 | use Longman\TelegramBot\Request; 37 | 38 | class PaymentCommand extends UserCommand 39 | { 40 | /** 41 | * @var string 42 | */ 43 | protected $name = 'payment'; 44 | 45 | /** 46 | * @var string 47 | */ 48 | protected $description = 'Create an invoice for the user using Telegram Payments'; 49 | 50 | /** 51 | * @var string 52 | */ 53 | protected $usage = '/payment'; 54 | 55 | /** 56 | * @var string 57 | */ 58 | protected $version = '0.1.0'; 59 | 60 | /** 61 | * Main command execution 62 | * 63 | * @return ServerResponse 64 | */ 65 | public function execute(): ServerResponse 66 | { 67 | // Who to send this invoice to. (Use the current user.) 68 | $chat_id = $this->getMessage()->getFrom()->getId(); 69 | 70 | // The currency of this invoice. 71 | // Supported currencies: https://core.telegram.org/bots/payments#supported-currencies 72 | $currency = 'EUR'; 73 | 74 | // List all items that will be shown on your invoice. 75 | // Amounts are in cents. So 1 Euro would be put as 100. 76 | $prices = [ 77 | new LabeledPrice(['label' => 'Small thing', 'amount' => 100]), // 1€ 78 | new LabeledPrice(['label' => 'Bigger thing', 'amount' => 2000]), // 20€ 79 | new LabeledPrice(['label' => 'Huge thing', 'amount' => 50000]), // 500€ 80 | ]; 81 | 82 | // Request a shipping address if necessary. 83 | $need_shipping_address = false; 84 | 85 | // If you have flexible pricing, depending on the shipping method chosen, set this to true. 86 | // You will also need to copy and adapt the `ShippingqueryCommand.php` file. 87 | $is_flexible = false; 88 | 89 | // Send the actual invoice! 90 | // Adjust any parameters to your needs. 91 | return Request::sendInvoice([ 92 | 'chat_id' => $chat_id, 93 | 'title' => 'Payment with PHP Telegram Bot', 94 | 'description' => 'A simple invoice to test Telegram Payments', 95 | 'payload' => 'payment_demo', 96 | 'start_parameter' => 'payment_demo', 97 | 'provider_token' => $this->getConfig('payment_provider_token'), 98 | 'currency' => $currency, 99 | 'prices' => $prices, 100 | 'need_shipping_address' => $need_shipping_address, 101 | 'is_flexible' => $is_flexible, 102 | ]); 103 | } 104 | 105 | /** 106 | * Send "Thank you" message to user who paid 107 | * 108 | * You will need to add some code to your custom `GenericmessageCommand::execute()` method. 109 | * Check the `GenericmessageCommand.php` file included in this folder. 110 | * 111 | * @param SuccessfulPayment $payment 112 | * @param int $user_id 113 | * 114 | * @return ServerResponse 115 | * @throws TelegramException 116 | */ 117 | public static function handleSuccessfulPayment($payment, $user_id): ServerResponse 118 | { 119 | // Send a message to the user after they have completed the payment. 120 | return Request::sendMessage([ 121 | 'chat_id' => $user_id, 122 | 'text' => 'Thank you for your order!', 123 | ]); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Commands/Payments/PrecheckoutqueryCommand.php: -------------------------------------------------------------------------------- 1 | getPreCheckoutQuery()->answer(true); 51 | 52 | // If we do make certain checks, you can define the error message displayed to the user like this. 53 | // return $this->getPreCheckoutQuery()->answer(false, [ 54 | // 'error_message' => 'Registration (or whatever) required...', 55 | // ]); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Commands/Payments/README.md: -------------------------------------------------------------------------------- 1 | # Telegram Payments 2 | 3 | With the files in this folder, you can create and send invoices to your users, require their shipping details, define custom / flexible shipping methods and send a confirmation message after payment. 4 | 5 | ## Enable payments for your bot 6 | 7 | Read all about Telegram Payments and follow the setup guide here: 8 | https://core.telegram.org/bots/payments 9 | 10 | ## Configuring the `/payment` command 11 | 12 | First of all, as a bare minimum, you need to copy the [`PaymentCommand.php`](PaymentCommand.php) and [`PrecheckoutqueryCommand.php`](PrecheckoutqueryCommand.php) files in this folder to your custom commands folder. 13 | 14 | If you want to allow flexible shipping options, you will also need to copy [`ShippingqueryCommand.php`](ShippingqueryCommand.php) to your custom commands folder. 15 | 16 | Should you want to send a message on a successful payment, you will need to copy the [`GenericmessageCommand.php`](GenericmessageCommand.php) file as well. 17 | If you already have a `GenericmessageCommand.php` file, you'll need to copy the code from the `execute` method into your file. 18 | 19 | Next, you will need to add the Payment Provider Token (that you received in the previous step when linking your bot), to your `hook.php` or `manager.php` config. 20 | 21 | For `hook.php`: 22 | ```php 23 | $telegram->setCommandConfig('payment', ['payment_provider_token' => 'your_payment_provider_token_here']); 24 | ``` 25 | 26 | For `manager.php` or using a general `config.php`, in the config array add: 27 | ```php 28 | ... 29 | 'commands' => [ 30 | 'configs' => [ 31 | 'payment' => ['payment_provider_token' => 'your_payment_provider_token_here'], 32 | ], 33 | ], 34 | ... 35 | ``` 36 | 37 | Now, when sending the `/payment` command to your bot, you should receive an invoice. 38 | 39 | Have fun with Telegram Payments! 40 | -------------------------------------------------------------------------------- /Commands/Payments/ShippingqueryCommand.php: -------------------------------------------------------------------------------- 1 | getShippingQuery()->answer(true, [ 56 | 'shipping_options' => [ 57 | new ShippingOption([ 58 | 'id' => 'basic', 59 | 'title' => 'Basic Shipping', 60 | 'prices' => [ 61 | new LabeledPrice(['label' => 'Basic Shipping', 'amount' => 800]), 62 | ], 63 | ]), 64 | new ShippingOption([ 65 | 'id' => 'premium', 66 | 'title' => 'Premium Shipping', 67 | 'prices' => [ 68 | new LabeledPrice(['label' => 'Premium Shipping', 'amount' => 1500]), 69 | new LabeledPrice(['label' => 'Extra speedy', 'amount' => 300]), 70 | ], 71 | ]), 72 | ], 73 | ]); 74 | 75 | // If we do make certain checks, you can define the error message displayed to the user like this. 76 | // return $this->getShippingQuery()->answer(false, [ 77 | // 'error_message' => 'We do not ship to your location :-(', 78 | // ]); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Commands/README.MD: -------------------------------------------------------------------------------- 1 | This Folder contains some examples of how to use the Telegram Bot API with the Telegram PHP Bot library 2 | 3 | 4 | **:exclamation: DO NOT USE THE COMMANDS FOLDER** 5 | 6 | As the title says, do not include the Commands folder into your config to use it as it is. 7 | 8 | Why? This folder contains some files multiple times, which is an issue for the bot software, since every file can only exist once! 9 | 10 | For example: GenericmessageCommand.php 11 | This file exists in the following folder: 12 | - Conversation 13 | - Group 14 | - Message 15 | - Payments 16 | 17 | Having any Command file more than once will cause conflicts between those file, causing only one file to be executed and the others ignored. 18 | 19 | Please copy each file and/or folder that you need into the CustomCommans folder. 20 | 21 | If you want to create your own Command file, please do it so in the CustomCommands folder as well. 22 | 23 | If you need, for example, the GenericmessageCommand.php from the Conversation and Group folder, you will need to compare and merge those files. 24 | 25 | -------------------------------------------------------------------------------- /Commands/ServiceMessages/GenericmessageCommand.php: -------------------------------------------------------------------------------- 1 | getMessage(); 52 | 53 | /** 54 | * Catch and handle any service messages here. 55 | */ 56 | 57 | // The chat photo was deleted 58 | $delete_chat_photo = $message->getDeleteChatPhoto(); 59 | 60 | // The group has been created 61 | $group_chat_created = $message->getGroupChatCreated(); 62 | 63 | // The supergroup has been created 64 | $supergroup_chat_created = $message->getSupergroupChatCreated(); 65 | 66 | // The channel has been created 67 | $channel_chat_created = $message->getChannelChatCreated(); 68 | 69 | // Information about the payment 70 | $successful_payment = $message->getSuccessfulPayment(); 71 | 72 | return Request::emptyResponse(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Commands/StartCommand.php: -------------------------------------------------------------------------------- 1 | getMessage()->getText(true); 66 | 67 | return $this->replyToChat( 68 | 'Hi there!' . PHP_EOL . 69 | 'Type /help to see all commands!' 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /CustomCommands/README.md: -------------------------------------------------------------------------------- 1 | # CustomCommands 2 | Uncomment line 47 in your config.php from 3 | `// __DIR__ . '/CustomCommands',` 4 | to 5 | `__DIR__ . '/CustomCommands',` 6 | to enable this folder. 7 | You can then copy example commands from the Command folder. 8 | 9 | **:exclamation: Important!** 10 | 11 | DO NOT COPY THE ENTIRE COMMAND FOLDER CONTENT!! 12 | 13 | Some command files are duplicated and may interfere with each other. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 PHP Telegram Bot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Telegram Bot Example 2 | 3 | > :construction: Work In Progress :construction: 4 | 5 | An A-Z example of Telegram bot using the [PHP Telegram Bot][core-github] library. 6 | 7 | This repository aims to demonstrate the usage of all the features offered by the PHP Telegram Bot library and as such contains all example commands. 8 | Also, it gives an example setup for both the standard usage and using the [PHP Telegram Bot Manager][bot-manager-github] 9 | 10 | **:exclamation: Important!** 11 | - Most of the commands found here are **not to be used exactly as they are**, they are mere demonstrations of features! They are provided as-is and any **extra security measures need to be added by you**, the developer. 12 | - Before getting started with this project, make sure you have read the official [readme][core-readme-github] to understand how the PHP Telegram Bot library works and what is required to run a Telegram bot. 13 | 14 | Let's get started then! :smiley: 15 | 16 | ## 0. Cloning this repository 17 | 18 | To start off, you can clone this repository using git: 19 | 20 | ```bash 21 | $ git clone https://github.com/php-telegram-bot/example-bot.git 22 | ``` 23 | 24 | or better yet, download it as a zip file: 25 | 26 | ```bash 27 | $ curl -o example-bot.zip https://github.com/php-telegram-bot/example-bot/archive/master.zip 28 | ``` 29 | 30 | Unzip the files to the root of your project folder. 31 | 32 | ## 1. Making it yours 33 | 34 | Now you can choose what installation you would like, either the default one or using the [Bot Manager][bot-manager-github] project. 35 | Depending on which one you choose, you can delete the files that are not required. 36 | 37 | --- 38 | 39 | First of all, you need to rename `config.example.php` to `config.php` and then replace all necessary values with those of your project. 40 | 41 | **Default** 42 | Some of these files require extra configurations to be added. Check `hook.php` how they are loaded. 43 | Thanks to reading the main readme file, you should know what these files do. 44 | 45 | - `composer.json` (Describes your project and it's dependencies) 46 | - `set.php` (Used to set the webhook) 47 | - `unset.php` (Used to unset the webhook) 48 | - `hook.php` (Used for the webhook method) 49 | - `getUpdatesCLI.php` (Used for the getUpdates method) 50 | - `cron.php` (Used to execute commands via cron) 51 | 52 | **Bot Manager** 53 | Using the bot manager makes life much easier, as all configuration goes into a single file, `manager.php`. 54 | 55 | If you decide to use the Bot Manager, be sure to [read all about it][bot-manager-readme-github] and change the `require` block in the `composer.json` file: 56 | ```json 57 | "require": { 58 | "php-telegram-bot/telegram-bot-manager": "*" 59 | } 60 | ``` 61 | 62 | Then, edit the following files, replacing all necessary values with those of your project. 63 | 64 | - `composer.json` (Describes your project and it's dependencies) 65 | - `manager.php` (Used as the main entry point for everything) 66 | 67 | --- 68 | 69 | Now you can install all dependencies using [composer]: 70 | ```bash 71 | $ composer install 72 | ``` 73 | 74 | ## 2. Adding your own commands 75 | 76 | To use Commands enable the [`CustomCommands`](CustomCommands) folder in the config. 77 | 78 | You can find a few example commands in the [`Commands`](Commands) folder. 79 | Do **NOT** just copy all of them to your bot, but instead learn from them and only add to your bot what you need. 80 | 81 | 82 | Adding any extra commands to your bot that you don't need can be a security risk! 83 | 84 | ## To be continued! 85 | 86 | [core-github]: https://github.com/php-telegram-bot/core "php-telegram-bot/core" 87 | [core-readme-github]: https://github.com/php-telegram-bot/core#readme "PHP Telegram Bot - README" 88 | [bot-manager-github]: https://github.com/php-telegram-bot/telegram-bot-manager "php-telegram-bot/telegram-bot-manager" 89 | [bot-manager-readme-github]: https://github.com/php-telegram-bot/telegram-bot-manager#readme "PHP Telegram Bot Manager - README" 90 | [composer]: https://getcomposer.org/ "Composer" 91 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "telegram-bot/example-bot", 3 | "type": "project", 4 | "description": "PHP Telegram Bot Example", 5 | "keywords": ["telegram", "bot", "example"], 6 | "license": "MIT", 7 | "homepage": "https://github.com/php-telegram-bot/example-bot", 8 | "support": { 9 | "issues": "https://github.com/php-telegram-bot/example-bot/issues", 10 | "source": "https://github.com/php-telegram-bot/example-bot" 11 | }, 12 | "authors": [ 13 | { 14 | "name": "PHP Telegram Bot Team", 15 | "homepage": "https://github.com/php-telegram-bot/example-bot/graphs/contributors", 16 | "role": "Developer" 17 | } 18 | ], 19 | "require": { 20 | "longman/telegram-bot": "*" 21 | }, 22 | "require-dev": { 23 | "php-parallel-lint/php-parallel-lint": "^1.2", 24 | "squizlabs/php_codesniffer": "^3.5" 25 | }, 26 | "scripts": { 27 | "check-code": [ 28 | "\"vendor/bin/phpcs\"" 29 | ] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /config.example.php: -------------------------------------------------------------------------------- 1 | 'your:bot_api_key', 29 | 'bot_username' => 'username_bot', // Without "@" 30 | 31 | // [Manager Only] Secret key required to access the webhook 32 | 'secret' => 'super_secret', 33 | 34 | // When using the getUpdates method, this can be commented out 35 | 'webhook' => [ 36 | 'url' => 'https://your-domain/path/to/hook-or-manager.php', 37 | // Use self-signed certificate 38 | // 'certificate' => __DIR__ . '/path/to/your/certificate.crt', 39 | // Limit maximum number of connections 40 | // 'max_connections' => 5, 41 | ], 42 | 43 | // All command related configs go here 44 | 'commands' => [ 45 | // Define all paths for your custom commands 46 | // DO NOT PUT THE COMMAND FOLDER THERE. IT WILL NOT WORK. 47 | // Copy each needed Commandfile into the CustomCommand folder and uncommend the Line 49 below 48 | 'paths' => [ 49 | // __DIR__ . '/CustomCommands', 50 | ], 51 | // Here you can set any command-specific parameters 52 | 'configs' => [ 53 | // - Google geocode/timezone API key for /date command (see DateCommand.php) 54 | // 'date' => ['google_api_key' => 'your_google_api_key_here'], 55 | // - OpenWeatherMap.org API key for /weather command (see WeatherCommand.php) 56 | // 'weather' => ['owm_api_key' => 'your_owm_api_key_here'], 57 | // - Payment Provider Token for /payment command (see Payments/PaymentCommand.php) 58 | // 'payment' => ['payment_provider_token' => 'your_payment_provider_token_here'], 59 | ], 60 | ], 61 | 62 | // Define all IDs of admin users 63 | 'admins' => [ 64 | // 123, 65 | ], 66 | 67 | // Enter your MySQL database credentials 68 | // 'mysql' => [ 69 | // 'host' => '127.0.0.1', 70 | // 'user' => 'root', 71 | // 'password' => 'root', 72 | // 'database' => 'telegram_bot', 73 | // ], 74 | 75 | // Logging (Debug, Error and Raw Updates) 76 | // 'logging' => [ 77 | // 'debug' => __DIR__ . '/php-telegram-bot-debug.log', 78 | // 'error' => __DIR__ . '/php-telegram-bot-error.log', 79 | // 'update' => __DIR__ . '/php-telegram-bot-update.log', 80 | // ], 81 | 82 | // Set custom Upload and Download paths 83 | 'paths' => [ 84 | 'download' => __DIR__ . '/Download', 85 | 'upload' => __DIR__ . '/Upload', 86 | ], 87 | 88 | // Requests Limiter (tries to prevent reaching Telegram API limits) 89 | 'limiter' => [ 90 | 'enabled' => true, 91 | ], 92 | ]; 93 | -------------------------------------------------------------------------------- /cron.php: -------------------------------------------------------------------------------- 1 | runCommands($commands); 40 | 41 | } catch (Longman\TelegramBot\Exception\TelegramException $e) { 42 | // Log telegram errors 43 | Longman\TelegramBot\TelegramLog::error($e); 44 | 45 | // Uncomment this to output any errors (ONLY FOR DEVELOPMENT!) 46 | // echo $e; 47 | } catch (Longman\TelegramBot\Exception\TelegramLogException $e) { 48 | // Uncomment this to output log initialisation errors (ONLY FOR DEVELOPMENT!) 49 | // echo $e; 50 | } 51 | -------------------------------------------------------------------------------- /getUpdatesCLI.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | handleGetUpdates(); 35 | 36 | if ($server_response->isOk()) { 37 | $update_count = count($server_response->getResult()); 38 | echo date('Y-m-d H:i:s') . ' - Processed ' . $update_count . ' updates'; 39 | } else { 40 | echo date('Y-m-d H:i:s') . ' - Failed to fetch updates' . PHP_EOL; 41 | echo $server_response->printError(); 42 | } 43 | 44 | } catch (Longman\TelegramBot\Exception\TelegramException $e) { 45 | // Log telegram errors 46 | Longman\TelegramBot\TelegramLog::error($e); 47 | 48 | // Uncomment this to output any errors (ONLY FOR DEVELOPMENT!) 49 | // echo $e; 50 | } catch (Longman\TelegramBot\Exception\TelegramLogException $e) { 51 | // Uncomment this to output log initialisation errors (ONLY FOR DEVELOPMENT!) 52 | // echo $e; 53 | } 54 | -------------------------------------------------------------------------------- /hook.php: -------------------------------------------------------------------------------- 1 | enableAdmins($config['admins']); 33 | 34 | // Add commands paths containing your custom commands 35 | $telegram->addCommandsPaths($config['commands']['paths']); 36 | 37 | // Enable MySQL if required 38 | // $telegram->enableMySql($config['mysql']); 39 | 40 | // Logging (Error, Debug and Raw Updates) 41 | // https://github.com/php-telegram-bot/core/blob/master/doc/01-utils.md#logging 42 | // 43 | // (this example requires Monolog: composer require monolog/monolog) 44 | // Longman\TelegramBot\TelegramLog::initialize( 45 | // new Monolog\Logger('telegram_bot', [ 46 | // (new Monolog\Handler\StreamHandler($config['logging']['debug'], Monolog\Logger::DEBUG))->setFormatter(new Monolog\Formatter\LineFormatter(null, null, true)), 47 | // (new Monolog\Handler\StreamHandler($config['logging']['error'], Monolog\Logger::ERROR))->setFormatter(new Monolog\Formatter\LineFormatter(null, null, true)), 48 | // ]), 49 | // new Monolog\Logger('telegram_bot_updates', [ 50 | // (new Monolog\Handler\StreamHandler($config['logging']['update'], Monolog\Logger::INFO))->setFormatter(new Monolog\Formatter\LineFormatter('%message%' . PHP_EOL)), 51 | // ]) 52 | // ); 53 | 54 | // Set custom Download and Upload paths 55 | // $telegram->setDownloadPath($config['paths']['download']); 56 | // $telegram->setUploadPath($config['paths']['upload']); 57 | 58 | // Load all command-specific configurations 59 | // foreach ($config['commands']['configs'] as $command_name => $command_config) { 60 | // $telegram->setCommandConfig($command_name, $command_config); 61 | // } 62 | 63 | // Requests Limiter (tries to prevent reaching Telegram API limits) 64 | $telegram->enableLimiter($config['limiter']); 65 | 66 | // Handle telegram webhook request 67 | $telegram->handle(); 68 | 69 | } catch (Longman\TelegramBot\Exception\TelegramException $e) { 70 | // Log telegram errors 71 | Longman\TelegramBot\TelegramLog::error($e); 72 | 73 | // Uncomment this to output any errors (ONLY FOR DEVELOPMENT!) 74 | // echo $e; 75 | } catch (Longman\TelegramBot\Exception\TelegramLogException $e) { 76 | // Uncomment this to output log initialisation errors (ONLY FOR DEVELOPMENT!) 77 | // echo $e; 78 | } 79 | -------------------------------------------------------------------------------- /manager.php: -------------------------------------------------------------------------------- 1 | run(); 32 | 33 | } catch (Longman\TelegramBot\Exception\TelegramException $e) { 34 | // Log telegram errors 35 | Longman\TelegramBot\TelegramLog::error($e); 36 | 37 | // Uncomment this to output any errors (ONLY FOR DEVELOPMENT!) 38 | // echo $e; 39 | } catch (Longman\TelegramBot\Exception\TelegramLogException $e) { 40 | // Uncomment this to output log initialisation errors (ONLY FOR DEVELOPMENT!) 41 | // echo $e; 42 | } 43 | -------------------------------------------------------------------------------- /phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | PHP Code Sniffer 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | . 13 | */vendor/* 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /set.php: -------------------------------------------------------------------------------- 1 | 'https://your-domain/path/to/hook.php' 31 | */ 32 | 33 | // Set the webhook 34 | $result = $telegram->setWebhook($config['webhook']['url']); 35 | 36 | // To use a self-signed certificate, use this line instead 37 | // $result = $telegram->setWebhook($config['webhook']['url'], ['certificate' => $config['webhook']['certificate']]); 38 | 39 | echo $result->getDescription(); 40 | } catch (Longman\TelegramBot\Exception\TelegramException $e) { 41 | echo $e->getMessage(); 42 | } 43 | -------------------------------------------------------------------------------- /unset.php: -------------------------------------------------------------------------------- 1 | deleteWebhook(); 30 | 31 | echo $result->getDescription(); 32 | } catch (Longman\TelegramBot\Exception\TelegramException $e) { 33 | echo $e->getMessage(); 34 | } 35 | --------------------------------------------------------------------------------