Issues (36)

src/Commands/Command.php (1 issue)

Severity
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\Commands;
13
14
use Longman\TelegramBot\DB;
15
use Longman\TelegramBot\Entities\CallbackQuery;
16
use Longman\TelegramBot\Entities\ChatJoinRequest;
17
use Longman\TelegramBot\Entities\ChatMemberUpdated;
18
use Longman\TelegramBot\Entities\ChosenInlineResult;
19
use Longman\TelegramBot\Entities\InlineQuery;
20
use Longman\TelegramBot\Entities\Message;
21
use Longman\TelegramBot\Entities\Payments\PreCheckoutQuery;
22
use Longman\TelegramBot\Entities\Payments\ShippingQuery;
23
use Longman\TelegramBot\Entities\Poll;
24
use Longman\TelegramBot\Entities\PollAnswer;
25
use Longman\TelegramBot\Entities\ServerResponse;
26
use Longman\TelegramBot\Entities\Update;
27
use Longman\TelegramBot\Exception\TelegramException;
28
use Longman\TelegramBot\Request;
29
use Longman\TelegramBot\Telegram;
30
31
/**
32
 * Class Command
33
 *
34
 * Base class for commands. It includes some helper methods that can fetch data directly from the Update object.
35
 *
36
 * @method Message             getMessage()            Optional. New incoming message of any kind — text, photo, sticker, etc.
37
 * @method Message             getEditedMessage()      Optional. New version of a message that is known to the bot and was edited
38
 * @method Message             getChannelPost()        Optional. New post in the channel, can be any kind — text, photo, sticker, etc.
39
 * @method Message             getEditedChannelPost()  Optional. New version of a post in the channel that is known to the bot and was edited
40
 * @method InlineQuery         getInlineQuery()        Optional. New incoming inline query
41
 * @method ChosenInlineResult  getChosenInlineResult() Optional. The result of an inline query that was chosen by a user and sent to their chat partner.
42
 * @method CallbackQuery       getCallbackQuery()      Optional. New incoming callback query
43
 * @method ShippingQuery       getShippingQuery()      Optional. New incoming shipping query. Only for invoices with flexible price
44
 * @method PreCheckoutQuery    getPreCheckoutQuery()   Optional. New incoming pre-checkout query. Contains full information about checkout
45
 * @method Poll                getPoll()               Optional. New poll state. Bots receive only updates about polls, which are sent or stopped by the bot
46
 * @method PollAnswer          getPollAnswer()         Optional. A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself.
47
 * @method ChatMemberUpdated   getMyChatMember()       Optional. The bot's chat member status was updated in a chat. For private chats, this update is received only when the bot is blocked or unblocked by the user.
48
 * @method ChatMemberUpdated   getChatMember()         Optional. A chat member's status was updated in a chat. The bot must be an administrator in the chat and must explicitly specify “chat_member” in the list of allowed_updates to receive these updates.
49
 * @method ChatJoinRequest     getChatJoinRequest()    Optional. A request to join the chat has been sent. The bot must have the can_invite_users administrator right in the chat to receive these updates.
50
 */
51
abstract class Command
52
{
53
    /**
54
     * Auth level for user commands
55
     */
56
    public const AUTH_USER = 'User';
57
58
    /**
59
     * Auth level for system commands
60
     */
61
    public const AUTH_SYSTEM = 'System';
62
63
    /**
64
     * Auth level for admin commands
65
     */
66
    public const AUTH_ADMIN = 'Admin';
67
68
    /**
69
     * Telegram object
70
     *
71
     * @var Telegram
72
     */
73
    protected $telegram;
74
75
    /**
76
     * Update object
77
     *
78
     * @var Update
79
     */
80
    protected $update;
81
82
    /**
83
     * Name
84
     *
85
     * @var string
86
     */
87
    protected $name = '';
88
89
    /**
90
     * Description
91
     *
92
     * @var string
93
     */
94
    protected $description = 'Command description';
95
96
    /**
97
     * Usage
98
     *
99
     * @var string
100
     */
101
    protected $usage = 'Command usage';
102
103
    /**
104
     * Show in Help
105
     *
106
     * @var bool
107
     */
108
    protected $show_in_help = true;
109
110
    /**
111
     * Version
112
     *
113
     * @var string
114
     */
115
    protected $version = '1.0.0';
116
117
    /**
118
     * If this command is enabled
119
     *
120
     * @var bool
121
     */
122
    protected $enabled = true;
123
124
    /**
125
     * If this command needs mysql
126
     *
127
     * @var bool
128
     */
129
    protected $need_mysql = false;
130
131
    /*
132
    * Make sure this command only executes on a private chat.
133
    *
134
    * @var bool
135
    */
136
    protected $private_only = false;
137
138
    /**
139
     * Command config
140
     *
141
     * @var array
142
     */
143
    protected $config = [];
144
145
    /**
146
     * Constructor
147
     *
148
     * @param Telegram    $telegram
149
     * @param Update|null $update
150
     */
151 16
    public function __construct(Telegram $telegram, ?Update $update = null)
152
    {
153 16
        $this->telegram = $telegram;
154 16
        if ($update !== null) {
155
            $this->setUpdate($update);
156
        }
157 16
        $this->config = $telegram->getCommandConfig($this->name);
158
    }
159
160
    /**
161
     * Set update object
162
     *
163
     * @param Update $update
164
     *
165
     * @return Command
166
     */
167 1
    public function setUpdate(Update $update): Command
168
    {
169 1
        $this->update = $update;
170
171 1
        return $this;
172
    }
173
174
    /**
175
     * Pre-execute command
176
     *
177
     * @return ServerResponse
178
     * @throws TelegramException
179
     */
180
    public function preExecute(): ServerResponse
181
    {
182
        if ($this->need_mysql && !($this->telegram->isDbEnabled() && DB::isDbConnected())) {
183
            return $this->executeNoDb();
184
        }
185
186
        if ($this->isPrivateOnly() && $this->removeNonPrivateMessage()) {
187
            $message = $this->getMessage();
188
189
            if ($user = $message->getFrom()) {
190
                return Request::sendMessage([
191
                    'chat_id'    => $user->getId(),
192
                    'parse_mode' => 'Markdown',
193
                    'text'       => sprintf(
194
                        "/%s command is only available in a private chat.\n(`%s`)",
195
                        $this->getName(),
196
                        $message->getText()
197
                    ),
198
                ]);
199
            }
200
201
            return Request::emptyResponse();
202
        }
203
204
        return $this->execute();
205
    }
206
207
    /**
208
     * Execute command
209
     *
210
     * @return ServerResponse
211
     * @throws TelegramException
212
     */
213
    abstract public function execute(): ServerResponse;
214
215
    /**
216
     * Execution if MySQL is required but not available
217
     *
218
     * @return ServerResponse
219
     * @throws TelegramException
220
     */
221
    public function executeNoDb(): ServerResponse
222
    {
223
        return $this->replyToChat('Sorry no database connection, unable to execute "' . $this->name . '" command.');
224
    }
225
226
    /**
227
     * Get update object
228
     *
229
     * @return Update|null
230
     */
231 2
    public function getUpdate(): ?Update
232
    {
233 2
        return $this->update;
234
    }
235
236
    /**
237
     * Relay any non-existing function calls to Update object.
238
     *
239
     * This is purely a helper method to make requests from within execute() method easier.
240
     *
241
     * @param string $name
242
     * @param array  $arguments
243
     *
244
     * @return Command
245
     */
246 1
    public function __call(string $name, array $arguments)
247
    {
248 1
        if ($this->update === null) {
249 1
            return null;
250
        }
251 1
        return call_user_func_array([$this->update, $name], $arguments);
252
    }
253
254
    /**
255
     * Get command config
256
     *
257
     * Look for config $name if found return it, if not return $default.
258
     * If $name is not set return all set config.
259
     *
260
     * @param string|null $name
261
     * @param mixed       $default
262
     *
263
     * @return mixed
264
     */
265 3
    public function getConfig(?string $name = null, $default = null)
266
    {
267 3
        if ($name === null) {
268 3
            return $this->config;
269
        }
270 1
        return $this->config[$name] ?? $default;
271
    }
272
273
    /**
274
     * Get telegram object
275
     *
276
     * @return Telegram
277
     */
278 1
    public function getTelegram(): Telegram
279
    {
280 1
        return $this->telegram;
281
    }
282
283
    /**
284
     * Get usage
285
     *
286
     * @return string
287
     */
288 1
    public function getUsage(): string
289
    {
290 1
        return $this->usage;
291
    }
292
293
    /**
294
     * Get version
295
     *
296
     * @return string
297
     */
298 1
    public function getVersion(): string
299
    {
300 1
        return $this->version;
301
    }
302
303
    /**
304
     * Get description
305
     *
306
     * @return string
307
     */
308 1
    public function getDescription(): string
309
    {
310 1
        return $this->description;
311
    }
312
313
    /**
314
     * Get name
315
     *
316
     * @return string
317
     */
318 3
    public function getName(): string
319
    {
320 3
        return $this->name;
321
    }
322
323
    /**
324
     * Get Show in Help
325
     *
326
     * @return bool
327
     */
328 1
    public function showInHelp(): bool
329
    {
330 1
        return $this->show_in_help;
331
    }
332
333
    /**
334
     * Check if command is enabled
335
     *
336
     * @return bool
337
     */
338 1
    public function isEnabled(): bool
339
    {
340 1
        return $this->enabled;
341
    }
342
343
    /**
344
     * If this command is intended for private chats only.
345
     *
346
     * @return bool
347
     */
348
    public function isPrivateOnly(): bool
349
    {
350
        return $this->private_only;
351
    }
352
353
    /**
354
     * If this is a SystemCommand
355
     *
356
     * @return bool
357
     */
358 2
    public function isSystemCommand(): bool
359
    {
360 2
        return ($this instanceof SystemCommand);
361
    }
362
363
    /**
364
     * If this is an AdminCommand
365
     *
366
     * @return bool
367
     */
368 2
    public function isAdminCommand(): bool
369
    {
370 2
        return ($this instanceof AdminCommand);
371
    }
372
373
    /**
374
     * If this is a UserCommand
375
     *
376
     * @return bool
377
     */
378 2
    public function isUserCommand(): bool
379
    {
380 2
        return ($this instanceof UserCommand);
381
    }
382
383
    /**
384
     * Delete the current message if it has been called in a non-private chat.
385
     *
386
     * @return bool
387
     */
388
    protected function removeNonPrivateMessage(): bool
389
    {
390
        $message = $this->getMessage() ?: $this->getEditedMessage();
391
392
        if ($message) {
0 ignored issues
show
$message is of type Longman\TelegramBot\Entities\Message, thus it always evaluated to true.
Loading history...
393
            $chat = $message->getChat();
394
395
            if (!$chat->isPrivateChat()) {
396
                // Delete the falsely called command message.
397
                Request::deleteMessage([
398
                    'chat_id'    => $chat->getId(),
399
                    'message_id' => $message->getMessageId(),
400
                ]);
401
402
                return true;
403
            }
404
        }
405
406
        return false;
407
    }
408
409
    /**
410
     * Helper to reply to a chat directly.
411
     *
412
     * @param string $text
413
     * @param array  $data
414
     *
415
     * @return ServerResponse
416
     * @throws TelegramException
417
     */
418
    public function replyToChat(string $text, array $data = []): ServerResponse
419
    {
420
        if ($message = $this->getMessage() ?: $this->getEditedMessage() ?: $this->getChannelPost() ?: $this->getEditedChannelPost()) {
421
            return Request::sendMessage(array_merge([
422
                'chat_id' => $message->getChat()->getId(),
423
                'text'    => $text,
424
            ], $data));
425
        }
426
427
        return Request::emptyResponse();
428
    }
429
430
    /**
431
     * Helper to reply to a user directly.
432
     *
433
     * @param string $text
434
     * @param array  $data
435
     *
436
     * @return ServerResponse
437
     * @throws TelegramException
438
     */
439
    public function replyToUser(string $text, array $data = []): ServerResponse
440
    {
441
        if ($message = $this->getMessage() ?: $this->getEditedMessage()) {
442
            return Request::sendMessage(array_merge([
443
                'chat_id' => $message->getFrom()->getId(),
444
                'text'    => $text,
445
            ], $data));
446
        }
447
448
        return Request::emptyResponse();
449
    }
450
}
451