]> Pileus Git - ~andy/spades/blob - src/org/pileus/spades/Message.java
Add aliases or /j and /p
[~andy/spades] / src / org / pileus / spades / Message.java
1 package org.pileus.spades;
2
3 import java.util.ArrayList;
4 import java.util.Date;
5 import java.util.List;
6 import java.util.regex.Pattern;
7 import java.util.regex.Matcher;
8
9 public class Message
10 {
11         /* Enumerations */
12         static enum Type {
13                 OTHER,    // Unknown message type
14                 JOIN,     // Join channel
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
24         };
25
26         static enum How {
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
33         };
34
35         /* Structures */
36         static class Color {
37                 public int     code;
38                 public String  hex;
39                 public String  name;
40                 public int     color;
41
42                 public Color(int code, String hex, String name)
43                 {
44                         this.code  = code;
45                         this.hex   = hex;
46                         this.name  = name;
47                         this.color = Os.getColor(hex);
48                 }
49         };
50
51         static class Format implements Cloneable {
52                 public boolean bold;
53                 public boolean italic;
54                 public boolean strike;
55                 public boolean underline;
56                 public boolean reverse;
57                 public Color   fg;
58                 public Color   bg;
59                 public String  txt;
60
61                 public Format clone()
62                 {
63                         try {
64                                 return (Format)super.clone();
65                         } catch (Exception e) {
66                                 return null;
67                         }
68                 }
69
70                 public String toString()
71                 {
72                         return (fg!=null  ? fg.hex : "xxxxxx") + ":" +
73                                (bg!=null  ? bg.hex : "xxxxxx") + ":" +
74                                (bold      ? "b"    : "-"     ) +
75                                (italic    ? "i"    : "-"     ) +
76                                (strike    ? "s"    : "-"     ) +
77                                (underline ? "u"    : "-"     ) +
78                                (reverse   ? "r"    : "-"     ) + ":" +
79                                "[" + txt + "]";
80                 }
81         };
82
83         /* Colors */
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"),
101         };
102
103         /* Constants */
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]+)( +(.*))?";
108
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);
113
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);
119
120         /* Public data */
121         public Date    time = null;
122
123         public String  line = "";
124
125         public String  src  = "";
126         public String  cmd  = "";
127         public String  dst  = "";
128         public String  arg  = "";
129         public String  msg  = "";
130
131         public Type    type = Type.OTHER;
132         public How     how  = How.OTHER;
133         public String  from = "";
134         public String  to   = "";
135         public String  txt  = "";
136
137         public List<Format> parts = new ArrayList<Format>();
138
139         /* Static methods */
140         private static Color getColor(String code)
141         {
142                 if (code == null)
143                         return null;
144                 int i = Integer.parseInt(code);
145                 if (i >= 0 && i < Message.colors.length)
146                         return Message.colors[i];
147                 return null;
148         }
149
150         /* Public Methods */
151         public Message(String dst, String from, String msg)
152         {
153                 this.time = new Date();
154                 this.how  = How.SENT;
155                 this.from = from;
156
157                 if (this.parseSlash(msg))
158                         return;
159
160                 this.type = Type.PRIVMSG;
161                 this.cmd  = "PRIVMSG";
162                 this.dst  = dst;
163                 this.msg  = msg;
164                 this.line = this.cmd + " " + this.dst + " :" + this.msg;
165         }
166
167         public Message(String line, String name)
168         {
169                 this.time = new Date();
170
171                 this.parseText(line);
172                 this.parseTypes(name);
173                 this.parseColors(this.msg);
174         }
175
176         public void debug()
177         {
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("---------------------");
189         }
190
191         public String toString()
192         {
193                 return this.from + ": " + this.txt;
194         }
195
196         /* Private methods */
197         private String notnull(String string)
198         {
199                 return string == null ? "" : string;
200         }
201
202         private boolean parseSlash(String msg)
203         {
204                 if (msg.charAt(0) != '/')
205                         return false;
206
207                 // Split up line
208                 Matcher mr = ptCmd.matcher(msg);
209                 if (!mr.matches())
210                         return false;
211
212                 String cmd = notnull(mr.group(1));
213                 String arg = notnull(mr.group(3));
214
215                 // Parse commands
216                 if (cmd.matches("j(oin)?")) {
217                         Os.debug("Message: /join");
218                         this.type = Type.JOIN;
219                         this.cmd  = "JOIN";
220                         this.msg  = arg;
221                         this.line = this.cmd + " :" + arg;
222                 }
223
224                 if (cmd.matches("p(art)?")) {
225                         Os.debug("Message: /part");
226                         this.type = Type.PART;
227                         this.cmd  = "PART";
228                         this.msg  = arg;
229                         this.line = this.cmd + " :" + arg;
230                 }
231
232                 // Print warning if command is not recognized
233                 if (this.line == null)
234                         Os.debug("Message: unknown command");
235
236                 return true;
237         }
238
239         private void parseText(String line)
240         {
241                 // Cleanup line
242                 line = line.replaceAll("\\s+",  " ");
243                 line = line.replaceAll("^ | $", "");
244                 this.line = line;
245
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));
254                 }
255
256                 // Determine friendly parts
257                 Matcher mrFrom = ptFrom.matcher(this.src);
258                 if (mrFrom.matches())
259                         this.from = notnull(mrFrom.group(1));
260
261                 Matcher mrTo = ptTo.matcher(this.msg);
262                 if (mrTo.matches())
263                         this.to   = notnull(mrTo.group(2));
264
265                 if (this.to.equals(""))
266                         this.txt  = notnull(this.msg);
267                 else
268                         this.txt  = notnull(mrTo.group(3));
269         }
270
271         private void parseTypes(String name)
272         {
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;
285
286                 // Set directed
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;
291         }
292
293         private void parseColors(String msg)
294         {
295                 // Setup regex matching
296                 Matcher match = Message.cPtrn.matcher(msg);
297
298                 // Initialize state variables
299                 int    pos = 0;
300                 Format fmt = new Format();
301                 ArrayList<Format> list = new ArrayList<Format>();
302
303                 // Parse the string
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());
309                         pos = match.end();
310
311                         // Abort at end of string
312                         if (match.hitEnd())
313                                 break;
314
315                         // Update format for next string
316                         switch (match.group().charAt(0)) {
317                                 // Format attributes
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;
324
325                                 // Reset
326                                 case 017:
327                                           fmt = new Format();
328                                           break;
329
330                                 // Colors
331                                 case 003:
332                                           String fg = match.group(1);
333                                           String bg = match.group(3);
334                                           fmt.fg = Message.getColor(fg);
335                                           fmt.bg = Message.getColor(bg);
336                                           break;
337                         }
338                 }
339
340                 // Cleanup extra space
341                 list.trimToSize();
342                 this.parts = list;
343                 this.msg   = msg.replaceAll(cRegex, "");
344                 this.to    = msg.replaceAll(cRegex, "");
345                 this.txt   = msg.replaceAll(cRegex, "");
346         }
347 }