Completed
Pull Request — develop (#291)
by Armando
03:51
created

SendtochannelCommand   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 332
Duplicated Lines 1.81 %

Coupling/Cohesion

Components 1
Dependencies 16

Test Coverage

Coverage 0%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
wmc 58
c 3
b 1
f 0
lcom 1
cbo 16
dl 6
loc 332
rs 4.5205
ccs 0
cts 223
cp 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
F execute() 6 191 43
A executeNoDb() 0 18 2
A publish() 0 16 2
C sendBack() 0 33 11

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like SendtochannelCommand often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SendtochannelCommand, and based on these observations, apply Extract Interface, too.

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\Request;
14
use Longman\TelegramBot\Conversation;
15
use Longman\TelegramBot\Commands\AdminCommand;
16
use Longman\TelegramBot\Entities\Message;
17
use Longman\TelegramBot\Entities\ReplyKeyboardHide;
18
use Longman\TelegramBot\Entities\ReplyKeyboardMarkup;
19
use Longman\TelegramBot\Exception\TelegramException;
20
21
class SendtochannelCommand extends AdminCommand
22
{
23
    /**
24
     * @var string
25
     */
26
    protected $name = 'sendtochannel';
27
28
    /**
29
     * @var string
30
     */
31
    protected $description = 'Send message to a channel';
32
33
    /**
34
     * @var string
35
     */
36
    protected $usage = '/sendtochannel <message to send>';
37
38
    /**
39
     * @var string
40
     */
41
    protected $version = '0.1.4';
42
43
    /**
44
     * @var bool
45
     */
46
    protected $need_mysql = true;
47
48
    /**
49
     * Conversation Object
50
     *
51
     * @var \Longman\TelegramBot\Conversation
52
     */
53
    protected $conversation;
54
55
    /**
56
     * Command execute method
57
     *
58
     * @return \Longman\TelegramBot\Entities\ServerResponse|mixed
59
     */
60
    public function execute()
61
    {
62
        $message = $this->getMessage();
63
        $chat_id = $message->getChat()->getId();
64
        $user_id = $message->getFrom()->getId();
65
        $type    = $message->getType();
66
        // 'Cast' the command type into message this protect the machine state
67
        // if the commmad is recolled when the conversation is already started
68
        $type = ($type == 'command') ? 'Message' : $type;
69
        $text = trim($message->getText(true));
70
71
        $data            = [];
72
        $data['chat_id'] = $chat_id;
73
74
        // Conversation
75
        $this->conversation = new Conversation($user_id, $chat_id, $this->getName());
76
77
        $channels = (array)$this->getConfig('your_channel');
78
        if (!isset($this->conversation->notes['state'])) {
79
            $state                                        = (count($channels) == 0) ? -1 : 0;
80
            $this->conversation->notes['last_message_id'] = $message->getMessageId();
81
        } else {
82
            $state = $this->conversation->notes['state'];
83
        }
84
        switch ($state) {
85
            case -1:
86
                // getConfig has not been configured asking for channel to administer
87
                if ($type != 'Message' || $text === '') {
88
                    $this->conversation->notes['state'] = -1;
89
                    $this->conversation->update();
90
91
                    $data['text']         = 'Insert the channel name: (@yourchannel)';
92
                    $data['reply_markup'] = new ReplyKeyBoardHide(['selective' => true]);
93
                    $result               = Request::sendMessage($data);
94
95
                    break;
96
                }
97
                $this->conversation->notes['channel']         = $text;
98
                $this->conversation->notes['last_message_id'] = $message->getMessageId();
99
                // Jump to state 1
100
                goto insert;
101
102
            // no break
103
            default:
104
            case 0:
0 ignored issues
show
Unused Code introduced by
case 0: // getConfig...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...
105
                // getConfig has been configured choose channel
106
                if ($type != 'Message' || !in_array($text, $channels)) {
107
                    $this->conversation->notes['state'] = 0;
108
                    $this->conversation->update();
109
110
                    $keyboard = [];
111
                    foreach ($channels as $channel) {
112
                        $keyboard[] = [$channel];
113
                    }
114
                    $reply_keyboard_markup = new ReplyKeyboardMarkup(
115
                        [
116
                            'keyboard'          => $keyboard,
117
                            'resize_keyboard'   => true,
118
                            'one_time_keyboard' => true,
119
                            'selective'         => true
120
                        ]
121
                    );
122
                    $data['reply_markup']  = $reply_keyboard_markup;
123
                    $data['text']          = 'Select a channel';
124
                    if ($type != 'Message' || !in_array($text, $channels)) {
125
                        $data['text'] = 'Select a channel from the keyboard:';
126
                    }
127
                    $result = Request::sendMessage($data);
128
                    break;
129
                }
130
                $this->conversation->notes['channel']         = $text;
131
                $this->conversation->notes['last_message_id'] = $message->getMessageId();
132
133
            // no break
134
            case 1:
135
                insert:
136
                if ($this->conversation->notes['last_message_id'] == $message->getMessageId() || ($type == 'Message' && $text === '')) {
137
                    $this->conversation->notes['state'] = 1;
138
                    $this->conversation->update();
139
140
                    $data['reply_markup'] = new ReplyKeyBoardHide(['selective' => true]);
141
                    $data['text']         = 'Insert the content you want to share: text, photo, audio...';
142
                    $result               = Request::sendMessage($data);
143
                    break;
144
                }
145
                $this->conversation->notes['last_message_id'] = $message->getMessageId();
146
                $this->conversation->notes['message']         = $message->reflect();
147
                $this->conversation->notes['message_type']    = $type;
148
            // no break
149
            case 2:
150
                if ($this->conversation->notes['last_message_id'] == $message->getMessageId() || !($text == 'Yes' || $text == 'No')) {
151
                    $this->conversation->notes['state'] = 2;
152
                    $this->conversation->update();
153
154
                    // Execute this just with object that allow caption
155
                    if ($this->conversation->notes['message_type'] == 'Video' || $this->conversation->notes['message_type'] == 'Photo') {
156
                        $keyboard              = [['Yes', 'No']];
157
                        $reply_keyboard_markup = new ReplyKeyboardMarkup(
158
                            [
159
                                'keyboard'          => $keyboard,
160
                                'resize_keyboard'   => true,
161
                                'one_time_keyboard' => true,
162
                                'selective'         => true
163
                            ]
164
                        );
165
                        $data['reply_markup']  = $reply_keyboard_markup;
166
167
                        $data['text'] = 'Would you insert caption?';
168 View Code Duplication
                        if ($this->conversation->notes['last_message_id'] != $message->getMessageId() && !($text == 'Yes' || $text == 'No')) {
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...
169
                            $data['text'] = 'Would you insert a caption?' . "\n" . 'Type Yes or No';
170
                        }
171
                        $result = Request::sendMessage($data);
172
                        break;
173
                    }
174
                }
175
                $this->conversation->notes['set_caption'] = false;
176
                if ($text == 'Yes') {
177
                    $this->conversation->notes['set_caption'] = true;
178
                }
179
                $this->conversation->notes['last_message_id'] = $message->getMessageId();
180
            // no break
181
            case 3:
182
                if (($this->conversation->notes['last_message_id'] == $message->getMessageId() || $type != 'Message') && $this->conversation->notes['set_caption']) {
183
                    $this->conversation->notes['state'] = 3;
184
                    $this->conversation->update();
185
186
                    $data['text']         = 'Insert caption:';
187
                    $data['reply_markup'] = new ReplyKeyBoardHide(['selective' => true]);
188
                    $result               = Request::sendMessage($data);
189
                    break;
190
                }
191
                $this->conversation->notes['last_message_id'] = $message->getMessageId();
192
                $this->conversation->notes['caption']         = $text;
193
            // no break
194
            case 4:
195
                if ($this->conversation->notes['last_message_id'] == $message->getMessageId() || !($text == 'Yes' || $text == 'No')) {
196
                    $this->conversation->notes['state'] = 4;
197
                    $this->conversation->update();
198
199
                    $data['text'] = 'Message will look like this:';
200
                    $result       = Request::sendMessage($data);
201
202
                    if ($this->conversation->notes['message_type'] != 'command') {
203
                        if ($this->conversation->notes['set_caption']) {
204
                            $data['caption'] = $this->conversation->notes['caption'];
205
                        }
206
                        $result = $this->sendBack(new Message($this->conversation->notes['message'], 'thisbot'), $data);
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
207
208
                        $data['text'] = 'Would you post it?';
209 View Code Duplication
                        if ($this->conversation->notes['last_message_id'] != $message->getMessageId() && !($text == 'Yes' || $text == 'No')) {
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...
210
                            $data['text'] = 'Would you post it?' . "\n" . 'Press Yes or No';
211
                        }
212
                        $keyboard              = [['Yes', 'No']];
213
                        $reply_keyboard_markup = new ReplyKeyboardMarkup(
214
                            [
215
                                'keyboard'          => $keyboard,
216
                                'resize_keyboard'   => true,
217
                                'one_time_keyboard' => true,
218
                                'selective'         => true
219
                            ]
220
                        );
221
                        $data['reply_markup']  = $reply_keyboard_markup;
222
                        $result                = Request::sendMessage($data);
223
                    }
224
                    break;
225
                }
226
227
                $this->conversation->notes['post_message'] = false;
228
                if ($text == 'Yes') {
229
                    $this->conversation->notes['post_message'] = true;
230
                }
231
                $this->conversation->notes['last_message_id'] = $message->getMessageId();
232
            // no break
233
            case 5:
234
                $data['reply_markup'] = new ReplyKeyBoardHide(['selective' => true]);
235
236
                if ($this->conversation->notes['post_message']) {
237
                    $data['text'] = $this->publish(
238
                        new Message($this->conversation->notes['message'], 'anystring'),
239
                        $this->conversation->notes['channel'],
240
                        $this->conversation->notes['caption']
241
                    );
242
                } else {
243
                    $data['text'] = 'Abort by user, message not sent..';
244
                }
245
246
                $this->conversation->stop();
247
                $result = Request::sendMessage($data);
248
        }
249
        return $result;
250
    }
251
252
    /**
253
     * Execute without db
254
     *
255
     * @return mixed
256
     */
257
    public function executeNoDb()
258
    {
259
        $message = $this->getMessage();
260
        $text    = trim($message->getText(true));
261
        $chat_id = $message->getChat()->getId();
262
263
        $data            = [];
264
        $data['chat_id'] = $chat_id;
265
266
        if ($text === '') {
267
            $data['text'] = 'Usage: /sendtochannel <text>';
268
        } else {
269
            $channels      = (array)$this->getConfig('your_channel');
270
            $first_channel = $channels[0];
271
            $data['text']  = $this->publish(new Message($message->reflect(), 'anystring'), $first_channel);
272
        }
273
        return Request::sendMessage($data);
274
    }
275
276
    /**
277
     * Publish a message to a channel and return success or failure message
278
     *
279
     * @param \Longman\TelegramBot\Entities\Message $message
280
     * @param int                                   $channel
281
     * @param string|null                           $caption
282
     *
283
     * @return string
284
     */
285
    protected function publish(Message $message, $channel, $caption = null)
286
    {
287
        $data = [
288
            'chat_id' => $channel,
289
            'caption' => $caption,
290
        ];
291
292
        if ($this->sendBack($message, $data)->isOk()) {
293
            $response = 'Message sent successfully to: ' . $channel;
294
        } else {
295
            $response = 'Message not sent to: ' . $channel . "\n" .
296
                '- Does the channel exist?' . "\n" .
297
                '- Is the bot an admin of the channel?';
298
        }
299
        return $response;
300
    }
301
302
    /**
303
     * SendBack
304
     *
305
     * Received a message, the bot can send a copy of it to another chat/channel.
306
     * You don't have to care about the type of the message, the function detect it and use the proper
307
     * REQUEST:: function to send it.
308
     * $data include all the var that you need to send the message to the proper chat
309
     *
310
     * @todo This method will be moved at an higher level maybe in AdminCommand or Command
311
     * @todo Looking for a more significative name
312
     *
313
     * @param \Longman\TelegramBot\Entities\Message $message
314
     * @param array                                 $data
315
     *
316
     * @return \Longman\TelegramBot\Entities\ServerResponse
317
     * @throws \Longman\TelegramBot\Exception\TelegramException
318
     */
319
    protected function sendBack(Message $message, array $data)
320
    {
321
        $type = $message->getType();
322
        $type = ($type == 'command') ? 'Message' : $type;
323
        if ($type == 'Message') {
324
            $data['text'] = $message->getText(true);
325
        } elseif ($type == 'Audio') {
326
            $data['audio']     = $message->getAudio()->getFileId();
327
            $data['duration']  = $message->getAudio()->getDuration();
328
            $data['performer'] = $message->getAudio()->getPerformer();
329
            $data['title']     = $message->getAudio()->getTitle();
330
        } elseif ($type == 'Document') {
331
            $data['document'] = $message->getDocument()->getFileId();
332
        } elseif ($type == 'Photo') {
333
            $data['photo'] = $message->getPhoto()[0]->getFileId();
334
        } elseif ($type == 'Sticker') {
335
            $data['sticker'] = $message->getSticker()->getFileId();
336
        } elseif ($type == 'Video') {
337
            $data['video'] = $message->getVideo()->getFileId();
338
        } elseif ($type == 'Voice') {
339
            $data['voice'] = $message->getVoice()->getFileId();
340
        } elseif ($type == 'Location') {
341
            $data['latitude']  = $message->getLocation()->getLatitude();
342
            $data['longitude'] = $message->getLocation()->getLongitude();
343
        }
344
        $callback_path     = 'Longman\TelegramBot\Request';
345
        $callback_function = 'send' . $type;
346
        if (!method_exists($callback_path, $callback_function)) {
347
            throw new TelegramException('Methods: ' . $callback_function . ' not found in class Request.');
348
        }
349
350
        return call_user_func_array($callback_path . '::' . $callback_function, [$data]);
351
    }
352
}
353