Completed
Push — getupdates_without_db_docs ( 0af266 )
by Armando
03:59
created

Telegram::setCommandConfig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 3
nc 1
nop 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
define('BASE_PATH', __DIR__);
14
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
    /**
29
     * Version
30
     *
31
     * @var string
32
     */
33
    protected $version = '0.52.0';
34
35
    /**
36
     * Telegram API key
37
     *
38
     * @var string
39
     */
40
    protected $api_key = '';
41
42
    /**
43
     * Telegram Bot username
44
     *
45
     * @var string
46
     */
47
    protected $bot_username = '';
48
49
    /**
50
     * Telegram Bot id
51
     *
52
     * @var string
53
     */
54
    protected $bot_id = '';
55
56
    /**
57
     * Raw request data (json) for webhook methods
58
     *
59
     * @var string
60
     */
61
    protected $input;
62
63
    /**
64
     * Custom commands paths
65
     *
66
     * @var array
67
     */
68
    protected $commands_paths = [];
69
70
    /**
71
     * Current Update object
72
     *
73
     * @var \Longman\TelegramBot\Entities\Update
74
     */
75
    protected $update;
76
77
    /**
78
     * Upload path
79
     *
80
     * @var string
81
     */
82
    protected $upload_path;
83
84
    /**
85
     * Download path
86
     *
87
     * @var string
88
     */
89
    protected $download_path;
90
91
    /**
92
     * MySQL integration
93
     *
94
     * @var boolean
95
     */
96
    protected $mysql_enabled = false;
97
98
    /**
99
     * PDO object
100
     *
101
     * @var \PDO
102
     */
103
    protected $pdo;
104
105
    /**
106
     * Commands config
107
     *
108
     * @var array
109
     */
110
    protected $commands_config = [];
111
112
    /**
113
     * Admins list
114
     *
115
     * @var array
116
     */
117
    protected $admins_list = [];
118
119
    /**
120
     * ServerResponse of the last Command execution
121
     *
122
     * @var \Longman\TelegramBot\Entities\ServerResponse
123
     */
124
    protected $last_command_response;
125
126
    /**
127
     * Botan.io integration
128
     *
129
     * @var boolean
130
     */
131
    protected $botan_enabled = false;
132
133
    /**
134
     * Check if runCommands() is running in this session
135
     *
136
     * @var boolean
137
     */
138
    protected $run_commands = false;
139
140
    /**
141
     * Is running getUpdates without DB enabled
142
     *
143
     * @var bool
144
     */
145
    protected $getupdates_without_database = false;
146
147
    /**
148
     * Last update ID
149
     * Only used when running getUpdates without a database
150
     *
151
     * @var integer
152
     */
153
    protected $last_update_id = null;
154
155
    /**
156
     * Telegram constructor.
157
     *
158
     * @param string $api_key
159
     * @param string $bot_username
160
     *
161
     * @throws \Longman\TelegramBot\Exception\TelegramException
162
     */
163
    public function __construct($api_key, $bot_username = '')
164
    {
165
        if (empty($api_key)) {
166
            throw new TelegramException('API KEY not defined!');
167
        }
168
        preg_match('/(\d+)\:[\w\-]+/', $api_key, $matches);
169
        if (!isset($matches[1])) {
170
            throw new TelegramException('Invalid API KEY defined!');
171
        }
172
        $this->bot_id  = $matches[1];
173
        $this->api_key = $api_key;
174
175
        if (!empty($bot_username)) {
176
            $this->bot_username = $bot_username;
177
        }
178
179
        //Add default system commands path
180
        $this->addCommandsPath(BASE_COMMANDS_PATH . '/SystemCommands');
181
182
        Request::initialize($this);
183
    }
184
185
    /**
186
     * Initialize Database connection
187
     *
188
     * @param array  $credential
189
     * @param string $table_prefix
190
     * @param string $encoding
191
     *
192
     * @return \Longman\TelegramBot\Telegram
193
     * @throws \Longman\TelegramBot\Exception\TelegramException
194
     */
195 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...
196
    {
197
        $this->pdo = DB::initialize($credential, $this, $table_prefix, $encoding);
198
        ConversationDB::initializeConversation();
199
        $this->mysql_enabled = true;
200
201
        return $this;
202
    }
203
204
    /**
205
     * Initialize Database external connection
206
     *
207
     * @param PDO    $external_pdo_connection PDO database object
208
     * @param string $table_prefix
209
     *
210
     * @return \Longman\TelegramBot\Telegram
211
     * @throws \Longman\TelegramBot\Exception\TelegramException
212
     */
213 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...
214
    {
215
        $this->pdo = DB::externalInitialize($external_pdo_connection, $this, $table_prefix);
216
        ConversationDB::initializeConversation();
217
        $this->mysql_enabled = true;
218
219
        return $this;
220
    }
221
222
    /**
223
     * Get commands list
224
     *
225
     * @return array $commands
226
     * @throws \Longman\TelegramBot\Exception\TelegramException
227
     */
228
    public function getCommandsList()
229
    {
230
        $commands = [];
231
232
        foreach ($this->commands_paths as $path) {
233
            try {
234
                //Get all "*Command.php" files
235
                $files = new RegexIterator(
236
                    new RecursiveIteratorIterator(
237
                        new RecursiveDirectoryIterator($path)
238
                    ),
239
                    '/^.+Command.php$/'
240
                );
241
242
                foreach ($files as $file) {
243
                    //Remove "Command.php" from filename
244
                    $command      = $this->sanitizeCommand(substr($file->getFilename(), 0, -11));
245
                    $command_name = strtolower($command);
246
247
                    if (array_key_exists($command_name, $commands)) {
248
                        continue;
249
                    }
250
251
                    require_once $file->getPathname();
252
253
                    $command_obj = $this->getCommandObject($command);
254
                    if ($command_obj instanceof Command) {
255
                        $commands[$command_name] = $command_obj;
256
                    }
257
                }
258
            } catch (Exception $e) {
259
                throw new TelegramException('Error getting commands from path: ' . $path);
260
            }
261
        }
262
263
        return $commands;
264
    }
265
266
    /**
267
     * Get an object instance of the passed command
268
     *
269
     * @param string $command
270
     *
271
     * @return \Longman\TelegramBot\Commands\Command|null
272
     */
273
    public function getCommandObject($command)
274
    {
275
        $which = ['System'];
276
        $this->isAdmin() && $which[] = 'Admin';
277
        $which[] = 'User';
278
279
        foreach ($which as $auth) {
280
            $command_namespace = __NAMESPACE__ . '\\Commands\\' . $auth . 'Commands\\' . $this->ucfirstUnicode($command) . 'Command';
281
            if (class_exists($command_namespace)) {
282
                return new $command_namespace($this, $this->update);
283
            }
284
        }
285
286
        return null;
287
    }
288
289
    /**
290
     * Set custom input string for debug purposes
291
     *
292
     * @param string $input (json format)
293
     *
294
     * @return \Longman\TelegramBot\Telegram
295
     */
296
    public function setCustomInput($input)
297
    {
298
        $this->input = $input;
299
300
        return $this;
301
    }
302
303
    /**
304
     * Get custom input string for debug purposes
305
     *
306
     * @return string
307
     */
308
    public function getCustomInput()
309
    {
310
        return $this->input;
311
    }
312
313
    /**
314
     * Get the ServerResponse of the last Command execution
315
     *
316
     * @return \Longman\TelegramBot\Entities\ServerResponse
317
     */
318
    public function getLastCommandResponse()
319
    {
320
        return $this->last_command_response;
321
    }
322
323
    /**
324
     * Handle getUpdates method
325
     *
326
     * @param int|null $limit
327
     * @param int|null $timeout
328
     *
329
     * @return \Longman\TelegramBot\Entities\ServerResponse
330
     * @throws \Longman\TelegramBot\Exception\TelegramException
331
     */
332
    public function handleGetUpdates($limit = null, $timeout = null)
333
    {
334
        if (empty($this->bot_username)) {
335
            throw new TelegramException('Bot Username is not defined!');
336
        }
337
338
        if (!DB::isDbConnected() && !$this->getupdates_without_database) {
339
            return new ServerResponse(
340
                [
341
                    'ok'          => false,
342
                    'description' => 'getUpdates needs MySQL connection! (This can be overridden - see documentation)',
343
                ],
344
                $this->bot_username
345
            );
346
        }
347
348
        $offset = 0;
349
350
        //Take custom input into account.
351
        if ($custom_input = $this->getCustomInput()) {
352
            $response = new ServerResponse(json_decode($custom_input, true), $this->bot_username);
353
        } else {
354
            if (DB::isDbConnected()) {
355
                //Get last update id from the database
356
                $last_update = DB::selectTelegramUpdate(1);
357
                $last_update = reset($last_update);
358
359
                $this->last_update_id = isset($last_update['id']) ? $last_update['id'] : null;
360
            }
361
362
            if ($this->last_update_id !== null) {
363
                $offset = $this->last_update_id + 1;    //As explained in the telegram bot API documentation
364
            }
365
366
            $response = Request::getUpdates(
367
                [
368
                    'offset'  => $offset,
369
                    'limit'   => $limit,
370
                    'timeout' => $timeout,
371
                ]
372
            );
373
        }
374
375
        if ($response->isOk()) {
376
            $results = $response->getResult();
377
378
            //Process all updates
379
            /** @var Update $result */
380
            foreach ($results as $result) {
381
                $this->processUpdate($result);
382
            }
383
384
            if (!DB::isDbConnected() && !$custom_input && $this->last_update_id !== null && $offset === 0) {
385
                //Mark update(s) as read after handling
386
                Request::getUpdates(
387
                    [
388
                        'offset'  => $this->last_update_id + 1,
389
                        'limit'   => 1,
390
                        'timeout' => $timeout,
391
                    ]
392
                );
393
            }
394
        }
395
396
        return $response;
397
    }
398
399
    /**
400
     * Handle bot request from webhook
401
     *
402
     * @return bool
403
     *
404
     * @throws \Longman\TelegramBot\Exception\TelegramException
405
     */
406
    public function handle()
407
    {
408
        if (empty($this->bot_username)) {
409
            throw new TelegramException('Bot Username is not defined!');
410
        }
411
412
        $this->input = Request::getInput();
413
414
        if (empty($this->input)) {
415
            throw new TelegramException('Input is empty!');
416
        }
417
418
        $post = json_decode($this->input, true);
419
        if (empty($post)) {
420
            throw new TelegramException('Invalid JSON!');
421
        }
422
423
        if ($response = $this->processUpdate(new Update($post, $this->bot_username))) {
424
            return $response->isOk();
425
        }
426
427
        return false;
428
    }
429
430
    /**
431
     * Get the command name from the command type
432
     *
433
     * @param string $type
434
     *
435
     * @return string
436
     */
437
    protected function getCommandFromType($type)
438
    {
439
        return $this->ucfirstUnicode(str_replace('_', '', $type));
440
    }
441
442
    /**
443
     * Process bot Update request
444
     *
445
     * @param \Longman\TelegramBot\Entities\Update $update
446
     *
447
     * @return \Longman\TelegramBot\Entities\ServerResponse
448
     * @throws \Longman\TelegramBot\Exception\TelegramException
449
     */
450
    public function processUpdate(Update $update)
451
    {
452
        $this->update = $update;
453
        $this->last_update_id = $update->getUpdateId();
454
455
        //If all else fails, it's a generic message.
456
        $command = 'genericmessage';
457
458
        $update_type = $this->update->getUpdateType();
459
        if ($update_type === 'message') {
460
            $message = $this->update->getMessage();
461
462
            //Load admin commands
463
            if ($this->isAdmin()) {
464
                $this->addCommandsPath(BASE_COMMANDS_PATH . '/AdminCommands', false);
465
            }
466
467
            $type = $message->getType();
468
            if ($type === 'command') {
469
                $command = $message->getCommand();
470
            } elseif (in_array($type, [
471
                'new_chat_members',
472
                'left_chat_member',
473
                'new_chat_title',
474
                'new_chat_photo',
475
                'delete_chat_photo',
476
                'group_chat_created',
477
                'supergroup_chat_created',
478
                'channel_chat_created',
479
                'migrate_to_chat_id',
480
                'migrate_from_chat_id',
481
                'pinned_message',
482
                'invoice',
483
                'successful_payment',
484
            ], true)
485
            ) {
486
                $command = $this->getCommandFromType($type);
487
            }
488
        } else {
489
            $command = $this->getCommandFromType($update_type);
490
        }
491
492
        //Make sure we have an up-to-date command list
493
        //This is necessary to "require" all the necessary command files!
494
        $this->getCommandsList();
495
496
        //Make sure we don't try to process update that was already processed
497
        $last_id = DB::selectTelegramUpdate(1, $this->update->getUpdateId());
498
        if ($last_id && count($last_id) === 1) {
499
            TelegramLog::debug('Duplicate update received, processing aborted!');
500
            return Request::emptyResponse();
501
        }
502
503
        DB::insertRequest($this->update);
504
505
        return $this->executeCommand($command);
506
    }
507
508
    /**
509
     * Execute /command
510
     *
511
     * @param string $command
512
     *
513
     * @return mixed
514
     * @throws \Longman\TelegramBot\Exception\TelegramException
515
     */
516
    public function executeCommand($command)
517
    {
518
        $command     = strtolower($command);
519
        $command_obj = $this->getCommandObject($command);
520
521
        if (!$command_obj || !$command_obj->isEnabled()) {
522
            //Failsafe in case the Generic command can't be found
523
            if ($command === 'generic') {
524
                throw new TelegramException('Generic command missing!');
525
            }
526
527
            //Handle a generic command or non existing one
528
            $this->last_command_response = $this->executeCommand('generic');
529
        } else {
530
            //Botan.io integration, make sure only the actual command user executed is reported
531
            if ($this->botan_enabled) {
532
                Botan::lock($command);
533
            }
534
535
            //execute() method is executed after preExecute()
536
            //This is to prevent executing a DB query without a valid connection
537
            $this->last_command_response = $command_obj->preExecute();
538
539
            //Botan.io integration, send report after executing the command
540
            if ($this->botan_enabled) {
541
                Botan::track($this->update, $command);
542
            }
543
        }
544
545
        return $this->last_command_response;
546
    }
547
548
    /**
549
     * Sanitize Command
550
     *
551
     * @param string $command
552
     *
553
     * @return string
554
     */
555
    protected function sanitizeCommand($command)
556
    {
557
        return str_replace(' ', '', $this->ucwordsUnicode(str_replace('_', ' ', $command)));
558
    }
559
560
    /**
561
     * Enable a single Admin account
562
     *
563
     * @param integer $admin_id Single admin id
564
     *
565
     * @return \Longman\TelegramBot\Telegram
566
     */
567
    public function enableAdmin($admin_id)
568
    {
569
        if (!is_int($admin_id) || $admin_id <= 0) {
570
            TelegramLog::error('Invalid value "%s" for admin.', $admin_id);
571
        } elseif (!in_array($admin_id, $this->admins_list, true)) {
572
            $this->admins_list[] = $admin_id;
573
        }
574
575
        return $this;
576
    }
577
578
    /**
579
     * Enable a list of Admin Accounts
580
     *
581
     * @param array $admin_ids List of admin ids
582
     *
583
     * @return \Longman\TelegramBot\Telegram
584
     */
585
    public function enableAdmins(array $admin_ids)
586
    {
587
        foreach ($admin_ids as $admin_id) {
588
            $this->enableAdmin($admin_id);
589
        }
590
591
        return $this;
592
    }
593
594
    /**
595
     * Get list of admins
596
     *
597
     * @return array
598
     */
599
    public function getAdminList()
600
    {
601
        return $this->admins_list;
602
    }
603
604
    /**
605
     * Check if the passed user is an admin
606
     *
607
     * If no user id is passed, the current update is checked for a valid message sender.
608
     *
609
     * @param int|null $user_id
610
     *
611
     * @return bool
612
     */
613
    public function isAdmin($user_id = null)
614
    {
615
        if ($user_id === null && $this->update !== null) {
616
            //Try to figure out if the user is an admin
617
            $update_methods = [
618
                'getMessage',
619
                'getEditedMessage',
620
                'getChannelPost',
621
                'getEditedChannelPost',
622
                'getInlineQuery',
623
                'getChosenInlineResult',
624
                'getCallbackQuery',
625
            ];
626
            foreach ($update_methods as $update_method) {
627
                $object = call_user_func([$this->update, $update_method]);
628
                if ($object !== null && $from = $object->getFrom()) {
629
                    $user_id = $from->getId();
630
                    break;
631
                }
632
            }
633
        }
634
635
        return ($user_id === null) ? false : in_array($user_id, $this->admins_list, true);
636
    }
637
638
    /**
639
     * Check if user required the db connection
640
     *
641
     * @return bool
642
     */
643
    public function isDbEnabled()
644
    {
645
        if ($this->mysql_enabled) {
646
            return true;
647
        } else {
648
            return false;
649
        }
650
    }
651
652
    /**
653
     * Add a single custom commands path
654
     *
655
     * @param string $path   Custom commands path to add
656
     * @param bool   $before If the path should be prepended or appended to the list
657
     *
658
     * @return \Longman\TelegramBot\Telegram
659
     */
660
    public function addCommandsPath($path, $before = true)
661
    {
662
        if (!is_dir($path)) {
663
            TelegramLog::error('Commands path "%s" does not exist.', $path);
664
        } elseif (!in_array($path, $this->commands_paths, true)) {
665
            if ($before) {
666
                array_unshift($this->commands_paths, $path);
667
            } else {
668
                $this->commands_paths[] = $path;
669
            }
670
        }
671
672
        return $this;
673
    }
674
675
    /**
676
     * Add multiple custom commands paths
677
     *
678
     * @param array $paths  Custom commands paths to add
679
     * @param bool  $before If the paths should be prepended or appended to the list
680
     *
681
     * @return \Longman\TelegramBot\Telegram
682
     */
683
    public function addCommandsPaths(array $paths, $before = true)
684
    {
685
        foreach ($paths as $path) {
686
            $this->addCommandsPath($path, $before);
687
        }
688
689
        return $this;
690
    }
691
692
    /**
693
     * Return the list of commands paths
694
     *
695
     * @return array
696
     */
697
    public function getCommandsPaths()
698
    {
699
        return $this->commands_paths;
700
    }
701
702
    /**
703
     * Set custom upload path
704
     *
705
     * @param string $path Custom upload path
706
     *
707
     * @return \Longman\TelegramBot\Telegram
708
     */
709
    public function setUploadPath($path)
710
    {
711
        $this->upload_path = $path;
712
713
        return $this;
714
    }
715
716
    /**
717
     * Get custom upload path
718
     *
719
     * @return string
720
     */
721
    public function getUploadPath()
722
    {
723
        return $this->upload_path;
724
    }
725
726
    /**
727
     * Set custom download path
728
     *
729
     * @param string $path Custom download path
730
     *
731
     * @return \Longman\TelegramBot\Telegram
732
     */
733
    public function setDownloadPath($path)
734
    {
735
        $this->download_path = $path;
736
737
        return $this;
738
    }
739
740
    /**
741
     * Get custom download path
742
     *
743
     * @return string
744
     */
745
    public function getDownloadPath()
746
    {
747
        return $this->download_path;
748
    }
749
750
    /**
751
     * Set command config
752
     *
753
     * Provide further variables to a particular commands.
754
     * For example you can add the channel name at the command /sendtochannel
755
     * Or you can add the api key for external service.
756
     *
757
     * @param string $command
758
     * @param array  $config
759
     *
760
     * @return \Longman\TelegramBot\Telegram
761
     */
762
    public function setCommandConfig($command, array $config)
763
    {
764
        $this->commands_config[$command] = $config;
765
766
        return $this;
767
    }
768
769
    /**
770
     * Get command config
771
     *
772
     * @param string $command
773
     *
774
     * @return array
775
     */
776
    public function getCommandConfig($command)
777
    {
778
        return isset($this->commands_config[$command]) ? $this->commands_config[$command] : [];
779
    }
780
781
    /**
782
     * Get API key
783
     *
784
     * @return string
785
     */
786
    public function getApiKey()
787
    {
788
        return $this->api_key;
789
    }
790
791
    /**
792
     * Get Bot name
793
     *
794
     * @return string
795
     */
796
    public function getBotUsername()
797
    {
798
        return $this->bot_username;
799
    }
800
801
    /**
802
     * Get Bot Id
803
     *
804
     * @return string
805
     */
806
    public function getBotId()
807
    {
808
        return $this->bot_id;
809
    }
810
811
    /**
812
     * Get Version
813
     *
814
     * @return string
815
     */
816
    public function getVersion()
817
    {
818
        return $this->version;
819
    }
820
821
    /**
822
     * Set Webhook for bot
823
     *
824
     * @param string $url
825
     * @param array  $data Optional parameters.
826
     *
827
     * @return \Longman\TelegramBot\Entities\ServerResponse
828
     * @throws \Longman\TelegramBot\Exception\TelegramException
829
     */
830
    public function setWebhook($url, array $data = [])
831
    {
832
        if (empty($url)) {
833
            throw new TelegramException('Hook url is empty!');
834
        }
835
836
        $data        = array_intersect_key($data, array_flip([
837
            'certificate',
838
            'max_connections',
839
            'allowed_updates',
840
        ]));
841
        $data['url'] = $url;
842
843
        // If the certificate is passed as a path, encode and add the file to the data array.
844
        if (!empty($data['certificate']) && is_string($data['certificate'])) {
845
            $data['certificate'] = Request::encodeFile($data['certificate']);
846
        }
847
848
        $result = Request::setWebhook($data);
849
850 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...
851
            throw new TelegramException(
852
                'Webhook was not set! Error: ' . $result->getErrorCode() . ' ' . $result->getDescription()
853
            );
854
        }
855
856
        return $result;
857
    }
858
859
    /**
860
     * Delete any assigned webhook
861
     *
862
     * @return mixed
863
     * @throws \Longman\TelegramBot\Exception\TelegramException
864
     */
865
    public function deleteWebhook()
866
    {
867
        $result = Request::deleteWebhook();
868
869 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...
870
            throw new TelegramException(
871
                'Webhook was not deleted! Error: ' . $result->getErrorCode() . ' ' . $result->getDescription()
872
            );
873
        }
874
875
        return $result;
876
    }
877
878
    /**
879
     * Replace function `ucwords` for UTF-8 characters in the class definition and commands
880
     *
881
     * @param string $str
882
     * @param string $encoding (default = 'UTF-8')
883
     *
884
     * @return string
885
     */
886
    protected function ucwordsUnicode($str, $encoding = 'UTF-8')
887
    {
888
        return mb_convert_case($str, MB_CASE_TITLE, $encoding);
889
    }
890
891
    /**
892
     * Replace function `ucfirst` for UTF-8 characters in the class definition and commands
893
     *
894
     * @param string $str
895
     * @param string $encoding (default = 'UTF-8')
896
     *
897
     * @return string
898
     */
899
    protected function ucfirstUnicode($str, $encoding = 'UTF-8')
900
    {
901
        return mb_strtoupper(mb_substr($str, 0, 1, $encoding), $encoding)
902
               . mb_strtolower(mb_substr($str, 1, mb_strlen($str), $encoding), $encoding);
903
    }
904
905
    /**
906
     * Enable Botan.io integration
907
     *
908
     * @param  string $token
909
     * @param  array  $options
910
     *
911
     * @return \Longman\TelegramBot\Telegram
912
     * @throws \Longman\TelegramBot\Exception\TelegramException
913
     */
914
    public function enableBotan($token, array $options = [])
915
    {
916
        Botan::initializeBotan($token, $options);
917
        $this->botan_enabled = true;
918
919
        return $this;
920
    }
921
922
    /**
923
     * Enable requests limiter
924
     *
925
     * @param  array $options
926
     *
927
     * @return \Longman\TelegramBot\Telegram
928
     */
929
    public function enableLimiter(array $options = [])
930
    {
931
        Request::setLimiter(true, $options);
932
933
        return $this;
934
    }
935
936
    /**
937
     * Run provided commands
938
     *
939
     * @param array $commands
940
     *
941
     * @throws TelegramException
942
     */
943
    public function runCommands($commands)
944
    {
945
        if (!is_array($commands) || empty($commands)) {
946
            throw new TelegramException('No command(s) provided!');
947
        }
948
949
        $this->run_commands  = true;
950
        $this->botan_enabled = false;   // Force disable Botan.io integration, we don't want to track self-executed commands!
951
952
        $result = Request::getMe();
953
954
        if ($result->isOk()) {
955
            $result = $result->getResult();
956
957
            $bot_id       = $result->getId();
958
            $bot_name     = $result->getFirstName();
959
            $bot_username = $result->getUsername();
960
        } else {
961
            $bot_id       = $this->getBotId();
962
            $bot_name     = $this->getBotUsername();
963
            $bot_username = $this->getBotUsername();
964
        }
965
966
967
        $this->enableAdmin($bot_id);    // Give bot access to admin commands
968
        $this->getCommandsList();       // Load full commands list
969
970
        foreach ($commands as $command) {
971
            $this->update = new Update(
972
                [
973
                    'update_id' => 0,
974
                    'message'   => [
975
                        'message_id' => 0,
976
                        'from'       => [
977
                            'id'         => $bot_id,
978
                            'first_name' => $bot_name,
979
                            'username'   => $bot_username,
980
                        ],
981
                        'date'       => time(),
982
                        'chat'       => [
983
                            'id'   => $bot_id,
984
                            'type' => 'private',
985
                        ],
986
                        'text'       => $command,
987
                    ],
988
                ]
989
            );
990
991
            $this->executeCommand($this->update->getMessage()->getCommand());
992
        }
993
    }
994
995
    /**
996
     * Is this session initiated by runCommands()
997
     *
998
     * @return bool
999
     */
1000
    public function isRunCommands()
1001
    {
1002
        return $this->run_commands;
1003
    }
1004
1005
    /**
1006
     * Switch to enable running getUpdates without a database
1007
     *
1008
     * @param bool $enable
1009
     */
1010
    public function useGetUpdatesWithoutDatabase($enable = true)
1011
    {
1012
        $this->getupdates_without_database = $enable;
1013
    }
1014
1015
    /**
1016
     * Return last update id
1017
     *
1018
     * @return int
1019
     */
1020
    public function getLastUpdateId()
1021
    {
1022
        return $this->last_update_id;
1023
    }
1024
}
1025