Completed
Push — feature/refactor-app-design ( aadb9c...84f9ff )
by Avtandil
02:37
created

Telegram::createCommandObject()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 19
ccs 9
cts 9
cp 1
rs 9.2
cc 4
eloc 11
nc 6
nop 1
crap 4
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 13 and the first side effect is on line 13.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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
defined('TB_BASE_PATH') || define('TB_BASE_PATH', __DIR__);
14 1
defined('TB_BASE_COMMANDS_PATH') || define('TB_BASE_COMMANDS_PATH', TB_BASE_PATH . '/Commands');
15
16
use Exception;
17
use Illuminate\Container\Container;
18
use Longman\TelegramBot\Commands\Command;
19
use Longman\TelegramBot\Console\Kernel as ConsoleKernel;
20
use Longman\TelegramBot\Entities\Update;
21
use Longman\TelegramBot\Exception\TelegramException;
22
use Longman\TelegramBot\Http\Client;
23
use Longman\TelegramBot\Http\Kernel;
24
use Longman\TelegramBot\Http\Request;
25
use Longman\TelegramBot\Http\Response;
26
use PDO;
27
use RecursiveDirectoryIterator;
28
use RecursiveIteratorIterator;
29
use RegexIterator;
30
31
class Telegram
32
{
33
    /**
34
     * Version
35
     *
36
     * @var string
37
     */
38
    const VERSION = '0.53.0';
39
40
    /**
41
     * Telegram API key
42
     *
43
     * @var string
44
     */
45
    protected $api_key = '';
46
47
    /**
48
     * Telegram Bot username
49
     *
50
     * @var string
51
     */
52
    protected $bot_username = '';
53
54
    /**
55
     * Telegram Bot id
56
     *
57
     * @var string
58
     */
59
    protected $bot_id = '';
60
61
    /**
62
     * Container
63
     *
64
     * @var \Illuminate\Contracts\Container\Container
65
     */
66
    protected $container;
67
68
    /**
69
     * Raw request data (json) for webhook methods
70
     *
71
     * @var string
72
     */
73
    protected $input;
74
75
    /**
76
     * Custom commands paths
77
     *
78
     * @var array
79
     */
80
    protected $commands_paths = [];
81
82
    /**
83
     * Current Update object
84
     *
85
     * @var \Longman\TelegramBot\Entities\Update
86
     */
87
    protected $update;
88
89
    /**
90
     * Upload path
91
     *
92
     * @var string
93
     */
94
    protected $upload_path;
95
96
    /**
97
     * Download path
98
     *
99
     * @var string
100
     */
101
    protected $download_path;
102
103
    /**
104
     * MySQL integration
105
     *
106
     * @var boolean
107
     */
108
    protected $mysql_enabled = false;
109
110
    /**
111
     * PDO object
112
     *
113
     * @var \PDO
114
     */
115
    protected $pdo;
116
117
    /**
118
     * Commands config
119
     *
120
     * @var array
121
     */
122
    protected $commands_config = [];
123
124
    /**
125
     * Admins list
126
     *
127
     * @var array
128
     */
129
    protected $admins_list = [];
130
131
    /**
132
     * ServerResponse of the last Command execution
133
     *
134
     * @var \Longman\TelegramBot\Http\Response
135
     */
136
    protected $last_command_response;
137
138
    /**
139
     * Botan.io integration
140
     *
141
     * @var boolean
142
     */
143
    protected $botan_enabled = false;
144
145
    /**
146
     * Check if runCommands() is running in this session
147
     *
148
     * @var boolean
149
     */
150
    protected $run_commands = false;
151
152
    /**
153
     * Is running getUpdates without DB enabled
154
     *
155
     * @var bool
156
     */
157
    public $getupdates_without_database = false;
158
159
    /**
160
     * Last update ID
161
     * Only used when running getUpdates without a database
162
     *
163
     * @var integer
164
     */
165
    public $last_update_id = null;
166
167
    /**
168
     * Telegram constructor.
169
     *
170
     * @param string $api_key
171
     * @param string $bot_username
172
     *
173
     * @throws \Longman\TelegramBot\Exception\TelegramException
174
     */
175 33
    public function __construct($api_key, $bot_username = '')
176
    {
177 33
        if (empty($api_key)) {
178 1
            throw new TelegramException('API KEY not defined!');
179
        }
180 33
        preg_match('/(\d+)\:[\w\-]+/', $api_key, $matches);
181 33
        if (! isset($matches[1])) {
182 1
            throw new TelegramException('Invalid API KEY defined!');
183
        }
184 33
        $this->bot_id = $matches[1];
185 33
        $this->api_key = $api_key;
186
187 33
        if (! empty($bot_username)) {
188 33
            $this->bot_username = $bot_username;
189
        }
190
191 33
        $this->registerContainer();
192
193
        //Add default system commands path
194 33
        $this->addCommandsPath(TB_BASE_COMMANDS_PATH . '/SystemCommands');
195
196 33
        Client::initialize($this);
197 33
    }
198
199
    /**
200
     * Register the container.
201
     *
202
     * @return void
203
     */
204 33
    protected function registerContainer()
205
    {
206 33
        $this->container = Container::getInstance();
207
208 33
        $this->container->instance(Telegram::class, $this);
209 33
    }
210
211
    /**
212
     * Get container instance.
213
     *
214
     * @return \Illuminate\Container\Container
215
     */
216 3
    public function getContainer()
217
    {
218 3
        return $this->container;
219
    }
220
221
    /**
222
     * Initialize Database connection
223
     *
224
     * @param array $credential
225
     * @param string $table_prefix
226
     * @param string $encoding
227
     *
228
     * @return \Longman\TelegramBot\Telegram
229
     * @throws \Longman\TelegramBot\Exception\TelegramException
230
     */
231 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...
232
    {
233 9
        $this->pdo = DB::initialize($credential, $this, $table_prefix, $encoding);
234 9
        ConversationDB::initializeConversation();
235 9
        $this->mysql_enabled = true;
236
237 9
        return $this;
238
    }
239
240
    /**
241
     * Initialize Database external connection
242
     *
243
     * @param PDO $external_pdo_connection PDO database object
244
     * @param string $table_prefix
245
     *
246
     * @return \Longman\TelegramBot\Telegram
247
     * @throws \Longman\TelegramBot\Exception\TelegramException
248
     */
249 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...
250
    {
251
        $this->pdo = DB::externalInitialize($external_pdo_connection, $this, $table_prefix);
252
        ConversationDB::initializeConversation();
253
        $this->mysql_enabled = true;
254
255
        return $this;
256
    }
257
258
    /**
259
     * Get commands list
260
     *
261
     * @return array $commands
262
     * @throws \Longman\TelegramBot\Exception\TelegramException
263
     */
264 3
    public function getCommandsList()
265
    {
266 3
        $commands = [];
267
268 3
        foreach ($this->commands_paths as $path) {
269
            try {
270
                // Get all "*Command.php" files
271 3
                $files = new RegexIterator(
272 3
                    new RecursiveIteratorIterator(
273 3
                        new RecursiveDirectoryIterator($path)
274
                    ),
275 3
                    '/^.+Command.php$/'
276
                );
277
278 3
                foreach ($files as $file) {
279
                    //Remove "Command.php" from filename
280 3
                    $command = $this->sanitizeCommand(substr($file->getFilename(), 0, -11));
281 3
                    $command_name = strtolower($command);
282
283 3
                    if (array_key_exists($command_name, $commands)) {
284
                        continue;
285
                    }
286
287 3
                    require_once $file->getPathname();
288
289 3
                    $command_obj = $this->createCommandObject($command);
290 3
                    if ($command_obj instanceof Command) {
291 3
                        $commands[$command_name] = $command_obj;
292
                    }
293
                }
294
            } catch (Exception $e) {
295 3
                throw new TelegramException('Error getting commands from path: ' . $path);
296
            }
297
        }
298
299 3
        return $commands;
300
    }
301
302
    /**
303
     * Get an object instance of the passed command
304
     *
305
     * @param string $command
306
     *
307
     * @return \Longman\TelegramBot\Commands\Command|null
308
     */
309 3
    public function createCommandObject($command)
310
    {
311 3
        $which = ['System'];
312 3
        if ($this->isAdmin()) {
313 3
            $which[] = 'Admin';
314
        }
315 3
        $which[] = 'User';
316 3
317 3
        $command_name = $this->ucfirstUnicode($command);
318 3
        foreach ($which as $auth) {
319
            $command_namespace = __NAMESPACE__ . '\\Commands\\' . $auth . 'Commands\\' . $command_name . 'Command';
320
321
            if (class_exists($command_namespace)) {
322 2
                return new $command_namespace($this, $this->update);
323
            }
324
        }
325
326
        return null;
327
    }
328
329
    /**
330
     * Set custom input string for debug purposes
331
     *
332
     * @param string $input (json format)
333
     *
334
     * @return \Longman\TelegramBot\Telegram
335
     */
336
    public function setCustomInput($input)
337
    {
338
        $this->input = $input;
339
340
        return $this;
341
    }
342
343
    /**
344
     * Get custom input string for debug purposes
345
     *
346
     * @return string
347
     */
348
    public function getCustomInput()
349
    {
350
        return $this->input;
351
    }
352
353
    /**
354
     * Get the ServerResponse of the last Command execution
355
     *
356
     * @return \Longman\TelegramBot\Http\Response
357
     */
358
    public function getLastCommandResponse()
359
    {
360
        return $this->last_command_response;
361
    }
362
363
    /**
364
     * Handle getUpdates method
365
     *
366
     * @param \Longman\TelegramBot\Http\Request|null $request
367
     * @param int|null $limit
368
     * @param int|null $timeout
369
     *
370
     * @return \Longman\TelegramBot\Http\Response
371
     * @throws \Longman\TelegramBot\Exception\TelegramException
372
     */
373
    public function handleGetUpdates(Request $request = null, $limit = null, $timeout = null)
374
    {
375
        if (empty($this->bot_username)) {
376
            throw new TelegramException('Bot Username is not defined!');
377
        }
378
379
        /** @var \Longman\TelegramBot\Console\Kernel $kernel */
380
        $kernel = $this->getContainer()->make(ConsoleKernel::class);
381
382
        if (is_null($request)) {
383
            $request = Request::capture();
384
        }
385
386
        $this->container->instance(Request::class, $request);
387
388
        $response = $kernel->handle($request, $limit, $timeout);
0 ignored issues
show
Bug introduced by
It seems like $limit defined by parameter $limit on line 373 can also be of type integer; however, Longman\TelegramBot\Console\Kernel::handle() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $timeout defined by parameter $timeout on line 373 can also be of type integer; however, Longman\TelegramBot\Console\Kernel::handle() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
389 2
390
        return $response;
391 2
    }
392
393
    /**
394
     * Handle bot request from webhook
395
     *
396 2
     * @param \Longman\TelegramBot\Http\Request|null $request
397
     * @return \Longman\TelegramBot\Http\Response
398 2
     *
399
     * @throws \Longman\TelegramBot\Exception\TelegramException
400 2
     */
401
    public function handle(Request $request = null)
402
    {
403
        if (empty($this->getBotUsername())) {
404
            throw new TelegramException('Bot Username is not defined!');
405
        }
406
407
        /** @var \Longman\TelegramBot\Http\Kernel $kernel */
408
        $kernel = $this->getContainer()->make(Kernel::class);
409
410 2
        if (is_null($request)) {
411
            $request = Request::capture();
412 2
        }
413
414
        $this->container->instance(Request::class, $request);
415
416
        $response = $kernel->handle($request);
417
418
        return $response;
419
    }
420
421
    /**
422
     * Get the command name from the command type
423 2
     *
424
     * @param string $type
425 2
     *
426 2
     * @return string
427
     */
428
    protected function getCommandFromType($type)
429 2
    {
430
        return $this->ucfirstUnicode(str_replace('_', '', $type));
431 2
    }
432 2
433
    /**
434
     * Process bot Update request
435
     *
436
     * @param \Longman\TelegramBot\Entities\Update $update
437
     *
438
     * @return \Longman\TelegramBot\Http\Response
439
     * @throws \Longman\TelegramBot\Exception\TelegramException
440
     */
441
    public function processUpdate(Update $update)
442
    {
443
        $this->update = $update;
444
        $this->last_update_id = $update->getUpdateId();
445
446
        // If all else fails, it's a generic message.
447
        $command = 'genericmessage';
448
449
        $update_type = $update->getUpdateType();
450
        if ($update_type === Update::TYPE_MESSAGE) {
451
            $message = $update->getMessage();
452
453
            // Load admin commands
454
            if ($this->isAdmin()) {
455
                $this->addCommandsPath(TB_BASE_COMMANDS_PATH . '/AdminCommands', false);
456
            }
457
458
            $type = $message->getType();
459
            if ($type === 'command') {
460
                $command = $message->getCommand();
461
            } else if (in_array($type, [
462 2
                'new_chat_members',
463
                'left_chat_member',
464
                'new_chat_title',
465
                'new_chat_photo',
466
                'delete_chat_photo',
467 2
                'group_chat_created',
468
                'supergroup_chat_created',
469
                'channel_chat_created',
470 2
                'migrate_to_chat_id',
471 2
                'migrate_from_chat_id',
472
                'pinned_message',
473
                'invoice',
474
                'successful_payment',
475
            ], true)
476
            ) {
477 2
                $command = $this->getCommandFromType($type);
478
            }
479 2
        } else {
480
            $command = $this->getCommandFromType($update_type);
481
        }
482
483
        // Make sure we have an up-to-date command list
484
        // This is necessary to "require" all the necessary command files!
485
        $this->getCommandsList();
486
487
        // Make sure we don't try to process update that was already processed
488
        $last_id = DB::selectTelegramUpdate(1, $update->getUpdateId());
489
        if ($last_id && count($last_id) === 1) {
490 2
            TelegramLog::debug('Duplicate update received, processing aborted!');
491
492 2
            return new Response(['ok' => true, 'result' => true]);
493 2
        }
494
495 2
        DB::insertRequest($update);
496
497 2
        return $this->executeCommand($command);
498
    }
499
500
    /**
501
     * Execute /command
502 2
     *
503
     * @param string $command
504
     *
505 2
     * @return mixed
506
     * @throws \Longman\TelegramBot\Exception\TelegramException
507
     */
508
    public function executeCommand($command)
509
    {
510
        $command = strtolower($command);
511 2
        $command_obj = $this->createCommandObject($command);
512
513
        if (! $command_obj || ! $command_obj->isEnabled()) {
514 2
            // Failsafe in case the Generic command can't be found
515
            if ($command === 'generic') {
516
                throw new TelegramException('Generic command missing!');
517
            }
518
519 2
            // Handle a generic command or non existing one
520
            $this->last_command_response = $this->executeCommand('generic');
521
        } else {
522
            // Botan.io integration, make sure only the actual command user executed is reported
523
            if ($this->botan_enabled) {
524
                Botan::lock($command);
525
            }
526
527
            // execute() method is executed after preExecute()
528
            // This is to prevent executing a DB query without a valid connection
529 3
            $this->last_command_response = $command_obj->preExecute();
530
531 3
            // Botan.io integration, send report after executing the command
532
            if ($this->botan_enabled) {
533
                Botan::track($this->update, $command);
534
            }
535
        }
536
537
        return $this->last_command_response;
538
    }
539
540
    /**
541 1
     * Sanitize Command
542
     *
543 1
     * @param string $command
544 1
     *
545 1
     * @return string
546 1
     */
547
    protected function sanitizeCommand($command)
548
    {
549 1
        return str_replace(' ', '', $this->ucwordsUnicode(str_replace('_', ' ', $command)));
550
    }
551
552
    /**
553
     * Enable a single Admin account
554
     *
555
     * @param integer $admin_id Single admin id
556
     *
557
     * @return \Longman\TelegramBot\Telegram
558
     */
559 1
    public function enableAdmin($admin_id)
560
    {
561 1
        if (! is_int($admin_id) || $admin_id <= 0) {
562 1
            TelegramLog::error('Invalid value "%s" for admin.', $admin_id);
563
        } else if (! in_array($admin_id, $this->admins_list, true)) {
564
            $this->admins_list[] = $admin_id;
565 1
        }
566
567
        return $this;
568
    }
569
570
    /**
571
     * Enable a list of Admin Accounts
572
     *
573 1
     * @param array $admin_ids List of admin ids
574
     *
575 1
     * @return \Longman\TelegramBot\Telegram
576
     */
577
    public function enableAdmins(array $admin_ids)
578
    {
579
        foreach ($admin_ids as $admin_id) {
580
            $this->enableAdmin($admin_id);
581
        }
582
583
        return $this;
584
    }
585
586
    /**
587 3
     * Get list of admins
588
     *
589 3
     * @return array
590
     */
591
    public function getAdminList()
592 2
    {
593
        return $this->admins_list;
594
    }
595
596
    /**
597
     * Check if the passed user is an admin
598
     *
599
     * If no user id is passed, the current update is checked for a valid message sender.
600 2
     *
601 2
     * @param int|null $user_id
602 2
     *
603
     * @return bool
604 2
     */
605
    public function isAdmin($user_id = null)
606
    {
607
        if ($user_id === null && $this->update !== null) {
608
            //Try to figure out if the user is an admin
609 3
            $update_methods = [
610
                'getMessage',
611
                'getEditedMessage',
612
                'getChannelPost',
613
                'getEditedChannelPost',
614
                'getInlineQuery',
615
                'getChosenInlineResult',
616
                'getCallbackQuery',
617
            ];
618
            foreach ($update_methods as $update_method) {
619
                $object = call_user_func([$this->update, $update_method]);
620
                if ($object !== null && $from = $object->getFrom()) {
621
                    $user_id = $from->getId();
622
                    break;
623
                }
624
            }
625
        }
626
627
        return ($user_id === null) ? false : in_array($user_id, $this->admins_list, true);
628
    }
629
630
    /**
631
     * Check if user required the db connection
632
     *
633
     * @return bool
634 33
     */
635
    public function isDbEnabled()
636 33
    {
637 1
        if ($this->mysql_enabled) {
638 33
            return true;
639 33
        } else {
640 33
            return false;
641
        }
642
    }
643
644
    /**
645
     * Add a single custom commands path
646 33
     *
647
     * @param string $path Custom commands path to add
648
     * @param bool $before If the path should be prepended or appended to the list
649
     *
650
     * @return \Longman\TelegramBot\Telegram
651
     */
652
    public function addCommandsPath($path, $before = true)
653
    {
654
        if (! is_dir($path)) {
655
            TelegramLog::error('Commands path "%s" does not exist.', $path);
656
        } else if (! in_array($path, $this->commands_paths, true)) {
657 1
            if ($before) {
658
                array_unshift($this->commands_paths, $path);
659 1
            } else {
660 1
                $this->commands_paths[] = $path;
661
            }
662
        }
663 1
664
        return $this;
665
    }
666
667
    /**
668
     * Add multiple custom commands paths
669
     *
670
     * @param array $paths Custom commands paths to add
671 1
     * @param bool $before If the paths should be prepended or appended to the list
672
     *
673 1
     * @return \Longman\TelegramBot\Telegram
674
     */
675
    public function addCommandsPaths(array $paths, $before = true)
676
    {
677
        foreach ($paths as $path) {
678
            $this->addCommandsPath($path, $before);
679
        }
680
681
        return $this;
682
    }
683
684
    /**
685
     * Return the list of commands paths
686
     *
687
     * @return array
688
     */
689
    public function getCommandsPaths()
690
    {
691
        return $this->commands_paths;
692
    }
693
694
    /**
695
     * Set custom upload path
696
     *
697
     * @param string $path Custom upload path
698
     *
699
     * @return \Longman\TelegramBot\Telegram
700
     */
701
    public function setUploadPath($path)
702
    {
703
        $this->upload_path = $path;
704
705
        return $this;
706
    }
707
708
    /**
709
     * Get custom upload path
710
     *
711
     * @return string
712
     */
713
    public function getUploadPath()
714
    {
715
        return $this->upload_path;
716
    }
717
718
    /**
719
     * Set custom download path
720
     *
721
     * @param string $path Custom download path
722
     *
723
     * @return \Longman\TelegramBot\Telegram
724
     */
725
    public function setDownloadPath($path)
726
    {
727
        $this->download_path = $path;
728
729
        return $this;
730
    }
731
732
    /**
733
     * Get custom download path
734
     *
735
     * @return string
736 14
     */
737
    public function getDownloadPath()
738 14
    {
739
        return $this->download_path;
740 14
    }
741
742
    /**
743
     * Set command config
744
     *
745
     * Provide further variables to a particular commands.
746
     * For example you can add the channel name at the command /sendtochannel
747
     * Or you can add the api key for external service.
748
     *
749
     * @param string $command
750 17
     * @param array $config
751
     *
752 17
     * @return \Longman\TelegramBot\Telegram
753
     */
754
    public function setCommandConfig($command, array $config)
755
    {
756
        $this->commands_config[$command] = $config;
757
758
        return $this;
759
    }
760 1
761
    /**
762 1
     * Get command config
763
     *
764
     * @param string $command
765
     *
766
     * @return array
767
     */
768
    public function getCommandConfig($command)
769
    {
770 3
        return isset($this->commands_config[$command]) ? $this->commands_config[$command] : [];
771
    }
772 3
773
    /**
774
     * Get API key
775
     *
776
     * @return string
777
     */
778
    public function getApiKey()
779
    {
780
        return $this->api_key;
781
    }
782
783
    /**
784
     * Get Bot name
785
     *
786
     * @return string
787
     */
788
    public function getBotUsername()
789
    {
790
        return $this->bot_username;
791
    }
792
793
    /**
794
     * Get Bot Id
795
     *
796
     * @return string
797
     */
798
    public function getBotId()
799
    {
800
        return $this->bot_id;
801
    }
802
803
    /**
804
     * Get Version
805
     *
806
     * @return string
807
     */
808
    public function getVersion()
809
    {
810
        return self::VERSION;
811
    }
812
813
    /**
814
     * Set Webhook for bot
815
     *
816
     * @param string $url
817
     * @param array $parameters Optional parameters.
818
     *
819
     * @return \Longman\TelegramBot\Http\Response
820
     * @throws \Longman\TelegramBot\Exception\TelegramException
821
     */
822
    public function setWebhook($url, array $parameters = [])
823
    {
824
        if (empty($url)) {
825
            throw new TelegramException('Hook url is empty!');
826
        }
827
828
        $parameters = array_intersect_key($parameters, array_flip([
829
            'certificate',
830
            'max_connections',
831
            'allowed_updates',
832
        ]));
833
        $parameters['url'] = $url;
834
835
        // If the certificate is passed as a path, encode and add the file to the data array.
836
        if (! empty($parameters['certificate']) && is_string($parameters['certificate'])) {
837
            $parameters['certificate'] = Client::encodeFile($parameters['certificate']);
838
        }
839
840
        $result = Client::setWebhook($parameters);
841
842 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...
843
            throw new TelegramException(
844
                'Webhook was not set! Error: ' . $result->getErrorCode() . ' ' . $result->getDescription()
845
            );
846
        }
847
848
        return $result;
849
    }
850
851
    /**
852
     * Delete any assigned webhook
853
     *
854
     * @return mixed
855
     * @throws \Longman\TelegramBot\Exception\TelegramException
856
     */
857
    public function deleteWebhook()
858
    {
859
        $result = Client::deleteWebhook();
860 3
861 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...
862 3
            throw new TelegramException(
863
                'Webhook was not deleted! Error: ' . $result->getErrorCode() . ' ' . $result->getDescription()
864
            );
865
        }
866
867
        return $result;
868
    }
869
870
    /**
871
     * Replace function `ucwords` for UTF-8 characters in the class definition and commands
872
     *
873 3
     * @param string $str
874
     * @param string $encoding (default = 'UTF-8')
875 3
     *
876 3
     * @return string
877
     */
878
    protected function ucwordsUnicode($str, $encoding = 'UTF-8')
879
    {
880
        return mb_convert_case($str, MB_CASE_TITLE, $encoding);
881
    }
882
883
    /**
884
     * Replace function `ucfirst` for UTF-8 characters in the class definition and commands
885
     *
886
     * @param string $str
887
     * @param string $encoding (default = 'UTF-8')
888
     *
889
     * @return string
890
     */
891
    protected function ucfirstUnicode($str, $encoding = 'UTF-8')
892
    {
893
        return mb_strtoupper(mb_substr($str, 0, 1, $encoding), $encoding)
894
            . mb_strtolower(mb_substr($str, 1, mb_strlen($str), $encoding), $encoding);
895
    }
896
897
    /**
898
     * Enable Botan.io integration
899
     *
900
     * @param  string $token
901
     * @param  array $options
902
     *
903
     * @return \Longman\TelegramBot\Telegram
904
     * @throws \Longman\TelegramBot\Exception\TelegramException
905
     */
906
    public function enableBotan($token, array $options = [])
907
    {
908
        Botan::initializeBotan($token, $options);
909
        $this->botan_enabled = true;
910
911
        return $this;
912
    }
913
914
    /**
915
     * Enable requests limiter
916
     *
917
     * @param  array $options
918
     *
919
     * @return \Longman\TelegramBot\Telegram
920
     */
921
    public function enableLimiter(array $options = [])
922
    {
923
        Client::setLimiter(true, $options);
924
925
        return $this;
926
    }
927
928
    /**
929
     * Run provided commands
930
     *
931
     * @param array $commands
932
     *
933
     * @throws TelegramException
934
     */
935
    public function runCommands($commands)
936
    {
937
        if (! is_array($commands) || empty($commands)) {
938
            throw new TelegramException('No command(s) provided!');
939
        }
940
941
        $this->run_commands = true;
942
        $this->botan_enabled = false;   // Force disable Botan.io integration, we don't want to track self-executed commands!
943
944
        $result = Client::getMe();
945
946
        if ($result->isOk()) {
947
            $result = $result->getResult();
948
949
            $bot_id = $result->getId();
0 ignored issues
show
Bug introduced by
The method getId cannot be called on $result (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
950
            $bot_name = $result->getFirstName();
0 ignored issues
show
Bug introduced by
The method getFirstName cannot be called on $result (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
951
            $bot_username = $result->getUsername();
0 ignored issues
show
Bug introduced by
The method getUsername cannot be called on $result (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
952
        } else {
953
            $bot_id = $this->getBotId();
954
            $bot_name = $this->getBotUsername();
955
            $bot_username = $this->getBotUsername();
956
        }
957
958
        $this->enableAdmin($bot_id);    // Give bot access to admin commands
959
        $this->getCommandsList();       // Load full commands list
960
961
        foreach ($commands as $command) {
962
            $this->update = new Update(
963
                [
964
                    'update_id' => 0,
965
                    'message'   => [
966
                        'message_id' => 0,
967
                        'from'       => [
968
                            'id'         => $bot_id,
969
                            'first_name' => $bot_name,
970
                            'username'   => $bot_username,
971
                        ],
972
                        'date'       => time(),
973
                        'chat'       => [
974
                            'id'   => $bot_id,
975
                            'type' => 'private',
976
                        ],
977
                        'text'       => $command,
978
                    ],
979
                ]
980
            );
981
982
            $this->executeCommand($this->update->getMessage()->getCommand());
983
        }
984
    }
985
986
    /**
987
     * Is this session initiated by runCommands()
988
     *
989
     * @return bool
990
     */
991
    public function isRunCommands()
992
    {
993
        return $this->run_commands;
994
    }
995
996
    /**
997
     * Switch to enable running getUpdates without a database
998
     *
999
     * @param bool $enable
1000
     */
1001
    public function useGetUpdatesWithoutDatabase($enable = true)
1002
    {
1003
        $this->getupdates_without_database = $enable;
1004
    }
1005
1006
    /**
1007
     * Return last update id
1008
     *
1009
     * @return int
1010
     */
1011
    public function getLastUpdateId()
1012
    {
1013
        return $this->last_update_id;
1014
    }
1015
}
1016