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

Telegram::getFileNamespace()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 7
ccs 4
cts 5
cp 0.8
crap 2.032
rs 10
c 0
b 0
f 0
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