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; // Message delivery time
123 public String line = ""; // Raw IRC line -- george@~g@example.com PRIVMSG #chat :larry: hello!
125 public String src = ""; // IRC Source -- george!~g@example.com
126 public String cmd = ""; // IRC Command -- PRIVMSG
127 public String dst = ""; // IRC Destination -- #chat
128 public String arg = ""; // IRC Arguments -- #chan in topic msg, etc
129 public String msg = ""; // IRC Message text -- larry: Hello!
131 public Type type = Type.OTHER; // Message Type -- PRIVMSG
132 public How how = How.OTHER; // How msg relates -- SENT=geroge, DIRECT=larry, CHANNEL=*
133 public String from = ""; // Nick of sender -- george
134 public String to = ""; // Addressed name -- larry
135 public String txt = ""; // Text of msg -- Hello!
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(2));
214 String txt = notnull(mr.group(3));
217 if (cmd.matches("j(oin)?")) {
218 Os.debug("Message: /join");
219 this.type = Type.JOIN;
222 this.line = this.cmd + " :" + arg;
225 if (cmd.matches("p(art)?")) {
226 Os.debug("Message: /part");
227 this.type = Type.PART;
230 this.line = this.cmd + " :" + arg;
233 if (cmd.matches("msg") && arg != null) {
234 Os.debug("Message: /msg");
235 this.type = Type.PRIVMSG;
236 this.cmd = "PRIVMSG";
239 this.line = this.cmd + " " + arg + " :" + txt;
242 // Print warning if command is not recognized
243 if (this.line == null)
244 Os.debug("Message: unknown command");
249 private void parseText(String line)
252 line = line.replaceAll("\\s+", " ");
253 line = line.replaceAll("^ | $", "");
256 // Split line into parts
257 Matcher mrMsg = ptMsg.matcher(line);
258 if (mrMsg.matches()) {
259 this.src = notnull(mrMsg.group(2));
260 this.cmd = notnull(mrMsg.group(4));
261 this.dst = notnull(mrMsg.group(6));
262 this.arg = notnull(mrMsg.group(8));
263 this.msg = notnull(mrMsg.group(10));
266 // Determine friendly parts
267 Matcher mrFrom = ptFrom.matcher(this.src);
268 if (mrFrom.matches())
269 this.from = notnull(mrFrom.group(1));
271 Matcher mrTo = ptTo.matcher(this.msg);
273 this.to = notnull(mrTo.group(2));
275 if (this.to.equals(""))
276 this.txt = notnull(this.msg);
278 this.txt = notnull(mrTo.group(3));
281 private void parseTypes(String name)
283 // Parse commands names
284 if (this.cmd.equals("PRIVMSG")) this.type = Type.PRIVMSG;
285 else if (this.cmd.equals("332")) this.type = Type.TOPIC;
286 else if (this.cmd.equals("353")) this.type = Type.NAMES;
287 else if (this.cmd.equals("ERROR")) this.type = Type.ERROR;
288 else if (this.cmd.equals("CAP")) this.type = Type.CAP;
289 else if (this.cmd.equals("AUTHENTICATE")) this.type = Type.AUTH;
290 else if (this.cmd.equals("903")) this.type = Type.AUTHOK;
291 else if (this.cmd.equals("904")) this.type = Type.AUTHFAIL;
292 else if (this.cmd.equals("905")) this.type = Type.AUTHFAIL;
293 else if (this.cmd.equals("906")) this.type = Type.AUTHFAIL;
294 else if (this.cmd.equals("907")) this.type = Type.AUTHFAIL;
297 if (this.dst.equals(name)) this.how = How.PRIVMSG;
298 else if (this.to.equals(name)) this.how = How.DIRECT;
299 else if (this.msg.contains(name)) this.how = How.MENTION;
300 else if (this.type == Type.PRIVMSG) this.how = How.CHANNEL;
303 private void parseColors(String msg)
305 // Setup regex matching
306 Matcher match = Message.cPtrn.matcher(msg);
308 // Initialize state variables
310 Format fmt = new Format();
311 ArrayList<Format> list = new ArrayList<Format>();
314 while (match.find()) {
315 // Push current string
316 fmt.txt = msg.substring(pos, match.start());
317 if (fmt.txt.length() > 0)
318 list.add(fmt.clone());
321 // Abort at end of string
325 // Update format for next string
326 switch (match.group().charAt(0)) {
328 case 002: fmt.bold ^= true; break;
329 case 011: fmt.italic ^= true; break;
330 case 023: fmt.strike ^= true; break;
331 case 025: fmt.underline ^= true; break;
332 case 037: fmt.underline ^= true; break;
333 case 026: fmt.reverse ^= true; break;
342 String fg = match.group(1);
343 String bg = match.group(3);
344 fmt.fg = Message.getColor(fg);
345 fmt.bg = Message.getColor(bg);
350 // Cleanup extra space
353 this.msg = this.msg.replaceAll(cRegex, "");
354 this.to = this.to.replaceAll(cRegex, "");
355 this.txt = this.txt.replaceAll(cRegex, "");