Completed
Push — improve_admin_send_commands ( 1f9f72 )
by Armando
02:32
created

SendtochannelCommand::publish()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 26
ccs 0
cts 22
cp 0
rs 8.5806
cc 4
eloc 17
nc 3
nop 3
crap 20
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\Commands\AdminCommands;
12
13
use Longman\TelegramBot\Commands\AdminCommand;
14
use Longman\TelegramBot\Conversation;
15
use Longman\TelegramBot\Entities\Chat;
16
use Longman\TelegramBot\Entities\Keyboard;
17
use Longman\TelegramBot\Entities\Message;
18
use Longman\TelegramBot\Request;
19
20
class SendtochannelCommand extends AdminCommand
21
{
22
    /**
23
     * @var string
24
     */
25
    protected $name = 'sendtochannel';
26
27
    /**
28
     * @var string
29
     */
30
    protected $description = 'Send message to a channel';
31
32
    /**
33
     * @var string
34
     */
35
    protected $usage = '/sendtochannel <message to send>';
36
37
    /**
38
     * @var string
39
     */
40
    protected $version = '0.3.0';
41
42
    /**
43
     * @var bool
44
     */
45
    protected $need_mysql = true;
46
47
    /**
48
     * Conversation Object
49
     *
50
     * @var \Longman\TelegramBot\Conversation
51
     */
52
    protected $conversation;
53
54
    /**
55
     * Command execute method
56
     *
57
     * @return \Longman\TelegramBot\Entities\ServerResponse|mixed
58
     * @throws \Longman\TelegramBot\Exception\TelegramException
59
     */
60
    public function execute()
61
    {
62
        $message = $this->getMessage();
63
        $chat_id = $message->getChat()->getId();
64
        $user_id = $message->getFrom()->getId();
65
66
        $type = $message->getType();
67
        // 'Cast' the command type to message to protect the machine state
68
        // if the command is recalled when the conversation is already started
69
        in_array($type, ['command', 'text'], true) && $type = 'message';
70
71
        $text           = trim($message->getText(true));
72
        $text_yes_or_no = ($text === 'Yes' || $text === 'No');
73
74
        $data = [
75
            'chat_id' => $chat_id,
76
        ];
77
78
        // Conversation
79
        $this->conversation = new Conversation($user_id, $chat_id, $this->getName());
80
81
        $notes = &$this->conversation->notes;
82
        !is_array($notes) && $notes = [];
83
84
        $channels = (array) $this->getConfig('your_channel');
85
        if (isset($notes['state'])) {
86
            $state = $notes['state'];
87
        } else {
88
            $state                    = (count($channels) === 0) ? -1 : 0;
89
            $notes['last_message_id'] = $message->getMessageId();
90
        }
91
92
        $yes_no_keyboard = new Keyboard(
93
            [
94
                'keyboard'          => [['Yes', 'No']],
95
                'resize_keyboard'   => true,
96
                'one_time_keyboard' => true,
97
                'selective'         => true,
98
            ]
99
        );
100
101
        switch ($state) {
102
            case -1:
103
                // getConfig has not been configured asking for channel to administer
104
                if ($type !== 'message' || $text === '') {
105
                    $notes['state'] = -1;
106
                    $this->conversation->update();
107
108
                    $result = $this->replyToChat(
109
                        'Insert the channel name or ID (_@yourchannel_ or _-12345_)',
110
                        [
111
                            'parse_mode'   => 'markdown',
112
                            'reply_markup' => Keyboard::remove(['selective' => true]),
113
                        ]
114
                    );
115
116
                    break;
117
                }
118
                $notes['channel']         = $text;
119
                $notes['last_message_id'] = $message->getMessageId();
120
                // Jump to state 1
121
                goto insert;
122
123
            // no break
124
            default:
125
            case 0:
0 ignored issues
show
Unused Code introduced by
case 0: if ($type !=...essage->getMessageId(); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
126
                // getConfig has been configured choose channel
127
                if ($type !== 'message' || $text === '') {
128
                    $notes['state'] = 0;
129
                    $this->conversation->update();
130
131
                    $keyboard = array_map(function ($channel) {
132
                        return [$channel];
133
                    }, $channels);
134
135
                    $result = $this->replyToChat(
136
                        'Choose a channel from the keyboard' . PHP_EOL .
137
                        '_or_ insert the channel name or ID (_@yourchannel_ or _-12345_)',
138
                        [
139
                            'parse_mode'   => 'markdown',
140
                            'reply_markup' => new Keyboard(
141
                                [
142
                                    'keyboard'          => $keyboard,
143
                                    'resize_keyboard'   => true,
144
                                    'one_time_keyboard' => true,
145
                                    'selective'         => true,
146
                                ]
147
                            ),
148
                        ]
149
                    );
150
                    break;
151
                }
152
                $notes['channel']         = $text;
153
                $notes['last_message_id'] = $message->getMessageId();
154
155
            // no break
156
            case 1:
157
                insert:
158 View Code Duplication
                if (($type === 'message' && $text === '') || $notes['last_message_id'] === $message->getMessageId()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
159
                    $notes['state'] = 1;
160
                    $this->conversation->update();
161
162
                    $result = $this->replyToChat(
163
                        'Insert the content you want to share: text, photo, audio...',
164
                        ['reply_markup' => Keyboard::remove(['selective' => true])]
165
                    );
166
                    break;
167
                }
168
                $notes['last_message_id'] = $message->getMessageId();
169
                $notes['message']         = $message->getRawData();
170
                $notes['message_type']    = $type;
171
            // no break
172
            case 2:
173
                if (!$text_yes_or_no || $notes['last_message_id'] === $message->getMessageId()) {
174
                    $notes['state'] = 2;
175
                    $this->conversation->update();
176
177
                    // Grab any existing caption.
178
                    if ($caption = $message->getCaption()) {
179
                        $notes['caption'] = $caption;
180
                        $text             = 'No';
181
                    } elseif (in_array($notes['message_type'], ['video', 'photo'], true)) {
182
183
                        $text = 'Would you like to insert a caption?';
184 View Code Duplication
                        if (!$text_yes_or_no && $notes['last_message_id'] !== $message->getMessageId()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
185
                            $text .= PHP_EOL . 'Type Yes or No';
186
                        }
187
188
                        $result = $this->replyToChat(
189
                            $text,
190
                            ['reply_markup' => $yes_no_keyboard]
191
                        );
192
                        break;
193
                    }
194
                }
195
                $notes['set_caption']     = ($text === 'Yes');
196
                $notes['last_message_id'] = $message->getMessageId();
197
            // no break
198
            case 3:
199 View Code Duplication
                if ($notes['set_caption'] && ($notes['last_message_id'] === $message->getMessageId() || $type !== 'message')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
200
                    $notes['state'] = 3;
201
                    $this->conversation->update();
202
203
                    $result = $this->replyToChat(
204
                        'Insert caption:',
205
                        ['reply_markup' => Keyboard::remove(['selective' => true])]
206
                    );
207
                    break;
208
                }
209
                $notes['last_message_id'] = $message->getMessageId();
210
                if (isset($notes['caption'])) {
211
                    // If caption has already been send with the file, no need to ask for it.
212
                    $notes['set_caption'] = true;
213
                } else {
214
                    $notes['caption'] = $text;
215
                }
216
            // no break
217
            case 4:
218
                if (!$text_yes_or_no || $notes['last_message_id'] === $message->getMessageId()) {
219
                    $notes['state'] = 4;
220
                    $this->conversation->update();
221
222
                    $result = $this->replyToChat('Message will look like this:');
223
224
                    if ($notes['message_type'] !== 'command') {
225
                        if ($notes['set_caption']) {
226
                            $data['caption'] = $notes['caption'];
227
                        }
228
                        $this->sendBack(new Message($notes['message'], $this->telegram->getBotUsername()), $data);
229
230
                        $data['reply_markup'] = $yes_no_keyboard;
231
232
                        $data['text'] = 'Would you like to post it?';
233 View Code Duplication
                        if (!$text_yes_or_no && $notes['last_message_id'] !== $message->getMessageId()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
234
                            $data['text'] .= PHP_EOL . 'Type Yes or No';
235
                        }
236
                        $result = Request::sendMessage($data);
237
                    }
238
                    break;
239
                }
240
241
                $notes['post_message']    = ($text === 'Yes');
242
                $notes['last_message_id'] = $message->getMessageId();
243
            // no break
244
            case 5:
245
                $data['reply_markup'] = Keyboard::remove(['selective' => true]);
246
247
                if ($notes['post_message']) {
248
                    $data['parse_mode'] = 'markdown';
249
                    $data['text']       = $this->publish(
250
                        new Message($notes['message'], $this->telegram->getBotUsername()),
251
                        $notes['channel'],
252
                        $notes['caption']
253
                    );
254
                } else {
255
                    $data['text'] = 'Aborted by user, message not sent..';
256
                }
257
258
                $this->conversation->stop();
259
                $result = Request::sendMessage($data);
260
        }
261
262
        return $result;
263
    }
264
265
    /**
266
     * SendBack
267
     *
268
     * Received a message, the bot can send a copy of it to another chat/channel.
269
     * You don't have to care about the type of the message, the function detect it and use the proper
270
     * REQUEST:: function to send it.
271
     * $data include all the var that you need to send the message to the proper chat
272
     *
273
     * @todo This method will be moved to a higher level maybe in AdminCommand or Command
274
     * @todo Looking for a more significant name
275
     *
276
     * @param \Longman\TelegramBot\Entities\Message $message
277
     * @param array                                 $data
278
     *
279
     * @return \Longman\TelegramBot\Entities\ServerResponse
280
     * @throws \Longman\TelegramBot\Exception\TelegramException
281
     */
282
    protected function sendBack(Message $message, array $data)
283
    {
284
        $type = $message->getType();
285
        in_array($type, ['command', 'text'], true) && $type = 'message';
286
287
        if ($type === 'message') {
288
            $data['text'] = $message->getText(true);
289
        } elseif ($type === 'audio') {
290
            $data['audio']     = $message->getAudio()->getFileId();
291
            $data['duration']  = $message->getAudio()->getDuration();
292
            $data['performer'] = $message->getAudio()->getPerformer();
293
            $data['title']     = $message->getAudio()->getTitle();
294
        } elseif ($type === 'document') {
295
            $data['document'] = $message->getDocument()->getFileId();
296
        } elseif ($type === 'photo') {
297
            $data['photo'] = $message->getPhoto()[0]->getFileId();
298
        } elseif ($type === 'sticker') {
299
            $data['sticker'] = $message->getSticker()->getFileId();
300
        } elseif ($type === 'video') {
301
            $data['video'] = $message->getVideo()->getFileId();
302
        } elseif ($type === 'voice') {
303
            $data['voice'] = $message->getVoice()->getFileId();
304
        } elseif ($type === 'location') {
305
            $data['latitude']  = $message->getLocation()->getLatitude();
306
            $data['longitude'] = $message->getLocation()->getLongitude();
307
        }
308
309
        return Request::send('send' . ucfirst($type), $data);
310
    }
311
312
    /**
313
     * Publish a message to a channel and return success or failure message in markdown format
314
     *
315
     * @param \Longman\TelegramBot\Entities\Message $message
316
     * @param string|int                            $channel_id
317
     * @param string|null                           $caption
318
     *
319
     * @return string
320
     * @throws \Longman\TelegramBot\Exception\TelegramException
321
     */
322
    protected function publish(Message $message, $channel_id, $caption = null)
323
    {
324
        $res = $this->sendBack($message, [
325
            'chat_id' => $channel_id,
326
            'caption' => $caption,
327
        ]);
328
329
        if ($res->isOk()) {
330
            /** @var Chat $channel */
331
            $channel          = $res->getResult()->getChat();
332
            $escaped_username = $channel->getUsername() ? $this->getMessage()->escapeMarkdown($channel->getUsername()) : '';
333
334
            $response = sprintf(
335
                'Message successfully sent to *%s*%s',
336
                filter_var($channel->getTitle(), FILTER_SANITIZE_SPECIAL_CHARS),
337
                $escaped_username ? " (@{$escaped_username})" : ''
338
            );
339
        } else {
340
            $escaped_username = $this->getMessage()->escapeMarkdown($channel_id);
341
            $response         = "Message not sent to *{$escaped_username}*" . PHP_EOL .
342
                                '- Does the channel exist?' . PHP_EOL .
343
                                '- Is the bot an admin of the channel?';
344
        }
345
346
        return $response;
347
    }
348
349
    /**
350
     * Execute without db
351
     *
352
     * @todo Why send just to the first found channel?
353
     *
354
     * @return mixed
355
     * @throws \Longman\TelegramBot\Exception\TelegramException
356
     */
357
    public function executeNoDb()
358
    {
359
        $message = $this->getMessage();
360
        $text    = trim($message->getText(true));
361
362
        if ($text === '') {
363
            return $this->replyToChat('Usage: ' . $this->getUsage());
364
        }
365
366
        $channels = array_filter((array) $this->getConfig('your_channel'));
367
        if (empty($channels)) {
368
            return $this->replyToChat('No channels defined in the command config!');
369
        }
370
371
        return $this->replyToChat($this->publish(
372
            new Message($message->getRawData(), $this->telegram->getBotUsername()),
373
            reset($channels)
374
        ), ['parse_mode' => 'markdown']);
375
    }
376
}
377