Completed
Push — master ( 44d9ca...67ee26 )
by Armando
04:46
created

BotManager::getOutput()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 0
1
<?php declare(strict_types = 1);
2
/**
3
 * This file is part of the TelegramBotManager package.
4
 *
5
 * (c) Armando Lüscher <[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 NPM\TelegramBotManager;
12
13
use Longman\TelegramBot\Entities;
14
use Longman\TelegramBot\Telegram;
15
use Longman\TelegramBot\TelegramLog;
16
17
/**
18
 * Class BotManager.php
19
 *
20
 * Leave all member variables public to allow easy modification.
21
 *
22
 * @package NPM\TelegramBotManager
23
 */
24
class BotManager
25
{
26
    /**
27
     * @var string The output for testing, instead of echoing
28
     */
29
    private $output = '';
30
31
    /**
32
     * @var \Longman\TelegramBot\Telegram
33
     */
34
    private $telegram;
35
36
    /**
37
     * @var \NPM\TelegramBotManager\Params Object that manages the parameters.
38
     */
39
    private $params;
40
41
    /**
42
     * @var \NPM\TelegramBotManager\Action Object that contains the current action.
43
     */
44
    private $action;
45
46
    /**
47
     * BotManager constructor.
48
     *
49
     * @param array $params
50
     *
51
     * @throws \InvalidArgumentException
52
     */
53
    public function __construct(array $params)
54
    {
55
        $this->params = new Params($params);
56
        $this->action = new Action($this->params->getScriptParam('a'));
57
    }
58
59
    /**
60
     * Return the Telegram object.
61
     *
62
     * @return \Longman\TelegramBot\Telegram
63
     */
64
    public function getTelegram(): Telegram
65
    {
66
        return $this->telegram;
67
    }
68
69
    /**
70
     * Get the Params object.
71
     *
72
     * @return \NPM\TelegramBotManager\Params
73
     */
74
    public function getParams(): Params
75
    {
76
        return $this->params;
77
    }
78
79
    /**
80
     * Get the Action object.
81
     *
82
     * @return \NPM\TelegramBotManager\Action
83
     */
84
    public function getAction(): Action
85
    {
86
        return $this->action;
87
    }
88
89
    /**
90
     * Run this thing in all its glory!
91
     *
92
     * @return \NPM\TelegramBotManager\BotManager
93
     * @throws \InvalidArgumentException
94
     */
95
    public function run(): self
96
    {
97
        // Initialise logging.
98
        $this->initLogging();
99
100
        // Make sure this is a valid call.
101
        $this->validateSecret();
102
103
        // Set up a new Telegram instance.
104
        $this->telegram = new Telegram(
105
            $this->params->getBotParam('api_key'),
106
            $this->params->getBotParam('botname')
107
        );
108
109
        if ($this->action->isAction(['set', 'unset', 'reset'])) {
110
            $this->validateAndSetWebhook();
111
        } elseif ($this->action->isAction('handle')) {
112
            // Set any extras.
113
            $this->setBotExtras();
114
            $this->handleRequest();
115
        }
116
117
        return $this;
118
    }
119
120
    /**
121
     * Initialise all loggers.
122
     *
123
     * @return \NPM\TelegramBotManager\BotManager
124
     */
125
    public function initLogging(): self
126
    {
127
        $logging = $this->params->getBotParam('logging');
128
        if (is_array($logging)) {
129
            /** @var array $logging */
130
            foreach ($logging as $logger => $logfile) {
131
                ('debug' === $logger) && TelegramLog::initDebugLog($logfile);
132
                ('error' === $logger) && TelegramLog::initErrorLog($logfile);
133
                ('update' === $logger) && TelegramLog::initUpdateLog($logfile);
134
            }
135
        }
136
137
        return $this;
138
    }
139
140
    /**
141
     * Make sure the passed secret is valid.
142
     *
143
     * @param bool $force Force validation, even on CLI.
144
     *
145
     * @return \NPM\TelegramBotManager\BotManager
146
     * @throws \InvalidArgumentException
147
     */
148
    public function validateSecret(bool $force = false): self
149
    {
150
        // If we're running from CLI, secret isn't necessary.
151
        if ($force || 'cli' !== PHP_SAPI) {
152
            $secret    = $this->params->getBotParam('secret');
153
            $secretGet = $this->params->getScriptParam('s');
154
            if ($secretGet !== $secret) {
155
                throw new \InvalidArgumentException('Invalid access');
156
            }
157
        }
158
159
        return $this;
160
    }
161
162
    /**
163
     * Make sure the webhook is valid and perform the requested webhook operation.
164
     *
165
     * @return \NPM\TelegramBotManager\BotManager
166
     * @throws \InvalidArgumentException
167
     */
168
    public function validateAndSetWebhook(): self
169
    {
170
        $webhook = $this->params->getBotParam('webhook');
171
        $selfcrt = $this->params->getBotParam('selfcrt');
172
        if (empty($webhook) && $this->action->isAction(['set', 'reset'])) {
173
            throw new \InvalidArgumentException('Invalid webhook');
174
        }
175
176
        if ($this->action->isAction(['unset', 'reset'])) {
177
            $this->handleOutput($this->telegram->unsetWebHook()->getDescription() . PHP_EOL);
178
            // When resetting the webhook, sleep for a bit to prevent too many requests.
179
            $this->action->isAction('reset') && sleep(1);
180
        }
181
        if ($this->action->isAction(['set', 'reset'])) {
182
            $this->handleOutput(
183
                $this->telegram->setWebHook(
184
                    $webhook . '?a=handle&s=' . $this->params->getBotParam('secret'),
185
                    $selfcrt
186
                )->getDescription() . PHP_EOL
187
            );
188
        }
189
190
        return $this;
191
    }
192
193
    /**
194
     * Save the test output and echo it if we're not in a test.
195
     *
196
     * @param string $output
197
     *
198
     * @return \NPM\TelegramBotManager\BotManager
199
     */
200
    private function handleOutput($output): self
201
    {
202
        $this->output .= $output;
203
204
        if (!(defined('PHPUNIT_TEST') && PHPUNIT_TEST === true)) {
205
            echo $output;
206
        }
207
208
        return $this;
209
    }
210
211
    /**
212
     * Set any extra bot features that have been assigned on construction.
213
     *
214
     * @return \NPM\TelegramBotManager\BotManager
215
     */
216
    public function setBotExtras(): self
217
    {
218
        $simple_extras = [
219
            'admins'         => 'enableAdmins',
220
            'mysql'          => 'enableMySql',
221
            'botan_token'    => 'enableBotan',
222
            'commands_paths' => 'addCommandsPaths',
223
            'custom_input'   => 'setCustomInput',
224
            'download_path'  => 'setDownloadPath',
225
            'upload_path'    => 'setUploadPath',
226
        ];
227
        // For simple extras, just pass the single param value to the Telegram method.
228
        foreach ($simple_extras as $param_key => $method) {
229
            $param = $this->params->getBotParam($param_key);
230
            if (null !== $param) {
231
                $this->telegram->$method($param);
232
            }
233
        }
234
235
        $command_configs = $this->params->getBotParam('command_configs');
236
        if (is_array($command_configs)) {
237
            /** @var array $command_configs */
238
            foreach ($command_configs as $command => $config) {
239
                $this->telegram->setCommandConfig($command, $config);
240
            }
241
        }
242
243
        return $this;
244
    }
245
246
    /**
247
     * Handle the request, which calls either the Webhook or getUpdates method respectively.
248
     *
249
     * @return \NPM\TelegramBotManager\BotManager
250
     */
251
    public function handleRequest(): self
252
    {
253
        if (empty($this->params->getBotParam('webhook'))) {
254
            if ($loop_time = $this->getLoopTime()) {
255
                $this->handleGetUpdatesLoop($loop_time, $this->getLoopInterval());
256
            } else {
257
                $this->handleGetUpdates();
258
            }
259
        } else {
260
            $this->handleWebhook();
261
        }
262
263
        return $this;
264
    }
265
266
    /**
267
     * Get the number of seconds the script should loop.
268
     *
269
     * @return int
270
     */
271 View Code Duplication
    public function getLoopTime(): int
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...
272
    {
273
        $loop_time = $this->params->getScriptParam('l');
274
275
        if (null === $loop_time) {
276
            return 0;
277
        }
278
279
        if (is_string($loop_time) && '' === trim($loop_time)) {
280
            return 604800; // Default to 7 days.
281
        }
282
283
        return max(0, (int)$loop_time);
284
    }
285
286
    /**
287
     * Get the number of seconds the script should wait after each getUpdates request.
288
     *
289
     * @return int
290
     */
291 View Code Duplication
    public function getLoopInterval(): int
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...
292
    {
293
        $interval_time = $this->params->getScriptParam('i');
294
295
        if (null === $interval_time || (is_string($interval_time) && '' === trim($interval_time))) {
296
            return 2;
297
        }
298
299
        // Minimum interval is 1 second.
300
        return max(1, (int)$interval_time);
301
    }
302
303
    /**
304
     * Loop the getUpdates method for the passed amount of seconds.
305
     *
306
     * @param int $loop_time_in_seconds
307
     * @param int $loop_interval_in_seconds
308
     *
309
     * @return \NPM\TelegramBotManager\BotManager
310
     */
311
    public function handleGetUpdatesLoop(int $loop_time_in_seconds, int $loop_interval_in_seconds = 2): self
312
    {
313
        // Remember the time we started this loop.
314
        $now = time();
315
316
        $this->handleOutput('Looping getUpdates until ' . date('Y-m-d H:i:s', $now + $loop_time_in_seconds) . PHP_EOL);
317
318
        while ($now > time() - $loop_time_in_seconds) {
319
            $this->handleGetUpdates();
320
321
            // Chill a bit.
322
            sleep($loop_interval_in_seconds);
323
        }
324
325
        return $this;
326
    }
327
328
    /**
329
     * Handle the updates using the getUpdates method.
330
     *
331
     * @return \NPM\TelegramBotManager\BotManager
332
     */
333
    public function handleGetUpdates(): self
334
    {
335
        $output = date('Y-m-d H:i:s', time()) . ' - ';
336
337
        $response = $this->telegram->handleGetUpdates();
338
        if ($response->isOk()) {
339
            $results = array_filter((array)$response->getResult());
340
341
            $output .= sprintf('Updates processed: %d' . PHP_EOL, count($results));
342
343
            /** @var Entities\Update $result */
344
            foreach ($results as $result) {
345
                $chat_id = 0;
346
                $text    = 'Nothing';
347
348
                $update_content = $result->getUpdateContent();
349
                if ($update_content instanceof Entities\Message) {
350
                    $chat_id = $update_content->getFrom()->getId();
351
                    $text    = $update_content->getText();
352
                } elseif ($update_content instanceof Entities\InlineQuery || $update_content instanceof Entities\ChosenInlineResult) {
353
                    $chat_id = $update_content->getFrom()->getId();
354
                    $text    = $update_content->getQuery();
355
                }
356
357
                $output .= sprintf(
358
                    '%d: %s' . PHP_EOL,
359
                    $chat_id,
360
                    preg_replace('/\s+/', ' ', trim($text))
361
                );
362
            }
363
        } else {
364
            $output .= sprintf('Failed to fetch updates: %s' . PHP_EOL, $response->printError());
365
        }
366
367
        $this->handleOutput($output);
368
369
        return $this;
370
    }
371
372
    /**
373
     * Handle the updates using the Webhook method.
374
     *
375
     * @return \NPM\TelegramBotManager\BotManager
376
     */
377
    public function handleWebhook(): self
378
    {
379
        $this->telegram->handle();
380
381
        return $this;
382
    }
383
384
    /**
385
     * Return the current test output and clear it.
386
     *
387
     * @return string
388
     */
389
    public function getOutput(): string
390
    {
391
        $output       = $this->output;
392
        $this->output = '';
393
394
        return $output;
395
    }
396
}
397