Completed
Push — feature/refactor-app-design ( b18d21...84977e )
by Avtandil
02:45
created

Telegram   F

Complexity

Total Complexity 85

Size/Duplication

Total Lines 909
Duplicated Lines 2.86 %

Coupling/Cohesion

Components 4
Dependencies 15

Test Coverage

Coverage 57.55%

Importance

Changes 0
Metric Value
wmc 85
c 0
b 0
f 0
lcom 4
cbo 15
dl 26
loc 909
ccs 141
cts 245
cp 0.5755
rs 1.263

43 Methods

Rating   Name   Duplication   Size   Complexity  
A getLastCommandResponse() 0 4 1
B __construct() 0 25 4
A registerContainer() 0 6 1
A initializeConfig() 0 6 1
A getConfig() 0 4 1
A getContainer() 0 4 1
A enableMySql() 8 8 1
A enableExternalMySql() 8 8 1
B getCommandsList() 0 38 6
A createCommandObject() 0 19 4
A handleGetUpdates() 0 19 3
A handle() 0 19 3
A getCommandFromType() 0 4 1
B processUpdate() 0 58 7
B executeCommand() 0 31 6
A sanitizeCommand() 0 4 1
A enableAdmin() 0 6 1
A enableAdmins() 0 6 1
A getAdminList() 0 4 1
C isAdmin() 0 25 7
A addCommandsPath() 0 6 1
A addCommandsPaths() 0 6 1
A getCommandsPaths() 0 5 1
A setUploadPath() 0 6 1
A getUploadPath() 0 4 1
A setDownloadPath() 0 6 1
A getDownloadPath() 0 4 1
A setCommandConfig() 0 6 1
A getCommandConfig() 0 4 2
A getApiKey() 0 4 1
A getBotUsername() 0 4 1
A getBotId() 0 4 1
A getVersion() 0 4 1
B setWebhook() 5 28 5
A deleteWebhook() 5 12 2
A ucwordsUnicode() 0 4 1
A ucfirstUnicode() 0 5 1
A enableBotan() 0 7 1
A enableLimiter() 0 6 1
B runCommands() 0 50 5
A isRunCommands() 0 4 1
A useGetUpdatesWithoutDatabase() 0 4 1
A getLastUpdateId() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Telegram often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Telegram, and based on these observations, apply Extract Interface, too.

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