Kaynağa Gözat

Added CommandOptions and implemented behaviour

Added skill command
Added config-based cache for EVE data
master
Chris Smith 15 yıl önce
ebeveyn
işleme
f75bd371f4

+ 1
- 1
externals/evetool

@@ -1 +1 @@
1
-Subproject commit 981207b68e01d080e71dcb1dbb574ab03c95492c
1
+Subproject commit c6d26678d8d54c9b4779459724f3f83cc944c2c9

+ 27
- 0
src/com/md87/charliebravo/CommandOptions.java Dosyayı Görüntüle

@@ -0,0 +1,27 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.charliebravo;
7
+
8
+import java.lang.annotation.ElementType;
9
+import java.lang.annotation.Retention;
10
+import java.lang.annotation.RetentionPolicy;
11
+import java.lang.annotation.Target;
12
+
13
+/**
14
+ *
15
+ * @author chris
16
+ */
17
+@Retention(RetentionPolicy.RUNTIME)
18
+@Target(ElementType.TYPE)
19
+public @interface CommandOptions {
20
+
21
+    boolean requireAuthorisation() default false;
22
+
23
+    int requireLevel() default -1;
24
+
25
+    String[] requiredSettings() default {};
26
+
27
+}

+ 4
- 0
src/com/md87/charliebravo/Config.java Dosyayı Görüntüle

@@ -89,4 +89,8 @@ public class Config implements Runnable {
89 89
         }
90 90
     }
91 91
 
92
+    public ConfigFile getConfigfile() {
93
+        return configfile;
94
+    }
95
+
92 96
 }

+ 54
- 0
src/com/md87/charliebravo/ConfigCache.java Dosyayı Görüntüle

@@ -0,0 +1,54 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.charliebravo;
7
+
8
+import com.dmdirc.util.ConfigFile;
9
+import java.util.Map;
10
+import uk.co.md87.evetool.api.io.ApiCache;
11
+import uk.co.md87.evetool.api.io.Downloader;
12
+
13
+/**
14
+ *
15
+ * @author chris
16
+ */
17
+public class ConfigCache implements ApiCache {
18
+    
19
+    private static final String DOMAIN = ".eve-cache";
20
+
21
+    private final ConfigFile config;
22
+
23
+    public ConfigCache(ConfigFile config) {
24
+        this.config = config;
25
+    }
26
+
27
+    public void setCache(String method, Map<String, String> args, String data, long cacheUntil) {
28
+        final String url = method + "?" + Downloader.encodeArguments(args);
29
+        config.getKeyDomain(DOMAIN).put(url, cacheUntil + ";"
30
+                + System.currentTimeMillis() + ";" + data);
31
+    }
32
+
33
+    public CacheStatus getCacheStatus(String method, Map<String, String> args) {
34
+        final String url = method + "?" + Downloader.encodeArguments(args);
35
+
36
+        if (config.getKeyDomain(DOMAIN).containsKey(url)) {
37
+            final String[] data = config.getKeyDomain(DOMAIN).get(url).split(";", 3);
38
+            final long expirary = Long.valueOf(data[0]);
39
+            return expirary < System.currentTimeMillis() ? CacheStatus.EXPIRED : CacheStatus.HIT;
40
+        } else {
41
+            return CacheStatus.MISS;
42
+        }
43
+    }
44
+
45
+    public CacheResult getCache(String method, Map<String, String> args) {
46
+        final String url = method + "?" + Downloader.encodeArguments(args);
47
+        final String[] data = config.getKeyDomain(DOMAIN).get(url).split(";", 3);
48
+        final long expirary = Long.valueOf(data[0]);
49
+        final long cachedAt = Long.valueOf(data[1]);
50
+
51
+        return new CacheResult(data[2], cachedAt, expirary);
52
+    }
53
+
54
+}

+ 0
- 63
src/com/md87/charliebravo/Formatter.java Dosyayı Görüntüle

@@ -1,63 +0,0 @@
1
-/*
2
- * To change this template, choose Tools | Templates
3
- * and open the template in the editor.
4
- */
5
-
6
-package com.md87.charliebravo;
7
-
8
-/**
9
- *
10
- * @author chris
11
- */
12
-public class Formatter {
13
-
14
-    /**
15
-     * Tests for and adds one component of the duration format.
16
-     *
17
-     * @param builder The string builder to append text to
18
-     * @param current The number of seconds in the duration
19
-     * @param duration The number of seconds in this component
20
-     * @param name The name of this component
21
-     * @return The number of seconds used by this component
22
-     */
23
-    private static int doDuration(final StringBuilder builder, final int current,
24
-            final int duration, final String name) {
25
-        int res = 0;
26
-
27
-        if (current >= duration) {
28
-            final int units = current / duration;
29
-            res = units * duration;
30
-
31
-            if (builder.length() > 0) {
32
-                builder.append(", ");
33
-            }
34
-
35
-            builder.append(units);
36
-            builder.append(' ');
37
-            builder.append(name + (units != 1 ? 's' : ""));
38
-        }
39
-
40
-        return res;
41
-    }
42
-
43
-    /**
44
-     * Formats the specified number of seconds as a string containing the
45
-     * number of days, hours, minutes and seconds.
46
-     *
47
-     * @param duration The duration in seconds to be formatted
48
-     * @return A textual version of the duration
49
-     */
50
-    public static String formatDuration(final int duration) {
51
-        final StringBuilder buff = new StringBuilder();
52
-
53
-        int seconds = duration;
54
-
55
-        seconds -= doDuration(buff, seconds, 60*60*24, "day");
56
-        seconds -= doDuration(buff, seconds, 60*60, "hour");
57
-        seconds -= doDuration(buff, seconds, 60, "minute");
58
-        seconds -= doDuration(buff, seconds, 1, "second");
59
-
60
-        return buff.toString();
61
-    }
62
-
63
-}

+ 91
- 3
src/com/md87/charliebravo/InputHandler.java Dosyayı Görüntüle

@@ -20,12 +20,14 @@ import com.md87.charliebravo.commands.HelpCommand;
20 20
 import com.md87.charliebravo.commands.IssueCommand;
21 21
 import com.md87.charliebravo.commands.QuitCommand;
22 22
 import com.md87.charliebravo.commands.SetCommand;
23
+import com.md87.charliebravo.commands.SkillCommand;
23 24
 import com.md87.charliebravo.commands.TranslateCommand;
24 25
 import com.md87.charliebravo.commands.WhoisCommand;
25 26
 import java.util.ArrayList;
26 27
 import java.util.HashMap;
27 28
 import java.util.List;
28 29
 import java.util.Map;
30
+import java.util.regex.Matcher;
29 31
 
30 32
 /**
31 33
  *
@@ -54,6 +56,7 @@ public class InputHandler implements IChannelMessage, IPrivateMessage {
54 56
         commands.add(new IssueCommand());
55 57
         commands.add(new GitCommand());
56 58
         commands.add(new SetCommand());
59
+        commands.add(new SkillCommand());
57 60
     }
58 61
 
59 62
     public Config getConfig() {
@@ -81,9 +84,9 @@ public class InputHandler implements IChannelMessage, IPrivateMessage {
81 84
 
82 85
     public void onChannelMessage(final IRCParser tParser, final ChannelInfo cChannel,
83 86
             final ChannelClientInfo cChannelClient, final String sMessage, final String sHost) {
84
-        if (sMessage.startsWith(tParser.getMyNickname() + ", ")) {
87
+        if (sMessage.matches("^" + Matcher.quoteReplacement(tParser.getMyNickname()) + "[,:!] .*")) {
85 88
             handleInput(ClientInfo.parseHost(sHost), cChannel.getName(),
86
-                    sMessage.substring((tParser.getMyNickname() + ", ").length()));
89
+                    sMessage.substring(tParser.getMyNickname().length() + 2));
87 90
         }
88 91
     }
89 92
 
@@ -92,6 +95,14 @@ public class InputHandler implements IChannelMessage, IPrivateMessage {
92 95
     }
93 96
 
94 97
     protected void handleInput(final String source, final String target, final String text) {
98
+        new Thread(new Runnable() {
99
+            public void run() {
100
+                handleInputImpl(source, target, text);
101
+            }
102
+        }).start();
103
+    }
104
+
105
+    protected void handleInputImpl(final String source, final String target, final String text) {
95 106
         final Response response = new Response(parser, source, target);
96 107
         Command command = null;
97 108
         int index = 0;
@@ -116,7 +127,59 @@ public class InputHandler implements IChannelMessage, IPrivateMessage {
116 127
             }
117 128
 
118 129
             if (command != null) {
119
-                command.execute(this, response, text.substring(Math.min(text.length(), index)));
130
+                boolean cont = true;
131
+
132
+                if (command.getClass().isAnnotationPresent(CommandOptions.class)) {
133
+                    final CommandOptions opts = command.getClass()
134
+                            .getAnnotation(CommandOptions.class);
135
+
136
+                    final String id = (String) parser.getClientInfoOrFake(source)
137
+                            .getMap().get("OpenID");
138
+
139
+                    if (opts.requireAuthorisation() && id == null) {
140
+                        response.sendMessage("You must be authorised to use that command", true);
141
+                        cont = false;
142
+                    } else if (opts.requireLevel() > -1 &&
143
+                            (!config.hasOption(id, "admin.level")
144
+                            || (Integer.valueOf(config.getOption(id, "admin.level"))
145
+                            < opts.requireLevel()))) {
146
+                        response.sendMessage("You have insufficient access to " +
147
+                                "use that command", true);
148
+                        response.addFollowup(new LevelErrorFollowup(response.getSource(),
149
+                                opts.requireLevel(),
150
+                                config.hasOption(id, "admin.level")
151
+                                ? Integer.valueOf(config.getOption(id, "admin.level")) : -1));
152
+                        cont = false;
153
+                    } else {
154
+                        int count = 0;
155
+                        final StringBuilder missing = new StringBuilder();
156
+                        
157
+                        for (String setting : opts.requiredSettings()) {
158
+
159
+                            if (!config.hasOption(id, setting)) {
160
+                                if (missing.length() > 0) {
161
+                                    missing.append(", ");
162
+                                }
163
+
164
+                                count++;
165
+                                missing.append(setting);
166
+                            }
167
+                        }
168
+
169
+                        if (count > 0) {
170
+                            cont = false;
171
+                            response.sendRawMessage("You must have the following setting"
172
+                                    + (count == 1 ? "" : "s")
173
+                                    + " in order to use that command, " + response.getSource()
174
+                                    + ": " + missing.toString()
175
+                                    .replaceAll("^(.*), (.*?)$", "$1 and $2") + ".");
176
+                        }
177
+                    }
178
+                }
179
+
180
+                if (cont) {
181
+                    command.execute(this, response, text.substring(Math.min(text.length(), index)));
182
+                }
120 183
             }
121 184
         } catch (Throwable ex) {
122 185
             response.sendMessage("an error has occured: " + ex.getMessage());
@@ -131,6 +194,31 @@ public class InputHandler implements IChannelMessage, IPrivateMessage {
131 194
         }
132 195
     }
133 196
 
197
+    protected static class LevelErrorFollowup implements Followup {
198
+
199
+        private final String source;
200
+        private final int required, desired;
201
+
202
+        public LevelErrorFollowup(String source, int required, int desired) {
203
+            this.source = source;
204
+            this.required = required;
205
+            this.desired = desired;
206
+        }
207
+
208
+        public boolean matches(String line) {
209
+            return line.equalsIgnoreCase("details");
210
+        }
211
+
212
+        public void execute(InputHandler handler, Response response, String line) throws Exception {
213
+            final boolean you = response.getSource().equals(source);
214
+            response.sendMessage("that command requires level " + required
215
+                    + " access, but " + (you ? "you" : source) + " "
216
+                    + (desired == -1 ? "do" + (you ? "" : "es") + " not have any assigned level"
217
+                    : "only ha" + (you ? "ve" : "s") + " level " + desired));
218
+        }
219
+
220
+    }
221
+
134 222
     protected static class StacktraceFollowup implements Followup {
135 223
 
136 224
         private final Throwable ex;

+ 2
- 0
src/com/md87/charliebravo/commands/QuitCommand.java Dosyayı Görüntüle

@@ -6,6 +6,7 @@
6 6
 package com.md87.charliebravo.commands;
7 7
 
8 8
 import com.md87.charliebravo.Command;
9
+import com.md87.charliebravo.CommandOptions;
9 10
 import com.md87.charliebravo.InputHandler;
10 11
 import com.md87.charliebravo.Response;
11 12
 
@@ -13,6 +14,7 @@ import com.md87.charliebravo.Response;
13 14
  *
14 15
  * @author chris
15 16
  */
17
+@CommandOptions(requireAuthorisation=true, requireLevel=100)
16 18
 public class QuitCommand implements Command {
17 19
 
18 20
     /** {@inheritDoc} */

+ 35
- 2
src/com/md87/charliebravo/commands/SetCommand.java Dosyayı Görüntüle

@@ -5,7 +5,9 @@
5 5
 
6 6
 package com.md87.charliebravo.commands;
7 7
 
8
+import com.dmdirc.parser.irc.ClientInfo;
8 9
 import com.md87.charliebravo.Command;
10
+import com.md87.charliebravo.CommandOptions;
9 11
 import com.md87.charliebravo.InputHandler;
10 12
 import com.md87.charliebravo.Response;
11 13
 
@@ -13,13 +15,44 @@ import com.md87.charliebravo.Response;
13 15
  *
14 16
  * @author chris
15 17
  */
18
+@CommandOptions(requireAuthorisation=true)
16 19
 public class SetCommand implements Command {
17 20
 
18 21
     public void execute(InputHandler handler, Response response, String line) throws Exception {
19 22
         final String openID = (String) handler.getParser().getClientInfoOrFake(response.getSource())
20 23
                 .getMap().get("OpenID");
24
+        String target = openID;
25
+        String value = line;
21 26
 
22
-        final String[] parts = line.split("\\s+", 2);
27
+        int offset;
28
+        if ((offset = line.toLowerCase().lastIndexOf("on behalf of")) > -1) {
29
+            if (!handler.getConfig().hasOption(openID, "admin.level")
30
+                || Integer.parseInt(handler.getConfig().getOption(openID, "admin.level")) < 100) {
31
+                response.sendMessage("You do not have sufficient access", true);
32
+                return;
33
+            } else {
34
+                final String user = line.substring(offset + 13);
35
+                value = line.substring(0, offset - 1);
36
+                
37
+                final ClientInfo client = handler.getParser().getClientInfo(user);
38
+
39
+                if (client != null) {
40
+                    if (client.getMap().get("OpenID") == null) {
41
+                        response.sendMessage(client.getNickname() + " isn't authed", true);
42
+                        return;
43
+                    }
44
+
45
+                    target = (String) client.getMap().get("OpenID");
46
+                } else if (handler.getConfig().hasOption(user, "internal.lastseen")) {
47
+                    target = user;
48
+                } else {
49
+                    response.sendMessage("I couldn't find anyone by that name", true);
50
+                    return;
51
+                }
52
+            }
53
+        }
54
+
55
+        final String[] parts = value.split("\\s+", 2);
23 56
 
24 57
         if (openID == null) {
25 58
             response.sendMessage("You must be authorised to use this command", true);
@@ -32,7 +65,7 @@ public class SetCommand implements Command {
32 65
                 || Integer.parseInt(handler.getConfig().getOption(openID, "admin.level")) < 100)) {
33 66
             response.sendMessage("You do not have sufficient access", true);
34 67
         } else {
35
-            handler.getConfig().setOption(openID, parts[0], parts[1]);
68
+            handler.getConfig().setOption(target, parts[0], parts[1]);
36 69
             response.sendMessage("OK", true);
37 70
         }
38 71
     }

+ 134
- 0
src/com/md87/charliebravo/commands/SkillCommand.java Dosyayı Görüntüle

@@ -0,0 +1,134 @@
1
+/*
2
+ * To change this template, choose Tools | Templates
3
+ * and open the template in the editor.
4
+ */
5
+
6
+package com.md87.charliebravo.commands;
7
+
8
+import com.dmdirc.ui.messages.Formatter;
9
+import com.md87.charliebravo.Command;
10
+import com.md87.charliebravo.CommandOptions;
11
+import com.md87.charliebravo.ConfigCache;
12
+import com.md87.charliebravo.Followup;
13
+import com.md87.charliebravo.InputHandler;
14
+import com.md87.charliebravo.Response;
15
+import uk.co.md87.evetool.api.ApiResponse;
16
+import uk.co.md87.evetool.api.EveApi;
17
+import uk.co.md87.evetool.api.wrappers.SkillInTraining;
18
+import uk.co.md87.evetool.api.wrappers.SkillList;
19
+
20
+/**
21
+ *
22
+ * @author chris
23
+ */
24
+@CommandOptions(requireAuthorisation=true, requiredSettings={"eve.apikey","eve.userid","eve.charid"})
25
+public class SkillCommand implements Command {
26
+
27
+    public void execute(InputHandler handler, Response response, String line) throws Exception {
28
+        final String openID = (String) handler.getParser().getClientInfoOrFake(response.getSource())
29
+                .getMap().get("OpenID");
30
+
31
+        if (openID == null) {
32
+            response.sendMessage("You must be authorised to use this command", true);
33
+        } else {
34
+            final EveApi api = new EveApi(new ConfigCache(handler.getConfig().getConfigfile()));
35
+            api.setApiKey(handler.getConfig().getOption(openID, "eve.apikey"));
36
+            api.setUserID(Integer.parseInt(handler.getConfig().getOption(openID, "eve.userid")));
37
+            api.setCharID(Integer.parseInt(handler.getConfig().getOption(openID, "eve.charid")));
38
+
39
+            final ApiResponse<SkillInTraining> res = api.getSkillInTraining();
40
+
41
+            if (res.wasSuccessful()) {
42
+                final SkillInTraining skill = res.getResult();
43
+
44
+                final ApiResponse<SkillList> res2 = api.getSkillTree();
45
+
46
+                if (res2.wasSuccessful()) {
47
+                    if (skill.isInTraining() && System.currentTimeMillis()
48
+                                < skill.getEndTime().getTime() - 1000) {
49
+                        skill.setSkill(res2.getResult().getSkillById(skill.getTypeId()));
50
+                        response.sendMessage("you are currently training "
51
+                                + skill.getSkill().getName() + " to level "
52
+                                + skill.getTargetLevel() + ". It will finish in "
53
+                                + Formatter.formatDuration((int) (skill.getEndTime()
54
+                                .getTime() - System.currentTimeMillis()) / 1000));
55
+                    } else {
56
+                        response.sendMessage("You are not training anything", true);
57
+                    }
58
+                } else {
59
+                    response.sendMessage("There was an error retrieving the EVE skill list", true);
60
+                    response.addFollowup(new ErrorFollowup(res2));
61
+                    response.addFollowup(new RetryFollowup(this));
62
+                }
63
+            } else {
64
+                response.sendMessage("There was an error retrieving your skill information", true);
65
+                response.addFollowup(new ErrorFollowup(res));
66
+                response.addFollowup(new RetryFollowup(this));
67
+            }
68
+
69
+            response.addFollowup(new CacheFollowup(res));
70
+        }
71
+    }
72
+
73
+    protected static class CacheFollowup implements Followup {
74
+
75
+        protected final ApiResponse<?> apiresponse;
76
+
77
+        public CacheFollowup(ApiResponse<?> apiresponse) {
78
+            this.apiresponse = apiresponse;
79
+        }
80
+
81
+        public boolean matches(String line) {
82
+            return line.equalsIgnoreCase("cache");
83
+        }
84
+
85
+        public void execute(InputHandler handler, Response response, String line) throws Exception {
86
+            response.setInheritFollows(true);
87
+            response.sendMessage("the result has been cached for " +
88
+                    Formatter.formatDuration((int) (System.currentTimeMillis()
89
+                    - apiresponse.getApiResult().getCachedSince().getTime()) / 1000)
90
+                    + ", and will expire in " +
91
+                    Formatter.formatDuration((int) (apiresponse.getApiResult()
92
+                    .getCachedUntil().getTime() - System.currentTimeMillis()) / 1000));
93
+        }
94
+
95
+    }
96
+
97
+    protected static class ErrorFollowup implements Followup {
98
+
99
+        protected final ApiResponse<?> apiresponse;
100
+
101
+        public ErrorFollowup(ApiResponse<?> response) {
102
+            this.apiresponse = response;
103
+        }
104
+
105
+        public boolean matches(String line) {
106
+            return line.equalsIgnoreCase("error");
107
+        }
108
+
109
+        public void execute(InputHandler handler, Response response, String line) throws Exception {
110
+            response.setInheritFollows(true);
111
+            response.sendMessage("the error message was: " + apiresponse.getError());
112
+        }
113
+
114
+    }
115
+
116
+    protected static class RetryFollowup implements Followup {
117
+
118
+        protected final Command command;
119
+
120
+        public RetryFollowup(Command command) {
121
+            this.command = command;
122
+        }
123
+
124
+        public boolean matches(String line) {
125
+            return line.equalsIgnoreCase("retry");
126
+        }
127
+
128
+        public void execute(InputHandler handler, Response response, String line) throws Exception {
129
+            command.execute(handler, response, line);
130
+        }
131
+
132
+    }
133
+
134
+}

+ 6
- 5
src/com/md87/charliebravo/commands/WhoisCommand.java Dosyayı Görüntüle

@@ -6,8 +6,8 @@
6 6
 package com.md87.charliebravo.commands;
7 7
 
8 8
 import com.dmdirc.parser.irc.ClientInfo;
9
+import com.dmdirc.ui.messages.Formatter;
9 10
 import com.md87.charliebravo.Command;
10
-import com.md87.charliebravo.Formatter;
11 11
 import com.md87.charliebravo.InputHandler;
12 12
 import com.md87.charliebravo.Response;
13 13
 
@@ -44,19 +44,20 @@ public class WhoisCommand implements Command {
44 44
                 }
45 45
             } else {
46 46
                 final String openid = (String) ci.getMap().get("OpenID");
47
+                final boolean you = ci.getNickname().equals(response.getSource());
47 48
 
48 49
                 if (openid == null) {
49
-                    response.sendMessage(ci.getNickname() + " has not authenticated with me", true);
50
+                    response.sendMessage((you ? "You have" : ci.getNickname() + " has")
51
+                            + " not authenticated with me", true);
50 52
                 } else {
51 53
                     final StringBuilder extra = new StringBuilder();
52 54
 
53 55
                     if (handler.getConfig().hasOption(openid, "admin.level")) {
54
-                        extra.append(", and has access level ");
56
+                        extra.append(", and " + (you ? "have" : "has") + " access level ");
55 57
                         extra.append(handler.getConfig().getOption(openid, "admin.level"));
56 58
                     }
57 59
 
58
-                    response.sendMessage((ci.getNickname().equals(response.getSource()) ?
59
-                            "you are " : ci.getNickname() + " is")
60
+                    response.sendMessage((you ? "you are" : ci.getNickname() + " is")
60 61
                             + " authenticated as " + openid + extra);
61 62
                 }
62 63
             }

Loading…
İptal
Kaydet