Passed
Pull Request — develop (#1077)
by Marco
02:09
created

SendtochannelCommand::execute()   F

Complexity

Conditions 37
Paths 14616

Size

Total Lines 202
Code Lines 128

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 1406

Importance

Changes 0
Metric Value
eloc 128
dl 0
loc 202
ccs 0
cts 161
cp 0
rs 0
c 0
b 0
f 0
cc 37
nc 14616
nop 0
crap 1406

How to fix   Long Method    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
/**
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 PhpTelegramBot\Core\Commands\AdminCommands;
13
14
use PhpTelegramBot\Core\Commands\AdminCommand;
15
use PhpTelegramBot\Core\Conversation;
16
use PhpTelegramBot\Core\Entities\Chat;
17
use PhpTelegramBot\Core\Entities\Keyboard;
18
use PhpTelegramBot\Core\Entities\Message;
19
use PhpTelegramBot\Core\Entities\ServerResponse;
20
use PhpTelegramBot\Core\Exception\TelegramException;
21
use PhpTelegramBot\Core\Request;
22
23
class SendtochannelCommand extends AdminCommand
24
{
25
    /**
26
     * @var string
27
     */
28
    protected $name = 'sendtochannel';
29
30
    /**
31
     * @var string
32
     */
33
    protected $description = 'Send message to a channel';
34
35
    /**
36
     * @var string
37
     */
38
    protected $usage = '/sendtochannel <message to send>';
39
40
    /**
41
     * @var string
42
     */
43
    protected $version = '0.3.0';
44
45
    /**
46
     * @var bool
47
     */
48
    protected $need_mysql = true;
49
50
    /**
51
     * Conversation Object
52
     *
53
     * @var Conversation
54
     */
55
    protected $conversation;
56
57
    /**
58
     * Command execute method
59
     *
60
     * @return ServerResponse|mixed
61
     * @throws TelegramException
62
     */
63
    public function execute()
64
    {
65
        $message = $this->getMessage();
66
        $chat_id = $message->getChat()->getId();
67
        $user_id = $message->getFrom()->getId();
68
69
        $type = $message->getType();
70
        // 'Cast' the command type to message to protect the machine state
71
        // if the command is recalled when the conversation is already started
72
        in_array($type, ['command', 'text'], true) && $type = 'message';
73
74
        $text           = trim($message->getText(true));
75
        $text_yes_or_no = ($text === 'Yes' || $text === 'No');
76
77
        $data = [
78
            'chat_id' => $chat_id,
79
        ];
80
81
        // Conversation
82
        $this->conversation = new Conversation($user_id, $chat_id, $this->getName());
83
84
        $notes = &$this->conversation->notes;
85
        !is_array($notes) && $notes = [];
86
87
        $channels = (array) $this->getConfig('your_channel');
88
        if (isset($notes['state'])) {
89
            $state = $notes['state'];
90
        } else {
91
            $state                    = (count($channels) === 0) ? -1 : 0;
92
            $notes['last_message_id'] = $message->getMessageId();
93
        }
94
95
        $yes_no_keyboard = new Keyboard(
96
            [
97
                'keyboard'          => [['Yes', 'No']],
98
                'resize_keyboard'   => true,
99
                'one_time_keyboard' => true,
100
                'selective'         => true,
101
            ]
102
        );
103
104
        switch ($state) {
105
            case -1:
106
                // getConfig has not been configured asking for channel to administer
107
                if ($type !== 'message' || $text === '') {
108
                    $notes['state'] = -1;
109
                    $this->conversation->update();
110
111
                    $result = $this->replyToChat(
112
                        'Insert the channel name or ID (_@yourchannel_ or _-12345_)',
113
                        [
114
                            'parse_mode'   => 'markdown',
115
                            'reply_markup' => Keyboard::remove(['selective' => true]),
116
                        ]
117
                    );
118
119
                    break;
120
                }
121
                $notes['channel']         = $text;
122
                $notes['last_message_id'] = $message->getMessageId();
123
                // Jump to state 1
124
                goto insert;
125
126
            // no break
127
            default:
128
            case 0:
129
                // getConfig has been configured choose channel
130
                if ($type !== 'message' || $text === '') {
131
                    $notes['state'] = 0;
132
                    $this->conversation->update();
133
134
                    $keyboard = array_map(function ($channel) {
135
                        return [$channel];
136
                    }, $channels);
137
138
                    $result = $this->replyToChat(
139
                        'Choose a channel from the keyboard' . PHP_EOL .
140
                        '_or_ insert the channel name or ID (_@yourchannel_ or _-12345_)',
141
                        [
142
                            'parse_mode'   => 'markdown',
143
                            'reply_markup' => new Keyboard(
144
                                [
145
                                    'keyboard'          => $keyboard,
146
                                    'resize_keyboard'   => true,
147
                                    'one_time_keyboard' => true,
148
                                    'selective'         => true,
149
                                ]
150
                            ),
151
                        ]
152
                    );
153
                    break;
154
                }
155
                $notes['channel']         = $text;
156
                $notes['last_message_id'] = $message->getMessageId();
157
158
            // no break
159
            case 1:
160
                insert:
161
                if (($type === 'message' && $text === '') || $notes['last_message_id'] === $message->getMessageId()) {
162
                    $notes['state'] = 1;
163
                    $this->conversation->update();
164
165
                    $result = $this->replyToChat(
166
                        'Insert the content you want to share: text, photo, audio...',
167
                        ['reply_markup' => Keyboard::remove(['selective' => true])]
168
                    );
169
                    break;
170
                }
171
                $notes['last_message_id'] = $message->getMessageId();
172
                $notes['message']         = $message->getRawData();
173
                $notes['message_type']    = $type;
174
            // no break
175
            case 2:
176
                if (!$text_yes_or_no || $notes['last_message_id'] === $message->getMessageId()) {
177
                    $notes['state'] = 2;
178
                    $this->conversation->update();
179
180
                    // Grab any existing caption.
181
                    if ($caption = $message->getCaption()) {
182
                        $notes['caption'] = $caption;
183
                        $text             = 'No';
184
                    } elseif (in_array($notes['message_type'], ['video', 'photo'], true)) {
185
                        $text = 'Would you like to insert a caption?';
186
                        if (!$text_yes_or_no && $notes['last_message_id'] !== $message->getMessageId()) {
187
                            $text .= PHP_EOL . 'Type Yes or No';
188
                        }
189
190
                        $result = $this->replyToChat(
191
                            $text,
192
                            ['reply_markup' => $yes_no_keyboard]
193
                        );
194
                        break;
195
                    }
196
                }
197
                $notes['set_caption']     = ($text === 'Yes');
198
                $notes['last_message_id'] = $message->getMessageId();
199
            // no break
200
            case 3:
201
                if ($notes['set_caption'] && ($notes['last_message_id'] === $message->getMessageId() || $type !== 'message')) {
202
                    $notes['state'] = 3;
203
                    $this->conversation->update();
204
205
                    $result = $this->replyToChat(
206
                        'Insert caption:',
207
                        ['reply_markup' => Keyboard::remove(['selective' => true])]
208
                    );
209
                    break;
210
                }
211
                $notes['last_message_id'] = $message->getMessageId();
212
                if (isset($notes['caption'])) {
213
                    // If caption has already been send with the file, no need to ask for it.
214
                    $notes['set_caption'] = true;
215
                } else {
216
                    $notes['caption'] = $text;
217
                }
218
            // no break
219
            case 4:
220
                if (!$text_yes_or_no || $notes['last_message_id'] === $message->getMessageId()) {
221
                    $notes['state'] = 4;
222
                    $this->conversation->update();
223
224
                    $result = $this->replyToChat('Message will look like this:');
225
226
                    if ($notes['message_type'] !== 'command') {
227
                        if ($notes['set_caption']) {
228
                            $data['caption'] = $notes['caption'];
229
                        }
230
                        $this->sendBack(new Message($notes['message'], $this->telegram->getBotUsername()), $data);
231
232
                        $data['reply_markup'] = $yes_no_keyboard;
233
234
                        $data['text'] = 'Would you like to post it?';
235
                        if (!$text_yes_or_no && $notes['last_message_id'] !== $message->getMessageId()) {
236
                            $data['text'] .= PHP_EOL . 'Type Yes or No';
237
                        }
238
                        $result = Request::sendMessage($data);
239
                    }
240
                    break;
241
                }
242
243
                $notes['post_message']    = ($text === 'Yes');
244
                $notes['last_message_id'] = $message->getMessageId();
245
            // no break
246
            case 5:
247
                $data['reply_markup'] = Keyboard::remove(['selective' => true]);
248
249
                if ($notes['post_message']) {
250
                    $data['parse_mode'] = 'markdown';
251
                    $data['text']       = $this->publish(
252
                        new Message($notes['message'], $this->telegram->getBotUsername()),
253
                        $notes['channel'],
254
                        $notes['caption']
255
                    );
256
                } else {
257
                    $data['text'] = 'Aborted by user, message not sent..';
258
                }
259
260
                $this->conversation->stop();
261
                $result = Request::sendMessage($data);
262
        }
263
264
        return $result;
265
    }
266
267
    /**
268
     * SendBack
269
     *
270
     * Received a message, the bot can send a copy of it to another chat/channel.
271
     * You don't have to care about the type of the message, the function detect it and use the proper
272
     * REQUEST:: function to send it.
273
     * $data include all the var that you need to send the message to the proper chat
274
     *
275
     * @todo This method will be moved to a higher level maybe in AdminCommand or Command
276
     * @todo Looking for a more significant name
277
     *
278
     * @param Message $message
279
     * @param array   $data
280
     *
281
     * @return ServerResponse
282
     * @throws TelegramException
283
     */
284
    protected function sendBack(Message $message, array $data)
285
    {
286
        $type = $message->getType();
287
        in_array($type, ['command', 'text'], true) && $type = 'message';
288
289
        if ($type === 'message') {
290
            $data['text'] = $message->getText(true);
291
        } elseif ($type === 'audio') {
292
            $data['audio']     = $message->getAudio()->getFileId();
293
            $data['duration']  = $message->getAudio()->getDuration();
294
            $data['performer'] = $message->getAudio()->getPerformer();
295
            $data['title']     = $message->getAudio()->getTitle();
296
        } elseif ($type === 'document') {
297
            $data['document'] = $message->getDocument()->getFileId();
298
        } elseif ($type === 'photo') {
299
            $data['photo'] = $message->getPhoto()[0]->getFileId();
300
        } elseif ($type === 'sticker') {
301
            $data['sticker'] = $message->getSticker()->getFileId();
302
        } elseif ($type === 'video') {
303
            $data['video'] = $message->getVideo()->getFileId();
304
        } elseif ($type === 'voice') {
305
            $data['voice'] = $message->getVoice()->getFileId();
306
        } elseif ($type === 'location') {
307
            $data['latitude']  = $message->getLocation()->getLatitude();
308
            $data['longitude'] = $message->getLocation()->getLongitude();
309
        }
310
311
        return Request::send('send' . ucfirst($type), $data);
312
    }
313
314
    /**
315
     * Publish a message to a channel and return success or failure message in markdown format
316
     *
317
     * @param Message     $message
318
     * @param string|int  $channel_id
319
     * @param string|null $caption
320
     *
321
     * @return string
322
     * @throws TelegramException
323
     */
324
    protected function publish(Message $message, $channel_id, $caption = null)
325
    {
326
        $res = $this->sendBack($message, [
327
            'chat_id' => $channel_id,
328
            'caption' => $caption,
329
        ]);
330
331
        if ($res->isOk()) {
332
            /** @var Chat $channel */
333
            $channel          = $res->getResult()->getChat();
334
            $escaped_username = $channel->getUsername() ? $this->getMessage()->escapeMarkdown($channel->getUsername()) : '';
335
336
            $response = sprintf(
337
                'Message successfully sent to *%s*%s',
338
                filter_var($channel->getTitle(), FILTER_SANITIZE_SPECIAL_CHARS),
339
                $escaped_username ? " (@{$escaped_username})" : ''
340
            );
341
        } else {
342
            $escaped_username = $this->getMessage()->escapeMarkdown($channel_id);
343
            $response         = "Message not sent to *{$escaped_username}*" . PHP_EOL .
344
                                '- Does the channel exist?' . PHP_EOL .
345
                                '- Is the bot an admin of the channel?';
346
        }
347
348
        return $response;
349
    }
350
351
    /**
352
     * Execute without db
353
     *
354
     * @todo Why send just to the first found channel?
355
     *
356
     * @return mixed
357
     * @throws TelegramException
358
     */
359
    public function executeNoDb()
360
    {
361
        $message = $this->getMessage();
362
        $text    = trim($message->getText(true));
363
364
        if ($text === '') {
365
            return $this->replyToChat('Usage: ' . $this->getUsage());
366
        }
367
368
        $channels = array_filter((array) $this->getConfig('your_channel'));
369
        if (empty($channels)) {
370
            return $this->replyToChat('No channels defined in the command config!');
371
        }
372
373
        return $this->replyToChat($this->publish(
374
            new Message($message->getRawData(), $this->telegram->getBotUsername()),
375
            reset($channels)
376
        ), ['parse_mode' => 'markdown']);
377
    }
378
}
379