Request::limitTelegramRequests()   C
last analyzed

Complexity

Conditions 15
Paths 21

Size

Total Lines 48
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 240

Importance

Changes 0
Metric Value
dl 0
loc 48
ccs 0
cts 19
cp 0
rs 5.0127
c 0
b 0
f 0
cc 15
eloc 32
nc 21
nop 2
crap 240

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * This file is part of the TelegramBot package.
4
 *
5
 * (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Longman\TelegramBot;
12
13
use GuzzleHttp\Client;
14
use GuzzleHttp\Exception\RequestException;
15
use Longman\TelegramBot\Entities\File;
16
use Longman\TelegramBot\Entities\ServerResponse;
17
use Longman\TelegramBot\Exception\TelegramException;
18
19
class Request
20
{
21
    /**
22
     * Telegram object
23
     *
24
     * @var \Longman\TelegramBot\Telegram
25
     */
26
    private static $telegram;
27
28
    /**
29
     * URI of the Telegram API
30
     *
31
     * @var string
32
     */
33
    private static $api_base_uri = 'https://api.telegram.org';
34
35
    /**
36
     * Guzzle Client object
37
     *
38
     * @var \GuzzleHttp\Client
39
     */
40
    private static $client;
41
42
    /**
43
     * Input value of the request
44
     *
45
     * @var string
46
     */
47
    private static $input;
48
49
    /**
50
     * Request limiter
51
     *
52
     * @var boolean
53
     */
54
    private static $limiter_enabled;
55
56
    /**
57
     * Available actions to send
58
     *
59
     * @var array
60
     */
61
    private static $actions = [
62
        'getUpdates',
63
        'setWebhook',
64
        'deleteWebhook',
65
        'getMe',
66
        'sendMessage',
67
        'forwardMessage',
68
        'sendPhoto',
69
        'sendAudio',
70
        'sendDocument',
71
        'sendSticker',
72
        'sendVideo',
73
        'sendVoice',
74
        'sendLocation',
75
        'sendVenue',
76
        'sendContact',
77
        'sendChatAction',
78
        'getUserProfilePhotos',
79
        'getFile',
80
        'kickChatMember',
81
        'leaveChat',
82
        'unbanChatMember',
83
        'getChat',
84
        'getChatAdministrators',
85
        'getChatMember',
86
        'getChatMembersCount',
87
        'answerCallbackQuery',
88
        'answerInlineQuery',
89
        'editMessageText',
90
        'editMessageCaption',
91
        'editMessageReplyMarkup',
92
        'getWebhookInfo',
93
    ];
94
95
    /**
96
     * Initialize
97
     *
98
     * @param \Longman\TelegramBot\Telegram $telegram
99
     *
100
     * @throws \Longman\TelegramBot\Exception\TelegramException
101
     */
102 41
    public static function initialize(Telegram $telegram)
103
    {
104 41
        if (is_object($telegram)) {
105 41
            self::$telegram = $telegram;
106 41
            self::$client   = new Client(['base_uri' => self::$api_base_uri]);
107
        } else {
108
            throw new TelegramException('Telegram pointer is empty!');
109
        }
110 41
    }
111
112
    /**
113
     * Set input from custom input or stdin and return it
114
     *
115
     * @return string
116
     * @throws \Longman\TelegramBot\Exception\TelegramException
117
     */
118
    public static function getInput()
119
    {
120
        // First check if a custom input has been set, else get the PHP input.
121
        if (!($input = self::$telegram->getCustomInput())) {
122
            $input = file_get_contents('php://input');
123
        }
124
125
        // Make sure we have a string to work with.
126
        if (is_string($input)) {
127
            self::$input = $input;
128
        } else {
129
            throw new TelegramException('Input must be a string!');
130
        }
131
132
        TelegramLog::update(self::$input);
133
134
        return self::$input;
135
    }
136
137
    /**
138
     * Generate general fake server response
139
     *
140
     * @param array $data Data to add to fake response
141
     *
142
     * @return array Fake response data
143
     */
144 7
    public static function generateGeneralFakeServerResponse(array $data = [])
145
    {
146
        //PARAM BINDED IN PHPUNIT TEST FOR TestServerResponse.php
147
        //Maybe this is not the best possible implementation
148
149
        //No value set in $data ie testing setWebhook
150
        //Provided $data['chat_id'] ie testing sendMessage
151
152 7
        $fake_response = ['ok' => true]; // :)
153
154 7
        if ($data === []) {
155 1
            $fake_response['result'] = true;
156
        }
157
158
        //some data to let iniatilize the class method SendMessage
159 7
        if (isset($data['chat_id'])) {
160 7
            $data['message_id'] = '1234';
161 7
            $data['date']       = '1441378360';
162 7
            $data['from']       = [
163
                'id'         => 123456789,
164
                'first_name' => 'botname',
165
                'username'   => 'namebot',
166
            ];
167 7
            $data['chat']       = ['id' => $data['chat_id']];
168
169 7
            $fake_response['result'] = $data;
170
        }
171
172 7
        return $fake_response;
173
    }
174
175
    /**
176
     * Properly set up the request params
177
     *
178
     * If any item of the array is a resource, reformat it to a multipart request.
179
     * Else, just return the passed data as form params.
180
     *
181
     * @param array $data
182
     *
183
     * @return array
184
     */
185
    private static function setUpRequestParams(array $data)
186
    {
187
        $has_resource = false;
188
        $multipart = [];
189
190
        // Convert any nested arrays into JSON strings.
191
        array_walk($data, function (&$item) {
192
            is_array($item) && $item = json_encode($item);
193
        });
194
195
        //Reformat data array in multipart way if it contains a resource
196
        foreach ($data as $key => $item) {
197
            $has_resource |= is_resource($item);
198
            $multipart[] = ['name' => $key, 'contents' => $item];
199
        }
200
        if ($has_resource) {
201
            return ['multipart' => $multipart];
202
        }
203
204
        return ['form_params' => $data];
205
    }
206
207
    /**
208
     * Execute HTTP Request
209
     *
210
     * @param string $action Action to execute
211
     * @param array  $data   Data to attach to the execution
212
     *
213
     * @return string Result of the HTTP Request
214
     * @throws \Longman\TelegramBot\Exception\TelegramException
215
     */
216
    public static function execute($action, array $data = [])
217
    {
218
        //Fix so that the keyboard markup is a string, not an object
219
        if (isset($data['reply_markup'])) {
220
            $data['reply_markup'] = json_encode($data['reply_markup']);
221
        }
222
223
        $result = null;
224
        $request_params = self::setUpRequestParams($data);
225
        $request_params['debug'] = TelegramLog::getDebugLogTempStream();
226
227
        try {
228
            $response = self::$client->post(
229
                '/bot' . self::$telegram->getApiKey() . '/' . $action,
230
                $request_params
231
            );
232
            $result = (string) $response->getBody();
233
234
            //Logging getUpdates Update
235
            if ($action === 'getUpdates') {
236
                TelegramLog::update($result);
237
            }
238
        } catch (RequestException $e) {
239
            $result = ($e->getResponse()) ? (string) $e->getResponse()->getBody() : '';
240
        } finally {
241
            //Logging verbose debug output
242
            TelegramLog::endDebugLogTempStream('Verbose HTTP Request output:' . PHP_EOL . '%s' . PHP_EOL);
243
        }
244
245
        return $result;
246
    }
247
248
    /**
249
     * Download file
250
     *
251
     * @param \Longman\TelegramBot\Entities\File $file
252
     *
253
     * @return boolean
254
     * @throws \Longman\TelegramBot\Exception\TelegramException
255
     */
256
    public static function downloadFile(File $file)
257
    {
258
        $tg_file_path = $file->getFilePath();
259
        $file_path    = self::$telegram->getDownloadPath() . '/' . $tg_file_path;
260
261
        $file_dir = dirname($file_path);
262
        //For safety reasons, first try to create the directory, then check that it exists.
263
        //This is in case some other process has created the folder in the meantime.
264
        if (!@mkdir($file_dir, 0755, true) && !is_dir($file_dir)) {
265
            throw new TelegramException('Directory ' . $file_dir . ' can\'t be created');
266
        }
267
268
        $debug_handle = TelegramLog::getDebugLogTempStream();
269
270
        try {
271
            self::$client->get(
272
                '/file/bot' . self::$telegram->getApiKey() . '/' . $tg_file_path,
273
                ['debug' => $debug_handle, 'sink' => $file_path]
274
            );
275
276
            return filesize($file_path) > 0;
277
        } catch (RequestException $e) {
278
            return ($e->getResponse()) ? (string) $e->getResponse()->getBody() : '';
279
        } finally {
280
            //Logging verbose debug output
281
            TelegramLog::endDebugLogTempStream('Verbose HTTP File Download Request output:' . PHP_EOL . '%s' . PHP_EOL);
282
        }
283
    }
284
285
    /**
286
     * Encode file
287
     *
288
     * @param string $file
289
     *
290
     * @return resource
291
     * @throws \Longman\TelegramBot\Exception\TelegramException
292
     */
293
    protected static function encodeFile($file)
294
    {
295
        $fp = fopen($file, 'r');
296
        if ($fp === false) {
297
            throw new TelegramException('Cannot open "' . $file . '" for reading');
298
        }
299
300
        return $fp;
301
    }
302
303
    /**
304
     * Send command
305
     *
306
     * @todo Fake response doesn't need json encoding?
307
     *
308
     * @param string $action
309
     * @param array  $data
310
     *
311
     * @return \Longman\TelegramBot\Entities\ServerResponse
312
     * @throws \Longman\TelegramBot\Exception\TelegramException
313
     */
314 6
    public static function send($action, array $data = [])
315
    {
316 6
        self::ensureValidAction($action);
317
318 6
        $bot_name = self::$telegram->getBotName();
319
320 6
        if (defined('PHPUNIT_TESTSUITE')) {
321 6
            $fake_response = self::generateGeneralFakeServerResponse($data);
322
323 6
            return new ServerResponse($fake_response, $bot_name);
324
        }
325
326
        self::ensureNonEmptyData($data);
327
328
        self::limitTelegramRequests($action, $data);
329
330
        $response = json_decode(self::execute($action, $data), true);
331
332
        if (null === $response) {
333
            throw new TelegramException('Telegram returned an invalid response! Please review your bot name and API key.');
334
        }
335
336
        return new ServerResponse($response, $bot_name);
337
    }
338
339
    /**
340
     * Make sure the data isn't empty, else throw an exception
341
     *
342
     * @param array $data
343
     *
344
     * @throws \Longman\TelegramBot\Exception\TelegramException
345
     */
346
    private static function ensureNonEmptyData(array $data)
347
    {
348
        if (count($data) === 0) {
349
            throw new TelegramException('Data is empty!');
350
        }
351
    }
352
353
    /**
354
     * Make sure the action is valid, else throw an exception
355
     *
356
     * @param string $action
357
     *
358
     * @throws \Longman\TelegramBot\Exception\TelegramException
359
     */
360 6
    private static function ensureValidAction($action)
361
    {
362 6
        if (!in_array($action, self::$actions, true)) {
363
            throw new TelegramException('The action "' . $action . '" doesn\'t exist!');
364
        }
365 6
    }
366
367
    /**
368
     * Assign an encoded file to a data array
369
     *
370
     * @param array  $data
371
     * @param string $field
372
     * @param string $file
373
     *
374
     * @throws \Longman\TelegramBot\Exception\TelegramException
375
     */
376
    private static function assignEncodedFile(&$data, $field, $file)
377
    {
378
        if ($file !== null && $file !== '') {
379
            $data[$field] = self::encodeFile($file);
380
        }
381
    }
382
383
    /**
384
     * Returns basic information about the bot in form of a User object
385
     *
386
     * @link https://core.telegram.org/bots/api#getme
387
     *
388
     * @return \Longman\TelegramBot\Entities\ServerResponse
389
     * @throws \Longman\TelegramBot\Exception\TelegramException
390
     */
391
    public static function getMe()
392
    {
393
        // Added fake parameter, because of some cURL version failed POST request without parameters
394
        // see https://github.com/akalongman/php-telegram-bot/pull/228
395
        return self::send('getMe', ['whoami']);
396
    }
397
398
    /**
399
     * Use this method to send text messages. On success, the sent Message is returned
400
     *
401
     * @link https://core.telegram.org/bots/api#sendmessage
402
     *
403
     * @param array $data
404
     *
405
     * @return \Longman\TelegramBot\Entities\ServerResponse
406
     * @throws \Longman\TelegramBot\Exception\TelegramException
407
     */
408 6
    public static function sendMessage(array $data)
409
    {
410 6
        $text = $data['text'];
411
412
        do {
413
            //Chop off and send the first message
414 6
            $data['text'] = mb_substr($text, 0, 4096);
415 6
            $response     = self::send('sendMessage', $data);
416
417
            //Prepare the next message
418 6
            $text = mb_substr($text, 4096);
419 6
        } while (mb_strlen($text, 'UTF-8') > 0);
420
421 6
        return $response;
422
    }
423
424
    /**
425
     * Use this method to forward messages of any kind. On success, the sent Message is returned
426
     *
427
     * @link https://core.telegram.org/bots/api#forwardmessage
428
     *
429
     * @param array $data
430
     *
431
     * @return \Longman\TelegramBot\Entities\ServerResponse
432
     * @throws \Longman\TelegramBot\Exception\TelegramException
433
     */
434
    public static function forwardMessage(array $data)
435
    {
436
        return self::send('forwardMessage', $data);
437
    }
438
439
    /**
440
     * Use this method to send photos. On success, the sent Message is returned
441
     *
442
     * @link https://core.telegram.org/bots/api#sendphoto
443
     *
444
     * @param array  $data
445
     * @param string $file
446
     *
447
     * @return \Longman\TelegramBot\Entities\ServerResponse
448
     * @throws \Longman\TelegramBot\Exception\TelegramException
449
     */
450
    public static function sendPhoto(array $data, $file = null)
451
    {
452
        self::assignEncodedFile($data, 'photo', $file);
453
454
        return self::send('sendPhoto', $data);
455
    }
456
457
    /**
458
     * Use this method to send audio files
459
     *
460
     * Your audio must be in the .mp3 format. On success, the sent Message is returned.
461
     * Bots can currently send audio files of up to 50 MB in size, this limit may be changed in the future.
462
     * For sending voice messages, use the sendVoice method instead.
463
     *
464
     * @link https://core.telegram.org/bots/api#sendaudio
465
     *
466
     * @param array  $data
467
     * @param string $file
468
     *
469
     * @return \Longman\TelegramBot\Entities\ServerResponse
470
     * @throws \Longman\TelegramBot\Exception\TelegramException
471
     */
472
    public static function sendAudio(array $data, $file = null)
473
    {
474
        self::assignEncodedFile($data, 'audio', $file);
475
476
        return self::send('sendAudio', $data);
477
    }
478
479
    /**
480
     * Use this method to send general files. On success, the sent Message is returned.
481
     *
482
     * Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future.
483
     *
484
     * @link https://core.telegram.org/bots/api#senddocument
485
     *
486
     * @param array  $data
487
     * @param string $file
488
     *
489
     * @return \Longman\TelegramBot\Entities\ServerResponse
490
     * @throws \Longman\TelegramBot\Exception\TelegramException
491
     */
492
    public static function sendDocument(array $data, $file = null)
493
    {
494
        self::assignEncodedFile($data, 'document', $file);
495
496
        return self::send('sendDocument', $data);
497
    }
498
499
    /**
500
     * Use this method to send .webp stickers. On success, the sent Message is returned.
501
     *
502
     * @link https://core.telegram.org/bots/api#sendsticker
503
     *
504
     * @param array  $data
505
     * @param string $file
506
     *
507
     * @return \Longman\TelegramBot\Entities\ServerResponse
508
     * @throws \Longman\TelegramBot\Exception\TelegramException
509
     */
510
    public static function sendSticker(array $data, $file = null)
511
    {
512
        self::assignEncodedFile($data, 'sticker', $file);
513
514
        return self::send('sendSticker', $data);
515
    }
516
517
    /**
518
     * Use this method to send video files. On success, the sent Message is returned.
519
     *
520
     * Telegram clients support mp4 videos (other formats may be sent as Document).
521
     * Bots can currently send video files of up to 50 MB in size, this limit may be changed in the future.
522
     *
523
     * @link https://core.telegram.org/bots/api#sendvideo
524
     *
525
     * @param array  $data
526
     * @param string $file
527
     *
528
     * @return \Longman\TelegramBot\Entities\ServerResponse
529
     * @throws \Longman\TelegramBot\Exception\TelegramException
530
     */
531
    public static function sendVideo(array $data, $file = null)
532
    {
533
        self::assignEncodedFile($data, 'video', $file);
534
535
        return self::send('sendVideo', $data);
536
    }
537
538
    /**
539
     * Use this method to send audio files. On success, the sent Message is returned.
540
     *
541
     * Telegram clients will display the file as a playable voice message.
542
     * For this to work, your audio must be in an .ogg file encoded with OPUS (other formats may be sent as Audio or Document).
543
     * Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future.
544
     *
545
     * @link https://core.telegram.org/bots/api#sendvoice
546
     *
547
     * @param array  $data
548
     * @param string $file
549
     *
550
     * @return \Longman\TelegramBot\Entities\ServerResponse
551
     * @throws \Longman\TelegramBot\Exception\TelegramException
552
     */
553
    public static function sendVoice(array $data, $file = null)
554
    {
555
        self::assignEncodedFile($data, 'voice', $file);
556
557
        return self::send('sendVoice', $data);
558
    }
559
560
    /**
561
     * Use this method to send point on the map. On success, the sent Message is returned.
562
     *
563
     * @link https://core.telegram.org/bots/api#sendlocation
564
     *
565
     * @param array $data
566
     *
567
     * @return \Longman\TelegramBot\Entities\ServerResponse
568
     * @throws \Longman\TelegramBot\Exception\TelegramException
569
     */
570
    public static function sendLocation(array $data)
571
    {
572
        return self::send('sendLocation', $data);
573
    }
574
575
    /**
576
     * Use this method to send information about a venue. On success, the sent Message is returned.
577
     *
578
     * @link https://core.telegram.org/bots/api#sendvenue
579
     *
580
     * @param array $data
581
     *
582
     * @return \Longman\TelegramBot\Entities\ServerResponse
583
     * @throws \Longman\TelegramBot\Exception\TelegramException
584
     */
585
    public static function sendVenue(array $data)
586
    {
587
        return self::send('sendVenue', $data);
588
    }
589
590
    /**
591
     * Use this method to send phone contacts. On success, the sent Message is returned.
592
     *
593
     * @link https://core.telegram.org/bots/api#sendcontact
594
     *
595
     * @param array $data
596
     *
597
     * @return \Longman\TelegramBot\Entities\ServerResponse
598
     * @throws \Longman\TelegramBot\Exception\TelegramException
599
     */
600
    public static function sendContact(array $data)
601
    {
602
        return self::send('sendContact', $data);
603
    }
604
605
    /**
606
     * Use this method when you need to tell the user that something is happening on the bot's side.
607
     *
608
     * The status is set for 5 seconds or less.
609
     * (when a message arrives from your bot, Telegram clients clear its typing status)
610
     *
611
     * @link https://core.telegram.org/bots/api#sendchataction
612
     *
613
     * @param array $data
614
     *
615
     * @return \Longman\TelegramBot\Entities\ServerResponse
616
     * @throws \Longman\TelegramBot\Exception\TelegramException
617
     */
618
    public static function sendChatAction(array $data)
619
    {
620
        return self::send('sendChatAction', $data);
621
    }
622
623
    /**
624
     * Use this method to get a list of profile pictures for a user. Returns a UserProfilePhotos object.
625
     *
626
     * @param array $data
627
     *
628
     * @return \Longman\TelegramBot\Entities\ServerResponse
629
     * @throws \Longman\TelegramBot\Exception\TelegramException
630
     */
631
    public static function getUserProfilePhotos(array $data)
632
    {
633
        return self::send('getUserProfilePhotos', $data);
634
    }
635
636
    /**
637
     * Use this method to get basic info about a file and prepare it for downloading. On success, a File object is returned.
638
     *
639
     * For the moment, bots can download files of up to 20MB in size.
640
     * The file can then be downloaded via the link https://api.telegram.org/file/bot<token>/<file_path>,
641
     * where <file_path> is taken from the response.
642
     * It is guaranteed that the link will be valid for at least 1 hour.
643
     * When the link expires, a new one can be requested by calling getFile again.
644
     *
645
     * @link https://core.telegram.org/bots/api#getfile
646
     *
647
     * @param array $data
648
     *
649
     * @return \Longman\TelegramBot\Entities\ServerResponse
650
     * @throws \Longman\TelegramBot\Exception\TelegramException
651
     */
652
    public static function getFile(array $data)
653
    {
654
        return self::send('getFile', $data);
655
    }
656
657
    /**
658
     * Use this method to kick a user from a group or a supergroup. Returns True on success.
659
     *
660
     * In the case of supergroups, the user will not be able to return to the group on their own using invite links, etc., unless unbanned first.
661
     * The bot must be an administrator in the group for this to work.
662
     *
663
     * @link https://core.telegram.org/bots/api#kickchatmember
664
     *
665
     * @param array $data
666
     *
667
     * @return \Longman\TelegramBot\Entities\ServerResponse
668
     * @throws \Longman\TelegramBot\Exception\TelegramException
669
     */
670
    public static function kickChatMember(array $data)
671
    {
672
        return self::send('kickChatMember', $data);
673
    }
674
675
    /**
676
     * Use this method for your bot to leave a group, supergroup or channel. Returns True on success.
677
     *
678
     * @link https://core.telegram.org/bots/api#leavechat
679
     *
680
     * @param array $data
681
     *
682
     * @return \Longman\TelegramBot\Entities\ServerResponse
683
     * @throws \Longman\TelegramBot\Exception\TelegramException
684
     */
685
    public static function leaveChat(array $data)
686
    {
687
        return self::send('leaveChat', $data);
688
    }
689
690
    /**
691
     * Use this method to unban a previously kicked user in a supergroup. Returns True on success.
692
     *
693
     * The user will not return to the group automatically, but will be able to join via link, etc.
694
     * The bot must be an administrator in the group for this to work.
695
     *
696
     * @link https://core.telegram.org/bots/api#unbanchatmember
697
     *
698
     * @param array $data
699
     *
700
     * @return \Longman\TelegramBot\Entities\ServerResponse
701
     * @throws \Longman\TelegramBot\Exception\TelegramException
702
     */
703
    public static function unbanChatMember(array $data)
704
    {
705
        return self::send('unbanChatMember', $data);
706
    }
707
708
    /**
709
     * Use this method to get up to date information about the chat (current name of the user for one-on-one conversations, current username of a user, group or channel, etc.). Returns a Chat object on success.
710
     *
711
     * @todo add get response in ServerResponse.php?
712
     *
713
     * @link https://core.telegram.org/bots/api#getchat
714
     *
715
     * @param array $data
716
     *
717
     * @return \Longman\TelegramBot\Entities\ServerResponse
718
     * @throws \Longman\TelegramBot\Exception\TelegramException
719
     */
720
    public static function getChat(array $data)
721
    {
722
        return self::send('getChat', $data);
723
    }
724
725
    /**
726
     * Use this method to get a list of administrators in a chat.
727
     *
728
     * On success, returns an Array of ChatMember objects that contains information about all chat administrators except other bots.
729
     * If the chat is a group or a supergroup and no administrators were appointed, only the creator will be returned.
730
     *
731
     * @todo add get response in ServerResponse.php?
732
     *
733
     * @link https://core.telegram.org/bots/api#getchatadministrators
734
     *
735
     * @param array $data
736
     *
737
     * @return \Longman\TelegramBot\Entities\ServerResponse
738
     * @throws \Longman\TelegramBot\Exception\TelegramException
739
     */
740
    public static function getChatAdministrators(array $data)
741
    {
742
        return self::send('getChatAdministrators', $data);
743
    }
744
745
    /**
746
     * Use this method to get the number of members in a chat. Returns Int on success.
747
     *
748
     * @todo add get response in ServerResponse.php?
749
     *
750
     * @link https://core.telegram.org/bots/api#getchatmemberscount
751
     *
752
     * @param array $data
753
     *
754
     * @return \Longman\TelegramBot\Entities\ServerResponse
755
     * @throws \Longman\TelegramBot\Exception\TelegramException
756
     */
757
    public static function getChatMembersCount(array $data)
758
    {
759
        return self::send('getChatMembersCount', $data);
760
    }
761
762
    /**
763
     * Use this method to get information about a member of a chat. Returns a ChatMember object on success.
764
     *
765
     * @todo add get response in ServerResponse.php?
766
     *
767
     * @link https://core.telegram.org/bots/api#getchatmember
768
     *
769
     * @param array $data
770
     *
771
     * @return \Longman\TelegramBot\Entities\ServerResponse
772
     * @throws \Longman\TelegramBot\Exception\TelegramException
773
     */
774
    public static function getChatMember(array $data)
775
    {
776
        return self::send('getChatMember', $data);
777
    }
778
779
    /**
780
     * Use this method to send answers to callback queries sent from inline keyboards. On success, True is returned.
781
     *
782
     * The answer will be displayed to the user as a notification at the top of the chat screen or as an alert.
783
     *
784
     * @link https://core.telegram.org/bots/api#answercallbackquery
785
     *
786
     * @param array $data
787
     *
788
     * @return \Longman\TelegramBot\Entities\ServerResponse
789
     * @throws \Longman\TelegramBot\Exception\TelegramException
790
     */
791
    public static function answerCallbackQuery(array $data)
792
    {
793
        return self::send('answerCallbackQuery', $data);
794
    }
795
796
    /**
797
     * Get updates
798
     *
799
     * @link https://core.telegram.org/bots/api#getupdates
800
     *
801
     * @param array $data
802
     *
803
     * @return \Longman\TelegramBot\Entities\ServerResponse
804
     * @throws \Longman\TelegramBot\Exception\TelegramException
805
     */
806
    public static function getUpdates(array $data)
807
    {
808
        return self::send('getUpdates', $data);
809
    }
810
811
    /**
812
     * Set webhook
813
     *
814
     * @link https://core.telegram.org/bots/api#setwebhook
815
     *
816
     * @param string $url
817
     * @param array  $data Optional parameters.
818
     *
819
     * @return \Longman\TelegramBot\Entities\ServerResponse
820
     * @throws \Longman\TelegramBot\Exception\TelegramException
821
     */
822
    public static function setWebhook($url = '', array $data = [])
823
    {
824
        $data        = array_intersect_key($data, array_flip([
825
            'certificate',
826
            'max_connections',
827
            'allowed_updates',
828
        ]));
829
        $data['url'] = $url;
830
831
        if (isset($data['certificate'])) {
832
            self::assignEncodedFile($data, 'certificate', $data['certificate']);
833
        }
834
835
        return self::send('setWebhook', $data);
836
    }
837
838
    /**
839
     * Delete webhook
840
     *
841
     * @link https://core.telegram.org/bots/api#deletewebhook
842
     *
843
     * @return \Longman\TelegramBot\Entities\ServerResponse
844
     * @throws \Longman\TelegramBot\Exception\TelegramException
845
     */
846
    public static function deleteWebhook()
847
    {
848
        // Must send some arbitrary data for this to work for now...
849
        return self::send('deleteWebhook', ['delete']);
850
    }
851
852
    /**
853
     * Use this method to edit text and game messages sent by the bot or via the bot (for inline bots).
854
     *
855
     * On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned.
856
     *
857
     * @link https://core.telegram.org/bots/api#editmessagetext
858
     *
859
     * @param array $data
860
     *
861
     * @return \Longman\TelegramBot\Entities\ServerResponse
862
     * @throws \Longman\TelegramBot\Exception\TelegramException
863
     */
864
    public static function editMessageText(array $data)
865
    {
866
        return self::send('editMessageText', $data);
867
    }
868
869
    /**
870
     * Use this method to edit captions of messages sent by the bot or via the bot (for inline bots).
871
     *
872
     * On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned.
873
     *
874
     * @link https://core.telegram.org/bots/api#editmessagecaption
875
     *
876
     * @param array $data
877
     *
878
     * @return \Longman\TelegramBot\Entities\ServerResponse
879
     * @throws \Longman\TelegramBot\Exception\TelegramException
880
     */
881
    public static function editMessageCaption(array $data)
882
    {
883
        return self::send('editMessageCaption', $data);
884
    }
885
886
    /**
887
     * Use this method to edit only the reply markup of messages sent by the bot or via the bot (for inline bots).
888
     *
889
     * On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned.
890
     *
891
     * @link https://core.telegram.org/bots/api#editmessagereplymarkup
892
     *
893
     * @param array $data
894
     *
895
     * @return \Longman\TelegramBot\Entities\ServerResponse
896
     * @throws \Longman\TelegramBot\Exception\TelegramException
897
     */
898
    public static function editMessageReplyMarkup(array $data)
899
    {
900
        return self::send('editMessageReplyMarkup', $data);
901
    }
902
903
    /**
904
     * Use this method to send answers to an inline query. On success, True is returned.
905
     *
906
     * No more than 50 results per query are allowed.
907
     *
908
     * @link https://core.telegram.org/bots/api#answerinlinequery
909
     *
910
     * @param array $data
911
     *
912
     * @return \Longman\TelegramBot\Entities\ServerResponse
913
     * @throws \Longman\TelegramBot\Exception\TelegramException
914
     */
915
    public static function answerInlineQuery(array $data)
916
    {
917
        return self::send('answerInlineQuery', $data);
918
    }
919
920
    /**
921
     * Return an empty Server Response
922
     *
923
     * No request to telegram are sent, this function is used in commands that
924
     * don't need to fire a message after execution
925
     *
926
     * @return \Longman\TelegramBot\Entities\ServerResponse
927
     * @throws \Longman\TelegramBot\Exception\TelegramException
928
     */
929
    public static function emptyResponse()
930
    {
931
        return new ServerResponse(['ok' => true, 'result' => true], null);
932
    }
933
934
    /**
935
     * Send message to all active chats
936
     *
937
     * @param string  $callback_function
938
     * @param array   $data
939
     * @param boolean $send_groups
940
     * @param boolean $send_super_groups
941
     * @param boolean $send_users
942
     * @param string  $date_from
943
     * @param string  $date_to
944
     *
945
     * @return array
946
     * @throws \Longman\TelegramBot\Exception\TelegramException
947
     */
948
    public static function sendToActiveChats(
949
        $callback_function,
950
        array $data,
951
        $send_groups = true,
952
        $send_super_groups = true,
953
        $send_users = true,
954
        $date_from = null,
955
        $date_to = null
956
    ) {
957
        $callback_path = __NAMESPACE__ . '\Request';
958
        if (!method_exists($callback_path, $callback_function)) {
959
            throw new TelegramException('Method "' . $callback_function . '" not found in class Request.');
960
        }
961
962
        $chats = DB::selectChats($send_groups, $send_super_groups, $send_users, $date_from, $date_to);
963
964
        $results = [];
965
        if (is_array($chats)) {
966
            foreach ($chats as $row) {
967
                $data['chat_id'] = $row['chat_id'];
968
                $results[]       = call_user_func_array($callback_path . '::' . $callback_function, [$data]);
969
            }
970
        }
971
972
        return $results;
973
    }
974
975
    /**
976
     * Use this method to get current webhook status.
977
     *
978
     * @link https://core.telegram.org/bots/api#getwebhookinfo
979
     *
980
     * @return Entities\ServerResponse
981
     * @throws \Longman\TelegramBot\Exception\TelegramException
982
     */
983
    public static function getWebhookInfo()
984
    {
985
        // Must send some arbitrary data for this to work for now...
986
        return self::send('getWebhookInfo', ['info']);
987
    }
988
989
    /**
990
     * Enable request limiter
991
     *
992
     * @param boolean $value
993
     */
994
    public static function setLimiter($value = true)
995
    {
996
        if (DB::isDbConnected()) {
997
            self::$limiter_enabled = $value;
998
        }
999
    }
1000
1001
    /**
1002
     * This functions delays API requests to prevent reaching Telegram API limits
1003
     *  Can be disabled while in execution by 'Request::setLimiter(false)'
1004
     *
1005
     * @link https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this
1006
     *
1007
     * @param string $action
1008
     * @param array  $data
1009
     *
1010
     * @throws \Longman\TelegramBot\Exception\TelegramException
1011
     */
1012
    private static function limitTelegramRequests($action, array $data = [])
1013
    {
1014
        if (self::$limiter_enabled) {
1015
            $limited_methods = [
1016
                'sendMessage',
1017
                'forwardMessage',
1018
                'sendPhoto',
1019
                'sendAudio',
1020
                'sendDocument',
1021
                'sendSticker',
1022
                'sendVideo',
1023
                'sendVoice',
1024
                'sendLocation',
1025
                'sendVenue',
1026
                'sendContact',
1027
                'editMessageText',
1028
                'editMessageCaption',
1029
                'editMessageReplyMarkup',
1030
            ];
1031
1032
            $chat_id = isset($data['chat_id']) ? $data['chat_id'] : null;
1033
            $inline_message_id = isset($data['inline_message_id']) ? $data['inline_message_id'] : null;
1034
1035
            if (($chat_id || $inline_message_id) && in_array($action, $limited_methods)) {
1036
                $timeout = 60;
1037
1038
                while (true) {
1039
                    if ($timeout <= 0) {
1040
                        throw new TelegramException('Timed out while waiting for a request spot!');
1041
                    }
1042
1043
                    $requests = DB::getTelegramRequestCount($chat_id, $inline_message_id);
1044
1045
                    if ($requests['LIMIT_PER_SEC'] == 0  // No more than one message per second inside a particular chat
1046
                        && ((($chat_id > 0 || $inline_message_id) && $requests['LIMIT_PER_SEC_ALL'] < 30)  // No more than 30 messages per second globally
1047
                        || ($chat_id < 0 && $requests['LIMIT_PER_MINUTE'] < 20))
1048
                    ) {
1049
                        break;
1050
                    }
1051
1052
                    $timeout--;
1053
                    sleep(1);
1054
                }
1055
1056
                DB::insertTelegramRequest($action, $data);
1057
            }
1058
        }
1059
    }
1060
}
1061