Completed
Push — master ( 8624b3...521491 )
by Roannel Fernández
03:03
created

sendMessage(String,Long)   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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