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

com.github.netkorp.telegram.framework.managers.CommandManager   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 249
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
wmc 27
c 8
b 0
f 0
dl 0
loc 249
eloc 72
rs 10

16 Methods

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