Completed
Push — feature/improve-code ( 7d6b6d...e986a1 )
by Avtandil
15:08
created

SendtochannelCommand   C

Complexity

Total Complexity 58

Size/Duplication

Total Lines 312
Duplicated Lines 1.92 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 58
lcom 1
cbo 8
dl 6
loc 312
rs 6.3005
c 2
b 1
f 0
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
     * {@inheritdoc}
25
     */
26
    protected $name = 'sendtochannel';
27
    protected $description = 'Send message to a channel';
28
    protected $usage = '/sendtochannel <message to send>';
29
    protected $version = '0.1.4';
30
    protected $need_mysql = true;
31
    /**#@-*/
32
33
    /**
34
     * Conversation Object
35
     *
36
     * @var \Longman\TelegramBot\Conversation
37
     */
38
    protected $conversation;
39
40
    /**
41
     * {@inheritdoc}
42
     */
43
    public function execute()
44
    {
45
        $message = $this->getMessage();
46
        $chat_id = $message->getChat()->getId();
47
        $user_id = $message->getFrom()->getId();
48
        $type = $message->getType();
49
        // 'Cast' the command type into message this protect the machine state
50
        // if the commmad is recolled when the conversation is already started
51
        $type = ($type == 'command') ? 'Message' : $type;
52
        $text = trim($message->getText(true));
53
54
        $data = [];
55
        $data['chat_id'] = $chat_id;
56
57
        // Conversation
58
        $this->conversation = new Conversation($user_id, $chat_id, $this->getName());
59
60
        $channels = (array) $this->getConfig('your_channel');
61
        if (!isset($this->conversation->notes['state'])) {
62
            $state = (count($channels) == 0) ? -1 : 0;
63
            $this->conversation->notes['last_message_id'] = $message->getMessageId();
64
        } else {
65
            $state = $this->conversation->notes['state'];
66
        }
67
        switch ($state) {
68
            case -1:
69
                // getConfig has not been configured asking for channel to administer
70
                if ($type != 'Message' || $text === '') {
71
                    $this->conversation->notes['state'] = -1;
72
                    $this->conversation->update();
73
74
                    $data['text'] = 'Insert the channel name: (@yourchannel)';
75
                    $data['reply_markup'] = new ReplyKeyBoardHide(['selective' => true]);
76
                    $result = Request::sendMessage($data);
77
78
                    break;
79
                }
80
                $this->conversation->notes['channel'] = $text;
81
                $this->conversation->notes['last_message_id'] = $message->getMessageId();
82
                // Jump to state 1
83
                goto insert;
84
85
                // no break
86
            default:
87
            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...
88
                // getConfig has been configured choose channel
89
                if ($type != 'Message' || !in_array($text, $channels)) {
90
                    $this->conversation->notes['state'] = 0;
91
                    $this->conversation->update();
92
93
                    $keyboard = [];
94
                    foreach ($channels as $channel) {
95
                        $keyboard[] = [$channel];
96
                    }
97
                    $reply_keyboard_markup = new ReplyKeyboardMarkup(
98
                        [
99
                            'keyboard' => $keyboard ,
100
                            'resize_keyboard' => true,
101
                            'one_time_keyboard' => true,
102
                            'selective' => true
103
                        ]
104
                    );
105
                    $data['reply_markup'] = $reply_keyboard_markup;
106
                    $data['text'] = 'Select a channel';
107
                    if ($type != 'Message' || !in_array($text, $channels)) {
108
                        $data['text'] = 'Select a channel from the keyboard:';
109
                    }
110
                    $result = Request::sendMessage($data);
111
                    break;
112
                }
113
                $this->conversation->notes['channel'] = $text;
114
                $this->conversation->notes['last_message_id'] = $message->getMessageId();
115
116
                // no break
117
            case 1:
118
                insert:
119
                if ($this->conversation->notes['last_message_id'] == $message->getMessageId() || ($type == 'Message' && $text === '')) {
120
                    $this->conversation->notes['state'] = 1;
121
                    $this->conversation->update();
122
123
                    $data['reply_markup'] = new ReplyKeyBoardHide(['selective' => true]);
124
                    $data['text'] = 'Insert the content you want to share: text, photo, audio...';
125
                    $result = Request::sendMessage($data);
126
                    break;
127
                }
128
                $this->conversation->notes['last_message_id'] = $message->getMessageId();
129
                $this->conversation->notes['message'] = $message->reflect();
130
                $this->conversation->notes['message_type'] = $type;
131
                // no break
132
            case 2:
133
                if ($this->conversation->notes['last_message_id'] == $message->getMessageId() || !($text == 'Yes' || $text == 'No')) {
134
                    $this->conversation->notes['state'] = 2;
135
                    $this->conversation->update();
136
137
                    // Execute this just with object that allow caption
138
                    if ($this->conversation->notes['message_type'] == 'Video' || $this->conversation->notes['message_type'] == 'Photo') {
139
                        $keyboard = [['Yes', 'No']];
140
                        $reply_keyboard_markup = new ReplyKeyboardMarkup(
141
                            [
142
                                'keyboard' => $keyboard ,
143
                                'resize_keyboard' => true,
144
                                'one_time_keyboard' => true,
145
                                'selective' => true
146
                            ]
147
                        );
148
                        $data['reply_markup'] = $reply_keyboard_markup;
149
150
                        $data['text'] = 'Would you insert caption?';
151 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...
152
                            $data['text'] = 'Would you insert a caption?' . "\n" . 'Type Yes or No';
153
                        }
154
                        $result = Request::sendMessage($data);
155
                        break;
156
                    }
157
                }
158
                $this->conversation->notes['set_caption'] = false;
159
                if ($text == 'Yes') {
160
                    $this->conversation->notes['set_caption'] = true;
161
                }
162
                $this->conversation->notes['last_message_id'] = $message->getMessageId();
163
                // no break
164
            case 3:
165
                if (($this->conversation->notes['last_message_id'] == $message->getMessageId() || $type != 'Message' ) && $this->conversation->notes['set_caption']) {
166
                    $this->conversation->notes['state'] = 3;
167
                    $this->conversation->update();
168
169
                    $data['text'] = 'Insert caption:';
170
                    $data['reply_markup'] = new ReplyKeyBoardHide(['selective' => true]);
171
                    $result = Request::sendMessage($data);
172
                    break;
173
                }
174
                $this->conversation->notes['last_message_id'] = $message->getMessageId();
175
                $this->conversation->notes['caption'] = $text;
176
                // no break
177
            case 4:
178
                if ($this->conversation->notes['last_message_id'] == $message->getMessageId() || !($text == 'Yes' || $text == 'No')) {
179
                    $this->conversation->notes['state'] = 4;
180
                    $this->conversation->update();
181
182
                    $data['text'] = 'Message will look like this:';
183
                    $result = Request::sendMessage($data);
184
185
                    if ($this->conversation->notes['message_type'] != 'command') {
186
                        if ($this->conversation->notes['set_caption']) {
187
                            $data['caption'] = $this->conversation->notes['caption'];
188
                        }
189
                        $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...
190
191
                        $data['text'] = 'Would you post it?';
192 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...
193
                            $data['text'] = 'Would you post it?' . "\n" . 'Press Yes or No';
194
                        }
195
                        $keyboard = [['Yes', 'No']];
196
                        $reply_keyboard_markup = new ReplyKeyboardMarkup(
197
                            [
198
                                'keyboard' => $keyboard ,
199
                                'resize_keyboard' => true,
200
                                'one_time_keyboard' => true,
201
                                'selective' => true
202
                            ]
203
                        );
204
                        $data['reply_markup'] = $reply_keyboard_markup;
205
                        $result = Request::sendMessage($data);
206
                    }
207
                    break;
208
                }
209
210
                $this->conversation->notes['post_message'] = false;
211
                if ($text == 'Yes') {
212
                    $this->conversation->notes['post_message'] = true;
213
                }
214
                $this->conversation->notes['last_message_id'] = $message->getMessageId();
215
                // no break
216
            case 5:
217
                $data['reply_markup'] = new ReplyKeyBoardHide(['selective' => true]);
218
219
                if ($this->conversation->notes['post_message']) {
220
                    $data['text'] = $this->publish(
221
                        new Message($this->conversation->notes['message'], 'anystring'),
222
                        $this->conversation->notes['channel'],
223
                        $this->conversation->notes['caption']
224
                    );
225
                } else {
226
                    $data['text'] = 'Abort by user, message not sent..';
227
                }
228
229
                $this->conversation->stop();
230
                $result = Request::sendMessage($data);
231
        }
232
        return $result;
233
    }
234
235
    /**
236
     * {@inheritdoc}
237
     */
238
    public function executeNoDb()
239
    {
240
        $message = $this->getMessage();
241
        $text = trim($message->getText(true));
242
        $chat_id = $message->getChat()->getId();
243
244
        $data = [];
245
        $data['chat_id'] = $chat_id;
246
247
        if ($text === '') {
248
            $data['text'] = 'Usage: /sendtochannel <text>';
249
        } else {
250
            $channels = (array) $this->getConfig('your_channel');
251
            $first_channel = $channels[0];
252
            $data['text'] = $this->publish(new Message($message->reflect(), 'anystring'), $first_channel);
253
        }
254
        return Request::sendMessage($data);
255
    }
256
257
    /**
258
     * Publish a message to a channel and return success or failure message
259
     *
260
     * @param \Longman\TelegramBot\Entities\Message $message
261
     * @param int              $channel
262
     * @param string|null      $caption
263
     *
264
     * @return string
265
     */
266
    protected function publish(Message $message, $channel, $caption = null)
267
    {
268
        $data = [
269
            'chat_id' => $channel,
270
            'caption' => $caption,
271
        ];
272
273
        if ($this->sendBack($message, $data)->isOk()) {
274
            $response = 'Message sent successfully to: ' . $channel;
275
        } else {
276
            $response = 'Message not sent to: ' .  $channel . "\n" .
277
                    '- Does the channel exist?' . "\n" .
278
                    '- Is the bot an admin of the channel?';
279
        }
280
        return $response;
281
    }
282
283
    /**
284
     * SendBack
285
     *
286
     * Received a message, the bot can send a copy of it to another chat/channel.
287
     * You don't have to care about the type of the message, the function detect it and use the proper
288
     * REQUEST:: function to send it.
289
     * $data include all the var that you need to send the message to the proper chat
290
     *
291
     * @todo This method will be moved at an higher level maybe in AdminCommand or Command
292
     * @todo Looking for a more significative name
293
     *
294
     * @param \Longman\TelegramBot\Entities\Message $message
295
     * @param array            $data
296
     *
297
     * @return \Longman\TelegramBot\Entities\ServerResponse
298
     */
299
    protected function sendBack(Message $message, array $data)
300
    {
301
        $type = $message->getType();
302
        $type = ($type == 'command') ? 'Message' : $type;
303
        if ($type == 'Message') {
304
            $data['text'] = $message->getText(true);
305
        } elseif ($type == 'Audio') {
306
            $data['audio'] = $message->getAudio()->getFileId();
307
            $data['duration'] = $message->getAudio()->getDuration();
308
            $data['performer'] = $message->getAudio()->getPerformer();
309
            $data['title'] = $message->getAudio()->getTitle();
310
        } elseif ($type == 'Document') {
311
            $data['document'] = $message->getDocument()->getFileId();
312
        } elseif ($type == 'Photo') {
313
            $data['photo'] = $message->getPhoto()[0]->getFileId();
314
        } elseif ($type == 'Sticker') {
315
            $data['sticker'] = $message->getSticker()->getFileId();
316
        } elseif ($type == 'Video') {
317
            $data['video'] = $message->getVideo()->getFileId();
318
        } elseif ($type == 'Voice') {
319
            $data['voice'] = $message->getVoice()->getFileId();
320
        } elseif ($type == 'Location') {
321
            $data['latitude'] = $message->getLocation()->getLatitude();
322
            $data['longitude'] = $message->getLocation()->getLongitude();
323
        }
324
        $callback_path = 'Longman\TelegramBot\Request';
325
        $callback_function = 'send' . $type;
326
        if (! method_exists($callback_path, $callback_function)) {
327
            throw new TelegramException('Methods: ' . $callback_function . ' not found in class Request.');
328
        }
329
330
        return call_user_func_array($callback_path . '::' . $callback_function, [$data]);
331
    }
332
}
333