api.telegram.php 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064
  1. <?php
  2. /**
  3. * Telegram bot API implementation
  4. */
  5. class UbillingTelegram {
  6. /**
  7. * Contains system alter config as key=>value
  8. *
  9. * @var array
  10. */
  11. protected $altCfg = array();
  12. /**
  13. * Contains current instance bot token
  14. *
  15. * @var string
  16. */
  17. protected $botToken = '';
  18. /**
  19. * Default debug flag wich enables telegram replies display
  20. *
  21. * @var bool
  22. */
  23. protected $debug = false;
  24. /**
  25. * Contains base Telegram API URL
  26. */
  27. protected $apiUrl = 'https://api.telegram.org/bot';
  28. /**
  29. * Contains telegram messages path
  30. */
  31. const QUEUE_PATH = 'content/telegram/';
  32. /**
  33. * Maximum message length
  34. */
  35. const MESSAGE_LIMIT = 4095;
  36. /**
  37. * Creates new Telegram object instance
  38. *
  39. * @param string $token
  40. */
  41. public function __construct($token = '') {
  42. if (!empty($token)) {
  43. $this->botToken = $token;
  44. }
  45. $this->loadAlter();
  46. $this->setOptions();
  47. }
  48. /**
  49. * Sets current instance auth token
  50. *
  51. * @param string $token
  52. *
  53. * @return void
  54. */
  55. public function setToken($token) {
  56. $this->botToken = $token;
  57. }
  58. /**
  59. * Object instance debug state setter
  60. *
  61. * @param bool $state
  62. *
  63. * @return void
  64. */
  65. public function setDebug($state) {
  66. $this->debug = $state;
  67. }
  68. /**
  69. * Loads system alter config into protected property for further usage
  70. *
  71. * @global object $ubillingConfig
  72. *
  73. * @return void
  74. */
  75. protected function loadAlter() {
  76. global $ubillingConfig;
  77. $this->altCfg = $ubillingConfig->getAlter();
  78. }
  79. /**
  80. * Sets some current instance options if required
  81. *
  82. * @return void
  83. */
  84. protected function setOptions() {
  85. //settin debug flag
  86. if (isset($this->altCfg['TELEGRAM_DEBUG'])) {
  87. if ($this->altCfg['TELEGRAM_DEBUG']) {
  88. $this->debug = true;
  89. }
  90. }
  91. if (isset($this->altCfg['TELEGRAM_API_URL'])) {
  92. if (!empty($this->altCfg['TELEGRAM_API_URL'])) {
  93. $this->setApiUrl($this->altCfg['TELEGRAM_API_URL']);
  94. }
  95. }
  96. }
  97. /**
  98. * Setter of custom API URL (legacy fallback)
  99. *
  100. * @param string $url
  101. *
  102. * @return void
  103. */
  104. protected function setApiUrl($url) {
  105. $this->apiUrl = $url;
  106. }
  107. /**
  108. * Stores message in telegram sending queue. Use this method in your modules.
  109. *
  110. * @param int $chatid
  111. * @param string $message
  112. * @param bool $translit
  113. * @param string $module
  114. *
  115. * @return bool
  116. */
  117. public function sendMessage($chatid, $message, $translit = false, $module = '') {
  118. $result = false;
  119. $chatid = trim($chatid);
  120. $module = (!empty($module)) ? ' MODULE ' . $module : '';
  121. $prefix = 'tlg_';
  122. if (!empty($chatid)) {
  123. $message = str_replace(array("\n\r", "\n", "\r"), ' ', $message);
  124. if ($translit) {
  125. $message = zb_TranslitString($message);
  126. }
  127. $message = trim($message);
  128. $queueId = time();
  129. $offset = 0;
  130. $filename = self::QUEUE_PATH . $prefix . $queueId . '_' . $offset;
  131. if (file_exists($filename)) {
  132. while (file_exists($filename)) {
  133. $offset++; //incremeting number of messages per second
  134. $filename = self::QUEUE_PATH . $prefix . $queueId . '_' . $offset;
  135. }
  136. }
  137. $storedata = 'CHATID="' . $chatid . '"' . "\n";
  138. $storedata .= 'MESSAGE="' . $message . '"' . "\n";
  139. file_put_contents($filename, $storedata);
  140. log_register('UTLG SEND MESSAGE FOR `' . $chatid . '` AS `' . $prefix . $queueId . '_' . $offset . '` ' . $module);
  141. $result = true;
  142. }
  143. return ($result);
  144. }
  145. /**
  146. * Returns count of messages available in queue
  147. *
  148. * @return int
  149. */
  150. public function getQueueCount() {
  151. $messagesQueueCount = rcms_scandir(self::QUEUE_PATH);
  152. $result = sizeof($messagesQueueCount);
  153. return ($result);
  154. }
  155. /**
  156. * Returns array containing all messages queue data as index=>data
  157. *
  158. * @return array
  159. */
  160. public function getQueueData() {
  161. $result = array();
  162. $messagesQueue = rcms_scandir(self::QUEUE_PATH);
  163. if (!empty($messagesQueue)) {
  164. foreach ($messagesQueue as $io => $eachmessage) {
  165. $messageDate = date("Y-m-d H:i:s", filectime(self::QUEUE_PATH . $eachmessage));
  166. $messageData = rcms_parse_ini_file(self::QUEUE_PATH . $eachmessage);
  167. $result[$io]['filename'] = $eachmessage;
  168. $result[$io]['date'] = $messageDate;
  169. $result[$io]['chatid'] = $messageData['CHATID'];
  170. $result[$io]['message'] = $messageData['MESSAGE'];
  171. }
  172. }
  173. return ($result);
  174. }
  175. /**
  176. * Deletes message from local queue
  177. *
  178. * @param string $filename Existing message filename
  179. *
  180. * @return int 0 - ok, 1 - deletion unsuccessful, 2 - file not found
  181. */
  182. public function deleteMessage($filename) {
  183. if (file_exists(self::QUEUE_PATH . $filename)) {
  184. unlink(self::QUEUE_PATH . $filename);
  185. $result = 0;
  186. if (file_exists(self::QUEUE_PATH . $filename)) {
  187. $result = 1;
  188. }
  189. } else {
  190. $result = 2;
  191. }
  192. return ($result);
  193. }
  194. /**
  195. * Returns raw updates array
  196. *
  197. * @param int $offset
  198. * @param int $limit
  199. * @param int $timeout
  200. *
  201. * @return array
  202. *
  203. * @throws Exception
  204. */
  205. protected function getUpdatesRaw($offset = '', $limit = '', $timeout = '') {
  206. $result = array();
  207. $timeout = vf($timeout, 3);
  208. $limit = vf($limit, 3);
  209. $offset = mysql_real_escape_string($offset);
  210. $timeout = (!empty($timeout)) ? $timeout : 0; //default timeout in seconds is 0
  211. $limit = (!empty($limit)) ? $limit : 100; //defult limit is 100
  212. /**
  213. * Identifier of the first update to be returned. Must be greater by one than the highest among the identifiers of previously received updates.
  214. * By default, updates starting with the earliest unconfirmed update are returned. An update is considered confirmed as soon as getUpdates is
  215. * called with an offset higher than its update_id. The negative offset can be specified to retrieve updates starting from -offset update from
  216. * the end of the updates queue. All previous updates will forgotten.
  217. */
  218. $offset = (!empty($offset)) ? '&offset=' . $offset : '';
  219. if (!empty($this->botToken)) {
  220. $options = '?timeout=' . $timeout . '&limit=' . $limit . $offset;
  221. $url = $this->apiUrl . $this->botToken . '/getUpdates' . $options;
  222. $ch = curl_init();
  223. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  224. curl_setopt($ch, CURLOPT_URL, $url);
  225. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
  226. curl_setopt($ch, CURLOPT_POST, 1);
  227. @$reply = curl_exec($ch);
  228. if ($this->debug) {
  229. $curlError = curl_error($ch);
  230. show_info($url);
  231. if (!empty($curlError)) {
  232. show_error(__('Error') . ' ' . __('Telegram') . ': ' . $curlError);
  233. } else {
  234. show_success(__('Telegram API connection to') . ' ' . $this->apiUrl . ' ' . __('success'));
  235. }
  236. }
  237. curl_close($ch);
  238. if (!empty($reply)) {
  239. $result = json_decode($reply, true);
  240. }
  241. if ($this->debug) {
  242. debarr($result);
  243. }
  244. } else {
  245. throw new Exception('EX_TOKEN_EMPTY');
  246. }
  247. return ($result);
  248. }
  249. /**
  250. * Returns all messages received by bot
  251. *
  252. * @return array
  253. * @throws Exception
  254. */
  255. public function getBotChats() {
  256. $result = array();
  257. if (!empty($this->botToken)) {
  258. $rawUpdates = $this->getUpdatesRaw();
  259. if (!empty($rawUpdates)) {
  260. if (isset($rawUpdates['result'])) {
  261. $allUpdates = $rawUpdates['result'];
  262. foreach ($allUpdates as $io => $each) {
  263. if (isset($each['message'])) {
  264. if (isset($each['message']['chat'])) {
  265. if (isset($each['message']['chat']['type'])) {
  266. $messageData = $each['message'];
  267. if ($messageData['chat']['type'] == 'private') {
  268. //direct message
  269. if (isset($messageData['message_id'])) {
  270. $messageId = $messageData['message_id'];
  271. $result[$messageId]['id'] = $messageId;
  272. $result[$messageId]['date'] = date("Y-m-d H:i:s", $messageData['date']);
  273. $result[$messageId]['chatid'] = $messageData['from']['id'];
  274. $result[$messageId]['from'] = @$messageData['from']['username'];
  275. $result[$messageId]['text'] = @$messageData['text'];
  276. $result[$messageId]['type'] = 'user';
  277. $result[$messageId]['chanid'] = '';
  278. $result[$messageId]['channame'] = '';
  279. $result[$messageId]['updateid'] = @$each['update_id'];
  280. }
  281. }
  282. //supergroup message
  283. if ($messageData['chat']['type'] == 'supergroup') {
  284. if (isset($messageData['message_id'])) {
  285. $messageId = $messageData['message_id'];
  286. $result[$messageId]['id'] = $messageId;
  287. $result[$messageId]['date'] = date("Y-m-d H:i:s", $messageData['date']);
  288. $result[$messageId]['chatid'] = $messageData['from']['id'];
  289. $result[$messageId]['from'] = @$messageData['from']['username'];
  290. $result[$messageId]['text'] = @$messageData['text'];
  291. $result[$messageId]['type'] = 'supergroup';
  292. $result[$messageId]['chanid'] = $messageData['chat']['id'];
  293. $result[$messageId]['channame'] = $messageData['chat']['username'];
  294. $result[$messageId]['updateid'] = '';
  295. $result[$messageId]['updateid'] = @$each['update_id'];
  296. }
  297. }
  298. }
  299. }
  300. }
  301. //channel message
  302. if (isset($each['channel_post'])) {
  303. $messageData = $each['channel_post'];
  304. if (isset($messageData['message_id'])) {
  305. $messageId = $messageData['message_id'];
  306. $result[$messageId]['id'] = $messageId;
  307. $result[$messageId]['date'] = date("Y-m-d H:i:s", $messageData['date']);
  308. $result[$messageId]['chatid'] = $messageData['chat']['id'];
  309. $result[$messageId]['from'] = @$messageData['chat']['username'];
  310. $result[$messageId]['text'] = @$messageData['text'];
  311. $result[$messageId]['type'] = 'channel';
  312. }
  313. }
  314. }
  315. }
  316. }
  317. } else {
  318. throw new Exception('EX_TOKEN_EMPTY');
  319. }
  320. return ($result);
  321. }
  322. /**
  323. * Returns current bot contacts list as chat_id=>name
  324. *
  325. * @return array
  326. */
  327. public function getBotContacts() {
  328. $result = array();
  329. $updatesRaw = $this->getUpdatesRaw();
  330. if (!empty($updatesRaw)) {
  331. if (isset($updatesRaw['result'])) {
  332. if (!empty($updatesRaw['result'])) {
  333. foreach ($updatesRaw['result'] as $io => $each) {
  334. //supergroup messages
  335. if (isset($each['message'])) {
  336. if (isset($each['message']['chat'])) {
  337. if (isset($each['message']['chat']['type'])) {
  338. if ($each['message']['chat']['type'] = 'supergroup') {
  339. $groupData = $each['message']['chat'];
  340. $result[$groupData['id']]['chatid'] = $groupData['id'];
  341. $groupName = (!empty($groupData['username'])) ? $groupData['username'] : @$groupData['title']; //only title for private groups
  342. $result[$groupData['id']]['name'] = $groupName;
  343. $result[$groupData['id']]['first_name'] = @$groupData['title'];
  344. $result[$groupData['id']]['last_name'] = '';
  345. $result[$groupData['id']]['type'] = 'supergroup';
  346. $result[$groupData['id']]['lastmessage'] = strip_tags(@$each['message']['text']);
  347. }
  348. }
  349. }
  350. }
  351. //direct user message
  352. if (isset($each['message'])) {
  353. if (isset($each['message']['from'])) {
  354. if (isset($each['message']['from']['id'])) {
  355. $messageData = $each['message']['from'];
  356. $result[$messageData['id']]['chatid'] = $messageData['id'];
  357. $result[$messageData['id']]['name'] = @$messageData['username']; //may be empty
  358. $result[$messageData['id']]['first_name'] = @$messageData['first_name'];
  359. $result[$messageData['id']]['last_name'] = @$messageData['last_name'];
  360. $result[$messageData['id']]['type'] = 'user';
  361. $result[$messageData['id']]['lastmessage'] = strip_tags(@$each['message']['text']);
  362. }
  363. }
  364. }
  365. //channel message
  366. if (isset($each['channel_post'])) {
  367. if (isset($each['channel_post']['chat'])) {
  368. if (isset($each['channel_post']['chat']['id'])) {
  369. $chatData = $each['channel_post']['chat'];
  370. $result[$chatData['id']]['chatid'] = $chatData['id'];
  371. $result[$chatData['id']]['name'] = $chatData['username'];
  372. $result[$chatData['id']]['first_name'] = '';
  373. $result[$chatData['id']]['last_name'] = '';
  374. $result[$chatData['id']]['type'] = 'channel';
  375. $result[$messageData['id']]['lastmessage'] = strip_tags(@$each['message']['text']);
  376. }
  377. }
  378. }
  379. }
  380. }
  381. }
  382. }
  383. return ($result);
  384. }
  385. /**
  386. * Preprocess keyboard for sending with directPushMessage
  387. *
  388. * @param array $buttonsArray
  389. * @param bool $inline
  390. * @param bool $resize
  391. * @param bool $oneTime
  392. *
  393. * @return array
  394. */
  395. public function makeKeyboard($buttonsArray, $inline = false, $resize = true, $oneTime = false) {
  396. $result = array();
  397. if (!empty($buttonsArray)) {
  398. if (!$inline) {
  399. $result['type'] = 'keyboard';
  400. $keyboardMarkup = array(
  401. 'keyboard' => $buttonsArray,
  402. 'resize_keyboard' => $resize,
  403. 'one_time_keyboard' => $oneTime
  404. );
  405. $result['markup'] = $keyboardMarkup;
  406. }
  407. if ($inline) {
  408. $result['type'] = 'inline';
  409. $keyboardMarkup = $buttonsArray;
  410. $result['markup'] = $keyboardMarkup;
  411. }
  412. }
  413. return ($result);
  414. }
  415. /**
  416. * Split message into chunks of safe size
  417. *
  418. * @param string $message
  419. *
  420. * @return array
  421. */
  422. protected function splitMessage($message) {
  423. $result = preg_split('~~u', $message, -1, PREG_SPLIT_NO_EMPTY);
  424. $chunks = array_chunk($result, self::MESSAGE_LIMIT);
  425. foreach ($chunks as $i => $chunk) {
  426. $chunks[$i] = join('', (array) $chunk);
  427. }
  428. $result = $chunks;
  429. return ($result);
  430. }
  431. /**
  432. * Sends message to some chat id using Telegram API
  433. *
  434. * @param int $chatid remote chatId
  435. * @param string $message text message to send
  436. * @param array $keyboard keyboard encoded with makeKeyboard method
  437. * @param bool $nosplit dont automatically split message into 4096 slices
  438. * @param int $replyToMsgId optional message ID which is reply for
  439. *
  440. * @return string/bool
  441. */
  442. public function directPushMessage($chatid, $message, $keyboard = array(), $noSplit = false, $replyToMsgId = '') {
  443. $result = '';
  444. if ($noSplit) {
  445. $result = $this->apiSendMessage($chatid, $message, $keyboard, $replyToMsgId);
  446. } else {
  447. $messageSize = mb_strlen($message, 'UTF-8');
  448. if ($messageSize > self::MESSAGE_LIMIT) {
  449. $messageSplit = $this->splitMessage($message);
  450. if (!empty($messageSplit)) {
  451. foreach ($messageSplit as $io => $eachMessagePart) {
  452. $result = $this->apiSendMessage($chatid, $eachMessagePart, $keyboard, $replyToMsgId);
  453. }
  454. }
  455. } else {
  456. $result = $this->apiSendMessage($chatid, $message, $keyboard, $replyToMsgId);
  457. }
  458. }
  459. return ($result);
  460. }
  461. /**
  462. * Sends message to some chat id via Telegram API
  463. *
  464. * @param int $chatid remote chatId
  465. * @param string $message text message to send
  466. * @param array $keyboard keyboard encoded with makeKeyboard method
  467. * @param int $replyToMsgId optional message ID which is reply for
  468. * @throws Exception
  469. *
  470. * @return string/bool
  471. */
  472. protected function apiSendMessage($chatid, $message, $keyboard = array(), $replyToMsgId = '') {
  473. $result = '';
  474. $data['chat_id'] = $chatid;
  475. $data['text'] = $message;
  476. if ($this->debug) {
  477. debarr($data);
  478. }
  479. //default sending method
  480. $method = 'sendMessage';
  481. //setting optional replied message ID for normal messages
  482. if ($replyToMsgId) {
  483. $method = 'sendMessage?reply_to_message_id=' . $replyToMsgId;
  484. }
  485. //location sending
  486. if (ispos($message, 'sendLocation:')) {
  487. $cleanGeo = str_replace('sendLocation:', '', $message);
  488. $cleanGeo = explode(',', $cleanGeo);
  489. $geoLat = trim($cleanGeo[0]);
  490. $geoLon = trim($cleanGeo[1]);
  491. $locationParams = '?chat_id=' . $chatid . '&latitude=' . $geoLat . '&longitude=' . $geoLon;
  492. if ($replyToMsgId) {
  493. $locationParams .= '&reply_to_message_id=' . $replyToMsgId;
  494. }
  495. $method = 'sendLocation' . $locationParams;
  496. }
  497. //custom markdown
  498. if (ispos($message, 'parseMode:{')) {
  499. if (preg_match('!\{(.*?)\}!si', $message, $tmpMode)) {
  500. $cleanParseMode = $tmpMode[1];
  501. $parseModeMask = 'parseMode:{' . $cleanParseMode . '}';
  502. $cleanMessage = str_replace($parseModeMask, '', $message);
  503. $data['text'] = $cleanMessage;
  504. $method = 'sendMessage?parse_mode=' . $cleanParseMode;
  505. if ($replyToMsgId) {
  506. $method .= '&reply_to_message_id=' . $replyToMsgId;
  507. }
  508. }
  509. }
  510. //venue sending
  511. if (ispos($message, 'sendVenue:')) {
  512. if (preg_match('!\[(.*?)\]!si', $message, $tmpGeo)) {
  513. $cleanGeo = $tmpGeo[1];
  514. }
  515. if (preg_match('!\((.*?)\)!si', $message, $tmpAddr)) {
  516. $cleanAddr = $tmpAddr[1];
  517. }
  518. if (preg_match('!\{(.*?)\}!si', $message, $tmpTitle)) {
  519. $cleanTitle = $tmpTitle[1];
  520. }
  521. $data['title'] = $cleanTitle;
  522. $data['address'] = $cleanAddr;
  523. $cleanGeo = explode(',', $cleanGeo);
  524. $geoLat = trim($cleanGeo[0]);
  525. $geoLon = trim($cleanGeo[1]);
  526. $locationParams = '?chat_id=' . $chatid . '&latitude=' . $geoLat . '&longitude=' . $geoLon;
  527. if ($replyToMsgId) {
  528. $locationParams .= '&reply_to_message_id=' . $replyToMsgId;
  529. }
  530. $method = 'sendVenue' . $locationParams;
  531. }
  532. //photo sending
  533. if (ispos($message, 'sendPhoto:')) {
  534. if (preg_match('!\[(.*?)\]!si', $message, $tmpPhoto)) {
  535. $cleanPhoto = $tmpPhoto[1];
  536. }
  537. if (preg_match('!\{(.*?)\}!si', $message, $tmpCaption)) {
  538. $cleanCaption = $tmpCaption[1];
  539. $cleanCaption = urlencode($cleanCaption);
  540. }
  541. $photoParams = '?chat_id=' . $chatid . '&photo=' . $cleanPhoto;
  542. if (!empty($cleanCaption)) {
  543. $photoParams .= '&caption=' . $cleanCaption;
  544. }
  545. if ($replyToMsgId) {
  546. $photoParams .= '&reply_to_message_id=' . $replyToMsgId;
  547. }
  548. $method = 'sendPhoto' . $photoParams;
  549. }
  550. //sending keyboard
  551. if (!empty($keyboard)) {
  552. if (isset($keyboard['type'])) {
  553. if ($keyboard['type'] == 'keyboard') {
  554. $encodedKeyboard = json_encode($keyboard['markup']);
  555. $data['reply_markup'] = $encodedKeyboard;
  556. }
  557. if ($keyboard['type'] == 'inline') {
  558. $encodedKeyboard = json_encode(array('inline_keyboard' => $keyboard['markup']));
  559. $data['reply_markup'] = $encodedKeyboard;
  560. $data['parse_mode'] = 'HTML';
  561. }
  562. $method = 'sendMessage';
  563. }
  564. }
  565. //removing keyboard
  566. if (ispos($message, 'removeKeyboard:')) {
  567. $keybRemove = array(
  568. 'remove_keyboard' => true
  569. );
  570. $encodedMarkup = json_encode($keybRemove);
  571. $cleanMessage = str_replace('removeKeyboard:', '', $message);
  572. if (empty($cleanMessage)) {
  573. $cleanMessage = __('Keyboard deleted');
  574. }
  575. $data['text'] = $cleanMessage;
  576. $data['reply_markup'] = $encodedMarkup;
  577. }
  578. //banChatMember
  579. if (ispos($message, 'banChatMember:')) {
  580. if (preg_match('!\[(.*?)\]!si', $message, $tmpBanString)) {
  581. $cleanBanString = explode('@', $tmpBanString[1]);
  582. $banUserId = $cleanBanString[0];
  583. $banChatId = $cleanBanString[1];
  584. }
  585. $banParams = '?chat_id=' . $banChatId . '&user_id=' . $banUserId;
  586. $method = 'banChatMember' . $banParams;
  587. }
  588. //unbanChatMember
  589. if (ispos($message, 'unbanChatMember:')) {
  590. if (preg_match('!\[(.*?)\]!si', $message, $tmpUnbanString)) {
  591. $cleanUnbanString = explode('@', $tmpUnbanString[1]);
  592. $unbanUserId = $cleanUnbanString[0];
  593. $unbanChatId = $cleanUnbanString[1];
  594. }
  595. $unbanParams = '?chat_id=' . $unbanChatId . '&user_id=' . $unbanUserId;
  596. $method = 'unbanChatMember' . $unbanParams;
  597. }
  598. //deleting message by its id
  599. if (ispos($message, 'removeChatMessage:')) {
  600. if (preg_match('!\[(.*?)\]!si', $message, $tmpRemoveString)) {
  601. $cleanRemoveString = explode('@', $tmpRemoveString[1]);
  602. $removeMessageId = $cleanRemoveString[0];
  603. $removeChatId = $cleanRemoveString[1];
  604. $removeParams = '?chat_id=' . $removeChatId . '&message_id=' . $removeMessageId;
  605. $method = 'deleteMessage' . $removeParams;
  606. }
  607. }
  608. //POST data encoding
  609. $data_json = json_encode($data);
  610. if (!empty($this->botToken)) {
  611. $url = $this->apiUrl . $this->botToken . '/' . $method;
  612. if ($this->debug) {
  613. deb($url);
  614. }
  615. $ch = curl_init();
  616. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  617. curl_setopt($ch, CURLOPT_URL, $url);
  618. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
  619. curl_setopt($ch, CURLOPT_POST, 1);
  620. curl_setopt($ch, CURLOPT_POSTFIELDS, $data_json);
  621. if ($this->debug) {
  622. $result = curl_exec($ch);
  623. deb($result);
  624. $curlError = curl_error($ch);
  625. if (!empty($curlError)) {
  626. show_error(__('Error') . ' ' . __('Telegram') . ': ' . $curlError);
  627. } else {
  628. show_success(__('Telegram API sending via') . ' ' . $this->apiUrl . ' ' . __('success'));
  629. }
  630. } else {
  631. $result = curl_exec($ch);
  632. }
  633. curl_close($ch);
  634. } else {
  635. throw new Exception('EX_TOKEN_EMPTY');
  636. }
  637. return ($result);
  638. }
  639. /**
  640. * Sets HTTPS web hook URL for some bot
  641. *
  642. * @param string $webHookUrl HTTPS url to send updates to. Use an empty string to remove webhook integration
  643. * @param int $maxConnections Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40.
  644. * @param array $allowedUpdates Array of updates types allowed for that hook. Example: array('update_id', 'message', 'chat_member', 'message_reaction')
  645. * some of this types https://core.telegram.org/bots/api#update or leave this empty in most cases
  646. * @return string
  647. */
  648. public function setWebHook($webHookUrl, $maxConnections = 40, $allowedUpdates = array()) {
  649. $result = '';
  650. if (!empty($this->botToken)) {
  651. $data = array();
  652. if (!empty($webHookUrl)) {
  653. $method = 'setWebhook';
  654. if (ispos($webHookUrl, 'https://')) {
  655. $data['url'] = $webHookUrl;
  656. $data['max_connections'] = $maxConnections;
  657. if (!empty($allowedUpdates)) {
  658. $data['allowed_updates'] = $allowedUpdates;
  659. }
  660. } else {
  661. throw new Exception('EX_NOT_SSL_URL');
  662. }
  663. } else {
  664. $method = 'deleteWebhook';
  665. }
  666. $url = $this->apiUrl . $this->botToken . '/' . $method;
  667. if ($this->debug) {
  668. deb($url);
  669. }
  670. $ch = curl_init();
  671. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  672. curl_setopt($ch, CURLOPT_URL, $url);
  673. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
  674. curl_setopt($ch, CURLOPT_POST, 1);
  675. if (!empty($data)) {
  676. $data_json = json_encode($data);
  677. curl_setopt($ch, CURLOPT_POSTFIELDS, $data_json);
  678. }
  679. if ($this->debug) {
  680. $result = curl_exec($ch);
  681. deb($result);
  682. $curlError = curl_error($ch);
  683. if (!empty($curlError)) {
  684. show_error(__('Error') . ' ' . __('Telegram') . ': ' . $curlError);
  685. } else {
  686. show_success(__('Telegram API Hook') . ' ' . $this->apiUrl . ' =>' . print_r($data, true) . '|' . $data_json . __('success'));
  687. show_success($url);
  688. }
  689. } else {
  690. $result = curl_exec($ch);
  691. }
  692. curl_close($ch);
  693. } else {
  694. throw new Exception('EX_TOKEN_EMPTY');
  695. }
  696. return ($result);
  697. }
  698. /**
  699. * Returns bot web hook info
  700. *
  701. * @return string
  702. */
  703. public function getWebHookInfo() {
  704. $result = '';
  705. if (!empty($this->botToken)) {
  706. $method = 'getWebhookInfo';
  707. $url = $this->apiUrl . $this->botToken . '/' . $method;
  708. if ($this->debug) {
  709. deb($url);
  710. }
  711. $ch = curl_init();
  712. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  713. curl_setopt($ch, CURLOPT_URL, $url);
  714. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
  715. if ($this->debug) {
  716. $result = curl_exec($ch);
  717. deb($result);
  718. $curlError = curl_error($ch);
  719. if (!empty($curlError)) {
  720. show_error(__('Error') . ' ' . __('Telegram') . ': ' . $curlError);
  721. } else {
  722. show_success(__('Telegram API sending via') . ' ' . $this->apiUrl . ' ' . __('success'));
  723. }
  724. } else {
  725. $result = curl_exec($ch);
  726. }
  727. curl_close($ch);
  728. }
  729. return ($result);
  730. }
  731. /**
  732. * Returns chat data array by its chatId
  733. *
  734. * @param int chatId
  735. *
  736. * @return array
  737. */
  738. public function getChatInfo($chatId) {
  739. $result = array();
  740. if (!empty($this->botToken) and (!empty($chatId))) {
  741. $method = 'getChat';
  742. $url = $this->apiUrl . $this->botToken . '/' . $method . '?chat_id=' . $chatId;
  743. if ($this->debug) {
  744. deb($url);
  745. }
  746. $ch = curl_init();
  747. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  748. curl_setopt($ch, CURLOPT_URL, $url);
  749. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
  750. if ($this->debug) {
  751. $result = curl_exec($ch);
  752. deb($result);
  753. $curlError = curl_error($ch);
  754. if (!empty($curlError)) {
  755. show_error(__('Error') . ' ' . __('Telegram') . ': ' . $curlError);
  756. } else {
  757. show_success(__('Telegram API sending via') . ' ' . $this->apiUrl . ' ' . __('success'));
  758. }
  759. } else {
  760. $result = curl_exec($ch);
  761. }
  762. curl_close($ch);
  763. if (!empty($result)) {
  764. $result = json_decode($result, true);
  765. }
  766. }
  767. return ($result);
  768. }
  769. /**
  770. * Returns file path by its file ID
  771. *
  772. * @param string $fileId
  773. *
  774. * @return string
  775. */
  776. public function getFilePath($fileId) {
  777. $result = '';
  778. if (!empty($this->botToken)) {
  779. $method = 'getFile';
  780. $url = $this->apiUrl . $this->botToken . '/' . $method . '?file_id=' . $fileId;
  781. if ($this->debug) {
  782. deb($url);
  783. }
  784. $ch = curl_init();
  785. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  786. curl_setopt($ch, CURLOPT_URL, $url);
  787. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
  788. if ($this->debug) {
  789. $result = curl_exec($ch);
  790. deb($result);
  791. $curlError = curl_error($ch);
  792. if (!empty($curlError)) {
  793. show_error(__('Error') . ' ' . __('Telegram') . ': ' . $curlError);
  794. } else {
  795. show_success(__('Telegram API sending via') . ' ' . $this->apiUrl . ' ' . __('success'));
  796. }
  797. } else {
  798. $result = curl_exec($ch);
  799. }
  800. curl_close($ch);
  801. if (!empty($result)) {
  802. $result = json_decode($result, true);
  803. if (@$result['ok']) {
  804. //we got it!
  805. $result = $result['result']['file_path'];
  806. } else {
  807. //something went wrong
  808. $result = '';
  809. }
  810. }
  811. }
  812. return ($result);
  813. }
  814. /**
  815. * Returns some file content
  816. *
  817. * @param string $filePath
  818. *
  819. * @return mixed
  820. */
  821. public function downloadFile($filePath) {
  822. $result = '';
  823. if (!empty($this->botToken)) {
  824. $cleanApiUrl = str_replace('bot', '', $this->apiUrl);
  825. $url = $cleanApiUrl . 'file/bot' . $this->botToken . '/' . $filePath;
  826. $ch = curl_init();
  827. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  828. curl_setopt($ch, CURLOPT_URL, $url);
  829. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
  830. $result = curl_exec($ch);
  831. curl_close($ch);
  832. }
  833. return ($result);
  834. }
  835. /**
  836. * Returns preprocessed message in standard, fixed fields format
  837. *
  838. * @param array $messageData
  839. * @param bool $isChannel
  840. *
  841. * @return array
  842. */
  843. protected function preprocessMessageData($messageData, $isChannel = false) {
  844. $result = array();
  845. $result['message_id'] = $messageData['message_id'];
  846. if (!$isChannel) {
  847. //normal messages/groups
  848. $result['from']['id'] = $messageData['from']['id'];
  849. $result['from']['first_name'] = $messageData['from']['first_name'];
  850. @$result['from']['username'] = $messageData['from']['username'];
  851. @$result['from']['language_code'] = $messageData['from']['language_code'];
  852. } else {
  853. //channel posts
  854. $result['from']['id'] = $messageData['sender_chat']['id'];
  855. $result['from']['first_name'] = $messageData['sender_chat']['title'];
  856. @$result['from']['username'] = $messageData['sender_chat']['username'];
  857. @$result['from']['language_code'] = '';
  858. }
  859. $result['chat']['id'] = $messageData['chat']['id'];
  860. $result['date'] = $messageData['date'];
  861. $result['chat']['type'] = $messageData['chat']['type'];
  862. @$result['text'] = $messageData['text'];
  863. @$result['contact'] = $messageData['contact'];
  864. @$result['photo'] = $messageData['photo'];
  865. @$result['video'] = $messageData['video'];
  866. @$result['document'] = $messageData['document'];
  867. //photos and documents have only caption
  868. if (!empty($result['photo']) or ! empty($result['document'])) {
  869. @$result['text'] = $messageData['caption'];
  870. }
  871. @$result['voice'] = $messageData['voice'];
  872. @$result['audio'] = $messageData['audio'];
  873. @$result['video_note'] = $messageData['video_note'];
  874. @$result['location'] = $messageData['location'];
  875. @$result['sticker'] = $messageData['sticker'];
  876. @$result['new_chat_member'] = $messageData['new_chat_member'];
  877. @$result['new_chat_members'] = $messageData['new_chat_members'];
  878. @$result['new_chat_participant'] = $messageData['new_chat_participant'];
  879. @$result['left_chat_member'] = $messageData['left_chat_member'];
  880. @$result['left_chat_participant'] = $messageData['left_chat_participant'];
  881. @$result['reply_to_message'] = $messageData['reply_to_message'];
  882. //decode replied message too if received
  883. if ($result['reply_to_message']) {
  884. $result['reply_to_message'] = $this->preprocessMessageData($result['reply_to_message']);
  885. }
  886. //Uncomment following line for total debug
  887. //@$result['rawMessageData'] = $messageData;
  888. return ($result);
  889. }
  890. /**
  891. * Returns webhook data
  892. *
  893. * @param bool $rawData receive raw reply or preprocess to something more simple.
  894. *
  895. * @return array
  896. */
  897. public function getHookData($rawData = false) {
  898. $result = array();
  899. $postRaw = file_get_contents('php://input');
  900. if (!empty($postRaw)) {
  901. $postRaw = json_decode($postRaw, true);
  902. if ($this->debug) {
  903. debarr($result);
  904. }
  905. if (!$rawData) {
  906. if (isset($postRaw['message'])) {
  907. if (isset($postRaw['message']['from'])) {
  908. $result = $this->preprocessMessageData($postRaw['message']);
  909. }
  910. } else {
  911. if (isset($postRaw['channel_post'])) {
  912. $result = $this->preprocessMessageData($postRaw['channel_post'], true);
  913. } else {
  914. //other object like chat_member or message_reaction etc
  915. if (is_array($postRaw) and !empty($postRaw)) {
  916. $result = $postRaw;
  917. }
  918. }
  919. }
  920. } else {
  921. $result = $postRaw;
  922. }
  923. }
  924. return ($result);
  925. }
  926. /**
  927. * Sends an action to a chat using the Telegram API.
  928. *
  929. * @param string $chatid The ID of the chat.
  930. * @param string $action The action to be sent. Like "typing".
  931. *
  932. * @return string The result of the API request.
  933. * @throws Exception If the bot token is empty.
  934. */
  935. public function apiSendAction($chatid, $action) {
  936. $result = '';
  937. $method = 'sendChatAction';
  938. $data['chat_id'] = $chatid;
  939. $data['action'] = $action;
  940. if ($this->debug) {
  941. debarr($data);
  942. }
  943. $data_json = json_encode($data);
  944. if (!empty($this->botToken)) {
  945. $url = $this->apiUrl . $this->botToken . '/' . $method;
  946. if ($this->debug) {
  947. deb($url);
  948. }
  949. $ch = curl_init();
  950. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  951. curl_setopt($ch, CURLOPT_URL, $url);
  952. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
  953. curl_setopt($ch, CURLOPT_POST, 1);
  954. curl_setopt($ch, CURLOPT_POSTFIELDS, $data_json);
  955. if ($this->debug) {
  956. $result = curl_exec($ch);
  957. deb($result);
  958. $curlError = curl_error($ch);
  959. if (!empty($curlError)) {
  960. show_error(__('Error') . ' ' . __('Telegram') . ': ' . $curlError);
  961. } else {
  962. show_success(__('Telegram API sending via') . ' ' . $this->apiUrl . ' ' . __('success'));
  963. }
  964. } else {
  965. $result = curl_exec($ch);
  966. }
  967. curl_close($ch);
  968. } else {
  969. throw new Exception('EX_TOKEN_EMPTY');
  970. }
  971. return ($result);
  972. }
  973. }