Completed
Pull Request — master (#815)
by Jazin
03:05
created

Telegram::addCommandsPaths()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 2
crap 2
1
<?php
2
/**
3
 * This file is part of the TelegramBot package.
4
 *
5
 * (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Longman\TelegramBot;
12
13 1
define('BASE_PATH', __DIR__);
14 1
define('BASE_COMMANDS_PATH', BASE_PATH . '/Commands');
15
16
use Exception;
17
use Longman\TelegramBot\Commands\Command;
18
use Longman\TelegramBot\Entities\ServerResponse;
19
use Longman\TelegramBot\Entities\Update;
20
use Longman\TelegramBot\Exception\TelegramException;
21
use PDO;
22
use RecursiveDirectoryIterator;
23
use RecursiveIteratorIterator;
24
use RegexIterator;
25
26
class Telegram
27
{
28
    const COMMAND_SUFFIX = 'Command';
29
    const COMMAND_SUFFIX_PREPROCESS = 'Command';
30
31
    /**
32
     * Version
33
     *
34
     * @var string
35
     */
36
    protected $version = '0.60.0';
37
38
    /**
39
     * Telegram API key
40
     *
41
     * @var string
42
     */
43
    protected $api_key = '';
44
45
    /**
46
     * Telegram Bot username
47
     *
48
     * @var string
49
     */
50
    protected $bot_username = '';
51
52
    /**
53
     * Telegram Bot id
54
     *
55
     * @var string
56
     */
57
    protected $bot_id = '';
58
59
    /**
60
     * Raw request data (json) for webhook methods
61
     *
62
     * @var string
63
     */
64
    protected $input;
65
66
    /**
67
     * Custom commands paths
68
     *
69
     * @var array
70
     */
71
    protected $commands_paths = [];
72
73
    /**
74
     * Current Update object
75
     *
76
     * @var \Longman\TelegramBot\Entities\Update
77
     */
78
    protected $update;
79
80
    /**
81
     * Upload path
82
     *
83
     * @var string
84
     */
85
    protected $upload_path;
86
87
    /**
88
     * Download path
89
     *
90
     * @var string
91
     */
92
    protected $download_path;
93
94
    /**
95
     * MySQL integration
96
     *
97
     * @var boolean
98
     */
99
    protected $mysql_enabled = false;
100
101
    /**
102
     * PDO object
103
     *
104
     * @var \PDO
105
     */
106
    protected $pdo;
107
108
    /**
109
     * Commands config
110
     *
111
     * @var array
112
     */
113
    protected $commands_config = [];
114
115
    /**
116
     * Admins list
117
     *
118
     * @var array
119
     */
120
    protected $admins_list = [];
121
122
    /**
123
     * ServerResponse of the last Command execution
124
     *
125
     * @var \Longman\TelegramBot\Entities\ServerResponse
126
     */
127
    protected $last_command_response;
128
129
    /**
130
     * Botan.io integration
131
     *
132
     * @var boolean
133
     */
134
    protected $botan_enabled = false;
135
136
    /**
137
     * Check if runCommands() is running in this session
138
     *
139
     * @var boolean
140
     */
141
    protected $run_commands = false;
142
143
    /**
144
     * Is running getUpdates without DB enabled
145
     *
146
     * @var bool
147
     */
148
    protected $getupdates_without_database = false;
149
150
    /**
151
     * Last update ID
152
     * Only used when running getUpdates without a database
153
     *
154
     * @var integer
155
     */
156
    protected $last_update_id = null;
157
158
    protected $bootstrap_level = 0;
159
160
    /**
161
     * Telegram constructor.
162
     *
163
     * @param string $api_key
164
     * @param string $bot_username
165
     *
166
     * @throws \Longman\TelegramBot\Exception\TelegramException
167
     */
168 30
    public function __construct($api_key, $bot_username = '')
169
    {
170 30
        if (empty($api_key)) {
171 1
            throw new TelegramException('API KEY not defined!');
172
        }
173 30
        preg_match('/(\d+)\:[\w\-]+/', $api_key, $matches);
174 30
        if (!isset($matches[1])) {
175 1
            throw new TelegramException('Invalid API KEY defined!');
176
        }
177 30
        $this->bot_id  = $matches[1];
178 30
        $this->api_key = $api_key;
179
180 30
        if (!empty($bot_username)) {
181 30
            $this->bot_username = $bot_username;
182
        }
183
184
        //Add default system commands path
185 30
        $this->addCommandsPath(BASE_COMMANDS_PATH . '/SystemCommands');
186
187 30
        Request::initialize($this);
188 30
    }
189
190
    /**
191
     * Initialize Database connection
192
     *
193
     * @param array  $credential
194
     * @param string $table_prefix
195
     * @param string $encoding
196
     *
197
     * @return \Longman\TelegramBot\Telegram
198
     * @throws \Longman\TelegramBot\Exception\TelegramException
199
     */
200 9 View Code Duplication
    public function enableMySql(array $credential, $table_prefix = null, $encoding = 'utf8mb4')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
201
    {
202 9
        $this->pdo = DB::initialize($credential, $this, $table_prefix, $encoding);
203 9
        ConversationDB::initializeConversation();
204 9
        $this->mysql_enabled = true;
205
206 9
        return $this;
207
    }
208
209
    /**
210
     * Initialize Database external connection
211
     *
212
     * @param PDO    $external_pdo_connection PDO database object
213
     * @param string $table_prefix
214
     *
215
     * @return \Longman\TelegramBot\Telegram
216
     * @throws \Longman\TelegramBot\Exception\TelegramException
217
     */
218 View Code Duplication
    public function enableExternalMySql($external_pdo_connection, $table_prefix = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
219
    {
220
        $this->pdo = DB::externalInitialize($external_pdo_connection, $this, $table_prefix);
221
        ConversationDB::initializeConversation();
222
        $this->mysql_enabled = true;
223
224
        return $this;
225
    }
226
227
    /**
228
     * Get commands list
229
     *
230
     * @return array $commands
231
     * @throws \Longman\TelegramBot\Exception\TelegramException
232
     */
233 1
    public function getCommandsList()
234
    {
235 1
        $commands = [];
236
237 1
        foreach ($this->commands_paths as $path) {
238
            try {
239
                //Get all "*Command.php" files
240 1
                $files = new RegexIterator(
241 1
                    new RecursiveIteratorIterator(
242 1
                        new RecursiveDirectoryIterator($path)
243
                    ),
244 1
                    "/^.+Command.php$/"
245
                );
246
247 1
                foreach ($files as $file) {
248
                    //Remove "Command.php" from filename
249 1
                    $command      = $this->sanitizeCommand(substr($file->getFilename(), 0, -11));
250 1
                    $command_name = strtolower($command);
251
252
                    // @fixme: Check if there is any impact here
253
//                    if (array_key_exists($command_name, $commands)) {
254
//                         continue;
255
//                    }
256
257 1
                    require_once $file->getPathname();
258
259 1
                    $command_obj = $this->getCommandObject($command);
260 1
                    if ($command_obj instanceof Command) {
261 1
                        $commands[$command_name] = $command_obj;
262
                    }
263
                }
264
            } catch (Exception $e) {
265 1
                throw new TelegramException('Error getting commands from path: ' . $path);
266
            }
267
        }
268
269 1
        return $commands;
270
    }
271
272
    /**
273
     * Get an object instance of the passed command
274
     *
275
     * @param string $command
276
     *
277
     * @return \Longman\TelegramBot\Commands\Command|null
278
     */
279 1
    public function getCommandObject($command, $scope = null)
280
    {
281 1
        $which = [];
282 1
        if ($scope !== null) {
283
            $which = is_array($scope) ? $scope : [$scope];
284
        }
285
        else {
286 1
            $which[] = 'CustomSystem';
287 1
            $which[] = 'System';
288 1
            $this->isAdmin() && $which[] = 'Admin';
289 1
            $which[] = 'User';
290
        }
291
292 1
        foreach ($which as $auth) {
293 1
            $command_namespace = __NAMESPACE__ . '\\Commands\\' . $auth . 'Commands\\' . $this->ucfirstUnicode($command) . 'Command';
294 1
            if (class_exists($command_namespace)) {
295 1
                return new $command_namespace($this, $this->update);
296
            }
297
        }
298
299
        return null;
300
    }
301
302
    /**
303
     * Set custom input string for debug purposes
304
     *
305
     * @param string $input (json format)
306
     *
307
     * @return \Longman\TelegramBot\Telegram
308
     */
309
    public function setCustomInput($input)
310
    {
311
        $this->input = $input;
312
313
        return $this;
314
    }
315
316
    /**
317
     * Get custom input string for debug purposes
318
     *
319
     * @return string
320
     */
321
    public function getCustomInput()
322
    {
323
        return $this->input;
324
    }
325
326
    /**
327
     * Get the ServerResponse of the last Command execution
328
     *
329
     * @return \Longman\TelegramBot\Entities\ServerResponse
330
     */
331
    public function getLastCommandResponse()
332
    {
333
        return $this->last_command_response;
334
    }
335
336
    /**
337
     * Handle getUpdates method
338
     *
339
     * @param int|null $limit
340
     * @param int|null $timeout
341
     *
342
     * @return \Longman\TelegramBot\Entities\ServerResponse
343
     * @throws \Longman\TelegramBot\Exception\TelegramException
344
     */
345
    public function handleGetUpdates($limit = null, $timeout = null)
346
    {
347
        if (empty($this->bot_username)) {
348
            throw new TelegramException('Bot Username is not defined!');
349
        }
350
351
        if (!DB::isDbConnected() && !$this->getupdates_without_database) {
352
            return new ServerResponse(
353
                [
354
                    'ok'          => false,
355
                    'description' => 'getUpdates needs MySQL connection! (This can be overridden - see documentation)',
356
                ],
357
                $this->bot_username
358
            );
359
        }
360
361
        $offset = 0;
362
363
        //Take custom input into account.
364
        if ($custom_input = $this->getCustomInput()) {
365
            $response = new ServerResponse(json_decode($custom_input, true), $this->bot_username);
366
        } else {
367
            if (DB::isDbConnected()) {
368
                //Get last update id from the database
369
                $last_update = DB::selectTelegramUpdate(1);
370
                $last_update = reset($last_update);
371
372
                $this->last_update_id = isset($last_update['id']) ? $last_update['id'] : null;
373
            }
374
375
            if ($this->last_update_id !== null) {
376
                $offset = $this->last_update_id + 1;    //As explained in the telegram bot API documentation
377
            }
378
379
            $response = Request::getUpdates(
380
                [
381
                    'offset'  => $offset,
382
                    'limit'   => $limit,
383
                    'timeout' => $timeout,
384
                ]
385
            );
386
        }
387
388
        if ($response->isOk()) {
389
            $results = $response->getResult();
390
391
            //Process all updates
392
            /** @var Update $result */
393
            foreach ($results as $result) {
394
                $this->processUpdate($result);
395
            }
396
397
            if (!DB::isDbConnected() && !$custom_input && $this->last_update_id !== null && $offset === 0) {
398
                //Mark update(s) as read after handling
399
                Request::getUpdates(
400
                    [
401
                        'offset'  => $this->last_update_id + 1,
402
                        'limit'   => 1,
403
                        'timeout' => $timeout,
404
                    ]
405
                );
406
            }
407
        }
408
409
        return $response;
410
    }
411
412
    /**
413
     * Handle bot request from webhook
414
     *
415
     * @return bool
416
     *
417
     * @throws \Longman\TelegramBot\Exception\TelegramException
418
     */
419
    public function handle()
420
    {
421
        if (empty($this->bot_username)) {
422
            throw new TelegramException('Bot Username is not defined!');
423
        }
424
425
        $this->input = Request::getInput();
426
427
        if (empty($this->input)) {
428
            throw new TelegramException('Input is empty!');
429
        }
430
431
        $post = json_decode($this->input, true);
432
        if (empty($post)) {
433
            throw new TelegramException('Invalid JSON!');
434
        }
435
436
        $update = new Update($post, $this->bot_username);
437
438
        $preprocess_response = $this->preProcessUpdate($update);
0 ignored issues
show
Unused Code introduced by
$preprocess_response is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
439
440
        if ($response = $this->processUpdate($update)) {
441
            return $response->isOk();
442
        }
443
444
        return false;
445
    }
446
447
    /**
448
     * Get the command name from the command type
449
     *
450
     * @param string $type
451
     *
452
     * @return string
453
     */
454
    protected function getCommandFromType($type)
455
    {
456
        return $this->ucfirstUnicode(str_replace('_', '', $type));
457
    }
458
459
    protected function getCommandFromUpdate(Update $update) {
460
        $this->update = $update;
461
        $this->last_update_id = $update->getUpdateId();
462
463
        //If all else fails, it's a generic message.
464
        $command = 'genericmessage';
465
466
        $update_type = $this->update->getUpdateType();
467
        if ($update_type === 'message') {
468
            $message = $this->update->getMessage();
469
470
            $type = $message->getType();
471
            if ($type === 'command') {
472
                $command = $message->getCommand();
473
            } elseif (in_array($type, [
474
                'new_chat_members',
475
                'left_chat_member',
476
                'new_chat_title',
477
                'new_chat_photo',
478
                'delete_chat_photo',
479
                'group_chat_created',
480
                'supergroup_chat_created',
481
                'channel_chat_created',
482
                'migrate_to_chat_id',
483
                'migrate_from_chat_id',
484
                'pinned_message',
485
                'invoice',
486
                'successful_payment',
487
            ], true)
488
            ) {
489
                $command = $this->getCommandFromType($type);
490
            }
491
        } else {
492
            $command = $this->getCommandFromType($update_type);
493
        }
494
495
        return $command;
496
    }
497
498
    public function preProcessUpdate(Update $update) {
499
        $command = $this->getCommandFromUpdate($update);
500
501
        $this->addCommandsPath('Commands/PreprocessCommands');
502
503
        //Load admin commands
504
        if ($this->isAdmin()) {
505
            $this->addCommandsPath(BASE_COMMANDS_PATH . '/AdminCommands', false);
506
        }
507
508
        // Load all commands here once.
509
        $this->getCommandsList();
510
511
        //Make sure we don't try to process update that was already processed
512
        $last_id = DB::selectTelegramUpdate(1, $this->update->getUpdateId());
513
        if ($last_id && count($last_id) === 1) {
514
            TelegramLog::debug('Duplicate update received, processing aborted!');
515
            return Request::emptyResponse();
516
        }
517
518
        return $this->executeCommand($command, 'Preprocess');
519
    }
520
521
    /**
522
     * Process bot Update request
523
     *
524
     * @param \Longman\TelegramBot\Entities\Update $update
525
     *
526
     * @return \Longman\TelegramBot\Entities\ServerResponse
527
     * @throws \Longman\TelegramBot\Exception\TelegramException
528
     */
529
    public function processUpdate(Update $update)
530
    {
531
        $this->update = $update;
532
        $this->last_update_id = $update->getUpdateId();
533
534
        //If all else fails, it's a generic message.
535
        $command = $this->getCommandFromUpdate($update);
536
537
        //Make sure we don't try to process update that was already processed
538
        $last_id = DB::selectTelegramUpdate(1, $this->update->getUpdateId());
539
        if ($last_id && count($last_id) === 1) {
540
            TelegramLog::debug('Duplicate update received, processing aborted!');
541
            return Request::emptyResponse();
542
        }
543
544
        DB::insertRequest($this->update);
545
546
        return $this->executeCommand($command);
547
    }
548
549
    /**
550
     * Execute /command
551
     *
552
     * @param string $command
553
     *
554
     * @return mixed
555
     * @throws \Longman\TelegramBot\Exception\TelegramException
556
     */
557
    public function executeCommand($command, $scope = NULL)
558
    {
559
        $command     = strtolower($command);
560
        $command_obj = $this->getCommandObject($command, $scope);
561
562
        if (!$command_obj || !$command_obj->isEnabled()) {
563
            //Failsafe in case the Generic command can't be found
564
            if ($command === 'generic') {
565
                throw new TelegramException('Generic command missing!');
566
            }
567
568
            //Handle a generic command or non existing one
569
            $this->last_command_response = $this->executeCommand('generic');
570
        } else {
571
            //Botan.io integration, make sure only the actual command user executed is reported
572
            if ($this->botan_enabled) {
573
                Botan::lock($command);
574
            }
575
576
            //execute() method is executed after preExecute()
577
            //This is to prevent executing a DB query without a valid connection
578
            $this->last_command_response = $command_obj->preExecute();
579
580
            //Botan.io integration, send report after executing the command
581
            if ($this->botan_enabled) {
582
                Botan::track($this->update, $command);
583
            }
584
        }
585
586
        return $this->last_command_response;
587
    }
588
589
    /**
590
     * Sanitize Command
591
     *
592
     * @param string $command
593
     *
594
     * @return string
595
     */
596 1
    protected function sanitizeCommand($command)
597
    {
598 1
        return str_replace(' ', '', $this->ucwordsUnicode(str_replace('_', ' ', $command)));
599
    }
600
601
    /**
602
     * Enable a single Admin account
603
     *
604
     * @param integer $admin_id Single admin id
605
     *
606
     * @return \Longman\TelegramBot\Telegram
607
     */
608 1
    public function enableAdmin($admin_id)
609
    {
610 1
        if (!is_int($admin_id) || $admin_id <= 0) {
611 1
            TelegramLog::error('Invalid value "%s" for admin.', $admin_id);
612 1
        } elseif (!in_array($admin_id, $this->admins_list, true)) {
613 1
            $this->admins_list[] = $admin_id;
614
        }
615
616 1
        return $this;
617
    }
618
619
    /**
620
     * Enable a list of Admin Accounts
621
     *
622
     * @param array $admin_ids List of admin ids
623
     *
624
     * @return \Longman\TelegramBot\Telegram
625
     */
626 1
    public function enableAdmins(array $admin_ids)
627
    {
628 1
        foreach ($admin_ids as $admin_id) {
629 1
            $this->enableAdmin($admin_id);
630
        }
631
632 1
        return $this;
633
    }
634
635
    /**
636
     * Get list of admins
637
     *
638
     * @return array
639
     */
640 1
    public function getAdminList()
641
    {
642 1
        return $this->admins_list;
643
    }
644
645
    /**
646
     * Check if the passed user is an admin
647
     *
648
     * If no user id is passed, the current update is checked for a valid message sender.
649
     *
650
     * @param int|null $user_id
651
     *
652
     * @return bool
653
     */
654 1
    public function isAdmin($user_id = null)
655
    {
656 1
        if ($user_id === null && $this->update !== null) {
657
            //Try to figure out if the user is an admin
658
            $update_methods = [
659
                'getMessage',
660
                'getEditedMessage',
661
                'getChannelPost',
662
                'getEditedChannelPost',
663
                'getInlineQuery',
664
                'getChosenInlineResult',
665
                'getCallbackQuery',
666
            ];
667
            foreach ($update_methods as $update_method) {
668
                $object = call_user_func([$this->update, $update_method]);
669
                if ($object !== null && $from = $object->getFrom()) {
670
                    $user_id = $from->getId();
671
                    break;
672
                }
673
            }
674
        }
675
676 1
        return ($user_id === null) ? false : in_array($user_id, $this->admins_list, true);
677
    }
678
679
    /**
680
     * Check if user required the db connection
681
     *
682
     * @return bool
683
     */
684
    public function isDbEnabled()
685
    {
686
        if ($this->mysql_enabled) {
687
            return true;
688
        } else {
689
            return false;
690
        }
691
    }
692
693
    /**
694
     * Add a single custom commands path
695
     *
696
     * @param string $path   Custom commands path to add
697
     * @param bool   $before If the path should be prepended or appended to the list
698
     *
699
     * @return \Longman\TelegramBot\Telegram
700
     */
701 30
    public function addCommandsPath($path, $before = true)
702
    {
703 30
        if (!is_dir($path)) {
704 1
            TelegramLog::error('Commands path "%s" does not exist.', $path);
705 30
        } elseif (!in_array($path, $this->commands_paths, true)) {
706 30
            if ($before) {
707 30
                array_unshift($this->commands_paths, $path);
708
            } else {
709
                $this->commands_paths[] = $path;
710
            }
711
        }
712
713 30
        return $this;
714
    }
715
716
    /**
717
     * Add multiple custom commands paths
718
     *
719
     * @param array $paths  Custom commands paths to add
720
     * @param bool  $before If the paths should be prepended or appended to the list
721
     *
722
     * @return \Longman\TelegramBot\Telegram
723
     */
724 1
    public function addCommandsPaths(array $paths, $before = true)
725
    {
726 1
        foreach ($paths as $path) {
727 1
            $this->addCommandsPath($path, $before);
728
        }
729
730 1
        return $this;
731
    }
732
733
    /**
734
     * Return the list of commands paths
735
     *
736
     * @return array
737
     */
738 1
    public function getCommandsPaths()
739
    {
740 1
        return $this->commands_paths;
741
    }
742
743
    /**
744
     * Set custom upload path
745
     *
746
     * @param string $path Custom upload path
747
     *
748
     * @return \Longman\TelegramBot\Telegram
749
     */
750
    public function setUploadPath($path)
751
    {
752
        $this->upload_path = $path;
753
754
        return $this;
755
    }
756
757
    /**
758
     * Get custom upload path
759
     *
760
     * @return string
761
     */
762
    public function getUploadPath()
763
    {
764
        return $this->upload_path;
765
    }
766
767
    /**
768
     * Set custom download path
769
     *
770
     * @param string $path Custom download path
771
     *
772
     * @return \Longman\TelegramBot\Telegram
773
     */
774
    public function setDownloadPath($path)
775
    {
776
        $this->download_path = $path;
777
778
        return $this;
779
    }
780
781
    /**
782
     * Get custom download path
783
     *
784
     * @return string
785
     */
786
    public function getDownloadPath()
787
    {
788
        return $this->download_path;
789
    }
790
791
    /**
792
     * Set command config
793
     *
794
     * Provide further variables to a particular commands.
795
     * For example you can add the channel name at the command /sendtochannel
796
     * Or you can add the api key for external service.
797
     *
798
     * @param string $command
799
     * @param array  $config
800
     *
801
     * @return \Longman\TelegramBot\Telegram
802
     */
803 14
    public function setCommandConfig($command, array $config)
804
    {
805 14
        $this->commands_config[$command] = $config;
806
807 14
        return $this;
808
    }
809
810
    /**
811
     * Get command config
812
     *
813
     * @param string $command
814
     *
815
     * @return array
816
     */
817 15
    public function getCommandConfig($command)
818
    {
819 15
        return isset($this->commands_config[$command]) ? $this->commands_config[$command] : [];
820
    }
821
822
    /**
823
     * Get API key
824
     *
825
     * @return string
826
     */
827 1
    public function getApiKey()
828
    {
829 1
        return $this->api_key;
830
    }
831
832
    /**
833
     * Get Bot name
834
     *
835
     * @return string
836
     */
837 1
    public function getBotUsername()
838
    {
839 1
        return $this->bot_username;
840
    }
841
842
    /**
843
     * Get Bot Id
844
     *
845
     * @return string
846
     */
847
    public function getBotId()
848
    {
849
        return $this->bot_id;
850
    }
851
852
    /**
853
     * Get Version
854
     *
855
     * @return string
856
     */
857
    public function getVersion()
858
    {
859
        return $this->version;
860
    }
861
862
    /**
863
     * Set Webhook for bot
864
     *
865
     * @param string $url
866
     * @param array  $data Optional parameters.
867
     *
868
     * @return \Longman\TelegramBot\Entities\ServerResponse
869
     * @throws \Longman\TelegramBot\Exception\TelegramException
870
     */
871
    public function setWebhook($url, array $data = [])
872
    {
873
        if (empty($url)) {
874
            throw new TelegramException('Hook url is empty!');
875
        }
876
877
        $data        = array_intersect_key($data, array_flip([
878
            'certificate',
879
            'max_connections',
880
            'allowed_updates',
881
        ]));
882
        $data['url'] = $url;
883
884
        // If the certificate is passed as a path, encode and add the file to the data array.
885
        if (!empty($data['certificate']) && is_string($data['certificate'])) {
886
            $data['certificate'] = Request::encodeFile($data['certificate']);
887
        }
888
889
        $result = Request::setWebhook($data);
890
891 View Code Duplication
        if (!$result->isOk()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
892
            throw new TelegramException(
893
                'Webhook was not set! Error: ' . $result->getErrorCode() . ' ' . $result->getDescription()
894
            );
895
        }
896
897
        return $result;
898
    }
899
900
    /**
901
     * Delete any assigned webhook
902
     *
903
     * @return mixed
904
     * @throws \Longman\TelegramBot\Exception\TelegramException
905
     */
906
    public function deleteWebhook()
907
    {
908
        $result = Request::deleteWebhook();
909
910 View Code Duplication
        if (!$result->isOk()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
911
            throw new TelegramException(
912
                'Webhook was not deleted! Error: ' . $result->getErrorCode() . ' ' . $result->getDescription()
913
            );
914
        }
915
916
        return $result;
917
    }
918
919
    /**
920
     * Replace function `ucwords` for UTF-8 characters in the class definition and commands
921
     *
922
     * @param string $str
923
     * @param string $encoding (default = 'UTF-8')
924
     *
925
     * @return string
926
     */
927 1
    protected function ucwordsUnicode($str, $encoding = 'UTF-8')
928
    {
929 1
        return mb_convert_case($str, MB_CASE_TITLE, $encoding);
930
    }
931
932
    /**
933
     * Replace function `ucfirst` for UTF-8 characters in the class definition and commands
934
     *
935
     * @param string $str
936
     * @param string $encoding (default = 'UTF-8')
937
     *
938
     * @return string
939
     */
940 1
    protected function ucfirstUnicode($str, $encoding = 'UTF-8')
941
    {
942 1
        return mb_strtoupper(mb_substr($str, 0, 1, $encoding), $encoding)
943 1
               . mb_strtolower(mb_substr($str, 1, mb_strlen($str), $encoding), $encoding);
944
    }
945
946
    /**
947
     * Enable Botan.io integration
948
     *
949
     * @param  string $token
950
     * @param  array  $options
951
     *
952
     * @return \Longman\TelegramBot\Telegram
953
     * @throws \Longman\TelegramBot\Exception\TelegramException
954
     */
955
    public function enableBotan($token, array $options = [])
956
    {
957
        Botan::initializeBotan($token, $options);
958
        $this->botan_enabled = true;
959
960
        return $this;
961
    }
962
963
    /**
964
     * Enable requests limiter
965
     *
966
     * @param  array $options
967
     *
968
     * @return \Longman\TelegramBot\Telegram
969
     */
970
    public function enableLimiter(array $options = [])
971
    {
972
        Request::setLimiter(true, $options);
973
974
        return $this;
975
    }
976
977
    /**
978
     * Run provided commands
979
     *
980
     * @param array $commands
981
     *
982
     * @throws TelegramException
983
     */
984
    public function runCommands($commands)
985
    {
986
        if (!is_array($commands) || empty($commands)) {
987
            throw new TelegramException('No command(s) provided!');
988
        }
989
990
        $this->run_commands  = true;
991
        $this->botan_enabled = false;   // Force disable Botan.io integration, we don't want to track self-executed commands!
992
993
        $result = Request::getMe();
994
995
        if ($result->isOk()) {
996
            $result = $result->getResult();
997
998
            $bot_id       = $result->getId();
999
            $bot_name     = $result->getFirstName();
1000
            $bot_username = $result->getUsername();
1001
        } else {
1002
            $bot_id       = $this->getBotId();
1003
            $bot_name     = $this->getBotUsername();
1004
            $bot_username = $this->getBotUsername();
1005
        }
1006
1007
1008
        $this->enableAdmin($bot_id);    // Give bot access to admin commands
1009
        $this->getCommandsList();       // Load full commands list
1010
1011
        foreach ($commands as $command) {
1012
            $this->update = new Update(
1013
                [
1014
                    'update_id' => 0,
1015
                    'message'   => [
1016
                        'message_id' => 0,
1017
                        'from'       => [
1018
                            'id'         => $bot_id,
1019
                            'first_name' => $bot_name,
1020
                            'username'   => $bot_username,
1021
                        ],
1022
                        'date'       => time(),
1023
                        'chat'       => [
1024
                            'id'   => $bot_id,
1025
                            'type' => 'private',
1026
                        ],
1027
                        'text'       => $command,
1028
                    ],
1029
                ]
1030
            );
1031
1032
            $this->executeCommand($this->update->getMessage()->getCommand());
1033
        }
1034
    }
1035
1036
    /**
1037
     * Is this session initiated by runCommands()
1038
     *
1039
     * @return bool
1040
     */
1041
    public function isRunCommands()
1042
    {
1043
        return $this->run_commands;
1044
    }
1045
1046
    /**
1047
     * Switch to enable running getUpdates without a database
1048
     *
1049
     * @param bool $enable
1050
     */
1051
    public function useGetUpdatesWithoutDatabase($enable = true)
1052
    {
1053
        $this->getupdates_without_database = $enable;
1054
    }
1055
1056
    /**
1057
     * Return last update id
1058
     *
1059
     * @return int
1060
     */
1061
    public function getLastUpdateId()
1062
    {
1063
        return $this->last_update_id;
1064
    }
1065
}
1066