Passed
Push — master ( ec853f...5c2486 )
by Roannel Fernández
02:47
created

reservedCommands(String,Update)   A

Complexity

Conditions 3

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 12
dl 0
loc 18
rs 9.8
c 1
b 0
f 0
cc 3
1
package com.github.netkorp.telegram.framework.bots;
2
3
import com.github.netkorp.telegram.framework.commands.interfaces.Command;
4
import com.github.netkorp.telegram.framework.commands.interfaces.MultistageCommand;
5
import com.github.netkorp.telegram.framework.commands.interfaces.SimpleCommand;
6
import com.github.netkorp.telegram.framework.commands.multistage.MultistageCloseCommand;
7
import com.github.netkorp.telegram.framework.commands.multistage.MultistageDoneCommand;
8
import com.github.netkorp.telegram.framework.exceptions.CommandNotActive;
9
import com.github.netkorp.telegram.framework.exceptions.CommandNotFound;
10
import com.github.netkorp.telegram.framework.exceptions.UserNotAuthorized;
11
import com.github.netkorp.telegram.framework.managers.CommandManager;
12
import com.github.netkorp.telegram.framework.managers.SecurityManager;
13
import org.slf4j.Logger;
14
import org.slf4j.LoggerFactory;
15
import org.springframework.beans.factory.annotation.Autowired;
16
import org.springframework.beans.factory.annotation.Value;
17
import org.springframework.context.annotation.Lazy;
18
import org.springframework.stereotype.Component;
19
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
20
import org.telegram.telegrambots.meta.api.methods.BotApiMethod;
21
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
22
import org.telegram.telegrambots.meta.api.objects.Update;
23
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
24
25
import java.lang.invoke.MethodHandles;
26
import java.util.AbstractMap;
27
import java.util.Arrays;
28
import java.util.Map;
29
import java.util.Optional;
30
31
/**
32
 * Provides the component for sharing information with Telegram using
33
 * <a href="https://core.telegram.org/bots/api#getupdates">long-polling</a> method.
34
 * It has the responsibility to execute the proper command when an incoming message is received.
35
 */
36
@Component
37
public class PollingTelegramBot extends TelegramLongPollingBot {
38
39
    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
40
41
    /**
42
     * The bot's username.
43
     */
44
    private String botUsername;
45
46
    /**
47
     * The bot's token.
48
     */
49
    private String botToken;
50
51
    /**
52
     * The component to know which user is authorized.
53
     */
54
    private final SecurityManager securityManager;
55
56
    /**
57
     * The component for managing all of the available commands in the bot.
58
     */
59
    private final CommandManager commandManager;
60
61
    /**
62
     * Constructs a new {@link PollingTelegramBot} instance with both username and token of the bot,
63
     * the {@link SecurityManager} component instance and the {@link CommandManager} instance.
64
     *
65
     * @param botUsername     the username of the bot.
66
     * @param botToken        the token of the bot.
67
     * @param securityManager the {@link SecurityManager} component instance.
68
     * @param commandManager  the {@link CommandManager} instance.
69
     */
70
    @Autowired
71
    public PollingTelegramBot(@Value("${telegram.bots.username}") String botUsername,
72
                              @Value("${telegram.bots.token}") String botToken,
73
                              SecurityManager securityManager,
74
                              @Lazy CommandManager commandManager) {
75
        this.botUsername = botUsername;
76
        this.botToken = botToken;
77
        this.securityManager = securityManager;
78
        this.commandManager = commandManager;
79
    }
80
81
    /**
82
     * This method is called when receiving updates via GetUpdates method.
83
     *
84
     * @param update Update received.
85
     */
86
    @Override
87
    public void onUpdateReceived(Update update) {
88
        // We check if the update has a message and the message has text
89
        if (update.hasMessage()) {
90
            Long chatId = update.getMessage().getChatId();
91
92
            if (commandManager.hasActiveCommand(chatId)) {
93
                if (!update.getMessage().isCommand()
94
                        || !reservedCommands(getCleanedCommand(update), update)) {
95
                    try {
96
                        commandManager.getActiveCommand(chatId).execute(update);
97
                    } catch (CommandNotActive commandNotActive) {
98
                        // Do nothing. This point is impossible to reach.
99
                    }
100
                }
101
102
                return;
103
            }
104
105
            // Checking if this is a command
106
            if (update.getMessage().isCommand()) {
107
                try {
108
                    Map.Entry<String, String[]> commandAndArgs = getCleanedCommandAndArgs(update);
109
                    Command command = commandManager.getCommand(commandAndArgs.getKey());
110
111
                    if (!securityManager.isAuthorized(chatId, command)) {
112
                        throw new UserNotAuthorized();
113
                    }
114
115
                    if (command instanceof MultistageCommand && ((MultistageCommand) command).init(update)) {
116
                        commandManager.setActiveCommand(chatId, ((MultistageCommand) command));
117
                    } else if (command instanceof SimpleCommand) {
118
                        if (commandAndArgs.getValue().length == 0) {
119
                            ((SimpleCommand) command).execute(update);
120
                        } else {
121
                            ((SimpleCommand) command).execute(update, commandAndArgs.getValue());
122
                        }
123
                    }
124
                } catch (CommandNotFound commandNotFound) {
125
                    sendMessage(commandNotFound.getMessage(), chatId);
126
                    commandManager.getHelpCommand()
127
                            .filter(command -> securityManager.isAuthorized(chatId, command))
128
                            .ifPresent(command -> command.execute(update));
129
                } catch (UserNotAuthorized userNotAuthorized) {
130
                    sendMessage(userNotAuthorized.getMessage(), chatId);
131
                }
132
            } else {
133
                sendMessage("That is not a command", chatId);
134
            }
135
        }
136
    }
137
138
    /**
139
     * Returns the command invoked by the user and the parameters, cleaning the text and deleting the bot's username.
140
     *
141
     * @param update the received update.
142
     * @return the command and the parameters.
143
     */
144
    private Map.Entry<String, String[]> getCleanedCommandAndArgs(Update update) {
145
        String cleanedCommand = getCleanedCommand(update);
146
147
        String[] dividedText = cleanedCommand.split(" ");
148
149
        return new AbstractMap.SimpleEntry<>(dividedText[0],
150
                Arrays.copyOfRange(dividedText, 1, dividedText.length));
151
    }
152
153
    /**
154
     * Returns the command invoked by the user, cleaning the text and deleting the bot's username.
155
     *
156
     * @param update the received update.
157
     * @return the command.
158
     */
159
    private String getCleanedCommand(Update update) {
160
        return update.getMessage().getText().toLowerCase()
161
                .replace(String.format("@%s", getBotUsername().toLowerCase()), "");
162
    }
163
164
    /**
165
     * Returns the bot's username.
166
     *
167
     * @return the bot's username.
168
     */
169
    @Override
170
    public String getBotUsername() {
171
        return botUsername;
172
    }
173
174
    /**
175
     * Returns the bot's token.
176
     *
177
     * @return the bot's token.
178
     */
179
    @Override
180
    public String getBotToken() {
181
        return botToken;
182
    }
183
184
    /**
185
     * Checks if the entered text matches with some reserved command.
186
     * If this is the case it will execute the corresponding command.
187
     *
188
     * @param commandText the text entered by the user.
189
     * @param update      the message sent by the user.
190
     * @return {@code true} if some reserved command was executed; {@code false} otherwise.
191
     */
192
    private boolean reservedCommands(String commandText, Update update) {
193
        Optional<MultistageCloseCommand> closeCommand = commandManager.getCloseCommand()
194
                .filter(command -> CommandManager.getCommandFullName(command).equals(commandText));
195
196
        if (closeCommand.isPresent()) {
197
            closeCommand.get().execute(update);
198
            return true;
199
        }
200
201
        Optional<MultistageDoneCommand> doneCommand = commandManager.getDoneCommand()
202
                .filter(command -> CommandManager.getCommandFullName(command).equals(commandText));
203
204
        if (doneCommand.isPresent()) {
205
            doneCommand.get().execute(update);
206
            return true;
207
        }
208
209
        return false;
210
    }
211
212
    /**
213
     * Sends a text message to Telegram.
214
     * This is a shortcut for {@link #sendMessage(String, Long, boolean)} with HTML format disabled.
215
     *
216
     * @param content the message content.
217
     * @param idChat  the chat identification to which the message should be sent.
218
     */
219
    public void sendMessage(String content, Long idChat) {
220
        sendMessage(content, idChat, false);
221
    }
222
223
    /**
224
     * Sends a text message to Telegram. This is a shortcut for {@link #execute(BotApiMethod)}.
225
     *
226
     * @param content the message content.
227
     * @param idChat  the chat identification to which the message should be sent.
228
     * @param html    {@code true} if HTML format is enabled or {@code false} otherwise.
229
     */
230
    public void sendMessage(String content, Long idChat, boolean html) {
231
        SendMessage message = new SendMessage() // Create a SendMessage object with mandatory fields
232
                .setChatId(idChat).setText(content).enableHtml(html);
233
234
        try {
235
            this.execute(message); // Call method to send the message
236
        } catch (TelegramApiException e) {
237
            LOG.error(e.getMessage(), e);
238
        }
239
    }
240
}
241