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
41 public Color(int code, String hex, String name)
49 static class Format implements Cloneable {
51 public boolean italic;
52 public boolean strike;
53 public boolean underline;
54 public boolean reverse;
62 return (Format)super.clone();
63 } catch (Exception e) {
68 public String toString()
70 return (fg!=null ? fg.hex : "xxxxxx") + ":" +
71 (bg!=null ? bg.hex : "xxxxxx") + ":" +
73 (italic ? "i" : "-" ) +
74 (strike ? "s" : "-" ) +
75 (underline ? "u" : "-" ) +
76 (reverse ? "r" : "-" ) + ":" +
82 private static final Color colors[] = {
83 new Color(0x0, "FFFFFF", "White"),
84 new Color(0x1, "000000", "Black"),
85 new Color(0x2, "000080", "Navy Blue"),
86 new Color(0x3, "008000", "Green"),
87 new Color(0x4, "FF0000", "Red"),
88 new Color(0x5, "804040", "Brown"),
89 new Color(0x6, "8000FF", "Purple"),
90 new Color(0x7, "808000", "Olive"),
91 new Color(0x8, "FFFF00", "Yellow"),
92 new Color(0x9, "00FF00", "Lime Green"),
93 new Color(0xA, "008080", "Teal"),
94 new Color(0xB, "00FFFF", "Aqua Light"),
95 new Color(0xC, "0000FF", "Royal Blue"),
96 new Color(0xD, "FF00FF", "Hot Pink"),
97 new Color(0xE, "808080", "Dark Gray"),
98 new Color(0xF, "C0C0C0", "Light Gray"),
102 private static final String reMsg = "(:([^ ]+) +)?(([A-Z0-9]+) +)(([^ ]+)[= ]+)?(([^: ]+) *)?(:(.*))?";
103 private static final String reFrom = "([^! ]+)!.*";
104 private static final String reTo = "(([^ :,]*)[:,] *)?(.*)";
105 private static final String reCmd = "/([a-z]+)( +(.*))?";
107 private static final Pattern ptMsg = Pattern.compile(reMsg);
108 private static final Pattern ptFrom = Pattern.compile(reFrom);
109 private static final Pattern ptTo = Pattern.compile(reTo);
110 private static final Pattern ptCmd = Pattern.compile(reCmd);
112 private static final String cReNum = "1[0-5]|0?[0-9]";
113 private static final String cReFmt = "[\\002\\011\\017\\023\\025\\026\\037]";
114 private static final String cReClr = "[\\003\\013]("+cReNum+")?(,("+cReNum+"))?";
115 private static final String cRegex = cReFmt + "|" + cReClr + "|$";
116 private static final Pattern cPtrn = Pattern.compile(cRegex);
119 public Date time = null;
121 public String line = "";
123 public String src = "";
124 public String cmd = "";
125 public String dst = "";
126 public String arg = "";
127 public String msg = "";
129 public Type type = Type.OTHER;
130 public How how = How.OTHER;
131 public String from = "";
132 public String to = "";
133 public String txt = "";
135 public List<Format> parts = new ArrayList<Format>();
138 private static Color getColor(String code)
142 int i = Integer.parseInt(code);
143 if (i >= 0 && i < Message.colors.length)
144 return Message.colors[i];
149 public Message(String dst, String from, String msg)
151 this.time = new Date();
155 if (this.parseSlash(msg))
158 this.type = Type.PRIVMSG;
159 this.cmd = "PRIVMSG";
162 this.line = this.cmd + " " + this.dst + " :" + this.msg;
165 public Message(String line, String name)
167 this.time = new Date();
169 this.parseText(line);
170 this.parseTypes(name);
171 this.parseColors(this.msg);
176 Os.debug("---------------------");
177 Os.debug("line = [" + line + "]");
178 Os.debug("src = " + this.src);
179 Os.debug("cmd = " + this.cmd);
180 Os.debug("dst = " + this.dst);
181 Os.debug("arg = " + this.arg);
182 Os.debug("msg = " + this.msg);
183 Os.debug("from = " + this.from);
184 Os.debug("to = " + this.to);
185 Os.debug("txt = " + this.txt);
186 Os.debug("---------------------");
189 public String toString()
191 return this.from + ": " + this.txt;
194 /* Private methods */
195 private String notnull(String string)
197 return string == null ? "" : string;
200 private boolean parseSlash(String msg)
202 if (msg.charAt(0) != '/')
206 Matcher mr = ptCmd.matcher(msg);
210 String cmd = notnull(mr.group(1));
211 String arg = notnull(mr.group(3));
214 if (cmd.matches("join")) {
215 Os.debug("Message: /join");
216 this.type = Type.JOIN;
219 this.line = this.cmd + " :" + arg;
222 if (cmd.matches("part")) {
223 Os.debug("Message: /part");
224 this.type = Type.PART;
227 this.line = this.cmd + " :" + arg;
230 // Print warning if command is not recognized
231 if (this.line == null)
232 Os.debug("Message: unknown command");
237 private void parseText(String line)
240 line = line.replaceAll("\\s+", " ");
241 line = line.replaceAll("^ | $", "");
244 // Split line into parts
245 Matcher mrMsg = ptMsg.matcher(line);
246 if (mrMsg.matches()) {
247 this.src = notnull(mrMsg.group(2));
248 this.cmd = notnull(mrMsg.group(4));
249 this.dst = notnull(mrMsg.group(6));
250 this.arg = notnull(mrMsg.group(8));
251 this.msg = notnull(mrMsg.group(10));
254 // Determine friendly parts
255 Matcher mrFrom = ptFrom.matcher(this.src);
256 if (mrFrom.matches())
257 this.from = notnull(mrFrom.group(1));
259 Matcher mrTo = ptTo.matcher(this.msg);
261 this.to = notnull(mrTo.group(2));
263 if (this.to.equals(""))
264 this.txt = notnull(this.msg);
266 this.txt = notnull(mrTo.group(3));
269 private void parseTypes(String name)
271 // Parse commands names
272 if (this.cmd.equals("PRIVMSG")) this.type = Type.PRIVMSG;
273 else if (this.cmd.equals("332")) this.type = Type.TOPIC;
274 else if (this.cmd.equals("353")) this.type = Type.NAMES;
275 else if (this.cmd.equals("ERROR")) this.type = Type.ERROR;
276 else if (this.cmd.equals("CAP")) this.type = Type.CAP;
277 else if (this.cmd.equals("AUTHENTICATE")) this.type = Type.AUTH;
278 else if (this.cmd.equals("903")) this.type = Type.AUTHOK;
279 else if (this.cmd.equals("904")) this.type = Type.AUTHFAIL;
280 else if (this.cmd.equals("905")) this.type = Type.AUTHFAIL;
281 else if (this.cmd.equals("906")) this.type = Type.AUTHFAIL;
282 else if (this.cmd.equals("907")) this.type = Type.AUTHFAIL;
285 if (this.dst.equals(name)) this.how = How.PRIVMSG;
286 else if (this.to.equals(name)) this.how = How.DIRECT;
287 else if (this.msg.contains(name)) this.how = How.MENTION;
288 else if (this.type == Type.PRIVMSG) this.how = How.CHANNEL;
291 private void parseColors(String msg)
293 // Setup regex matching
294 Matcher match = Message.cPtrn.matcher(msg);
296 // Initialize state variables
298 Format fmt = new Format();
299 ArrayList<Format> list = new ArrayList<Format>();
302 while (match.find()) {
303 // Push current string
304 fmt.txt = msg.substring(pos, match.start());
305 if (fmt.txt.length() > 0)
306 list.add(fmt.clone());
309 // Abort at end of string
313 // Update format for next string
314 switch (match.group().charAt(0)) {
316 case 002: fmt.bold ^= true; break;
317 case 011: fmt.italic ^= true; break;
318 case 023: fmt.strike ^= true; break;
319 case 025: fmt.underline ^= true; break;
320 case 037: fmt.underline ^= true; break;
321 case 026: fmt.reverse ^= true; break;
330 String fg = match.group(1);
331 String bg = match.group(3);
332 fmt.fg = Message.getColor(fg);
333 fmt.bg = Message.getColor(bg);
338 // Cleanup extra space
341 this.msg = msg.replaceAll(cRegex, "");
342 this.to = msg.replaceAll(cRegex, "");
343 this.txt = msg.replaceAll(cRegex, "");