Completed
Push — master ( 517572...36253a )
by Roannel Fernández
03:06
created

CommandManager(List,CommandProperties)   A

Complexity

Conditions 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 10
rs 9.9
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 free 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 secured 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 free commands into a map to get a quick access from its name.
36
     */
37
    private final Map<String, Command> freeCommands;
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.freeCommands = 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.getFree()));
79
    }
80
81
    /**
82
     * Adds the command to the list of available/free 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 freeCommandNames the free command list.
87
     * @see #commands
88
     * @see #freeCommands
89
     * @see #closeCommand
90
     * @see #doneCommand
91
     * @see #helpCommand
92
     */
93
    private void addCommand(Command command, List<String> freeCommandNames) {
94
        this.commands.put(getCommand(command), command);
95
96
        if (isFreeCommand(command, freeCommandNames)) {
97
            this.freeCommands.put(getCommand(command), command);
98
        }
99
100
        if (command instanceof MultistageCloseCommand) {
101
            closeCommand = getCommand(command);
102
        } else if (command instanceof MultistageDoneCommand) {
103
            doneCommand = getCommand(command);
104
        } else if (command instanceof HelpCommand) {
105
            helpCommand = getCommand(command);
106
        }
107
    }
108
109
    /**
110
     * Returns {@code true} if the command is free.
111
     *
112
     * @param command          the command to be processed.
113
     * @param freeCommandNames the list of free commands.
114
     * @return {@code true} if the command is free; {@code false} otherwise.
115
     */
116
    private boolean isFreeCommand(Command command, List<String> freeCommandNames) {
117
        return freeCommandNames.contains(getCommandName(command))
118
                || freeCommandNames.contains(getCommand(command))
119
                || command.getClass().getAnnotation(TelegramCommand.class).free();
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 command the command from which the name will be identified.
138
     * @return the name of the command.
139
     */
140
    public static String getCommand(Command command) {
141
        String commandName = getCommandName(command);
142
        return commandName.startsWith("/") ? commandName : String.format("/%s", commandName);
143
    }
144
145
    /**
146
     * Returns the {@link Command} instance from the command name.
147
     *
148
     * @param command the command name.
149
     * @return the {@link Command} instance.
150
     * @throws CommandNotFound if the name is not related to any commands.
151
     */
152
    public Command getCommand(String command) throws CommandNotFound {
153
        if (!this.commands.containsKey(command)) {
154
            throw new CommandNotFound();
155
        }
156
157
        return this.commands.get(command);
158
    }
159
160
    /**
161
     * Returns the free {@link Command} instance from the command name.
162
     *
163
     * @param command the command name.
164
     * @return the free {@link Command} instance.
165
     * @throws CommandNotFound if the name is not related to any commands.
166
     */
167
    public Command getFreeCommand(String command) throws CommandNotFound {
168
        if (!this.freeCommands.containsKey(command)) {
169
            throw new CommandNotFound();
170
        }
171
172
        return this.freeCommands.get(command);
173
    }
174
175
    /**
176
     * Sets the multistage command as the active one.
177
     *
178
     * @param idChat  the chat identification of the user for whom the multistage command will be active.
179
     * @param command the command to activate.
180
     */
181
    public void setActiveCommand(final Long idChat, final MultistageCommand command) {
182
        this.activeCommand.put(idChat, command);
183
    }
184
185
    /**
186
     * Gets the active command for the user.
187
     *
188
     * @param idChat the chat identification of the user.
189
     * @return the active command.
190
     * @throws CommandNotActive if there is no an active command.
191
     */
192
    public MultistageCommand getActiveCommand(Long idChat) throws CommandNotActive {
193
        if (!hasActiveCommand(idChat)) {
194
            throw new CommandNotActive();
195
        }
196
197
        return activeCommand.get(idChat);
198
    }
199
200
    /**
201
     * Removes the active command.
202
     *
203
     * @param idChat the chat identification of the user.
204
     */
205
    public void removeActiveCommand(Long idChat) {
206
        activeCommand.remove(idChat);
207
    }
208
209
    /**
210
     * Returns {@code true} if there is an active command.
211
     *
212
     * @param idChat the chat identification of the user.
213
     * @return {@code true} if there is an active command; {@code false} otherwise.
214
     */
215
    public boolean hasActiveCommand(Long idChat) {
216
        return activeCommand.containsKey(idChat);
217
    }
218
219
    /**
220
     * Returns the {@link MultistageCloseCommand} if it exists.
221
     *
222
     * @return the {@link MultistageCloseCommand} instance.
223
     */
224
    public Optional<Command> getCloseCommand() {
225
        try {
226
            return Optional.ofNullable(getCommand(this.closeCommand));
227
        } catch (CommandNotFound commandNotFound) {
228
            return Optional.empty();
229
        }
230
    }
231
232
    /**
233
     * Returns the {@link MultistageDoneCommand} if it exists.
234
     *
235
     * @return the {@link MultistageDoneCommand} instance.
236
     */
237
    public Optional<Command> getDoneCommand() {
238
        try {
239
            return Optional.ofNullable(getCommand(this.doneCommand));
240
        } catch (CommandNotFound commandNotFound) {
241
            return Optional.empty();
242
        }
243
    }
244
245
    /**
246
     * Returns the {@link HelpCommand} if it exists.
247
     *
248
     * @return the {@link HelpCommand} instance.
249
     */
250
    public Optional<Command> getHelpCommand() {
251
        try {
252
            return Optional.ofNullable(getCommand(this.helpCommand));
253
        } catch (CommandNotFound commandNotFound) {
254
            return Optional.empty();
255
        }
256
    }
257
258
    /**
259
     * Returns a list with the available commands.
260
     *
261
     * @return the available commands.
262
     */
263
    public Collection<Command> getAvailableCommands() {
264
        return commands.values();
265
    }
266
267
    /**
268
     * Returns a list with the available free commands.
269
     *
270
     * @return the available free commands.
271
     */
272
    public Collection<Command> getAvailableFreeCommands() {
273
        return freeCommands.values();
274
    }
275
}
276