Passed
Push — develop ( 57a649...a01996 )
by Armando
33:04 queued 27:25
created

Message   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 222
Duplicated Lines 0 %

Test Coverage

Coverage 94.81%

Importance

Changes 7
Bugs 1 Features 1
Metric Value
eloc 138
c 7
b 1
f 1
dl 0
loc 222
ccs 128
cts 135
cp 0.9481
rs 10
wmc 22

6 Methods

Rating   Name   Duplication   Size   Complexity  
A botAddedInChat() 0 9 4
A getFullCommand() 0 12 3
B subEntities() 0 57 1
B getType() 0 63 5
A getCommand() 0 25 5
A getText() 0 13 4
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 Longman\TelegramBot\Entities;
13
14
use Longman\TelegramBot\Entities\Games\Game;
15
use Longman\TelegramBot\Entities\Giveaway\Giveaway;
16
use Longman\TelegramBot\Entities\Giveaway\GiveawayCompleted;
17
use Longman\TelegramBot\Entities\Giveaway\GiveawayCreated;
18
use Longman\TelegramBot\Entities\Giveaway\GiveawayWinners;
19
use Longman\TelegramBot\Entities\Message\Factory as MaybeInaccessibleMessageFactory;
20
use Longman\TelegramBot\Entities\Message\MaybeInaccessibleMessage;
21
use Longman\TelegramBot\Entities\MessageOrigin\Factory as MessageOriginFactory;
22
use Longman\TelegramBot\Entities\MessageOrigin\MessageOrigin;
23
use Longman\TelegramBot\Entities\Payments\Invoice;
24
use Longman\TelegramBot\Entities\Payments\SuccessfulPayment;
25
use Longman\TelegramBot\Entities\TelegramPassport\PassportData;
26
use Longman\TelegramBot\Entities\Topics\ForumTopicClosed;
27
use Longman\TelegramBot\Entities\Topics\ForumTopicCreated;
28
use Longman\TelegramBot\Entities\Topics\ForumTopicEdited;
29
use Longman\TelegramBot\Entities\Topics\ForumTopicReopened;
30
use Longman\TelegramBot\Entities\Topics\GeneralForumTopicHidden;
31
use Longman\TelegramBot\Entities\Topics\GeneralForumTopicUnhidden;
32
33
/**
34
 * Class Message
35
 *
36
 * Represents a message
37
 *
38
 * @link https://core.telegram.org/bots/api#message
39
 *
40
 * @method int                                    getMessageId()                              Unique message identifier
41
 * @method int                                    getMessageThreadId()                        Optional. Unique identifier of a message thread to which the message belongs; for supergroups only
42
 * @method User                                   getFrom()                                   Optional. Sender, can be empty for messages sent to channels
43
 * @method Chat                                   getSenderChat()                             Optional. Sender of the message, sent on behalf of a chat. The channel itself for channel messages. The supergroup itself for messages from anonymous group administrators. The linked channel for messages automatically forwarded to the discussion group
44
 * @method int                                    getDate()                                   Date the message was sent in Unix time
45
 * @method Chat                                   getChat()                                   Conversation the message belongs to
46
 * @method MessageOrigin                          getForwardOrigin()                          Optional. Information about the original message for forwarded messages
47
 * @method bool                                   getIsTopicMessage()                         Optional. True, if the message is sent to a forum topic
48
 * @method bool                                   getIsAutomaticForward()                     Optional. True, if the message is a channel post that was automatically forwarded to the connected discussion group
49
 * @method ReplyToMessage                         getReplyToMessage()                         Optional. For replies, the original message. Note that the Message object in this field will not contain further reply_to_message fields even if it itself is a reply.
50
 * @method ExternalReplyInfo                      getExternalReply()                          Optional. Information about the message that is being replied to, which may come from another chat or forum topic
51
 * @method TextQuote                              getQuote()                                  Optional. For replies that quote part of the original message, the quoted part of the message
52
 * @method User                                   getViaBot()                                 Optional. Bot through which the message was sent
53
 * @method int                                    getEditDate()                               Optional. Date the message was last edited in Unix time
54
 * @method bool                                   getHasProtectedContent()                    Optional. True, if the message can't be forwarded
55
 * @method string                                 getMediaGroupId()                           Optional. The unique identifier of a media message group this message belongs to
56
 * @method string                                 getAuthorSignature()                        Optional. Signature of the post author for messages in channels
57
 * @method LinkPreviewOptions                     getLinkPreviewOptions()                     Optional. Options used for link preview generation for the message, if it is a text message and link preview options were changed
58
 * @method MessageEntity[]                        getEntities()                               Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text
59
 * @method MessageEntity[]                        getCaptionEntities()                        Optional. For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption
60
 * @method Audio                                  getAudio()                                  Optional. Message is an audio file, information about the file
61
 * @method Document                               getDocument()                               Optional. Message is a general file, information about the file
62
 * @method Animation                              getAnimation()                              Optional. Message is an animation, information about the animation. For backward compatibility, when this field is set, the document field will also be set
63
 * @method Game                                   getGame()                                   Optional. Message is a game, information about the game.
64
 * @method PhotoSize[]                            getPhoto()                                  Optional. Message is a photo, available sizes of the photo
65
 * @method Sticker                                getSticker()                                Optional. Message is a sticker, information about the sticker
66
 * @method Story                                  getStory()                                  Optional. Message is a forwarded story
67
 * @method Video                                  getVideo()                                  Optional. Message is a video, information about the video
68
 * @method Voice                                  getVoice()                                  Optional. Message is a voice message, information about the file
69
 * @method VideoNote                              getVideoNote()                              Optional. Message is a video note message, information about the video
70
 * @method string                                 getCaption()                                Optional. Caption for the document, photo or video, 0-200 characters
71
 * @method bool                                   getHasMediaSpoiler()                        Optional. True, if the message media is covered by a spoiler animation
72
 * @method Contact                                getContact()                                Optional. Message is a shared contact, information about the contact
73
 * @method Location                               getLocation()                               Optional. Message is a shared location, information about the location
74
 * @method Venue                                  getVenue()                                  Optional. Message is a venue, information about the venue
75
 * @method Poll                                   getPoll()                                   Optional. Message is a native poll, information about the poll
76
 * @method Dice                                   getDice()                                   Optional. Message is a dice with random value, 1-6 for “🎲” and “🎯” base emoji, 1-5 for “🏀” and “⚽” base emoji, 1-64 for “🎰” base emoji
77
 * @method User[]                                 getNewChatMembers()                         Optional. A new member(s) was added to the group, information about them (one of this members may be the bot itself)
78
 * @method User                                   getLeftChatMember()                         Optional. A member was removed from the group, information about them (this member may be the bot itself)
79
 * @method string                                 getNewChatTitle()                           Optional. A chat title was changed to this value
80
 * @method PhotoSize[]                            getNewChatPhoto()                           Optional. A chat photo was changed to this value
81
 * @method MessageAutoDeleteTimerChanged          getMessageAutoDeleteTimerChanged()          Optional. Service message: auto-delete timer settings changed in the chat
82
 * @method bool                                   getDeleteChatPhoto()                        Optional. Service message: the chat photo was deleted
83
 * @method bool                                   getGroupChatCreated()                       Optional. Service message: the group has been created
84
 * @method bool                                   getSupergroupChatCreated()                  Optional. Service message: the supergroup has been created. This field can't be received in a message coming through updates, because bot can’t be a member of a supergroup when it is created. It can only be found in reply_to_message if someone replies to a very first message in a directly created supergroup.
85
 * @method bool                                   getChannelChatCreated()                     Optional. Service message: the channel has been created. This field can't be received in a message coming through updates, because bot can’t be a member of a channel when it is created. It can only be found in reply_to_message if someone replies to a very first message in a channel.
86
 * @method int                                    getMigrateToChatId()                        Optional. The group has been migrated to a supergroup with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.
87
 * @method int                                    getMigrateFromChatId()                      Optional. The supergroup has been migrated from a group with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.
88
 * @method MaybeInaccessibleMessage               getPinnedMessage()                          Optional. Specified message was pinned. Note that the Message object in this field will not contain further reply_to_message fields even if it is itself a reply.
89
 * @method Invoice                                getInvoice()                                Optional. Message is an invoice for a payment, information about the invoice.
90
 * @method SuccessfulPayment                      getSuccessfulPayment()                      Optional. Message is a service message about a successful payment, information about the payment.
91
 * @method UsersShared                            getUsersShared()                            Optional. Service message: users were shared with the bot
92
 * @method ChatShared                             getChatShared()                             Optional. Service message: a chat was shared with the bot
93
 * @method string                                 getConnectedWebsite()                       Optional. The domain name of the website on which the user has logged in.
94
 * @method WriteAccessAllowed                     getWriteAccessAllowed()                     Optional. Service message: the user allowed the bot added to the attachment menu to write messages
95
 * @method PassportData                           getPassportData()                           Optional. Telegram Passport data
96
 * @method ProximityAlertTriggered                getProximityAlertTriggered()                Optional. Service message. A user in the chat triggered another user's proximity alert while sharing Live Location.
97
 * @method ForumTopicCreated                      getForumTopicCreated()                      Optional. Service message: forum topic created
98
 * @method ForumTopicEdited                       getForumTopicEdited()                       Optional. Service message: forum topic edited
99
 * @method ForumTopicClosed                       getForumTopicClosed()                       Optional. Service message: forum topic closed
100
 * @method ForumTopicReopened                     getForumTopicReopened()                     Optional. Service message: forum topic reopened
101
 * @method GeneralForumTopicHidden                getGeneralForumTopicHidden()                Optional. Service message: the 'General' forum topic hidden
102
 * @method GeneralForumTopicUnhidden              getGeneralForumTopicUnhidden()              Optional. Service message: the 'General' forum topic unhidden
103
 * @method GiveawayCreated                        getGiveawayCreated()                        Optional. Service message: a scheduled giveaway was created
104
 * @method Giveaway                               getGiveaway()                               Optional. The message is a scheduled giveaway message
105
 * @method GiveawayWinners                        getGiveawayWinners()                        Optional. A giveaway with public winners was completed
106
 * @method GiveawayCompleted                      getGiveawayCompleted()                      Optional. Service message: a giveaway without public winners was completed
107
 * @method VideoChatScheduled                     getVideoChatScheduled()                     Optional. Service message: voice chat scheduled
108
 * @method VideoChatStarted                       getVideoChatStarted()                       Optional. Service message: voice chat started
109 11
 * @method VideoChatEnded                         getVideoChatEnded()                         Optional. Service message: voice chat ended
110
 * @method VideoChatParticipantsInvited           getVideoChatParticipantsInvited()           Optional. Service message: new participants invited to a voice chat
111 11
 * @method WebAppData                             getWebAppData()                             Optional. Service message: data sent by a Web App
112 11
 * @method InlineKeyboard                         getReplyMarkup()                            Optional. Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons.
113 11
 */
114 11
class Message extends Entity implements MaybeInaccessibleMessage
115 11
{
116 11
    /**
117 11
     * {@inheritdoc}
118 11
     */
119 11
    protected function subEntities(): array
120 11
    {
121 11
        return [
122 11
            'from'                              => User::class,
123 11
            'sender_chat'                       => Chat::class,
124 11
            'chat'                              => Chat::class,
125 11
            'forward_origin'                    => MessageOriginFactory::class,
126 11
            'reply_to_message'                  => ReplyToMessage::class,
127 11
            'external_reply'                    => ExternalReplyInfo::class,
128 11
            'quote'                             => TextQuote::class,
129 11
            'via_bot'                           => User::class,
130 11
            'link_preview_options'              => LinkPreviewOptions::class,
131 11
            'entities'                          => [MessageEntity::class],
132 11
            'animation'                         => Animation::class,
133 11
            'audio'                             => Audio::class,
134 11
            'document'                          => Document::class,
135 11
            'photo'                             => [PhotoSize::class],
136 11
            'sticker'                           => Sticker::class,
137 11
            'story'                             => Story::class,
138 11
            'video'                             => Video::class,
139 11
            'video_note'                        => VideoNote::class,
140 11
            'voice'                             => Voice::class,
141 11
            'caption_entities'                  => [MessageEntity::class],
142 11
            'contact'                           => Contact::class,
143 11
            'dice'                              => Dice::class,
144 11
            'game'                              => Game::class,
145 11
            'poll'                              => Poll::class,
146 11
            'venue'                             => Venue::class,
147 11
            'location'                          => Location::class,
148 11
            'new_chat_members'                  => [User::class],
149 11
            'left_chat_member'                  => User::class,
150 11
            'new_chat_photo'                    => [PhotoSize::class],
151 11
            'message_auto_delete_timer_changed' => MessageAutoDeleteTimerChanged::class,
152 11
            'pinned_message'                    => MaybeInaccessibleMessageFactory::class,
153 11
            'invoice'                           => Invoice::class,
154 11
            'successful_payment'                => SuccessfulPayment::class,
155 11
            'users_shared'                      => UsersShared::class,
156 11
            'chat_shared'                       => ChatShared::class,
157 11
            'write_access_allowed'              => WriteAccessAllowed::class,
158 11
            'passport_data'                     => PassportData::class,
159 11
            'proximity_alert_triggered'         => ProximityAlertTriggered::class,
160 11
            'forum_topic_created'               => ForumTopicCreated::class,
161
            'forum_topic_edited'                => ForumTopicEdited::class,
162
            'forum_topic_closed'                => ForumTopicClosed::class,
163
            'forum_topic_reopened'              => ForumTopicReopened::class,
164
            'general_forum_topic_hidden'        => GeneralForumTopicHidden::class,
165
            'general_forum_topic_unhidden'      => GeneralForumTopicUnhidden::class,
166
            'giveaway_created'                  => GiveawayCreated::class,
167
            'giveaway'                          => Giveaway::class,
168 2
            'giveaway_winners'                  => GiveawayWinners::class,
169
            'giveaway_completed'                => GiveawayCompleted::class,
170 2
            'video_chat_scheduled'              => VideoChatScheduled::class,
171 2
            'video_chat_started'                => VideoChatStarted::class,
172 2
            'video_chat_ended'                  => VideoChatEnded::class,
173
            'video_chat_participants_invited'   => VideoChatParticipantsInvited::class,
174
            'web_app_data'                      => WebAppData::class,
175 2
            'reply_markup'                      => InlineKeyboard::class,
176 2
        ];
177
    }
178
179 2
    /**
180
     * return the entire command like /echo or /echo@bot1 if specified
181
     *
182
     * @return string|null
183
     */
184
    public function getFullCommand(): ?string
185
    {
186
        $text = $this->getProperty('text') ?? '';
187 2
        if (strpos($text, '/') !== 0) {
188
            return null;
189 2
        }
190
191
        $no_EOL = strtok($text, PHP_EOL);
192
        $no_space = strtok($text, ' ');
193 2
194 2
        //try to understand which separator \n or space divide /command from text
195 2
        return strlen($no_space) < strlen($no_EOL) ? $no_space : $no_EOL;
196
    }
197 2
198
    /**
199
     * Get command
200 2
     *
201 2
     * @return string|null
202
     */
203 2
    public function getCommand(): ?string
204
    {
205
        if ($command = $this->getProperty('command')) {
206 1
            return $command;
207
        }
208 1
209
        $full_command = $this->getFullCommand() ?? '';
210
        if (strpos($full_command, '/') !== 0) {
211
            return null;
212
        }
213
        $full_command = substr($full_command, 1);
214
215
        //check if command is followed by bot username
216
        $split_cmd = explode('@', $full_command);
217
        if (! isset($split_cmd[1])) {
218
            //command is not followed by name
219
            return $full_command;
220
        }
221 9
222
        if (strtolower($split_cmd[1]) === strtolower($this->getBotUsername())) {
223 9
            //command is addressed to me
224
            return $split_cmd[0];
225 9
        }
226 1
227 1
        return null;
228
    }
229
230 1
    /**
231
     * For text messages, the actual UTF-8 text of the message, 0-4096 characters.
232
     *
233 9
     * @param  bool  $without_cmd
234
     *
235
     * @return string|null
236
     */
237
    public function getText($without_cmd = false): ?string
238
    {
239
        $text = $this->getProperty('text');
240
241
        if ($without_cmd && $command = $this->getFullCommand()) {
242
            if (strlen($command) + 1 < strlen($text)) {
0 ignored issues
show
Bug introduced by
It seems like $text can also be of type null; however, parameter $string of strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

242
            if (strlen($command) + 1 < strlen(/** @scrutinizer ignore-type */ $text)) {
Loading history...
243
                return substr($text, strlen($command) + 1);
0 ignored issues
show
Bug introduced by
It seems like $text can also be of type null; however, parameter $string of substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

243
                return substr(/** @scrutinizer ignore-type */ $text, strlen($command) + 1);
Loading history...
244
            }
245
246
            return '';
247
        }
248
249
        return $text;
250
    }
251
252
    /**
253
     * Bot added in chat
254
     *
255
     * @return bool
256
     */
257 1
    public function botAddedInChat(): bool
258
    {
259 1
        foreach ($this->getNewChatMembers() as $member) {
0 ignored issues
show
Bug introduced by
The method getNewChatMembers() does not exist on Longman\TelegramBot\Entities\Message. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

259
        foreach ($this->/** @scrutinizer ignore-call */ getNewChatMembers() as $member) {
Loading history...
260 1
            if ($member instanceof User && $member->getUsername() === $this->getBotUsername()) {
261 1
                return true;
262 1
            }
263 1
        }
264 1
265 1
        return false;
266 1
    }
267 1
268 1
    /**
269 1
     * Detect type based on properties.
270 1
     *
271 1
     * @return string
272 1
     */
273 1
    public function getType(): string
274 1
    {
275 1
        $types = [
276 1
            'text',
277 1
            'animation',
278 1
            'audio',
279 1
            'document',
280 1
            'photo',
281 1
            'sticker',
282 1
            'video',
283 1
            'video_note',
284 1
            'voice',
285 1
            'contact',
286 1
            'dice',
287 1
            'game',
288 1
            'poll',
289 1
            'venue',
290 1
            'location',
291 1
            'new_chat_members',
292 1
            'left_chat_member',
293 1
            'new_chat_title',
294 1
            'new_chat_photo',
295 1
            'delete_chat_photo',
296 1
            'group_chat_created',
297 1
            'supergroup_chat_created',
298 1
            'channel_chat_created',
299 1
            'message_auto_delete_timer_changed',
300 1
            'migrate_to_chat_id',
301 1
            'migrate_from_chat_id',
302 1
            'pinned_message',
303 1
            'invoice',
304 1
            'successful_payment',
305 1
            'users_shared',
306 1
            'chat_shared',
307
            'write_access_allowed',
308 1
            'passport_data',
309 1
            'proximity_alert_triggered',
310 1
            'forum_topic_created',
311 1
            'forum_topic_edited',
312 1
            'forum_topic_closed',
313
            'forum_topic_reopened',
314
            'general_forum_topic_hidden',
315 1
            'general_forum_topic_unhidden',
316
            'video_chat_scheduled',
317
            'video_chat_started',
318
            'video_chat_ended',
319 1
            'video_chat_participants_invited',
320
            'web_app_data',
321
            'reply_markup',
322
        ];
323
324
        $is_command = $this->getCommand() !== null;
325
        foreach ($types as $type) {
326
            if ($this->getProperty($type) !== null) {
327
                if ($is_command && $type === 'text') {
328
                    return 'command';
329
                }
330
331
                return $type;
332
            }
333
        }
334
335
        return 'message';
336
    }
337
}
338