api.wolfdispatcher.php 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. <?php
  2. /**
  3. * Universal Telegram bot hooks processing extendable class
  4. */
  5. class WolfDispatcher {
  6. /**
  7. * Contains current instance bot token
  8. *
  9. * @var string
  10. */
  11. protected $botToken = '';
  12. /**
  13. * Contains text commands=>actions mappings
  14. *
  15. * @var array
  16. */
  17. protected $commands = array();
  18. /**
  19. * Group chats commands array which overrides normal actions only for group chats
  20. *
  21. * @var array
  22. */
  23. protected $groupChatCommands = array();
  24. /**
  25. * Chats commands array which required user set in adminChatIds struct to be executed.
  26. *
  27. * @var array
  28. */
  29. protected $adminCommands = array();
  30. /**
  31. * Contains text reactions=>actions mappings
  32. *
  33. * @var array
  34. */
  35. protected $textReacts = array();
  36. /**
  37. * Array of chatIds which is denied for any actions performing
  38. *
  39. * @var array
  40. */
  41. protected $ignoredChatIds = array();
  42. /**
  43. * Contains administrator users chatIds as chatId=>index
  44. *
  45. * @var array
  46. */
  47. protected $adminChatIds = array();
  48. /**
  49. * Array of chatIds which is allowed for actions execution. Ignored if empty.
  50. *
  51. * @var array
  52. */
  53. protected $allowedChatIds = array();
  54. /**
  55. * Telegram interraction layer object placeholder
  56. *
  57. * @var object
  58. */
  59. protected $telegram = '';
  60. /**
  61. * Input data storage
  62. *
  63. * @var array
  64. */
  65. protected $receivedData = array();
  66. /**
  67. * Current conversation client chatId
  68. *
  69. * @var int
  70. */
  71. protected $chatId = 0;
  72. /**
  73. * Current conversation latest messageId
  74. *
  75. * @var int
  76. */
  77. protected $messageId = 0;
  78. /**
  79. * Current converstation chat type, like private,group
  80. *
  81. * @var string
  82. */
  83. protected $chatType = '';
  84. /**
  85. * Method name which will be executed on any image receive
  86. *
  87. * @var string
  88. */
  89. protected $photoHandleMethod = '';
  90. /**
  91. * Method name which will be executed if any new chat member appears
  92. *
  93. * @var string
  94. */
  95. protected $chatMemberAppearMethod = '';
  96. /**
  97. * Method name which will be executed if any new chat member left chat
  98. *
  99. * @var string
  100. */
  101. protected $chatMemberLeftMethod = '';
  102. /**
  103. * Dispatcher debugging flag
  104. *
  105. * @var bool
  106. */
  107. protected $debugFlag = false;
  108. /**
  109. * Contains all called actions/methods
  110. *
  111. * @var string
  112. */
  113. protected $calledActions = array();
  114. /**
  115. * Contains current bot instance class name as is
  116. *
  117. * @var string
  118. */
  119. protected $botImplementation = '';
  120. /**
  121. * Web-hook automatic installation flag
  122. *
  123. * @var bool
  124. */
  125. protected $hookAutoSetup = false;
  126. /**
  127. * Hook allowed updates array
  128. *
  129. * @var array
  130. */
  131. protected $allowedUpdates = array();
  132. /**
  133. * Contains default webhook maxConnections value
  134. *
  135. * @var int
  136. */
  137. protected $maxConnections = 100;
  138. /**
  139. * Contains default debug log path
  140. */
  141. const LOG_PATH = 'exports/';
  142. /**
  143. * Contains path to save web hooks PIDs due autosetup.
  144. */
  145. const HOOK_PID_PATH = 'exports/';
  146. /**
  147. * Creates new dispatcher instance
  148. *
  149. * @param string $token
  150. *
  151. * @return void
  152. */
  153. public function __construct($token) {
  154. if (!empty($token)) {
  155. $this->botToken = $token;
  156. }
  157. // , ,
  158. // |\---/|
  159. // / , , |
  160. // __.-'| / \ /
  161. // __ ___.-' ._O|
  162. // .-' ' : _/
  163. // / , . . |
  164. // : ; : : _/
  165. // | | .' __: /
  166. // | : /'----'| \ |
  167. // \ |\ | | /| |
  168. // '.'| / || \ |
  169. // | /|.' '.l \\_
  170. // || || '-'
  171. // '-''-'
  172. $this->initTelegram();
  173. $this->setBotName();
  174. }
  175. /**
  176. * Sets current bot instance implementation property
  177. *
  178. * @return void
  179. */
  180. protected function setBotName() {
  181. $this->botImplementation = get_class($this);
  182. }
  183. /**
  184. * Instance debugging flag setter. Debug log: exports/botname_debug.log
  185. *
  186. * @param bool $state
  187. *
  188. * @return void
  189. */
  190. public function setDebug($state) {
  191. if ($state) {
  192. $this->debugFlag = true;
  193. }
  194. }
  195. /**
  196. * Inits protected telegram instance
  197. *
  198. * @throws Exception
  199. *
  200. * @return void
  201. */
  202. protected function initTelegram() {
  203. if (!empty($this->botToken)) {
  204. if (class_exists('UbillingTelegram')) {
  205. $this->telegram = new UbillingTelegram($this->botToken);
  206. } else {
  207. if (class_exists('WolfGram')) {
  208. $this->telegram = new WolfGram($this->botToken);
  209. }
  210. }
  211. if (empty($this->telegram)) {
  212. throw new Exception('EX_NO_TELEGRAM_LIB');
  213. }
  214. } else {
  215. throw new Exception('EX_EMPTY_TOKEN');
  216. }
  217. }
  218. /**
  219. * Sets new dispatcher actions dataset
  220. *
  221. * @param array $commands dataset as text input=>method or function name
  222. *
  223. * @return void
  224. */
  225. public function setActions($commands) {
  226. if (!empty($commands)) {
  227. if (is_array($commands)) {
  228. $this->commands = $commands;
  229. }
  230. }
  231. }
  232. /**
  233. * Sets group commands data set
  234. * If not empty data set its overrides all default actions for not private chats
  235. *
  236. * @param array $groupCommands dataset as text input=>method or function name
  237. *
  238. * @return void
  239. */
  240. public function setGroupActions($groupCommands) {
  241. $this->groupChatCommands = $groupCommands;
  242. }
  243. /**
  244. * Sets admin commands data set. This actions requires user to be isAdmin() for execution
  245. *
  246. * @param array $adminCommands dataset as text input=>method or function name
  247. *
  248. * @return void
  249. */
  250. public function setAdminActions($adminCommands) {
  251. $this->adminCommands = $adminCommands;
  252. }
  253. /**
  254. * Sets administrative user chatIDs
  255. *
  256. * @param array $chatIds just array of chatids of administrative users like array('111111','222222')
  257. *
  258. * @return void
  259. */
  260. public function setAdminChatId($chatIds) {
  261. if (!empty($chatIds)) {
  262. $chatIds = array_flip($chatIds);
  263. $this->adminChatIds = $chatIds;
  264. }
  265. }
  266. /**
  267. * Sets new dispatcher text reactions dataset. Basic setActions dataset overrides this.
  268. *
  269. * @param array $commands dataset as text input=>method or function name
  270. *
  271. * @return void
  272. */
  273. public function setTextReactions($commands) {
  274. if (!empty($commands)) {
  275. if (is_array($commands)) {
  276. $this->textReacts = $commands;
  277. }
  278. }
  279. }
  280. /**
  281. * Sets method name which will be executed on any image input
  282. *
  283. * @param string $name existing method name to process received images
  284. *
  285. * @return void
  286. */
  287. public function setPhotoHandler($name) {
  288. if (!empty($name)) {
  289. $this->photoHandleMethod = $name;
  290. }
  291. }
  292. /**
  293. * Sets method names which will be executed if some member appears or left chat
  294. *
  295. * @param string $methodAppear
  296. * @param string $methodLeft
  297. *
  298. * @return void
  299. */
  300. public function setOnChatMemberActions($methodAppear = '', $methodLeft = '') {
  301. if (!empty($methodAppear)) {
  302. $this->chatMemberAppearMethod = $methodAppear;
  303. }
  304. if (!empty($methodLeft)) {
  305. $this->chatMemberLeftMethod = $methodLeft;
  306. }
  307. }
  308. /**
  309. * Sets allowed hook updates list. Example: array('update_id', 'message', 'chat_member')
  310. *
  311. * https://core.telegram.org/bots/api#update
  312. * https://core.telegram.org/bots/api#setwebhook
  313. *
  314. * @param array $allowedHookUpdates
  315. *
  316. * @return void
  317. */
  318. public function setAllowedUpdates($allowedHookUpdates = array()) {
  319. $this->allowedUpdates = $allowedHookUpdates;
  320. }
  321. /**
  322. * Sets hook max connections limit
  323. *
  324. * @param int $limit
  325. *
  326. * @return void
  327. */
  328. public function setMaxConnections($limit = 100) {
  329. $this->maxConnections = $limit;
  330. }
  331. /**
  332. * Sets allowed chat IDs for this instance
  333. *
  334. * @param array $chatIds chatIds which only allowed to interract this bot instance just like array('1234','4321')
  335. *
  336. * @return void
  337. */
  338. public function setAllowedChatIds($chatIds) {
  339. if (!empty($chatIds)) {
  340. if (is_array($chatIds)) {
  341. $this->allowedChatIds = array_flip($chatIds);
  342. }
  343. }
  344. }
  345. /**
  346. * Sets denied chat IDs for this instance
  347. *
  348. * @param array $chatIds chatIds which is denied from interraction with this instance just like array('1234','4321')
  349. *
  350. * @return void
  351. */
  352. public function setIgnoredChatIds($chatIds) {
  353. if (!empty($chatIds)) {
  354. if (is_array($chatIds)) {
  355. $this->ignoredChatIds = array_flip($chatIds);
  356. }
  357. }
  358. }
  359. /**
  360. * Getting current input text action name if it exists
  361. *
  362. * @return string
  363. */
  364. protected function getTextAction() {
  365. $result = '';
  366. //basic commands processing
  367. if (!empty($this->commands)) {
  368. foreach ($this->commands as $eachCommand => $eachAction) {
  369. if (mb_stripos($this->receivedData['text'], $eachCommand, 0, 'UTF-8') !== false) {
  370. $result = $eachAction;
  371. }
  372. }
  373. }
  374. //administrative commands processing
  375. if (!empty($this->adminCommands)) {
  376. if ($this->isAdmin()) {
  377. foreach ($this->adminCommands as $eachCommand => $eachAction) {
  378. if (mb_stripos($this->receivedData['text'], $eachCommand, 0, 'UTF-8') !== false) {
  379. $result = $eachAction;
  380. }
  381. }
  382. }
  383. }
  384. //text reactions if no of main commands detected
  385. if (empty($result)) {
  386. if (!empty($this->textReacts)) {
  387. foreach ($this->textReacts as $eachTextReact => $eachAction) {
  388. if (mb_stripos($this->receivedData['text'], $eachTextReact, 0, 'UTF-8') !== false) {
  389. $result = $eachAction;
  390. }
  391. }
  392. }
  393. }
  394. return ($result);
  395. }
  396. /**
  397. * Performs run of some action into current dispatcher instance
  398. *
  399. * @param string $actionName
  400. *
  401. * @return void
  402. */
  403. protected function runAction($actionName) {
  404. if (!empty($actionName)) {
  405. if (method_exists($this, $actionName)) {
  406. //class methods have priority
  407. $this->$actionName();
  408. if ($this->debugFlag) {
  409. $this->calledActions[] = 'METHOD: ' . $actionName;
  410. }
  411. } else {
  412. if (function_exists($actionName)) {
  413. $actionName($this->receivedData);
  414. if ($this->debugFlag) {
  415. $this->calledActions[] = 'FUNC: ' . $actionName;
  416. }
  417. } else {
  418. if ($this->debugFlag) {
  419. //any command/reaction handler found
  420. $chatId = $this->receivedData['chat']['id'];
  421. $message = __('Any existing function on method named') . ' `' . $actionName . '` ' . __('not found by dispatcher');
  422. $this->telegram->directPushMessage($chatId, $message);
  423. if ($this->debugFlag) {
  424. $this->calledActions[] = 'FAILED: ' . $actionName;
  425. }
  426. }
  427. }
  428. }
  429. }
  430. }
  431. /**
  432. * Run some actions on non empty input data received
  433. *
  434. * @return void
  435. */
  436. protected function reactInput() {
  437. $currentInputAction = '';
  438. if (!empty($this->receivedData)) {
  439. if (isset($this->receivedData['from']) and isset($this->receivedData['chat'])) {
  440. $chatId = $this->receivedData['chat']['id']; //yeah, we waiting for preprocessed data here
  441. $interractionAllowed = true;
  442. //separate allows here
  443. if (!empty($this->allowedChatIds)) {
  444. if (!isset($this->allowedChatIds[$chatId])) {
  445. $interractionAllowed = false;
  446. }
  447. }
  448. //something like ban list
  449. if (isset($this->ignoredChatIds[$chatId])) {
  450. $interractionAllowed = false;
  451. }
  452. if ($interractionAllowed) {
  453. //interraction with this chat id is allowed
  454. if (!empty($this->receivedData['text'])) {
  455. $currentInputAction = $this->getTextAction();
  456. if (!empty($currentInputAction)) {
  457. $this->runAction($currentInputAction);
  458. } else {
  459. //empty actions here. No of existing commands or reactions found here.
  460. $this->handleEmptyAction();
  461. }
  462. } else {
  463. //empty text actions here
  464. $this->handleEmptyText();
  465. }
  466. //this method will be executed on image receive if set
  467. if (!empty($this->photoHandleMethod)) {
  468. if ($this->isPhotoReceived()) {
  469. $this->runAction($this->photoHandleMethod);
  470. }
  471. }
  472. //following methods will be executed while new member appears in chat or lefts the chat
  473. if (!empty($this->chatMemberAppearMethod)) {
  474. if ($this->isNewChatMemberAppear()) {
  475. $this->runAction($this->chatMemberAppearMethod);
  476. }
  477. }
  478. if (!empty($this->chatMemberLeftMethod)) {
  479. if ($this->isChatMemberLeft()) {
  480. $this->runAction($this->chatMemberLeftMethod);
  481. }
  482. }
  483. //this will be executed if some image received anyway
  484. if ($this->isPhotoReceived()) {
  485. $this->handlePhotoReceived();
  486. }
  487. }
  488. }
  489. //this shall be executed on any non empty data recieve
  490. $this->handleAnyWay();
  491. }
  492. }
  493. /**
  494. * Dummy method which will be executed on receive empty text actions on listener
  495. *
  496. * @return void
  497. */
  498. protected function handleEmptyAction() {
  499. //will be executed on messages with no detected action for message
  500. }
  501. /**
  502. * Dummy method which will be executed on receive empty text actions on listener
  503. *
  504. * @return void
  505. */
  506. protected function handleEmptyText() {
  507. //will be executed on messages with empty text field
  508. }
  509. /**
  510. * Dummy method which will be executed on receive any non empty data on listener
  511. *
  512. * @return void
  513. */
  514. protected function handleAnyWay() {
  515. //will be executed on eny non empty received data
  516. }
  517. /**
  518. * Dummy method which will be executed on receive any image file
  519. *
  520. * @return void
  521. */
  522. protected function handlePhotoReceived() {
  523. //will be executed if any image received
  524. }
  525. /**
  526. * Writes debug data to separate per-class log if debugging flag enabled.
  527. *
  528. * @global int $starttime
  529. * @global int $query_counter
  530. *
  531. * @return void
  532. */
  533. protected function writeDebugLog() {
  534. global $starttime, $query_counter;
  535. if ($this->debugFlag) {
  536. $nowmtime = explode(' ', microtime());
  537. $wtotaltime = $nowmtime[0] + $nowmtime[1] - $starttime;
  538. $logData = $this->botImplementation . ': ' . curdatetime() . PHP_EOL;
  539. $logData .= print_r($this->receivedData, true) . PHP_EOL;
  540. $logData .= 'GT: ' . round($wtotaltime, 4) . ' QC: ' . $query_counter . PHP_EOL;
  541. if (!empty($this->calledActions)) {
  542. $logData .= PHP_EOL . 'Called actions: ' . print_r($this->calledActions, true) . PHP_EOL;
  543. } else {
  544. $logData .= PHP_EOL . 'Called actions: NONE' . PHP_EOL;
  545. }
  546. $logData .= '==================' . PHP_EOL;
  547. $logFileName = strtolower($this->botImplementation) . '_debug.log';
  548. file_put_contents(self::LOG_PATH . $logFileName, $logData, FILE_APPEND);
  549. }
  550. }
  551. /**
  552. * Listens for some events
  553. *
  554. * @return array
  555. */
  556. public function listen() {
  557. //may be automatic setup required?
  558. $this->installWebHook();
  559. //is something here?
  560. $this->receivedData = $this->telegram->getHookData();
  561. if (!empty($this->receivedData)) {
  562. @$this->chatId = $this->receivedData['chat']['id'];
  563. @$this->messageId = $this->receivedData['message_id'];
  564. @$this->chatType = $this->receivedData['chat']['type'];
  565. //wow, some separate group commands here. They overrides all another actions.
  566. if (!empty($this->groupChatCommands)) {
  567. if ($this->chatType != 'private') {
  568. //override actions with another group set
  569. $this->setActions($this->groupChatCommands);
  570. }
  571. }
  572. $this->reactInput();
  573. }
  574. $this->writeDebugLog();
  575. return ($this->receivedData);
  576. }
  577. /**
  578. * Checks is any image received?
  579. *
  580. * @return bool
  581. */
  582. protected function isPhotoReceived() {
  583. $result = false;
  584. if ($this->receivedData['photo'] or $this->receivedData['document']) {
  585. $imageMimeTypes = array('image/png', 'image/jpeg');
  586. if ($this->receivedData['photo']) {
  587. $result = true;
  588. } else {
  589. if ($this->receivedData['document']) {
  590. $imageMimeTypes = array_flip($imageMimeTypes);
  591. if (isset($imageMimeTypes[$this->receivedData['document']['mime_type']])) {
  592. $result = true;
  593. }
  594. }
  595. }
  596. }
  597. return ($result);
  598. }
  599. /**
  600. * Checks is new chat member appeared in chat? Returns his data on this event.
  601. *
  602. * Data fields:
  603. * id, is_bot, first_name, username, language_code, is_premium - normal users
  604. * id, is_bot, first_name, username - bots
  605. *
  606. * @return array/bool
  607. */
  608. protected function isNewChatMemberAppear() {
  609. $result = false;
  610. if ($this->receivedData['new_chat_member']) {
  611. $result = $this->receivedData['new_chat_member'];
  612. }
  613. return ($result);
  614. }
  615. /**
  616. * Checks is any chat member lefts chat? Returns his chatId on this event.
  617. * Data fields:
  618. * id, is_bot, first_name, username, language_code, is_premium - normal users
  619. * id, is_bot, first_name, username - bots
  620. *
  621. * @return array/bool
  622. */
  623. protected function isChatMemberLeft() {
  624. $result = false;
  625. if ($this->receivedData['left_chat_member']) {
  626. $result = $this->receivedData['left_chat_member'];
  627. }
  628. return ($result);
  629. }
  630. /**
  631. * Checks if a new chat member has appeared in a group chat.
  632. *
  633. * This method inspects the received data to determine if a new chat member
  634. * has joined a group chat. It returns the received data if the new chat member's
  635. * status is 'member' and the chat ID is negative (indicating a group chat).
  636. *
  637. * @return array|bool Returns the received data if a new chat member has appeared in a group chat, otherwise false.
  638. */
  639. protected function isGroupMemberAppear() {
  640. $result = false;
  641. //chat_member object update
  642. if (isset($this->receivedData['chat_member'])) {
  643. $data = $this->receivedData['chat_member'];
  644. if ($data['new_chat_member']['status'] == 'member') {
  645. if ($data['chat']['id'] < 0) {
  646. $result = $this->receivedData;
  647. }
  648. }
  649. }
  650. return ($result);
  651. }
  652. /**
  653. * Checks if a group member has left the chat by the own will
  654. *
  655. * This method inspects the received data to determine if a chat member's status
  656. * has changed to 'left' in a group chat. If the status is 'left' and the chat ID
  657. * is less than 0 (indicating a group chat), it returns the received data.
  658. *
  659. * @return array|false Returns the received data if a group member has left, otherwise false.
  660. */
  661. protected function isGroupMemberLeft() {
  662. $result = false;
  663. //chat_member object update
  664. if (isset($this->receivedData['chat_member'])) {
  665. $data = $this->receivedData['chat_member'];
  666. if ($data['new_chat_member']['status'] == 'left' and $data['old_chat_member']['status'] == 'member') {
  667. if ($data['chat']['id'] < 0) {
  668. $result = $this->receivedData;
  669. }
  670. }
  671. }
  672. return ($result);
  673. }
  674. /**
  675. * Checks if a group member has been kicked/banned from the chat.
  676. *
  677. * This method inspects the received data to determine if a member's status
  678. * has been updated to 'kicked' in a group chat. If the member has been kicked
  679. * and the chat ID is negative (indicating a group chat), it returns the
  680. * received data.
  681. *
  682. * @return array|false Returns the received data if a member has been kicked
  683. * from a group chat, otherwise returns false.
  684. */
  685. protected function isGroupMemberBanned() {
  686. $result = false;
  687. //chat_member object update
  688. if (isset($this->receivedData['chat_member'])) {
  689. $data = $this->receivedData['chat_member'];
  690. if ($data['new_chat_member']['status'] == 'kicked' and ($data['old_chat_member']['status'] == 'member' or $data['old_chat_member']['status'] == 'left')) {
  691. if ($data['chat']['id'] < 0) {
  692. $result = $this->receivedData;
  693. }
  694. }
  695. }
  696. return ($result);
  697. }
  698. /**
  699. * Checks if a group member has been unbanned.
  700. *
  701. * This method verifies if the received data indicates that a member who was previously kicked
  702. * from the group has now left the group, which implies they have been unbanned.
  703. *
  704. * @return mixed Returns the received data if the member has been unbanned, otherwise false.
  705. */
  706. protected function isGroupMemberUnbanned() {
  707. $result = false;
  708. if (isset($this->receivedData['chat_member'])) {
  709. $data = $this->receivedData['chat_member'];
  710. if ($data['new_chat_member']['status'] == 'left' and $data['old_chat_member']['status'] == 'kicked') {
  711. if ($data['chat']['id'] < 0) {
  712. $result = $this->receivedData;
  713. }
  714. }
  715. }
  716. return ($result);
  717. }
  718. /**
  719. * Checks is current user chatId listed as administrator?
  720. *
  721. * @return bool
  722. */
  723. protected function isAdmin() {
  724. $result = false;
  725. if (!empty($this->adminChatIds)) {
  726. //direct message in private chat?
  727. if (isset($this->adminChatIds[$this->chatId])) {
  728. $result = true;
  729. } else {
  730. //may be group chat message from admin user?
  731. if (isset($this->adminChatIds[$this->receivedData['from']['id']])) {
  732. $result = true;
  733. }
  734. }
  735. }
  736. return ($result);
  737. }
  738. /**
  739. * Returns current received message as receivedData struct
  740. *
  741. * @return array
  742. */
  743. protected function message() {
  744. return ($this->receivedData);
  745. }
  746. /**
  747. * Returns received image file content
  748. *
  749. * @return mixed
  750. */
  751. protected function getPhoto() {
  752. $result = '';
  753. $filePath = '';
  754. $fileId = '';
  755. $imageMimeTypes = array('image/png', 'image/jpeg');
  756. //normal compressed image
  757. if ($this->receivedData['photo']) {
  758. $maxSizeFile = end($this->receivedData['photo']);
  759. $fileId = $maxSizeFile['file_id'];
  760. } else {
  761. //image received as-is without compression
  762. $imageMimeTypes = array_flip($imageMimeTypes);
  763. if ($this->receivedData['document']) {
  764. if (isset($imageMimeTypes[$this->receivedData['document']['mime_type']])) {
  765. $fileId = $this->receivedData['document']['file_id'];
  766. }
  767. }
  768. }
  769. //downloading remote file
  770. if ($fileId) {
  771. $filePath = $this->telegram->getFilePath($fileId);
  772. if ($filePath) {
  773. $result = $this->telegram->downloadFile($filePath);
  774. }
  775. }
  776. return ($result);
  777. }
  778. /**
  779. * Saves received photo to the specified path on filesystem. Returns filepath on success.
  780. *
  781. * @param string $savePath
  782. *
  783. * @return string/void
  784. */
  785. protected function savePhoto($savePath) {
  786. $result = '';
  787. if (!empty($savePath)) {
  788. if ($this->isPhotoReceived()) {
  789. $receivedPhoto = $this->getPhoto();
  790. if ($receivedPhoto) {
  791. file_put_contents($savePath, $receivedPhoto);
  792. if (file_exists($savePath)) {
  793. $result = $savePath;
  794. }
  795. }
  796. }
  797. }
  798. return ($result);
  799. }
  800. /**
  801. * Sends fast reply to current chat member
  802. *
  803. * @param string $message
  804. * @param array $keyboard
  805. *
  806. * @return string/bool
  807. */
  808. protected function reply($message = '', $keyboard = array()) {
  809. $result = '';
  810. if (!empty($message)) {
  811. $replyResult = $this->telegram->directPushMessage($this->chatId, $message, $keyboard);
  812. if ($replyResult) {
  813. $result = json_decode($replyResult, true);
  814. }
  815. }
  816. return ($result);
  817. }
  818. /**
  819. * Sends fast reply to current chat member for latest message or some specified messageId
  820. *
  821. * @param string $message
  822. * @param array $keyboard
  823. * @param int $replyToMsg
  824. *
  825. * @return string/bool
  826. */
  827. protected function replyTo($message = '', $keyboard = array(), $replyToMsg = '') {
  828. $result = '';
  829. if (!empty($message)) {
  830. if (empty($replyToMsg)) {
  831. $replyToMsg = $this->messageId;
  832. }
  833. $replyResult = $this->telegram->directPushMessage($this->chatId, $message, $keyboard, false, $replyToMsg);
  834. if ($replyResult) {
  835. $result = json_decode($replyResult, true);
  836. }
  837. }
  838. return ($result);
  839. }
  840. /**
  841. * Removes a chat message by its ID from a specified chat.
  842. *
  843. * @param int $messageId The ID of the message to be removed.
  844. * @param int $chatId The ID of the chat from which the message will be removed.
  845. *
  846. * @return array
  847. */
  848. protected function removeChatMessage($messageId, $chatId) {
  849. $result = array();
  850. $deleteResult = $this->telegram->directPushMessage($chatId, 'removeChatMessage:[' . $messageId . '@' . $chatId . ']');
  851. if ($deleteResult) {
  852. $result = json_decode($deleteResult, true);
  853. }
  854. return ($result);
  855. }
  856. /**
  857. * Bans a member from a specified group.
  858. *
  859. * This function sends a direct push message to the Telegram API to ban a member from a group.
  860. *
  861. * @param int $memberId The ID of the member to be banned.
  862. * @param int $groupId The ID of the group from which the member will be banned.
  863. *
  864. * @return array
  865. */
  866. protected function banGroupMember($memberId, $groupId) {
  867. $result = array();
  868. $banResult = $this->telegram->directPushMessage($groupId, 'banChatMember:[' . $memberId . '@' . $groupId . ']');
  869. if ($banResult) {
  870. $result = json_decode($banResult, true);
  871. }
  872. return ($result);
  873. }
  874. /**
  875. * Unbans a member from a group.
  876. *
  877. * This method sends a direct push message to unban a member from a specified group using the Telegram API.
  878. *
  879. * @param int $memberId The ID of the member to be unbanned.
  880. * @param int $groupId The ID of the group from which the member will be unbanned.
  881. *
  882. * @return array
  883. */
  884. protected function unbanGroupMember($memberId, $groupId) {
  885. $result = array();
  886. $unbanResult = $this->telegram->directPushMessage($groupId, 'unbanChatMember:[' . $memberId . '@' . $groupId . ']');
  887. if ($unbanResult) {
  888. $result = json_decode($unbanResult, true);
  889. }
  890. return ($result);
  891. }
  892. /**
  893. * Sends some keyboard to current chat
  894. *
  895. * @param array $buttons
  896. * @param string $text
  897. *
  898. * @return void
  899. */
  900. protected function castKeyboard($buttons, $text = '⌨️') {
  901. if (!empty($buttons)) {
  902. $keyboard = $this->telegram->makeKeyboard($buttons);
  903. $this->reply($text, $keyboard);
  904. }
  905. }
  906. /**
  907. * Enables or disables web-hook automatic installation
  908. *
  909. * @param bool $enabled is web-hook autosetup enabled?
  910. *
  911. * @return void
  912. */
  913. public function hookAutosetup($enabled = true) {
  914. $this->hookAutoSetup = $enabled;
  915. }
  916. /**
  917. * Automatically registers new web-hook URL for bot if isnt registered yet.
  918. *
  919. * @return void
  920. */
  921. protected function installWebHook() {
  922. if ($this->hookAutoSetup) {
  923. $listenerUrl = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
  924. $tokenHash = md5($this->botToken . $listenerUrl);
  925. $hookPidName = self::HOOK_PID_PATH . $this->botImplementation . $tokenHash . '.hook';
  926. //need to be installed?
  927. if (!file_exists($hookPidName)) {
  928. $hookInfo = json_decode($this->telegram->getWebHookInfo(), true);
  929. $hookInfo = $hookInfo['result'];
  930. $hookAllowedUpdates = isset($hookInfo['allowed_updates']) ? $hookInfo['allowed_updates'] : array();
  931. if ($hookInfo['url'] != $listenerUrl or $hookAllowedUpdates != $this->allowedUpdates or $hookInfo['max_connections'] != $this->maxConnections) {
  932. //need to be installed new URL with new params
  933. $this->telegram->setWebHook($listenerUrl, $this->maxConnections, $this->allowedUpdates);
  934. if (function_exists('show_success')) {
  935. show_success($this->botImplementation . 'installed web-hook URL: ' . $hookInfo['url']);
  936. }
  937. } else {
  938. //already set, but no PID
  939. if (function_exists('show_warning')) {
  940. show_warning($this->botImplementation . ' PID saved for web-hook URL: ' . $hookInfo['url']);
  941. }
  942. }
  943. //write hook pid
  944. file_put_contents($hookPidName, $listenerUrl);
  945. //some logging
  946. if ($this->debugFlag) {
  947. $logFileName = strtolower($this->botImplementation) . '_debug.log';
  948. $logData = $this->botImplementation . ': ' . curdatetime() . PHP_EOL;
  949. $logData .= 'INSTALLED WEB HOOK: ' . $listenerUrl . PHP_EOL;
  950. $logData .= 'HOOK PID: ' . $hookPidName . PHP_EOL;
  951. file_put_contents(self::LOG_PATH . $logFileName, $logData, FILE_APPEND);
  952. }
  953. } else {
  954. //ok, hook is already installed
  955. $currentHookUrl = file_get_contents($hookPidName);
  956. if (function_exists('show_info')) {
  957. show_info($this->botImplementation . ' web-hook URL: ' . $currentHookUrl);
  958. }
  959. }
  960. }
  961. }
  962. }