1 package org.pileus.spades;
3 import java.util.ArrayList;
6 import java.util.regex.Pattern;
7 import java.util.regex.Matcher;
13 OTHER, // Unknown message type
15 PART, // Leave channel
16 PRIVMSG, // Private message
17 TOPIC, // Display current topic
18 NAMES, // Display user names
19 ERROR, // Error message from server
20 CAP, // Server capabilities
21 AUTH, // Authentication message
22 AUTHOK, // Authentication succeeded
23 AUTHFAIL, // Authentication failed
27 OTHER, // Unknown message type
28 CHANNEL, // Normal message to a channel
29 MENTION, // User was mentioned in message text
30 DIRECT, // Message directed towards user
31 PRIVMSG, // Private message to user only
32 SENT // Message was sent by the user
42 public Color(int code, String hex, String name)
47 this.color = Os.getColor(hex);
51 static class Format implements Cloneable {
53 public boolean italic;
54 public boolean strike;
55 public boolean underline;
56 public boolean reverse;
64 return (Format)super.clone();
65 } catch (Exception e) {
70 public String toString()
72 return (fg!=null ? fg.hex : "xxxxxx") + ":" +
73 (bg!=null ? bg.hex : "xxxxxx") + ":" +
75 (italic ? "i" : "-" ) +
76 (strike ? "s" : "-" ) +
77 (underline ? "u" : "-" ) +
78 (reverse ? "r" : "-" ) + ":" +
84 private static final Color colors[] = {
85 new Color(0x0, "FFFFFF", "White"),
86 new Color(0x1, "000000", "Black"),
87 new Color(0x2, "000080", "Navy Blue"),
88 new Color(0x3, "008000", "Green"),
89 new Color(0x4, "FF0000", "Red"),
90 new Color(0x5, "804040", "Brown"),
91 new Color(0x6, "8000FF", "Purple"),
92 new Color(0x7, "808000", "Olive"),
93 new Color(0x8, "FFFF00", "Yellow"),
94 new Color(0x9, "00FF00", "Lime Green"),
95 new Color(0xA, "008080", "Teal"),
96 new Color(0xB, "00FFFF", "Aqua Light"),
97 new Color(0xC, "0000FF", "Royal Blue"),
98 new Color(0xD, "FF00FF", "Hot Pink"),
99 new Color(0xE, "808080", "Dark Gray"),
100 new Color(0xF, "C0C0C0", "Light Gray"),
104 private static final String reMsg = "(:([^ ]+) +)?(([A-Z0-9]+) +)(([^ ]+)[= ]+)?(([^: ]+) *)?(:(.*))?";
105 private static final String reFrom = "([^! ]+)!.*";
106 private static final String reTo = "(([^ :,]*)[:,] *)?(.*)";
107 private static final String reCmd = "/([a-z]+)( +(.*))?";
109 private static final Pattern ptMsg = Pattern.compile(reMsg);
110 private static final Pattern ptFrom = Pattern.compile(reFrom);
111 private static final Pattern ptTo = Pattern.compile(reTo);
112 private static final Pattern ptCmd = Pattern.compile(reCmd);
114 private static final String cReNum = "1[0-5]|0?[0-9]";
115 private static final String cReFmt = "[\\002\\011\\017\\023\\025\\026\\037]";
116 private static final String cReClr = "[\\003\\013]("+cReNum+")?(,("+cReNum+"))?";
117 private static final String cRegex = cReFmt + "|" + cReClr + "|$";
118 private static final Pattern cPtrn = Pattern.compile(cRegex);
121 public Date time = null;
123 public String line = "";
125 public String src = "";
126 public String cmd = "";
127 public String dst = "";
128 public String arg = "";
129 public String msg = "";
131 public Type type = Type.OTHER;
132 public How how = How.OTHER;
133 public String from = "";
134 public String to = "";
135 public String txt = "";
137 public List<Format> parts = new ArrayList<Format>();
140 private static Color getColor(String code)
144 int i = Integer.parseInt(code);
145 if (i >= 0 && i < Message.colors.length)
146 return Message.colors[i];
151 public Message(String dst, String from, String msg)
153 this.time = new Date();
157 if (this.parseSlash(msg))
160 this.type = Type.PRIVMSG;
161 this.cmd = "PRIVMSG";
164 this.line = this.cmd + " " + this.dst + " :" + this.msg;
167 public Message(String line, String name)
169 this.time = new Date();
171 this.parseText(line);
172 this.parseTypes(name);
173 this.parseColors(this.msg);
178 Os.debug("---------------------");
179 Os.debug("line = [" + line + "]");
180 Os.debug("src = " + this.src);
181 Os.debug("cmd = " + this.cmd);
182 Os.debug("dst = " + this.dst);
183 Os.debug("arg = " + this.arg);
184 Os.debug("msg = " + this.msg);
185 Os.debug("from = " + this.from);
186 Os.debug("to = " + this.to);
187 Os.debug("txt = " + this.txt);
188 Os.debug("---------------------");
191 public String toString()
193 return this.from + ": " + this.txt;
196 /* Private methods */
197 private String notnull(String string)
199 return string == null ? "" : string;
202 private boolean parseSlash(String msg)
204 if (msg.charAt(0) != '/')
208 Matcher mr = ptCmd.matcher(msg);
212 String cmd = notnull(mr.group(1));
213 String arg = notnull(mr.group(3));
216 if (cmd.matches("join")) {
217 Os.debug("Message: /join");
218 this.type = Type.JOIN;
221 this.line = this.cmd + " :" + arg;
224 if (cmd.matches("part")) {
225 Os.debug("Message: /part");
226 this.type = Type.PART;
229 this.line = this.cmd + " :" + arg;
232 // Print warning if command is not recognized
233 if (this.line == null)
234 Os.debug("Message: unknown command");
239 private void parseText(String line)
242 line = line.replaceAll("\\s+", " ");
243 line = line.replaceAll("^ | $", "");
246 // Split line into parts
247 Matcher mrMsg = ptMsg.matcher(line);
248 if (mrMsg.matches()) {
249 this.src = notnull(mrMsg.group(2));
250 this.cmd = notnull(mrMsg.group(4));
251 this.dst = notnull(mrMsg.group(6));
252 this.arg = notnull(mrMsg.group(8));
253 this.msg = notnull(mrMsg.group(10));
256 // Determine friendly parts
257 Matcher mrFrom = ptFrom.matcher(this.src);
258 if (mrFrom.matches())
259 this.from = notnull(mrFrom.group(1));
261 Matcher mrTo = ptTo.matcher(this.msg);
263 this.to = notnull(mrTo.group(2));
265 if (this.to.equals(""))
266 this.txt = notnull(this.msg);
268 this.txt = notnull(mrTo.group(3));
271 private void parseTypes(String name)
273 // Parse commands names
274 if (this.cmd.equals("PRIVMSG")) this.type = Type.PRIVMSG;
275 else if (this.cmd.equals("332")) this.type = Type.TOPIC;
276 else if (this.cmd.equals("353")) this.type = Type.NAMES;
277 else if (this.cmd.equals("ERROR")) this.type = Type.ERROR;
278 else if (this.cmd.equals("CAP")) this.type = Type.CAP;
279 else if (this.cmd.equals("AUTHENTICATE")) this.type = Type.AUTH;
280 else if (this.cmd.equals("903")) this.type = Type.AUTHOK;
281 else if (this.cmd.equals("904")) this.type = Type.AUTHFAIL;
282 else if (this.cmd.equals("905")) this.type = Type.AUTHFAIL;
283 else if (this.cmd.equals("906")) this.type = Type.AUTHFAIL;
284 else if (this.cmd.equals("907")) this.type = Type.AUTHFAIL;
287 if (this.dst.equals(name)) this.how = How.PRIVMSG;
288 else if (this.to.equals(name)) this.how = How.DIRECT;
289 else if (this.msg.contains(name)) this.how = How.MENTION;
290 else if (this.type == Type.PRIVMSG) this.how = How.CHANNEL;
293 private void parseColors(String msg)
295 // Setup regex matching
296 Matcher match = Message.cPtrn.matcher(msg);
298 // Initialize state variables
300 Format fmt = new Format();
301 ArrayList<Format> list = new ArrayList<Format>();
304 while (match.find()) {
305 // Push current string
306 fmt.txt = msg.substring(pos, match.start());
307 if (fmt.txt.length() > 0)
308 list.add(fmt.clone());
311 // Abort at end of string
315 // Update format for next string
316 switch (match.group().charAt(0)) {
318 case 002: fmt.bold ^= true; break;
319 case 011: fmt.italic ^= true; break;
320 case 023: fmt.strike ^= true; break;
321 case 025: fmt.underline ^= true; break;
322 case 037: fmt.underline ^= true; break;
323 case 026: fmt.reverse ^= true; break;
332 String fg = match.group(1);
333 String bg = match.group(3);
334 fmt.fg = Message.getColor(fg);
335 fmt.bg = Message.getColor(bg);
340 // Cleanup extra space
343 this.msg = msg.replaceAll(cRegex, "");
344 this.to = msg.replaceAll(cRegex, "");
345 this.txt = msg.replaceAll(cRegex, "");