1 | package com.github.netkorp.telegram.framework.bots; |
||
0 ignored issues
–
show
Code Smell
introduced
by
![]() |
|||
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 { |
||
0 ignored issues
–
show
|
|||
38 | |||
39 | private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); |
||
0 ignored issues
–
show
|
|||
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) { |
||
0 ignored issues
–
show
|
|||
75 | this.botUsername = botUsername; |
||
0 ignored issues
–
show
|
|||
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) { |
||
0 ignored issues
–
show
|
|||
88 | // We check if the update has a message and the message has text |
||
89 | if (update.hasMessage()) { |
||
0 ignored issues
–
show
|
|||
90 | Long chatId = update.getMessage().getChatId(); |
||
0 ignored issues
–
show
|
|||
91 | |||
92 | if (commandManager.hasActiveCommand(chatId)) { |
||
0 ignored issues
–
show
|
|||
93 | if (!update.getMessage().isCommand() |
||
0 ignored issues
–
show
|
|||
94 | || !reservedCommands(getCleanedCommand(update), update)) { |
||
0 ignored issues
–
show
|
|||
95 | try { |
||
0 ignored issues
–
show
|
|||
96 | commandManager.getActiveCommand(chatId).execute(update); |
||
0 ignored issues
–
show
|
|||
97 | } catch (CommandNotActive commandNotActive) { |
||
0 ignored issues
–
show
Ignoring an Exception may lead to hard to find bugs. Consider logging or rethrowing the original exception. If you want to throw a different exception, you can set the original exception as its cause to preserve the stacktrace.
When instantiating a new Exception, you can set another Exception as its cause. See the Oracle documentation on Throwables. Usage example throw new Exception("Exception Message", originalException);
Complete Example: class ReThrowException {
public static void throwsException() {
try {
throw new Exception("I am the original exception");
} catch (final Exception e) {
throw new RuntimeException("I am the new exception", e);
}
}
public static void main(String[] args) {
try {
throwsException();
}
catch (final RuntimeException e) {
System.out.println(e.getMessage());
System.out.println("and my cause is: " + e.getCause().getMessage());
e.printStackTrace();
}
}
}
![]() |
|||
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()) { |
||
0 ignored issues
–
show
|
|||
107 | try { |
||
0 ignored issues
–
show
|
|||
108 | Map.Entry<String, String[]> commandAndArgs = getCleanedCommandAndArgs(update); |
||
0 ignored issues
–
show
|
|||
109 | Command command = commandManager.getCommand(commandAndArgs.getKey()); |
||
110 | |||
111 | if (!securityManager.isAuthorized(chatId, command)) { |
||
0 ignored issues
–
show
|
|||
112 | throw new UserNotAuthorized(); |
||
0 ignored issues
–
show
As defined in your coding style, remove the checked exception:
UserNotAuthorized . Consider throwing an unchecked exception instead.
Opionions on checked exceptions are divided. On the positive side they do make code safer and provide self-documentation on possible error conditions that the caller has to be aware of. On the negative side, they do force the caller to deal with these errors, either by catching or specifying them in the method definition. This may make the code more verbose than necessary. The Java documentation recommends using them wisely. ![]() |
|||
113 | } |
||
114 | |||
115 | if (command instanceof MultistageCommand && ((MultistageCommand) command).init(update)) { |
||
0 ignored issues
–
show
|
|||
116 | commandManager.setActiveCommand(chatId, ((MultistageCommand) command)); |
||
0 ignored issues
–
show
|
|||
117 | } else if (command instanceof SimpleCommand) { |
||
0 ignored issues
–
show
|
|||
118 | if (commandAndArgs.getValue().length == 0) { |
||
0 ignored issues
–
show
|
|||
119 | ((SimpleCommand) command).execute(update); |
||
0 ignored issues
–
show
|
|||
120 | } else { |
||
0 ignored issues
–
show
|
|||
121 | ((SimpleCommand) command).execute(update, commandAndArgs.getValue()); |
||
0 ignored issues
–
show
|
|||
122 | } |
||
123 | } |
||
124 | } catch (CommandNotFound commandNotFound) { |
||
0 ignored issues
–
show
Ignoring an Exception may lead to hard to find bugs. Consider logging or rethrowing the original exception. If you want to throw a different exception, you can set the original exception as its cause to preserve the stacktrace.
When instantiating a new Exception, you can set another Exception as its cause. See the Oracle documentation on Throwables. Usage example throw new Exception("Exception Message", originalException);
Complete Example: class ReThrowException {
public static void throwsException() {
try {
throw new Exception("I am the original exception");
} catch (final Exception e) {
throw new RuntimeException("I am the new exception", e);
}
}
public static void main(String[] args) {
try {
throwsException();
}
catch (final RuntimeException e) {
System.out.println(e.getMessage());
System.out.println("and my cause is: " + e.getCause().getMessage());
e.printStackTrace();
}
}
}
![]() |
|||
125 | sendMessage(commandNotFound.getMessage(), chatId); |
||
0 ignored issues
–
show
|
|||
126 | commandManager.getHelpCommand() |
||
127 | .filter(command -> securityManager.isAuthorized(chatId, command)) |
||
0 ignored issues
–
show
|
|||
128 | .ifPresent(command -> command.execute(update)); |
||
0 ignored issues
–
show
|
|||
129 | } catch (UserNotAuthorized userNotAuthorized) { |
||
0 ignored issues
–
show
Ignoring an Exception may lead to hard to find bugs. Consider logging or rethrowing the original exception. If you want to throw a different exception, you can set the original exception as its cause to preserve the stacktrace.
When instantiating a new Exception, you can set another Exception as its cause. See the Oracle documentation on Throwables. Usage example throw new Exception("Exception Message", originalException);
Complete Example: class ReThrowException {
public static void throwsException() {
try {
throw new Exception("I am the original exception");
} catch (final Exception e) {
throw new RuntimeException("I am the new exception", e);
}
}
public static void main(String[] args) {
try {
throwsException();
}
catch (final RuntimeException e) {
System.out.println(e.getMessage());
System.out.println("and my cause is: " + e.getCause().getMessage());
e.printStackTrace();
}
}
}
![]() |
|||
130 | sendMessage(userNotAuthorized.getMessage(), chatId); |
||
0 ignored issues
–
show
|
|||
131 | } |
||
132 | } else { |
||
0 ignored issues
–
show
|
|||
133 | sendMessage("That is not a command", chatId); |
||
0 ignored issues
–
show
|
|||
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) { |
||
0 ignored issues
–
show
|
|||
145 | String cleanedCommand = getCleanedCommand(update); |
||
0 ignored issues
–
show
|
|||
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) { |
||
0 ignored issues
–
show
|
|||
160 | return update.getMessage().getText().toLowerCase() |
||
0 ignored issues
–
show
|
|||
161 | .replace(String.format("@%s", getBotUsername().toLowerCase()), ""); |
||
0 ignored issues
–
show
|
|||
162 | } |
||
163 | |||
164 | /** |
||
165 | * Returns the bot's username. |
||
166 | * |
||
167 | * @return the bot's username. |
||
168 | */ |
||
169 | @Override |
||
170 | public String getBotUsername() { |
||
0 ignored issues
–
show
|
|||
171 | return botUsername; |
||
0 ignored issues
–
show
|
|||
172 | } |
||
173 | |||
174 | /** |
||
175 | * Returns the bot's token. |
||
176 | * |
||
177 | * @return the bot's token. |
||
178 | */ |
||
179 | @Override |
||
180 | public String getBotToken() { |
||
0 ignored issues
–
show
|
|||
181 | return botToken; |
||
0 ignored issues
–
show
|
|||
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) { |
||
0 ignored issues
–
show
|
|||
193 | Optional<MultistageCloseCommand> closeCommand = commandManager.getCloseCommand() |
||
0 ignored issues
–
show
|
|||
194 | .filter(command -> CommandManager.getCommandFullNames(command).get(0).equals(commandText)); |
||
0 ignored issues
–
show
|
|||
195 | |||
196 | if (closeCommand.isPresent()) { |
||
0 ignored issues
–
show
|
|||
197 | closeCommand.get().execute(update); |
||
0 ignored issues
–
show
|
|||
198 | return true; |
||
199 | } |
||
200 | |||
201 | Optional<MultistageDoneCommand> doneCommand = commandManager.getDoneCommand() |
||
202 | .filter(command -> CommandManager.getCommandFullNames(command).get(0).equals(commandText)); |
||
0 ignored issues
–
show
|
|||
203 | |||
204 | if (doneCommand.isPresent()) { |
||
0 ignored issues
–
show
|
|||
205 | doneCommand.get().execute(update); |
||
0 ignored issues
–
show
|
|||
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) { |
||
0 ignored issues
–
show
|
|||
220 | sendMessage(content, idChat, false); |
||
0 ignored issues
–
show
|
|||
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) { |
||
0 ignored issues
–
show
|
|||
231 | SendMessage message = new SendMessage() // Create a SendMessage object with mandatory fields |
||
0 ignored issues
–
show
|
|||
232 | .setChatId(idChat).setText(content).enableHtml(html); |
||
233 | |||
234 | try { |
||
0 ignored issues
–
show
|
|||
235 | this.execute(message); // Call method to send the message |
||
0 ignored issues
–
show
|
|||
236 | } catch (TelegramApiException e) { |
||
0 ignored issues
–
show
|
|||
237 | LOG.error(e.getMessage(), e); |
||
0 ignored issues
–
show
|
|||
238 | } |
||
239 | } |
||
240 | } |
||
241 |