Passed
Push — master ( fb0f34...1cf5d1 )
by Armando
03:56 queued 02:05
created

Telegram::getCommandObject()   C

Complexity

Conditions 12
Paths 35

Size

Total Lines 44
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 24.6381

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 12
eloc 27
c 2
b 0
f 0
nc 35
nop 2
dl 0
loc 44
ccs 15
cts 27
cp 0.5556
crap 24.6381
rs 6.9666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file is part of the TelegramBot package.
5
 *
6
 * (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Longman\TelegramBot;
13
14 1
defined('TB_BASE_PATH') || define('TB_BASE_PATH', __DIR__);
15 1
defined('TB_BASE_COMMANDS_PATH') || define('TB_BASE_COMMANDS_PATH', TB_BASE_PATH . '/Commands');
16
17
use Exception;
18
use Longman\TelegramBot\Commands\AdminCommand;
19
use Longman\TelegramBot\Commands\Command;
20
use Longman\TelegramBot\Commands\SystemCommand;
21
use Longman\TelegramBot\Commands\UserCommand;
22
use Longman\TelegramBot\Entities\ServerResponse;
23
use Longman\TelegramBot\Entities\Update;
24
use Longman\TelegramBot\Exception\TelegramException;
25
use PDO;
26
use RecursiveDirectoryIterator;
27
use RecursiveIteratorIterator;
28
use RegexIterator;
29
30
class Telegram
31
{
32
    /**
33
     * Version
34
     *
35
     * @var string
36
     */
37
    protected $version = '0.63.1';
38
39
    /**
40
     * Telegram API key
41
     *
42
     * @var string
43
     */
44
    protected $api_key = '';
45
46
    /**
47
     * Telegram Bot username
48
     *
49
     * @var string
50
     */
51
    protected $bot_username = '';
52
53
    /**
54
     * Telegram Bot id
55
     *
56
     * @var string
57
     */
58
    protected $bot_id = '';
59
60
    /**
61
     * Raw request data (json) for webhook methods
62
     *
63
     * @var string
64
     */
65
    protected $input;
66
67
    /**
68
     * Custom commands paths
69
     *
70
     * @var array
71
     */
72
    protected $commands_paths = [];
73
74
    /**
75
     * Custom commands objects
76
     *
77
     * @var array
78
     */
79
    protected $commands_objects = [];
80
81
    /**
82
     * Current Update object
83
     *
84
     * @var Update
85
     */
86
    protected $update;
87
88
    /**
89
     * Upload path
90
     *
91
     * @var string
92
     */
93
    protected $upload_path;
94
95
    /**
96
     * Download path
97
     *
98
     * @var string
99
     */
100
    protected $download_path;
101
102
    /**
103
     * MySQL integration
104
     *
105
     * @var bool
106
     */
107
    protected $mysql_enabled = false;
108
109
    /**
110
     * PDO object
111
     *
112
     * @var PDO
113
     */
114
    protected $pdo;
115
116
    /**
117
     * Commands config
118
     *
119
     * @var array
120
     */
121
    protected $commands_config = [];
122
123
    /**
124
     * Admins list
125
     *
126
     * @var array
127
     */
128
    protected $admins_list = [];
129
130
    /**
131
     * ServerResponse of the last Command execution
132
     *
133
     * @var ServerResponse
134
     */
135
    protected $last_command_response;
136
137
    /**
138
     * Check if runCommands() is running in this session
139
     *
140
     * @var bool
141
     */
142
    protected $run_commands = false;
143
144
    /**
145
     * Is running getUpdates without DB enabled
146
     *
147
     * @var bool
148
     */
149
    protected $getupdates_without_database = false;
150
151
    /**
152
     * Last update ID
153
     * Only used when running getUpdates without a database
154
     *
155
     * @var integer
156
     */
157
    protected $last_update_id;
158
159
    /**
160
     * The command to be executed when there's a new message update and nothing more suitable is found
161
     */
162
    const GENERIC_MESSAGE_COMMAND = 'genericmessage';
163
164
    /**
165
     * The command to be executed by default (when no other relevant commands are applicable)
166
     */
167
    const GENERIC_COMMAND = 'generic';
168
169
    /**
170
     * Update filter
171
     * Filter updates
172
     *
173
     * @var callback
174
     */
175
    protected $update_filter;
176
177
    /**
178
     * Telegram constructor.
179
     *
180
     * @param string $api_key
181
     * @param string $bot_username
182
     *
183
     * @throws TelegramException
184
     */
185 30
    public function __construct($api_key, $bot_username = '')
186
    {
187 30
        if (empty($api_key)) {
188 1
            throw new TelegramException('API KEY not defined!');
189
        }
190 30
        preg_match('/(\d+)\:[\w\-]+/', $api_key, $matches);
191 30
        if (!isset($matches[1])) {
192 1
            throw new TelegramException('Invalid API KEY defined!');
193
        }
194 30
        $this->bot_id  = $matches[1];
195 30
        $this->api_key = $api_key;
196
197 30
        if (!empty($bot_username)) {
198 30
            $this->bot_username = $bot_username;
199
        }
200
201
        //Add default system commands path
202 30
        $this->addCommandsPath(TB_BASE_COMMANDS_PATH . '/SystemCommands');
203
204 30
        Request::initialize($this);
205 30
    }
206
207
    /**
208
     * Initialize Database connection
209
     *
210
     * @param array  $credential
211
     * @param string $table_prefix
212
     * @param string $encoding
213
     *
214
     * @return Telegram
215
     * @throws TelegramException
216
     */
217 9
    public function enableMySql(array $credential, $table_prefix = null, $encoding = 'utf8mb4')
218
    {
219 9
        $this->pdo = DB::initialize($credential, $this, $table_prefix, $encoding);
220 9
        ConversationDB::initializeConversation();
221 9
        $this->mysql_enabled = true;
222
223 9
        return $this;
224
    }
225
226
    /**
227
     * Initialize Database external connection
228
     *
229
     * @param PDO    $external_pdo_connection PDO database object
230
     * @param string $table_prefix
231
     *
232
     * @return Telegram
233
     * @throws TelegramException
234
     */
235
    public function enableExternalMySql($external_pdo_connection, $table_prefix = null)
236
    {
237
        $this->pdo = DB::externalInitialize($external_pdo_connection, $this, $table_prefix);
238
        ConversationDB::initializeConversation();
239
        $this->mysql_enabled = true;
240
241
        return $this;
242
    }
243
244
    /**
245
     * Get commands list
246
     *
247
     * @return array $commands
248
     * @throws TelegramException
249
     */
250 1
    public function getCommandsList()
251
    {
252 1
        $commands = [];
253
254 1
        foreach ($this->commands_paths as $path) {
255
            try {
256
                //Get all "*Command.php" files
257 1
                $files = new RegexIterator(
258 1
                    new RecursiveIteratorIterator(
259 1
                        new RecursiveDirectoryIterator($path)
260
                    ),
261 1
                    '/^.+Command.php$/'
262
                );
263
264 1
                foreach ($files as $file) {
265
                    //Remove "Command.php" from filename
266 1
                    $command      = $this->sanitizeCommand(substr($file->getFilename(), 0, -11));
267 1
                    $command_name = mb_strtolower($command);
268
269 1
                    if (array_key_exists($command_name, $commands)) {
270
                        continue;
271
                    }
272
273 1
                    require_once $file->getPathname();
274
275 1
                    $command_obj = $this->getCommandObject($command, $file->getPathname());
276 1
                    if ($command_obj instanceof Command) {
277 1
                        $commands[$command_name] = $command_obj;
278
                    }
279
                }
280
            } catch (Exception $e) {
281
                throw new TelegramException('Error getting commands from path: ' . $path, $e);
0 ignored issues
show
Bug introduced by
$e of type Exception is incompatible with the type integer expected by parameter $code of Longman\TelegramBot\Exce...xception::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

281
                throw new TelegramException('Error getting commands from path: ' . $path, /** @scrutinizer ignore-type */ $e);
Loading history...
282
            }
283
        }
284
285 1
        return $commands;
286
    }
287
288
    /**
289
     * Get an object instance of the passed command
290
     *
291
     * @param string $command
292
     * @param string $filepath
293
     *
294
     * @return Command|null
295
     */
296 1
    public function getCommandObject($command, $filepath = null)
297
    {
298 1
        if (isset($this->commands_objects[$command])) {
299
            return $this->commands_objects[$command];
300
        }
301
302 1
        $which = ['System'];
303 1
        $this->isAdmin() && $which[] = 'Admin';
304 1
        $which[] = 'User';
305
306 1
        foreach ($which as $auth) {
307 1
            if ($filepath) {
308 1
                $command_namespace = $this->getFileNamespace($filepath);
309
            } else {
310
                $command_namespace = __NAMESPACE__ . '\\Commands\\' . $auth . 'Commands';
311
            }
312 1
            $command_class = $command_namespace . '\\' . $this->ucfirstUnicode($command) . 'Command';
313
314 1
            if (class_exists($command_class)) {
315 1
                $command_obj = new $command_class($this, $this->update);
316
317 1
                switch ($auth) {
318 1
                    case 'System':
319 1
                        if ($command_obj instanceof SystemCommand) {
320 1
                            return $command_obj;
321
                        }
322
                        break;
323
324
                    case 'Admin':
325
                        if ($command_obj instanceof AdminCommand) {
326
                            return $command_obj;
327
                        }
328
                        break;
329
330
                    case 'User':
331
                        if ($command_obj instanceof UserCommand) {
332
                            return $command_obj;
333
                        }
334
                        break;
335
                }
336
            }
337
        }
338
339
        return null;
340
    }
341
342
    /**
343
     * Get namespace from php file by src path
344
     *
345
     * @param string $src (absolute path to file)
346
     *
347
     * @return string ("Longman\TelegramBot\Commands\SystemCommands" for example)
348
     */
349 1
    protected function getFileNamespace($src)
350
    {
351 1
        $content = file_get_contents($src);
352 1
        if (preg_match('#^namespace\s+(.+?);#m', $content, $m)) {
353 1
            return $m[1];
354
        }
355
        return null;
356
    }
357
358
    /**
359
     * Set custom input string for debug purposes
360
     *
361
     * @param string $input (json format)
362
     *
363
     * @return Telegram
364
     */
365
    public function setCustomInput($input)
366
    {
367
        $this->input = $input;
368
369
        return $this;
370
    }
371
372
    /**
373
     * Get custom input string for debug purposes
374
     *
375
     * @return string
376
     */
377
    public function getCustomInput()
378
    {
379
        return $this->input;
380
    }
381
382
    /**
383
     * Get the ServerResponse of the last Command execution
384
     *
385
     * @return ServerResponse
386
     */
387
    public function getLastCommandResponse()
388
    {
389
        return $this->last_command_response;
390
    }
391
392
    /**
393
     * Handle getUpdates method
394
     *
395
     * @param int|null $limit
396
     * @param int|null $timeout
397
     *
398
     * @return ServerResponse
399
     * @throws TelegramException
400
     */
401
    public function handleGetUpdates($limit = null, $timeout = null)
402
    {
403
        if (empty($this->bot_username)) {
404
            throw new TelegramException('Bot Username is not defined!');
405
        }
406
407
        if (!DB::isDbConnected() && !$this->getupdates_without_database) {
408
            return new ServerResponse(
409
                [
410
                    'ok'          => false,
411
                    'description' => 'getUpdates needs MySQL connection! (This can be overridden - see documentation)',
412
                ],
413
                $this->bot_username
414
            );
415
        }
416
417
        $offset = 0;
418
419
        //Take custom input into account.
420
        if ($custom_input = $this->getCustomInput()) {
421
            $response = new ServerResponse(json_decode($custom_input, true), $this->bot_username);
422
        } else {
423
            if (DB::isDbConnected() && $last_update = DB::selectTelegramUpdate(1)) {
424
                //Get last update id from the database
425
                $last_update = reset($last_update);
426
427
                $this->last_update_id = isset($last_update['id']) ? $last_update['id'] : null;
428
            }
429
430
            if ($this->last_update_id !== null) {
431
                $offset = $this->last_update_id + 1;    //As explained in the telegram bot API documentation
432
            }
433
434
            $response = Request::getUpdates(
435
                [
436
                    'offset'  => $offset,
437
                    'limit'   => $limit,
438
                    'timeout' => $timeout,
439
                ]
440
            );
441
        }
442
443
        if ($response->isOk()) {
444
            $results = $response->getResult();
445
446
            //Process all updates
447
            /** @var Update $result */
448
            foreach ($results as $result) {
449
                $this->processUpdate($result);
450
            }
451
452
            if (!DB::isDbConnected() && !$custom_input && $this->last_update_id !== null && $offset === 0) {
453
                //Mark update(s) as read after handling
454
                Request::getUpdates(
455
                    [
456
                        'offset'  => $this->last_update_id + 1,
457
                        'limit'   => 1,
458
                        'timeout' => $timeout,
459
                    ]
460
                );
461
            }
462
        }
463
464
        return $response;
465
    }
466
467
    /**
468
     * Handle bot request from webhook
469
     *
470
     * @return bool
471
     *
472
     * @throws TelegramException
473
     */
474
    public function handle()
475
    {
476
        if (empty($this->bot_username)) {
477
            throw new TelegramException('Bot Username is not defined!');
478
        }
479
480
        $this->input = Request::getInput();
481
482
        if (empty($this->input)) {
483
            throw new TelegramException('Input is empty!');
484
        }
485
486
        $post = json_decode($this->input, true);
487
        if (empty($post)) {
488
            throw new TelegramException('Invalid JSON!');
489
        }
490
491
        if ($response = $this->processUpdate(new Update($post, $this->bot_username))) {
492
            return $response->isOk();
493
        }
494
495
        return false;
496
    }
497
498
    /**
499
     * Get the command name from the command type
500
     *
501
     * @param string $type
502
     *
503
     * @return string
504
     */
505
    protected function getCommandFromType($type)
506
    {
507
        return $this->ucfirstUnicode(str_replace('_', '', $type));
508
    }
509
510
    /**
511
     * Process bot Update request
512
     *
513
     * @param Update $update
514
     *
515
     * @return ServerResponse
516
     * @throws TelegramException
517
     */
518 1
    public function processUpdate(Update $update)
519
    {
520 1
        $this->update         = $update;
521 1
        $this->last_update_id = $update->getUpdateId();
522
523 1
        if (is_callable($this->update_filter)) {
524 1
            $reason = 'Update denied by update_filter';
525
            try {
526 1
                $allowed = (bool) call_user_func_array($this->update_filter, [$update, $this, &$reason]);
527
            } catch (\Exception $e) {
528
                $allowed = false;
529
            }
530
531 1
            if (!$allowed) {
532 1
                TelegramLog::debug($reason);
533 1
                return new ServerResponse(['ok' => false, 'description' => 'denied'], null);
534
            }
535
        }
536
537
        //Load admin commands
538
        if ($this->isAdmin()) {
539
            $this->addCommandsPath(TB_BASE_COMMANDS_PATH . '/AdminCommands', false);
540
        }
541
542
        //Make sure we have an up-to-date command list
543
        //This is necessary to "require" all the necessary command files!
544
        $this->commands_objects = $this->getCommandsList();
545
546
        //If all else fails, it's a generic message.
547
        $command = self::GENERIC_MESSAGE_COMMAND;
548
549
        $update_type = $this->update->getUpdateType();
550
        if ($update_type === 'message') {
551
            $message = $this->update->getMessage();
552
            $type    = $message->getType();
553
554
            // Let's check if the message object has the type field we're looking for...
555
            $command_tmp = $type === 'command' ? $message->getCommand() : $this->getCommandFromType($type);
556
            // ...and if a fitting command class is available.
557
            $command_obj = $this->getCommandObject($command_tmp);
558
559
            // Empty usage string denotes a non-executable command.
560
            // @see https://github.com/php-telegram-bot/core/issues/772#issuecomment-388616072
561
            if (
562
                ($command_obj === null && $type === 'command')
563
                || ($command_obj !== null && $command_obj->getUsage() !== '')
564
            ) {
565
                $command = $command_tmp;
566
            }
567
        } else {
568
            $command = $this->getCommandFromType($update_type);
569
        }
570
571
        //Make sure we don't try to process update that was already processed
572
        $last_id = DB::selectTelegramUpdate(1, $this->update->getUpdateId());
573
        if ($last_id && count($last_id) === 1) {
574
            TelegramLog::debug('Duplicate update received, processing aborted!');
575
            return Request::emptyResponse();
576
        }
577
578
        DB::insertRequest($this->update);
579
580
        return $this->executeCommand($command);
581
    }
582
583
    /**
584
     * Execute /command
585
     *
586
     * @param string $command
587
     *
588
     * @return ServerResponse
589
     * @throws TelegramException
590
     */
591
    public function executeCommand($command)
592
    {
593
        $command = mb_strtolower($command);
594
595
        if (isset($this->commands_objects[$command])) {
596
            $command_obj = $this->commands_objects[$command];
597
        } else {
598
            $command_obj = $this->getCommandObject($command);
599
        }
600
601
        if (!$command_obj || !$command_obj->isEnabled()) {
602
            //Failsafe in case the Generic command can't be found
603
            if ($command === self::GENERIC_COMMAND) {
604
                throw new TelegramException('Generic command missing!');
605
            }
606
607
            //Handle a generic command or non existing one
608
            $this->last_command_response = $this->executeCommand(self::GENERIC_COMMAND);
609
        } else {
610
            //execute() method is executed after preExecute()
611
            //This is to prevent executing a DB query without a valid connection
612
            $this->last_command_response = $command_obj->preExecute();
613
        }
614
615
        return $this->last_command_response;
616
    }
617
618
    /**
619
     * Sanitize Command
620
     *
621
     * @param string $command
622
     *
623
     * @return string
624
     */
625 1
    protected function sanitizeCommand($command)
626
    {
627 1
        return str_replace(' ', '', $this->ucwordsUnicode(str_replace('_', ' ', $command)));
628
    }
629
630
    /**
631
     * Enable a single Admin account
632
     *
633
     * @param integer $admin_id Single admin id
634
     *
635
     * @return Telegram
636
     */
637 1
    public function enableAdmin($admin_id)
638
    {
639 1
        if (!is_int($admin_id) || $admin_id <= 0) {
0 ignored issues
show
introduced by
The condition is_int($admin_id) is always true.
Loading history...
640 1
            TelegramLog::error('Invalid value "' . $admin_id . '" for admin.');
641 1
        } elseif (!in_array($admin_id, $this->admins_list, true)) {
642 1
            $this->admins_list[] = $admin_id;
643
        }
644
645 1
        return $this;
646
    }
647
648
    /**
649
     * Enable a list of Admin Accounts
650
     *
651
     * @param array $admin_ids List of admin ids
652
     *
653
     * @return Telegram
654
     */
655 1
    public function enableAdmins(array $admin_ids)
656
    {
657 1
        foreach ($admin_ids as $admin_id) {
658 1
            $this->enableAdmin($admin_id);
659
        }
660
661 1
        return $this;
662
    }
663
664
    /**
665
     * Get list of admins
666
     *
667
     * @return array
668
     */
669 1
    public function getAdminList()
670
    {
671 1
        return $this->admins_list;
672
    }
673
674
    /**
675
     * Check if the passed user is an admin
676
     *
677
     * If no user id is passed, the current update is checked for a valid message sender.
678
     *
679
     * @param int|null $user_id
680
     *
681
     * @return bool
682
     */
683 1
    public function isAdmin($user_id = null)
684
    {
685 1
        if ($user_id === null && $this->update !== null) {
686
            //Try to figure out if the user is an admin
687
            $update_methods = [
688
                'getMessage',
689
                'getEditedMessage',
690
                'getChannelPost',
691
                'getEditedChannelPost',
692
                'getInlineQuery',
693
                'getChosenInlineResult',
694
                'getCallbackQuery',
695
            ];
696
            foreach ($update_methods as $update_method) {
697
                $object = call_user_func([$this->update, $update_method]);
698
                if ($object !== null && $from = $object->getFrom()) {
699
                    $user_id = $from->getId();
700
                    break;
701
                }
702
            }
703
        }
704
705 1
        return ($user_id === null) ? false : in_array($user_id, $this->admins_list, true);
706
    }
707
708
    /**
709
     * Check if user required the db connection
710
     *
711
     * @return bool
712
     */
713
    public function isDbEnabled()
714
    {
715
        if ($this->mysql_enabled) {
716
            return true;
717
        } else {
718
            return false;
719
        }
720
    }
721
722
    /**
723
     * Add a single custom commands path
724
     *
725
     * @param string $path   Custom commands path to add
726
     * @param bool   $before If the path should be prepended or appended to the list
727
     *
728
     * @return Telegram
729
     */
730 30
    public function addCommandsPath($path, $before = true)
731
    {
732 30
        if (!is_dir($path)) {
733 1
            TelegramLog::error('Commands path "' . $path . '" does not exist.');
734 30
        } elseif (!in_array($path, $this->commands_paths, true)) {
735 30
            if ($before) {
736 30
                array_unshift($this->commands_paths, $path);
737
            } else {
738
                $this->commands_paths[] = $path;
739
            }
740
        }
741
742 30
        return $this;
743
    }
744
745
    /**
746
     * Add multiple custom commands paths
747
     *
748
     * @param array $paths  Custom commands paths to add
749
     * @param bool  $before If the paths should be prepended or appended to the list
750
     *
751
     * @return Telegram
752
     */
753 1
    public function addCommandsPaths(array $paths, $before = true)
754
    {
755 1
        foreach ($paths as $path) {
756 1
            $this->addCommandsPath($path, $before);
757
        }
758
759 1
        return $this;
760
    }
761
762
    /**
763
     * Return the list of commands paths
764
     *
765
     * @return array
766
     */
767 1
    public function getCommandsPaths()
768
    {
769 1
        return $this->commands_paths;
770
    }
771
772
    /**
773
     * Set custom upload path
774
     *
775
     * @param string $path Custom upload path
776
     *
777
     * @return Telegram
778
     */
779
    public function setUploadPath($path)
780
    {
781
        $this->upload_path = $path;
782
783
        return $this;
784
    }
785
786
    /**
787
     * Get custom upload path
788
     *
789
     * @return string
790
     */
791
    public function getUploadPath()
792
    {
793
        return $this->upload_path;
794
    }
795
796
    /**
797
     * Set custom download path
798
     *
799
     * @param string $path Custom download path
800
     *
801
     * @return Telegram
802
     */
803
    public function setDownloadPath($path)
804
    {
805
        $this->download_path = $path;
806
807
        return $this;
808
    }
809
810
    /**
811
     * Get custom download path
812
     *
813
     * @return string
814
     */
815
    public function getDownloadPath()
816
    {
817
        return $this->download_path;
818
    }
819
820
    /**
821
     * Set command config
822
     *
823
     * Provide further variables to a particular commands.
824
     * For example you can add the channel name at the command /sendtochannel
825
     * Or you can add the api key for external service.
826
     *
827
     * @param string $command
828
     * @param array  $config
829
     *
830
     * @return Telegram
831
     */
832 13
    public function setCommandConfig($command, array $config)
833
    {
834 13
        $this->commands_config[$command] = $config;
835
836 13
        return $this;
837
    }
838
839
    /**
840
     * Get command config
841
     *
842
     * @param string $command
843
     *
844
     * @return array
845
     */
846 14
    public function getCommandConfig($command)
847
    {
848 14
        return isset($this->commands_config[$command]) ? $this->commands_config[$command] : [];
849
    }
850
851
    /**
852
     * Get API key
853
     *
854
     * @return string
855
     */
856 1
    public function getApiKey()
857
    {
858 1
        return $this->api_key;
859
    }
860
861
    /**
862
     * Get Bot name
863
     *
864
     * @return string
865
     */
866 2
    public function getBotUsername()
867
    {
868 2
        return $this->bot_username;
869
    }
870
871
    /**
872
     * Get Bot Id
873
     *
874
     * @return string
875
     */
876
    public function getBotId()
877
    {
878
        return $this->bot_id;
879
    }
880
881
    /**
882
     * Get Version
883
     *
884
     * @return string
885
     */
886
    public function getVersion()
887
    {
888
        return $this->version;
889
    }
890
891
    /**
892
     * Set Webhook for bot
893
     *
894
     * @param string $url
895
     * @param array  $data Optional parameters.
896
     *
897
     * @return ServerResponse
898
     * @throws TelegramException
899
     */
900
    public function setWebhook($url, array $data = [])
901
    {
902
        if (empty($url)) {
903
            throw new TelegramException('Hook url is empty!');
904
        }
905
906
        $data        = array_intersect_key($data, array_flip([
907
            'certificate',
908
            'max_connections',
909
            'allowed_updates',
910
        ]));
911
        $data['url'] = $url;
912
913
        // If the certificate is passed as a path, encode and add the file to the data array.
914
        if (!empty($data['certificate']) && is_string($data['certificate'])) {
915
            $data['certificate'] = Request::encodeFile($data['certificate']);
916
        }
917
918
        $result = Request::setWebhook($data);
919
920
        if (!$result->isOk()) {
921
            throw new TelegramException(
922
                'Webhook was not set! Error: ' . $result->getErrorCode() . ' ' . $result->getDescription()
923
            );
924
        }
925
926
        return $result;
927
    }
928
929
    /**
930
     * Delete any assigned webhook
931
     *
932
     * @return mixed
933
     * @throws TelegramException
934
     */
935
    public function deleteWebhook()
936
    {
937
        $result = Request::deleteWebhook();
938
939
        if (!$result->isOk()) {
940
            throw new TelegramException(
941
                'Webhook was not deleted! Error: ' . $result->getErrorCode() . ' ' . $result->getDescription()
942
            );
943
        }
944
945
        return $result;
946
    }
947
948
    /**
949
     * Replace function `ucwords` for UTF-8 characters in the class definition and commands
950
     *
951
     * @param string $str
952
     * @param string $encoding (default = 'UTF-8')
953
     *
954
     * @return string
955
     */
956 1
    protected function ucwordsUnicode($str, $encoding = 'UTF-8')
957
    {
958 1
        return mb_convert_case($str, MB_CASE_TITLE, $encoding);
959
    }
960
961
    /**
962
     * Replace function `ucfirst` for UTF-8 characters in the class definition and commands
963
     *
964
     * @param string $str
965
     * @param string $encoding (default = 'UTF-8')
966
     *
967
     * @return string
968
     */
969 1
    protected function ucfirstUnicode($str, $encoding = 'UTF-8')
970
    {
971 1
        return mb_strtoupper(mb_substr($str, 0, 1, $encoding), $encoding)
972 1
            . mb_strtolower(mb_substr($str, 1, mb_strlen($str), $encoding), $encoding);
973
    }
974
975
    /**
976
     * Enable requests limiter
977
     *
978
     * @param array $options
979
     *
980
     * @return Telegram
981
     * @throws TelegramException
982
     */
983
    public function enableLimiter(array $options = [])
984
    {
985
        Request::setLimiter(true, $options);
986
987
        return $this;
988
    }
989
990
    /**
991
     * Run provided commands
992
     *
993
     * @param array $commands
994
     *
995
     * @throws TelegramException
996
     */
997
    public function runCommands($commands)
998
    {
999
        if (!is_array($commands) || empty($commands)) {
0 ignored issues
show
introduced by
The condition is_array($commands) is always true.
Loading history...
1000
            throw new TelegramException('No command(s) provided!');
1001
        }
1002
1003
        $this->run_commands = true;
1004
1005
        $result = Request::getMe();
1006
1007
        if ($result->isOk()) {
1008
            $result = $result->getResult();
1009
1010
            $bot_id       = $result->getId();
1011
            $bot_name     = $result->getFirstName();
1012
            $bot_username = $result->getUsername();
1013
        } else {
1014
            $bot_id       = $this->getBotId();
1015
            $bot_name     = $this->getBotUsername();
1016
            $bot_username = $this->getBotUsername();
1017
        }
1018
1019
        // Give bot access to admin commands
1020
        $this->enableAdmin($bot_id);
1021
1022
        $newUpdate = static function ($text = '') use ($bot_id, $bot_name, $bot_username) {
1023
            return new Update(
1024
                [
1025
                    'update_id' => 0,
1026
                    'message'   => [
1027
                        'message_id' => 0,
1028
                        'from'       => [
1029
                            'id'         => $bot_id,
1030
                            'first_name' => $bot_name,
1031
                            'username'   => $bot_username,
1032
                        ],
1033
                        'date'       => time(),
1034
                        'chat'       => [
1035
                            'id'   => $bot_id,
1036
                            'type' => 'private',
1037
                        ],
1038
                        'text'       => $text,
1039
                    ],
1040
                ]
1041
            );
1042
        };
1043
1044
        $this->update = $newUpdate();   // Required for isAdmin() check inside getCommandObject()
1045
        $this->commands_objects = $this->getCommandsList();       // Load up-to-date commands list
1046
1047
        foreach ($commands as $command) {
1048
            $this->update = $newUpdate($command);
1049
1050
            $this->executeCommand($this->update->getMessage()->getCommand());
1051
        }
1052
    }
1053
1054
    /**
1055
     * Is this session initiated by runCommands()
1056
     *
1057
     * @return bool
1058
     */
1059
    public function isRunCommands()
1060
    {
1061
        return $this->run_commands;
1062
    }
1063
1064
    /**
1065
     * Switch to enable running getUpdates without a database
1066
     *
1067
     * @param bool $enable
1068
     */
1069
    public function useGetUpdatesWithoutDatabase($enable = true)
1070
    {
1071
        $this->getupdates_without_database = $enable;
1072
    }
1073
1074
    /**
1075
     * Return last update id
1076
     *
1077
     * @return int
1078
     */
1079
    public function getLastUpdateId()
1080
    {
1081
        return $this->last_update_id;
1082
    }
1083
1084
    /**
1085
     * Set an update filter callback
1086
     *
1087
     * @param callable $callback
1088
     *
1089
     * @return Telegram
1090
     */
1091 1
    public function setUpdateFilter(callable $callback)
1092
    {
1093 1
        $this->update_filter = $callback;
1094
1095 1
        return $this;
1096
    }
1097
1098
    /**
1099
     * Return update filter callback
1100
     *
1101
     * @return callable|null
1102
     */
1103
    public function getUpdateFilter()
1104
    {
1105
        return $this->update_filter;
1106
    }
1107
}
1108