Passed
Push — master ( 482350...d5f627 )
by Roannel Fernández
03:16
created

getNonSecureCommand(String)   A

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
package com.github.netkorp.telegram.framework.managers;
2
3
import com.github.netkorp.telegram.framework.annotations.TelegramCommand;
4
import com.github.netkorp.telegram.framework.commands.interfaces.Command;
5
import com.github.netkorp.telegram.framework.commands.interfaces.HelpCommand;
6
import com.github.netkorp.telegram.framework.commands.interfaces.MultistageCommand;
7
import com.github.netkorp.telegram.framework.commands.multistage.MultistageCloseCommand;
8
import com.github.netkorp.telegram.framework.commands.multistage.MultistageDoneCommand;
9
import com.github.netkorp.telegram.framework.properties.CommandProperties;
10
import com.github.netkorp.telegram.framework.exceptions.CommandNotActive;
11
import com.github.netkorp.telegram.framework.exceptions.CommandNotFound;
12
import org.springframework.beans.factory.annotation.Autowired;
13
import org.springframework.stereotype.Component;
14
15
import java.util.Collection;
16
import java.util.HashMap;
17
import java.util.List;
18
import java.util.Map;
19
import java.util.Optional;
20
21
/**
22
 * Provides the component for managing all of the commands available in the bot.
23
 * It includes the management of non-secure commands, active command, basic commands and
24
 * those commands that are involved in the multistage command flow.
25
 */
26
@Component
27
public class CommandManager {
28
29
    /**
30
     * The list of the available commands into a map to get a quick access from its name.
31
     */
32
    private final Map<String, Command> commands;
33
34
    /**
35
     * The list of the non-secure commands into a map to get a quick access from its name.
36
     */
37
    private final Map<String, Command> nonSecureCommands;
38
39
    /**
40
     * The command that is active for each user.
41
     */
42
    private final Map<Long, MultistageCommand> activeCommand;
43
44
    /**
45
     * The name used for invoking the command that closes an active conversation with the bot indicating to the active command that the conversation is closed.
46
     * It is kept here for a quick access to the close command.
47
     */
48
    private String closeCommand;
49
50
    /**
51
     * The name used for invoking the command that closes an active conversation with the bot indicating to the active command that the conversation is done.
52
     * It is kept here for a quick access to the done command.
53
     */
54
    private String doneCommand;
55
56
    /**
57
     * The name used for invoking the command that shows the help of the bot.
58
     * It is kept here for a quick access to the help command.
59
     */
60
    private String helpCommand;
61
62
    /**
63
     * Constructs a new {@link CommandManager} instance with the list of available {@link Command}
64
     * and the properties of the commands.
65
     *
66
     * @param commands          the list of available {@link Command}.
67
     * @param commandProperties the properties of the commands.
68
     */
69
    @Autowired
70
    public CommandManager(List<Command> commands, CommandProperties commandProperties) {
71
        this.commands = new HashMap<>();
72
        this.nonSecureCommands = new HashMap<>();
73
74
        this.activeCommand = new HashMap<>();
75
76
        commands.stream()
77
                .filter(item -> item.getClass().isAnnotationPresent(TelegramCommand.class))
78
                .forEach(command -> addCommand(command, commandProperties.getNonSecure()));
79
    }
80
81
    /**
82
     * Adds the command to the list of available/non-secure commands and
83
     * sets the name for {@link #closeCommand}, {@link #doneCommand} and {@link #helpCommand}.
84
     *
85
     * @param command           the command to be added.
86
     * @param nonSecureCommands the non-secure command list.
87
     * @see #commands
88
     * @see #nonSecureCommands
89
     * @see #closeCommand
90
     * @see #doneCommand
91
     * @see #helpCommand
92
     */
93
    private void addCommand(Command command, List<String> nonSecureCommands) {
94
        this.commands.put(getCommandFullName(command), command);
95
96
        if (isNonSecureCommand(command, nonSecureCommands)) {
97
            this.nonSecureCommands.put(getCommandFullName(command), command);
98
        }
99
100
        if (command instanceof MultistageCloseCommand) {
101
            closeCommand = getCommandFullName(command);
102
        } else if (command instanceof MultistageDoneCommand) {
103
            doneCommand = getCommandFullName(command);
104
        } else if (command instanceof HelpCommand) {
105
            helpCommand = getCommandFullName(command);
106
        }
107
    }
108
109
    /**
110
     * Returns {@code true} if the command is non-secure.
111
     *
112
     * @param command           the command to be processed.
113
     * @param nonSecureCommands the list of non-secure commands.
114
     * @return {@code true} if the command is non-secure; {@code false} otherwise.
115
     */
116
    private boolean isNonSecureCommand(Command command, List<String> nonSecureCommands) {
117
        return nonSecureCommands.contains(getCommandName(command))
118
                || nonSecureCommands.contains(getCommandFullName(command))
119
                || !command.getClass().getAnnotation(TelegramCommand.class).secure();
120
    }
121
122
    /**
123
     * Returns the name of the command (the same name declared on {@link TelegramCommand#name()}).
124
     * The name may include the slash (/).
125
     *
126
     * @param command the command from which the name will be identified.
127
     * @return the name of the command.
128
     */
129
    @SuppressWarnings("WeakerAccess")
130
    public static String getCommandName(Command command) {
131
        return command.getClass().getAnnotation(TelegramCommand.class).name();
132
    }
133
134
    /**
135
     * Returns the name of the command. It includes the slash (/).
136
     *
137
     * @param commandName the command's name (the same name declared on {@link TelegramCommand#name()})
138
     * @return the name of the command.
139
     */
140
    public static String getCommandFullName(String commandName) {
141
        return commandName.startsWith("/") ? commandName : String.format("/%s", commandName);
142
    }
143
144
    /**
145
     * Returns the name of the command. It includes the slash (/).
146
     *
147
     * @param command the command from which the name will be identified.
148
     * @return the name of the command.
149
     */
150
    public static String getCommandFullName(Command command) {
151
        return getCommandFullName(getCommandName(command));
152
    }
153
154
    /**
155
     * Returns the {@link Command} instance from the command name.
156
     *
157
     * @param command the command name.
158
     * @return the {@link Command} instance.
159
     * @throws CommandNotFound if the name is not related to any commands.
160
     */
161
    public Command getCommand(String command) throws CommandNotFound {
162
        if (!this.commands.containsKey(command)) {
163
            throw new CommandNotFound();
164
        }
165
166
        return this.commands.get(command);
167
    }
168
169
    /**
170
     * Returns the non-secure {@link Command} instance from the command name.
171
     *
172
     * @param command the command name.
173
     * @return the non-secure {@link Command} instance.
174
     * @throws CommandNotFound if the name is not related to any non-secure commands.
175
     */
176
    public Command getNonSecureCommand(String command) throws CommandNotFound {
177
        if (!this.nonSecureCommands.containsKey(command)) {
178
            throw new CommandNotFound();
179
        }
180
181
        return this.nonSecureCommands.get(command);
182
    }
183
184
    /**
185
     * Sets the multistage command as the active one.
186
     *
187
     * @param idChat  the chat identification of the user for whom the multistage command will be active.
188
     * @param command the command to activate.
189
     */
190
    public void setActiveCommand(final Long idChat, final MultistageCommand command) {
191
        this.activeCommand.put(idChat, command);
192
    }
193
194
    /**
195
     * Gets the active command for the user.
196
     *
197
     * @param idChat the chat identification of the user.
198
     * @return the active command.
199
     * @throws CommandNotActive if there is no an active command.
200
     */
201
    public MultistageCommand getActiveCommand(Long idChat) throws CommandNotActive {
202
        if (!hasActiveCommand(idChat)) {
203
            throw new CommandNotActive();
204
        }
205
206
        return activeCommand.get(idChat);
207
    }
208
209
    /**
210
     * Removes the active command.
211
     *
212
     * @param idChat the chat identification of the user.
213
     */
214
    public void removeActiveCommand(Long idChat) {
215
        activeCommand.remove(idChat);
216
    }
217
218
    /**
219
     * Returns {@code true} if there is an active command.
220
     *
221
     * @param idChat the chat identification of the user.
222
     * @return {@code true} if there is an active command; {@code false} otherwise.
223
     */
224
    public boolean hasActiveCommand(Long idChat) {
225
        return activeCommand.containsKey(idChat);
226
    }
227
228
    /**
229
     * Returns the {@link MultistageCloseCommand} if it exists.
230
     *
231
     * @return the {@link MultistageCloseCommand} instance.
232
     */
233
    public Optional<MultistageCloseCommand> getCloseCommand() {
234
        try {
235
            return Optional.ofNullable(((MultistageCloseCommand) getCommand(this.closeCommand)));
236
        } catch (CommandNotFound commandNotFound) {
237
            return Optional.empty();
238
        }
239
    }
240
241
    /**
242
     * Returns the {@link MultistageDoneCommand} if it exists.
243
     *
244
     * @return the {@link MultistageDoneCommand} instance.
245
     */
246
    public Optional<MultistageDoneCommand> getDoneCommand() {
247
        try {
248
            return Optional.ofNullable(((MultistageDoneCommand) getCommand(this.doneCommand)));
249
        } catch (CommandNotFound commandNotFound) {
250
            return Optional.empty();
251
        }
252
    }
253
254
    /**
255
     * Returns the {@link HelpCommand} if it exists.
256
     *
257
     * @return the {@link HelpCommand} instance.
258
     */
259
    public Optional<HelpCommand> getHelpCommand() {
260
        try {
261
            return Optional.ofNullable(((HelpCommand) getCommand(this.helpCommand)));
262
        } catch (CommandNotFound commandNotFound) {
263
            return Optional.empty();
264
        }
265
    }
266
267
    /**
268
     * Returns a list with the available commands.
269
     *
270
     * @return the available commands.
271
     */
272
    public Collection<Command> getAvailableCommands() {
273
        return commands.values();
274
    }
275
276
    /**
277
     * Returns a list with the available non-secure commands.
278
     *
279
     * @return the available non-secure commands.
280
     */
281
    public Collection<Command> getAvailableNonSecureCommands() {
282
        return nonSecureCommands.values();
283
    }
284
}
285