Passed
Push — master ( 78024d...fb0f34 )
by Armando
02:41
created

Telegram   F

Complexity

Total Complexity 122

Size/Duplication

Total Lines 1066
Duplicated Lines 0 %

Test Coverage

Coverage 36.61%

Importance

Changes 41
Bugs 5 Features 3
Metric Value
eloc 294
dl 0
loc 1066
ccs 108
cts 295
cp 0.3661
rs 2
c 41
b 5
f 3
wmc 122

44 Methods

Rating   Name   Duplication   Size   Complexity  
C handleGetUpdates() 0 64 15
A setUploadPath() 0 5 1
A deleteWebhook() 0 11 2
A setDownloadPath() 0 5 1
A getCustomInput() 0 3 1
A getCommandFromType() 0 3 1
A addCommandsPath() 0 13 4
C processUpdate() 0 63 13
B isAdmin() 0 23 7
A getAdminList() 0 3 1
A setUpdateFilter() 0 5 1
A getCommandConfig() 0 3 2
A getApiKey() 0 3 1
A enableMySql() 0 7 1
A isDbEnabled() 0 6 2
A ucfirstUnicode() 0 4 1
A getFileNamespace() 0 7 2
A isRunCommands() 0 3 1
A getBotId() 0 3 1
A addCommandsPaths() 0 7 2
A getBotUsername() 0 3 1
A enableExternalMySql() 0 7 1
A executeCommand() 0 25 5
A setCommandConfig() 0 5 1
A enableLimiter() 0 5 1
A setWebhook() 0 27 5
A getUploadPath() 0 3 1
A useGetUpdatesWithoutDatabase() 0 3 1
A getDownloadPath() 0 3 1
A enableAdmin() 0 9 4
A getCommandsPaths() 0 3 1
A getCommandsList() 0 36 6
A getVersion() 0 3 1
A setCustomInput() 0 5 1
A handle() 0 22 5
A getUpdateFilter() 0 3 1
A ucwordsUnicode() 0 3 1
A runCommands() 0 48 5
A sanitizeCommand() 0 3 1
A enableAdmins() 0 7 2
A getLastCommandResponse() 0 3 1
A getLastUpdateId() 0 3 1
B getCommandObject() 0 40 11
A __construct() 0 20 4

How to fix   Complexity   

Complex Class

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.

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
2
3
/**
4
 * This file is part of the TelegramBot package.
5
 *
6
 * (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Longman\TelegramBot;
13
14 1
defined('TB_BASE_PATH') || define('TB_BASE_PATH', __DIR__);
15 1
defined('TB_BASE_COMMANDS_PATH') || define('TB_BASE_COMMANDS_PATH', TB_BASE_PATH . '/Commands');
16
17
use Exception;
18
use Longman\TelegramBot\Commands\AdminCommand;
19
use Longman\TelegramBot\Commands\Command;
20
use Longman\TelegramBot\Commands\SystemCommand;
21
use Longman\TelegramBot\Commands\UserCommand;
22
use Longman\TelegramBot\Entities\ServerResponse;
23
use Longman\TelegramBot\Entities\Update;
24
use Longman\TelegramBot\Exception\TelegramException;
25
use PDO;
26
use RecursiveDirectoryIterator;
27
use RecursiveIteratorIterator;
28
use RegexIterator;
29
30
class Telegram
31
{
32
    /**
33
     * Version
34
     *
35
     * @var string
36
     */
37
    protected $version = '0.63.0';
38
39
    /**
40
     * Telegram API key
41
     *
42
     * @var string
43
     */
44
    protected $api_key = '';
45
46
    /**
47
     * Telegram Bot username
48
     *
49
     * @var string
50
     */
51
    protected $bot_username = '';
52
53
    /**
54
     * Telegram Bot id
55
     *
56
     * @var string
57
     */
58
    protected $bot_id = '';
59
60
    /**
61
     * Raw request data (json) for webhook methods
62
     *
63
     * @var string
64
     */
65
    protected $input;
66
67
    /**
68
     * Custom commands paths
69
     *
70
     * @var array
71
     */
72
    protected $commands_paths = [];
73
74
    /**
75
     * Custom commands objects
76
     *
77
     * @var array
78
     */
79
    protected $commands_objects = [];
80
81
    /**
82
     * Current Update object
83
     *
84
     * @var Update
85
     */
86
    protected $update;
87
88
    /**
89
     * Upload path
90
     *
91
     * @var string
92
     */
93
    protected $upload_path;
94
95
    /**
96
     * Download path
97
     *
98
     * @var string
99
     */
100
    protected $download_path;
101
102
    /**
103
     * MySQL integration
104
     *
105
     * @var bool
106
     */
107
    protected $mysql_enabled = false;
108
109
    /**
110
     * PDO object
111
     *
112
     * @var PDO
113
     */
114
    protected $pdo;
115
116
    /**
117
     * Commands config
118
     *
119
     * @var array
120
     */
121
    protected $commands_config = [];
122
123
    /**
124
     * Admins list
125
     *
126
     * @var array
127
     */
128
    protected $admins_list = [];
129
130
    /**
131
     * ServerResponse of the last Command execution
132
     *
133
     * @var ServerResponse
134
     */
135
    protected $last_command_response;
136
137
    /**
138
     * Check if runCommands() is running in this session
139
     *
140
     * @var bool
141
     */
142
    protected $run_commands = false;
143
144
    /**
145
     * Is running getUpdates without DB enabled
146
     *
147
     * @var bool
148
     */
149
    protected $getupdates_without_database = false;
150
151
    /**
152
     * Last update ID
153
     * Only used when running getUpdates without a database
154
     *
155
     * @var integer
156
     */
157
    protected $last_update_id;
158
159
    /**
160
     * The command to be executed when there's a new message update and nothing more suitable is found
161
     */
162
    const GENERIC_MESSAGE_COMMAND = 'genericmessage';
163
164
    /**
165
     * The command to be executed by default (when no other relevant commands are applicable)
166
     */
167
    const GENERIC_COMMAND = 'generic';
168
169
    /**
170
     * Update filter
171
     * Filter updates
172
     *
173
     * @var callback
174
     */
175
    protected $update_filter;
176
177
    /**
178
     * Telegram constructor.
179
     *
180
     * @param string $api_key
181
     * @param string $bot_username
182
     *
183
     * @throws TelegramException
184
     */
185 30
    public function __construct($api_key, $bot_username = '')
186
    {
187 30
        if (empty($api_key)) {
188 1
            throw new TelegramException('API KEY not defined!');
189
        }
190 30
        preg_match('/(\d+)\:[\w\-]+/', $api_key, $matches);
191 30
        if (!isset($matches[1])) {
192 1
            throw new TelegramException('Invalid API KEY defined!');
193
        }
194 30
        $this->bot_id  = $matches[1];
195 30
        $this->api_key = $api_key;
196
197 30
        if (!empty($bot_username)) {
198 30
            $this->bot_username = $bot_username;
199
        }
200
201
        //Add default system commands path
202 30
        $this->addCommandsPath(TB_BASE_COMMANDS_PATH . '/SystemCommands');
203
204 30
        Request::initialize($this);
205 30
    }
206
207
    /**
208
     * Initialize Database connection
209
     *
210
     * @param array  $credential
211
     * @param string $table_prefix
212
     * @param string $encoding
213
     *
214
     * @return Telegram
215
     * @throws TelegramException
216
     */
217 9
    public function enableMySql(array $credential, $table_prefix = null, $encoding = 'utf8mb4')
218
    {
219 9
        $this->pdo = DB::initialize($credential, $this, $table_prefix, $encoding);
220 9
        ConversationDB::initializeConversation();
221 9
        $this->mysql_enabled = true;
222
223 9
        return $this;
224
    }
225
226
    /**
227
     * Initialize Database external connection
228
     *
229
     * @param PDO    $external_pdo_connection PDO database object
230
     * @param string $table_prefix
231
     *
232
     * @return Telegram
233
     * @throws TelegramException
234
     */
235
    public function enableExternalMySql($external_pdo_connection, $table_prefix = null)
236
    {
237
        $this->pdo = DB::externalInitialize($external_pdo_connection, $this, $table_prefix);
238
        ConversationDB::initializeConversation();
239
        $this->mysql_enabled = true;
240
241
        return $this;
242
    }
243
244
    /**
245
     * Get commands list
246
     *
247
     * @return array $commands
248
     * @throws TelegramException
249
     */
250 1
    public function getCommandsList()
251
    {
252 1
        $commands = [];
253
254 1
        foreach ($this->commands_paths as $path) {
255
            try {
256
                //Get all "*Command.php" files
257 1
                $files = new RegexIterator(
258 1
                    new RecursiveIteratorIterator(
259 1
                        new RecursiveDirectoryIterator($path)
260
                    ),
261 1
                    '/^.+Command.php$/'
262
                );
263
264 1
                foreach ($files as $file) {
265
                    //Remove "Command.php" from filename
266 1
                    $command      = $this->sanitizeCommand(substr($file->getFilename(), 0, -11));
267 1
                    $command_name = mb_strtolower($command);
268
269 1
                    if (array_key_exists($command_name, $commands)) {
270
                        continue;
271
                    }
272
273 1
                    require_once $file->getPathname();
274
275 1
                    $command_obj = $this->getCommandObject($command, $file->getPathname());
276 1
                    if ($command_obj instanceof Command) {
277 1
                        $commands[$command_name] = $command_obj;
278
                    }
279
                }
280
            } catch (Exception $e) {
281
                throw new TelegramException('Error getting commands from path: ' . $path, $e);
0 ignored issues
show
Bug introduced by
$e of type Exception is incompatible with the type integer expected by parameter $code of Longman\TelegramBot\Exce...xception::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

281
                throw new TelegramException('Error getting commands from path: ' . $path, /** @scrutinizer ignore-type */ $e);
Loading history...
282
            }
283
        }
284
285 1
        return $commands;
286
    }
287
288
    /**
289
     * Get an object instance of the passed command
290
     *
291
     * @param string $command
292
     * @param string $filepath
293
     *
294
     * @return Command|null
295
     */
296 1
    public function getCommandObject($command, $filepath = null)
297
    {
298 1
        $which = ['System'];
299 1
        $this->isAdmin() && $which[] = 'Admin';
300 1
        $which[] = 'User';
301
302 1
        foreach ($which as $auth) {
303 1
            if ($filepath) {
304 1
                $command_namespace = $this->getFileNamespace($filepath);
305
            } else {
306
                $command_namespace = __NAMESPACE__ . '\\Commands\\' . $auth . 'Commands';
307
            }
308 1
            $command_class = $command_namespace . '\\' . $this->ucfirstUnicode($command) . 'Command';
309
310 1
            if (class_exists($command_class)) {
311 1
                $command_obj = new $command_class($this, $this->update);
312
313 1
                switch ($auth) {
314 1
                    case 'System':
315 1
                        if ($command_obj instanceof SystemCommand) {
316 1
                            return $command_obj;
317
                        }
318
                        break;
319
320
                    case 'Admin':
321
                        if ($command_obj instanceof AdminCommand) {
322
                            return $command_obj;
323
                        }
324
                        break;
325
326
                    case 'User':
327
                        if ($command_obj instanceof UserCommand) {
328
                            return $command_obj;
329
                        }
330
                        break;
331
                }
332
            }
333
        }
334
335
        return null;
336
    }
337
338
    /**
339
     * Get namespace from php file by src path
340
     *
341
     * @param string $src (absolute path to file)
342
     *
343
     * @return string ("Longman\TelegramBot\Commands\SystemCommands" for example)
344
     */
345 1
    protected function getFileNamespace($src)
346
    {
347 1
        $content = file_get_contents($src);
348 1
        if (preg_match('#^namespace\s+(.+?);$#sm', $content, $m)) {
349 1
            return $m[1];
350
        }
351
        return null;
352
    }
353
354
    /**
355
     * Set custom input string for debug purposes
356
     *
357
     * @param string $input (json format)
358
     *
359
     * @return Telegram
360
     */
361
    public function setCustomInput($input)
362
    {
363
        $this->input = $input;
364
365
        return $this;
366
    }
367
368
    /**
369
     * Get custom input string for debug purposes
370
     *
371
     * @return string
372
     */
373
    public function getCustomInput()
374
    {
375
        return $this->input;
376
    }
377
378
    /**
379
     * Get the ServerResponse of the last Command execution
380
     *
381
     * @return ServerResponse
382
     */
383
    public function getLastCommandResponse()
384
    {
385
        return $this->last_command_response;
386
    }
387
388
    /**
389
     * Handle getUpdates method
390
     *
391
     * @param int|null $limit
392
     * @param int|null $timeout
393
     *
394
     * @return ServerResponse
395
     * @throws TelegramException
396
     */
397
    public function handleGetUpdates($limit = null, $timeout = null)
398
    {
399
        if (empty($this->bot_username)) {
400
            throw new TelegramException('Bot Username is not defined!');
401
        }
402
403
        if (!DB::isDbConnected() && !$this->getupdates_without_database) {
404
            return new ServerResponse(
405
                [
406
                    'ok'          => false,
407
                    'description' => 'getUpdates needs MySQL connection! (This can be overridden - see documentation)',
408
                ],
409
                $this->bot_username
410
            );
411
        }
412
413
        $offset = 0;
414
415
        //Take custom input into account.
416
        if ($custom_input = $this->getCustomInput()) {
417
            $response = new ServerResponse(json_decode($custom_input, true), $this->bot_username);
418
        } else {
419
            if (DB::isDbConnected() && $last_update = DB::selectTelegramUpdate(1)) {
420
                //Get last update id from the database
421
                $last_update = reset($last_update);
422
423
                $this->last_update_id = isset($last_update['id']) ? $last_update['id'] : null;
424
            }
425
426
            if ($this->last_update_id !== null) {
427
                $offset = $this->last_update_id + 1;    //As explained in the telegram bot API documentation
428
            }
429
430
            $response = Request::getUpdates(
431
                [
432
                    'offset'  => $offset,
433
                    'limit'   => $limit,
434
                    'timeout' => $timeout,
435
                ]
436
            );
437
        }
438
439
        if ($response->isOk()) {
440
            $results = $response->getResult();
441
442
            //Process all updates
443
            /** @var Update $result */
444
            foreach ($results as $result) {
445
                $this->processUpdate($result);
446
            }
447
448
            if (!DB::isDbConnected() && !$custom_input && $this->last_update_id !== null && $offset === 0) {
449
                //Mark update(s) as read after handling
450
                Request::getUpdates(
451
                    [
452
                        'offset'  => $this->last_update_id + 1,
453
                        'limit'   => 1,
454
                        'timeout' => $timeout,
455
                    ]
456
                );
457
            }
458
        }
459
460
        return $response;
461
    }
462
463
    /**
464
     * Handle bot request from webhook
465
     *
466
     * @return bool
467
     *
468
     * @throws TelegramException
469
     */
470
    public function handle()
471
    {
472
        if (empty($this->bot_username)) {
473
            throw new TelegramException('Bot Username is not defined!');
474
        }
475
476
        $this->input = Request::getInput();
477
478
        if (empty($this->input)) {
479
            throw new TelegramException('Input is empty!');
480
        }
481
482
        $post = json_decode($this->input, true);
483
        if (empty($post)) {
484
            throw new TelegramException('Invalid JSON!');
485
        }
486
487
        if ($response = $this->processUpdate(new Update($post, $this->bot_username))) {
488
            return $response->isOk();
489
        }
490
491
        return false;
492
    }
493
494
    /**
495
     * Get the command name from the command type
496
     *
497
     * @param string $type
498
     *
499
     * @return string
500
     */
501
    protected function getCommandFromType($type)
502
    {
503
        return $this->ucfirstUnicode(str_replace('_', '', $type));
504
    }
505
506
    /**
507
     * Process bot Update request
508
     *
509
     * @param Update $update
510
     *
511
     * @return ServerResponse
512
     * @throws TelegramException
513
     */
514 1
    public function processUpdate(Update $update)
515
    {
516 1
        $this->update         = $update;
517 1
        $this->last_update_id = $update->getUpdateId();
518
519 1
        if (is_callable($this->update_filter)) {
520 1
            $reason = 'Update denied by update_filter';
521
            try {
522 1
                $allowed = (bool) call_user_func_array($this->update_filter, [$update, $this, &$reason]);
523
            } catch (\Exception $e) {
524
                $allowed = false;
525
            }
526
527 1
            if (!$allowed) {
528 1
                TelegramLog::debug($reason);
529 1
                return new ServerResponse(['ok' => false, 'description' => 'denied'], null);
530
            }
531
        }
532
533
        //Load admin commands
534
        if ($this->isAdmin()) {
535
            $this->addCommandsPath(TB_BASE_COMMANDS_PATH . '/AdminCommands', false);
536
        }
537
538
        //Make sure we have an up-to-date command list
539
        //This is necessary to "require" all the necessary command files!
540
        $this->commands_objects = $this->getCommandsList();
541
542
        //If all else fails, it's a generic message.
543
        $command = self::GENERIC_MESSAGE_COMMAND;
544
545
        $update_type = $this->update->getUpdateType();
546
        if ($update_type === 'message') {
547
            $message = $this->update->getMessage();
548
            $type    = $message->getType();
549
550
            // Let's check if the message object has the type field we're looking for...
551
            $command_tmp = $type === 'command' ? $message->getCommand() : $this->getCommandFromType($type);
552
            // ...and if a fitting command class is available.
553
            $command_obj = $this->getCommandObject($command_tmp);
554
555
            // Empty usage string denotes a non-executable command.
556
            // @see https://github.com/php-telegram-bot/core/issues/772#issuecomment-388616072
557
            if (
558
                ($command_obj === null && $type === 'command')
559
                || ($command_obj !== null && $command_obj->getUsage() !== '')
560
            ) {
561
                $command = $command_tmp;
562
            }
563
        } else {
564
            $command = $this->getCommandFromType($update_type);
565
        }
566
567
        //Make sure we don't try to process update that was already processed
568
        $last_id = DB::selectTelegramUpdate(1, $this->update->getUpdateId());
569
        if ($last_id && count($last_id) === 1) {
570
            TelegramLog::debug('Duplicate update received, processing aborted!');
571
            return Request::emptyResponse();
572
        }
573
574
        DB::insertRequest($this->update);
575
576
        return $this->executeCommand($command);
577
    }
578
579
    /**
580
     * Execute /command
581
     *
582
     * @param string $command
583
     *
584
     * @return ServerResponse
585
     * @throws TelegramException
586
     */
587
    public function executeCommand($command)
588
    {
589
        $command = mb_strtolower($command);
590
591
        if (isset($this->commands_objects[$command])) {
592
            $command_obj = $this->commands_objects[$command];
593
        } else {
594
            $command_obj = $this->getCommandObject($command);
595
        }
596
597
        if (!$command_obj || !$command_obj->isEnabled()) {
598
            //Failsafe in case the Generic command can't be found
599
            if ($command === self::GENERIC_COMMAND) {
600
                throw new TelegramException('Generic command missing!');
601
            }
602
603
            //Handle a generic command or non existing one
604
            $this->last_command_response = $this->executeCommand(self::GENERIC_COMMAND);
605
        } else {
606
            //execute() method is executed after preExecute()
607
            //This is to prevent executing a DB query without a valid connection
608
            $this->last_command_response = $command_obj->preExecute();
609
        }
610
611
        return $this->last_command_response;
612
    }
613
614
    /**
615
     * Sanitize Command
616
     *
617
     * @param string $command
618
     *
619
     * @return string
620
     */
621 1
    protected function sanitizeCommand($command)
622
    {
623 1
        return str_replace(' ', '', $this->ucwordsUnicode(str_replace('_', ' ', $command)));
624
    }
625
626
    /**
627
     * Enable a single Admin account
628
     *
629
     * @param integer $admin_id Single admin id
630
     *
631
     * @return Telegram
632
     */
633 1
    public function enableAdmin($admin_id)
634
    {
635 1
        if (!is_int($admin_id) || $admin_id <= 0) {
0 ignored issues
show
introduced by
The condition is_int($admin_id) is always true.
Loading history...
636 1
            TelegramLog::error('Invalid value "' . $admin_id . '" for admin.');
637 1
        } elseif (!in_array($admin_id, $this->admins_list, true)) {
638 1
            $this->admins_list[] = $admin_id;
639
        }
640
641 1
        return $this;
642
    }
643
644
    /**
645
     * Enable a list of Admin Accounts
646
     *
647
     * @param array $admin_ids List of admin ids
648
     *
649
     * @return Telegram
650
     */
651 1
    public function enableAdmins(array $admin_ids)
652
    {
653 1
        foreach ($admin_ids as $admin_id) {
654 1
            $this->enableAdmin($admin_id);
655
        }
656
657 1
        return $this;
658
    }
659
660
    /**
661
     * Get list of admins
662
     *
663
     * @return array
664
     */
665 1
    public function getAdminList()
666
    {
667 1
        return $this->admins_list;
668
    }
669
670
    /**
671
     * Check if the passed user is an admin
672
     *
673
     * If no user id is passed, the current update is checked for a valid message sender.
674
     *
675
     * @param int|null $user_id
676
     *
677
     * @return bool
678
     */
679 1
    public function isAdmin($user_id = null)
680
    {
681 1
        if ($user_id === null && $this->update !== null) {
682
            //Try to figure out if the user is an admin
683
            $update_methods = [
684
                'getMessage',
685
                'getEditedMessage',
686
                'getChannelPost',
687
                'getEditedChannelPost',
688
                'getInlineQuery',
689
                'getChosenInlineResult',
690
                'getCallbackQuery',
691
            ];
692
            foreach ($update_methods as $update_method) {
693
                $object = call_user_func([$this->update, $update_method]);
694
                if ($object !== null && $from = $object->getFrom()) {
695
                    $user_id = $from->getId();
696
                    break;
697
                }
698
            }
699
        }
700
701 1
        return ($user_id === null) ? false : in_array($user_id, $this->admins_list, true);
702
    }
703
704
    /**
705
     * Check if user required the db connection
706
     *
707
     * @return bool
708
     */
709
    public function isDbEnabled()
710
    {
711
        if ($this->mysql_enabled) {
712
            return true;
713
        } else {
714
            return false;
715
        }
716
    }
717
718
    /**
719
     * Add a single custom commands path
720
     *
721
     * @param string $path   Custom commands path to add
722
     * @param bool   $before If the path should be prepended or appended to the list
723
     *
724
     * @return Telegram
725
     */
726 30
    public function addCommandsPath($path, $before = true)
727
    {
728 30
        if (!is_dir($path)) {
729 1
            TelegramLog::error('Commands path "' . $path . '" does not exist.');
730 30
        } elseif (!in_array($path, $this->commands_paths, true)) {
731 30
            if ($before) {
732 30
                array_unshift($this->commands_paths, $path);
733
            } else {
734
                $this->commands_paths[] = $path;
735
            }
736
        }
737
738 30
        return $this;
739
    }
740
741
    /**
742
     * Add multiple custom commands paths
743
     *
744
     * @param array $paths  Custom commands paths to add
745
     * @param bool  $before If the paths should be prepended or appended to the list
746
     *
747
     * @return Telegram
748
     */
749 1
    public function addCommandsPaths(array $paths, $before = true)
750
    {
751 1
        foreach ($paths as $path) {
752 1
            $this->addCommandsPath($path, $before);
753
        }
754
755 1
        return $this;
756
    }
757
758
    /**
759
     * Return the list of commands paths
760
     *
761
     * @return array
762
     */
763 1
    public function getCommandsPaths()
764
    {
765 1
        return $this->commands_paths;
766
    }
767
768
    /**
769
     * Set custom upload path
770
     *
771
     * @param string $path Custom upload path
772
     *
773
     * @return Telegram
774
     */
775
    public function setUploadPath($path)
776
    {
777
        $this->upload_path = $path;
778
779
        return $this;
780
    }
781
782
    /**
783
     * Get custom upload path
784
     *
785
     * @return string
786
     */
787
    public function getUploadPath()
788
    {
789
        return $this->upload_path;
790
    }
791
792
    /**
793
     * Set custom download path
794
     *
795
     * @param string $path Custom download path
796
     *
797
     * @return Telegram
798
     */
799
    public function setDownloadPath($path)
800
    {
801
        $this->download_path = $path;
802
803
        return $this;
804
    }
805
806
    /**
807
     * Get custom download path
808
     *
809
     * @return string
810
     */
811
    public function getDownloadPath()
812
    {
813
        return $this->download_path;
814
    }
815
816
    /**
817
     * Set command config
818
     *
819
     * Provide further variables to a particular commands.
820
     * For example you can add the channel name at the command /sendtochannel
821
     * Or you can add the api key for external service.
822
     *
823
     * @param string $command
824
     * @param array  $config
825
     *
826
     * @return Telegram
827
     */
828 13
    public function setCommandConfig($command, array $config)
829
    {
830 13
        $this->commands_config[$command] = $config;
831
832 13
        return $this;
833
    }
834
835
    /**
836
     * Get command config
837
     *
838
     * @param string $command
839
     *
840
     * @return array
841
     */
842 14
    public function getCommandConfig($command)
843
    {
844 14
        return isset($this->commands_config[$command]) ? $this->commands_config[$command] : [];
845
    }
846
847
    /**
848
     * Get API key
849
     *
850
     * @return string
851
     */
852 1
    public function getApiKey()
853
    {
854 1
        return $this->api_key;
855
    }
856
857
    /**
858
     * Get Bot name
859
     *
860
     * @return string
861
     */
862 2
    public function getBotUsername()
863
    {
864 2
        return $this->bot_username;
865
    }
866
867
    /**
868
     * Get Bot Id
869
     *
870
     * @return string
871
     */
872
    public function getBotId()
873
    {
874
        return $this->bot_id;
875
    }
876
877
    /**
878
     * Get Version
879
     *
880
     * @return string
881
     */
882
    public function getVersion()
883
    {
884
        return $this->version;
885
    }
886
887
    /**
888
     * Set Webhook for bot
889
     *
890
     * @param string $url
891
     * @param array  $data Optional parameters.
892
     *
893
     * @return ServerResponse
894
     * @throws TelegramException
895
     */
896
    public function setWebhook($url, array $data = [])
897
    {
898
        if (empty($url)) {
899
            throw new TelegramException('Hook url is empty!');
900
        }
901
902
        $data        = array_intersect_key($data, array_flip([
903
            'certificate',
904
            'max_connections',
905
            'allowed_updates',
906
        ]));
907
        $data['url'] = $url;
908
909
        // If the certificate is passed as a path, encode and add the file to the data array.
910
        if (!empty($data['certificate']) && is_string($data['certificate'])) {
911
            $data['certificate'] = Request::encodeFile($data['certificate']);
912
        }
913
914
        $result = Request::setWebhook($data);
915
916
        if (!$result->isOk()) {
917
            throw new TelegramException(
918
                'Webhook was not set! Error: ' . $result->getErrorCode() . ' ' . $result->getDescription()
919
            );
920
        }
921
922
        return $result;
923
    }
924
925
    /**
926
     * Delete any assigned webhook
927
     *
928
     * @return mixed
929
     * @throws TelegramException
930
     */
931
    public function deleteWebhook()
932
    {
933
        $result = Request::deleteWebhook();
934
935
        if (!$result->isOk()) {
936
            throw new TelegramException(
937
                'Webhook was not deleted! Error: ' . $result->getErrorCode() . ' ' . $result->getDescription()
938
            );
939
        }
940
941
        return $result;
942
    }
943
944
    /**
945
     * Replace function `ucwords` for UTF-8 characters in the class definition and commands
946
     *
947
     * @param string $str
948
     * @param string $encoding (default = 'UTF-8')
949
     *
950
     * @return string
951
     */
952 1
    protected function ucwordsUnicode($str, $encoding = 'UTF-8')
953
    {
954 1
        return mb_convert_case($str, MB_CASE_TITLE, $encoding);
955
    }
956
957
    /**
958
     * Replace function `ucfirst` for UTF-8 characters in the class definition and commands
959
     *
960
     * @param string $str
961
     * @param string $encoding (default = 'UTF-8')
962
     *
963
     * @return string
964
     */
965 1
    protected function ucfirstUnicode($str, $encoding = 'UTF-8')
966
    {
967 1
        return mb_strtoupper(mb_substr($str, 0, 1, $encoding), $encoding)
968 1
            . mb_strtolower(mb_substr($str, 1, mb_strlen($str), $encoding), $encoding);
969
    }
970
971
    /**
972
     * Enable requests limiter
973
     *
974
     * @param array $options
975
     *
976
     * @return Telegram
977
     * @throws TelegramException
978
     */
979
    public function enableLimiter(array $options = [])
980
    {
981
        Request::setLimiter(true, $options);
982
983
        return $this;
984
    }
985
986
    /**
987
     * Run provided commands
988
     *
989
     * @param array $commands
990
     *
991
     * @throws TelegramException
992
     */
993
    public function runCommands($commands)
994
    {
995
        if (!is_array($commands) || empty($commands)) {
0 ignored issues
show
introduced by
The condition is_array($commands) is always true.
Loading history...
996
            throw new TelegramException('No command(s) provided!');
997
        }
998
999
        $this->run_commands = true;
1000
1001
        $result = Request::getMe();
1002
1003
        if ($result->isOk()) {
1004
            $result = $result->getResult();
1005
1006
            $bot_id       = $result->getId();
1007
            $bot_name     = $result->getFirstName();
1008
            $bot_username = $result->getUsername();
1009
        } else {
1010
            $bot_id       = $this->getBotId();
1011
            $bot_name     = $this->getBotUsername();
1012
            $bot_username = $this->getBotUsername();
1013
        }
1014
1015
1016
        $this->enableAdmin($bot_id);    // Give bot access to admin commands
1017
        $this->getCommandsList();       // Load full commands list
1018
1019
        foreach ($commands as $command) {
1020
            $this->update = new Update(
1021
                [
1022
                    'update_id' => 0,
1023
                    'message'   => [
1024
                        'message_id' => 0,
1025
                        'from'       => [
1026
                            'id'         => $bot_id,
1027
                            'first_name' => $bot_name,
1028
                            'username'   => $bot_username,
1029
                        ],
1030
                        'date'       => time(),
1031
                        'chat'       => [
1032
                            'id'   => $bot_id,
1033
                            'type' => 'private',
1034
                        ],
1035
                        'text'       => $command,
1036
                    ],
1037
                ]
1038
            );
1039
1040
            $this->executeCommand($this->update->getMessage()->getCommand());
1041
        }
1042
    }
1043
1044
    /**
1045
     * Is this session initiated by runCommands()
1046
     *
1047
     * @return bool
1048
     */
1049
    public function isRunCommands()
1050
    {
1051
        return $this->run_commands;
1052
    }
1053
1054
    /**
1055
     * Switch to enable running getUpdates without a database
1056
     *
1057
     * @param bool $enable
1058
     */
1059
    public function useGetUpdatesWithoutDatabase($enable = true)
1060
    {
1061
        $this->getupdates_without_database = $enable;
1062
    }
1063
1064
    /**
1065
     * Return last update id
1066
     *
1067
     * @return int
1068
     */
1069
    public function getLastUpdateId()
1070
    {
1071
        return $this->last_update_id;
1072
    }
1073
1074
    /**
1075
     * Set an update filter callback
1076
     *
1077
     * @param callable $callback
1078
     *
1079
     * @return Telegram
1080
     */
1081 1
    public function setUpdateFilter(callable $callback)
1082
    {
1083 1
        $this->update_filter = $callback;
1084
1085 1
        return $this;
1086
    }
1087
1088
    /**
1089
     * Return update filter callback
1090
     *
1091
     * @return callable|null
1092
     */
1093
    public function getUpdateFilter()
1094
    {
1095
        return $this->update_filter;
1096
    }
1097
}
1098