Completed
Push — master ( 695e63...f2fe6e )
by Armando
02:21 queued 01:02
created

Commands/AdminCommands/SendtochannelCommand.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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