ratchet 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. data/gem_bin/ratchet +23 -0
  2. data/lib/ratchet.rb +613 -0
  3. data/lib/ratchet/aliases.rb +106 -0
  4. data/lib/ratchet/bufferparser.rb +409 -0
  5. data/lib/ratchet/commandbuffer.rb +66 -0
  6. data/lib/ratchet/commandparser.rb +668 -0
  7. data/lib/ratchet/configuration.rb +278 -0
  8. data/lib/ratchet/connections.rb +403 -0
  9. data/lib/ratchet/constants.rb +111 -0
  10. data/lib/ratchet/contrib/instance_exec.rb +21 -0
  11. data/lib/ratchet/eventparser.rb +486 -0
  12. data/lib/ratchet/gtk/bufferlistview.rb +514 -0
  13. data/lib/ratchet/gtk/bufferview.rb +167 -0
  14. data/lib/ratchet/gtk/configwindow.rb +229 -0
  15. data/lib/ratchet/gtk/connectionwindow.rb +218 -0
  16. data/lib/ratchet/gtk/keybinding.rb +356 -0
  17. data/lib/ratchet/gtk/linkwindow.rb +137 -0
  18. data/lib/ratchet/gtk/mainwindow.rb +504 -0
  19. data/lib/ratchet/gtk/networkpresenceconf.rb +567 -0
  20. data/lib/ratchet/gtk/pluginconfig.rb +94 -0
  21. data/lib/ratchet/gtk/pluginwindow.rb +146 -0
  22. data/lib/ratchet/gtk/userlistview.rb +161 -0
  23. data/lib/ratchet/help.rb +64 -0
  24. data/lib/ratchet/items.rb +271 -0
  25. data/lib/ratchet/lines.rb +63 -0
  26. data/lib/ratchet/networks.rb +652 -0
  27. data/lib/ratchet/plugins.rb +616 -0
  28. data/lib/ratchet/queue.rb +47 -0
  29. data/lib/ratchet/ratchet-version.rb +21 -0
  30. data/lib/ratchet/replies.rb +134 -0
  31. data/lib/ratchet/replyparser.rb +441 -0
  32. data/lib/ratchet/tabcomplete.rb +98 -0
  33. data/lib/ratchet/users.rb +237 -0
  34. data/lib/ratchet/utils.rb +178 -0
  35. data/share/defaults.yaml +169 -0
  36. data/share/glade/config.glade +2634 -0
  37. data/share/glade/connect.glade +950 -0
  38. data/share/glade/keybindings.glade +109 -0
  39. data/share/glade/linkwindow.glade +188 -0
  40. data/share/glade/mainwindow.glade +335 -0
  41. data/share/glade/network-presences.glade +1373 -0
  42. data/share/glade/pluginconf.glade +97 -0
  43. data/share/glade/plugins.glade +360 -0
  44. data/share/plugins/colorewrite.rb +193 -0
  45. data/share/plugins/highlighter.rb +115 -0
  46. data/share/plugins/mpdplay.rb +123 -0
  47. data/share/plugins/numberswitcher.rb +30 -0
  48. data/share/plugins/sysinfo.rb +82 -0
  49. metadata +96 -0
@@ -0,0 +1,106 @@
1
+ =begin
2
+ This file is part of the Ratchet project, a client for Icecap.
3
+ Copyright (C) 2005-6 Andrew Thompson
4
+
5
+ This program is free software; you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation; either version 2 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program; if not, write to the Free Software
17
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ =end
19
+
20
+ #### aliases.rb ####
21
+ # Contains the alias class, the class that is instanciated when an alias is
22
+ # input, the class provides methods to split multiple commands, and to insert
23
+ # arguments passed to the alias
24
+ ####
25
+
26
+ #TODO possibly create *one* instance of this class per-alias, then pass the string
27
+ # in and output the resulting commands. Also provide a classvar for easily alias lookup
28
+
29
+ module Ratchet
30
+ class Alias
31
+ attr_reader :commands
32
+ def initialize(string, arguments, target)
33
+ @target = target
34
+ # puts 'alias: '+string, 'arguments: '+arguments, '=>'
35
+ @string = string
36
+ @arguments = arguments.to_s.split(' ')
37
+ @commands = []
38
+ parse
39
+ end
40
+
41
+ def parse
42
+ re = /\s*;\s*\//
43
+ string = @string.dup
44
+
45
+ while md = re.match(string)
46
+ command, string = string.split(md[0], 2)
47
+ string = '/'+string
48
+ @commands << replace_args(command)
49
+ end
50
+
51
+ @commands << replace_args(string)
52
+ end
53
+
54
+ def replace_args(string)
55
+ unused = @arguments.dup#keep track of any unused arguments
56
+ re = /\$(([\-0-9.]+([.]{2,3}[\-0-9]+|))|[@^!]|(?:network|presence|username|buffername))/
57
+ md = re.match string
58
+ return unused.unshift(string).join(' ') unless md
59
+
60
+ while md
61
+ if md[1] =~ /([\-0-9]+)([.]{2,3})([\-0-9]+)/
62
+ puts md[1], @arguments.inspect
63
+ if ($1.to_i > $3.to_i and $3.to_i > 0) or ($1.to_i < 0 and $3.to_i >= 0)
64
+ puts 'less'
65
+ puts eval("@arguments[#{$3}#{$2}#{$1}]").reverse.join(' ')
66
+ string.sub!(md[0], eval("@arguments[#{$3}#{$2}#{$1}]").reverse.join(' '))
67
+ else
68
+ puts 'not less'
69
+ puts eval("@arguments[#{md[1]}]").join(' ')
70
+ string.sub!(md[0], eval("@arguments[#{md[1]}]").join(' '))
71
+ end
72
+ puts string
73
+ elsif md[1].numeric?
74
+ if @arguments[md[1].to_i]
75
+ string.sub!(md[0], @arguments[md[1].to_i])
76
+ unused.delete(@arguments[md[1].to_i])
77
+ else #can't replace an argument that we don't have in the array
78
+ string.sub!(md[0], '$\\\\'+md[1])#escape it so we don't infinitely loop
79
+ end
80
+ elsif md[1] == '@'
81
+ string.sub!(md[0], @arguments.join(' '))
82
+ elsif md[1] == '^'
83
+ string.sub!(md[0], unused.join(' '))
84
+ elsif md[1] == '!'
85
+ string.sub!(md[0], '')
86
+ else
87
+ case md[1]
88
+ when 'network'
89
+ replace = @target.network.name
90
+ when 'presence'
91
+ replace = @target.presence
92
+ when 'username'
93
+ replace = @target.username
94
+ when 'buffername'
95
+ replace = @target.name
96
+ else
97
+ replace = ''
98
+ end
99
+ string.sub!(md[0], replace)
100
+ end
101
+ md = re.match string
102
+ end
103
+ string.gsub('$\\', '$') #unescape any escaped $<num>s
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,409 @@
1
+ =begin
2
+ This file is part of the Ratchet project, a client for Icecap.
3
+ Copyright (C) 2005-6 Andrew Thompson
4
+
5
+ This program is free software; you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation; either version 2 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program; if not, write to the Free Software
17
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ =end
19
+
20
+ #### bufferparser.rb ####
21
+ # Formats strings to be sent to the buffer
22
+ ####
23
+
24
+ #TODO: a more suitable name? is this too GTK specific to remain here?
25
+ #TODO: cleanup some of the redundant junk in here
26
+
27
+ module Ratchet
28
+ module BufferParser
29
+ #send an event from the user to the buffer
30
+ def send_user_event(line, type)
31
+ unless @buffer
32
+ puts 'not connected'
33
+ return
34
+ end
35
+ x = Line.new
36
+
37
+ line.each do |k,v|
38
+ x[k.to_sym] = v
39
+ end
40
+
41
+ line = x
42
+ time = Time.new
43
+ time = time - @window.main.drift if @config['canonicaltime'] == 'server'
44
+ line[TIME] = time
45
+ line[ID] = 'client'+rand(1000).to_s
46
+ while @buffer.has_id? line[ID]
47
+ line[ID] = 'client'+rand(1000).to_s
48
+ end
49
+ send_event(line, type)
50
+ end
51
+
52
+ #send a line to the buffer
53
+ def send_event(line, type, insert_location=BUFFER_END)
54
+ #return if !@connected
55
+ unless @buffer
56
+ puts 'not connected'
57
+ return
58
+ end
59
+
60
+ if @buffer.has_id? line[ID]
61
+ # puts line[ID]
62
+ # puts @buffer.has_id?(line[ID])
63
+ # puts 'event already in buffer'
64
+ return
65
+ end
66
+
67
+ raise ArgumentError unless line.class == Line
68
+
69
+ pattern = ''
70
+
71
+ uname = ''
72
+
73
+ links = []
74
+ users = []
75
+
76
+ local = self
77
+
78
+ begin
79
+ cmd = 'buffer_'+type
80
+ if self.respond_to?(cmd)
81
+ res = callback(cmd, line, pattern, uname, users, insert_location)
82
+ return if res === true
83
+ res2 = self.send(cmd, *res)
84
+ return if res2 === true
85
+ uname, pattern, users, insert_location = callback_after(cmd, *res2)
86
+ else
87
+ return
88
+ end
89
+ #rescue any exceptions...
90
+ rescue =>exception
91
+ puts 'Error sending : '+$!
92
+ puts exception.backtrace
93
+ end
94
+
95
+ unless pattern.nil?
96
+ if insert_location == BUFFER_START
97
+ return @buffer.prepend([line[TIME].to_i, parse_tags(uname), parse_tags(pattern)], line[ID])
98
+ elsif insert_location == BUFFER_END
99
+ return @buffer.append([line[TIME].to_i, parse_tags(uname), parse_tags(pattern)], line[ID])
100
+ end
101
+ end
102
+
103
+ end
104
+
105
+ def marklastread
106
+ @buffer.marklastread
107
+ end
108
+
109
+ def xhtml_im2pango(string)
110
+ return unless string
111
+ #puts string
112
+ re = /\<span style=[\"\'](.+?)[\"\']\>([^\<]+)\<\/span\>/i
113
+ md = re.match(string)
114
+
115
+ while md.class == MatchData
116
+ x = parse_style(md[1])
117
+
118
+ replacement = '<s '+x+'>'+md[2]+'</s>'
119
+
120
+ string.sub!(md[0], replacement)
121
+
122
+ md = re.match(string)
123
+ end
124
+
125
+ return string.gsub('<s ', '<span ').gsub('</s>', '</span>')
126
+ end
127
+
128
+ def parse_style(style)
129
+ result = []
130
+ styles = style.split(';').map{|e| e.downcase.strip}
131
+
132
+ styles.each do |style|
133
+ attribute, value = style.split(':', 2).map{|e| e.strip}
134
+
135
+ if TAGMAP[attribute]
136
+ attribute = TAGMAP[attribute]
137
+ elsif attribute == 'text-decoration' and value == 'underline'
138
+ attribute = 'underline'
139
+ value = 'single'
140
+ end
141
+
142
+ if attribute == 'foreground' or attribute == 'background'
143
+ re = Regexp.new('#[a-fA-F0-9]+')
144
+ #pad any hex colors out to 7 characters
145
+ while value =~ re and value.length < 7
146
+ value['#'] = '#0'
147
+ end
148
+ end
149
+ result.push(attribute+'="'+value+'"') if attribute and value
150
+ end
151
+
152
+ return result.join(' ')
153
+ end
154
+
155
+ def parse_tags(string)
156
+ re = /((%(C[0-9]{1}[0-5]*|U|B|I))(.+?)\2)/
157
+ md = re.match(string)
158
+
159
+ while md.class == MatchData
160
+ tag = nil
161
+
162
+ if md[2] =~ /^%C[0-9]{1}[0-5]*$/
163
+ colorid = md[2].gsub!('%C', '')
164
+ tag = 'span'
165
+ attributes = 'foreground="'+@config['color'+colorid].to_hex+'"'
166
+
167
+ elsif md[2] == '%U'
168
+ tag = 'u'
169
+
170
+ elsif md[2] == '%B'
171
+ tag = 'b'
172
+
173
+ elsif md[2] == '%I'
174
+ tag = 'i'
175
+ end
176
+
177
+ new = md[4]
178
+
179
+ if attributes
180
+ x = '<'+tag+' '+attributes+'>'
181
+ else
182
+ x = '<'+tag+'>'
183
+ end
184
+
185
+ y = '</'+tag+'>'
186
+
187
+ new = x+new+y
188
+
189
+ string.sub!(md[1], new)
190
+
191
+ md = re.match(string)
192
+ end
193
+
194
+ #strip out any empty patterns
195
+ re = /((%(C[0-9]{1}[0-5]*|U|B|I))\2)/
196
+ md = re.match(string)
197
+ while md.class == MatchData
198
+ string.sub!(md[0], '')
199
+ md = re.match(string)
200
+ end
201
+
202
+ md = HYPERLINKREGEXP.match(string)
203
+
204
+ while md.class == MatchData
205
+ if md[0].scan('.').size >= 2 or md[0].scan('://').size > 0
206
+ #links.push(md[0])
207
+ string.sub!(md[0], '<action id="url">'+md[0]+'</action>')
208
+ end
209
+ md = HYPERLINKREGEXP.match(md.post_match)
210
+ end
211
+
212
+ return string
213
+ end
214
+
215
+ def presence2username(name, padding=true)
216
+ return name unless @config['show_usermode']
217
+ if self.class == ChannelBuffer
218
+ if @users.include?(name)
219
+ user = @users[name]
220
+ mode = user.mode_symbol
221
+ mode = ' ' if mode =='' and padding and @config['pad_usermode']
222
+ return mode+name
223
+ end
224
+ end
225
+ return name
226
+ end
227
+
228
+ def buffer_message(line, pattern, uname, users, insert_location)
229
+ set_status(NEWMSG) if insert_location == BUFFER_END
230
+ if line[TYPE] == 'action' and line[PRESENCE]
231
+ pattern = @config.get_pattern('action')
232
+ pattern['%u'] = presence2username(line[PRESENCE], false)
233
+ if line[MSG_XHTML]
234
+ pattern = escape_xml(pattern)
235
+ pattern['%m'] = xhtml_im2pango(line[MSG_XHTML])
236
+ else
237
+ pattern['%m'] = line[MSG].to_s
238
+ pattern = escape_xml(pattern)
239
+ end
240
+ else
241
+ pattern = @config.get_pattern('message')
242
+ if line[PRESENCE]
243
+ uname = @config.get_pattern('otherusernameformat')
244
+ uname = escape_xml(uname)
245
+ uname['%u'] = '<action id="user">'+escape_xml(presence2username(line[PRESENCE]))+'</action>'
246
+ #uname['%u'] = presence2username(line[PRESENCE])
247
+ users.push(line[PRESENCE])
248
+ end
249
+ if line[MSG_XHTML]
250
+ pattern = escape_xml(pattern)
251
+ pattern['%m'] = xhtml_im2pango(line[MSG_XHTML])
252
+ else
253
+ pattern['%m'] = line[MSG].to_s
254
+ pattern = escape_xml(pattern)
255
+ end
256
+ end
257
+ return [uname, pattern, users, insert_location]
258
+ end
259
+
260
+ def buffer_usermessage(line, pattern, uname, users, insert_location)
261
+ set_status(NEWMSG) if insert_location == BUFFER_END
262
+ if line[TYPE] == 'action' and username
263
+ pattern = @config.get_pattern('action')
264
+ pattern['%u'] = presence2username(username, false)
265
+ users.push(username)
266
+ elsif username
267
+ pattern = @config.get_pattern('usermessage')
268
+ uname = @config.get_pattern('usernameformat')
269
+ uname = escape_xml(uname)
270
+ uname['%u'] = '<action id="user">'+escape_xml(presence2username(username))+'</action>'
271
+ #uname['%u'] = presence2username(username)
272
+ users.push(username)
273
+ end
274
+ pattern['%m'] = line[MSG].to_s
275
+ pattern = escape_xml(pattern)
276
+ return [uname, pattern, users, insert_location]
277
+ end
278
+
279
+ def buffer_join(line, pattern, uname, users, insert_location)
280
+ return true if @config['dropjoinpart']
281
+ set_status(NEWDATA) if insert_location == BUFFER_END
282
+ pattern = @config.get_pattern('join')
283
+ pattern['%u'] = line[PRESENCE]
284
+ users.push(line[PRESENCE])
285
+ pattern['%c'] = line[CHANNEL]
286
+ if user = @users[line[PRESENCE]] and user.hostname
287
+ pattern['%h'] = user.hostname
288
+ else
289
+ pattern['%h'] = line[ADDRESS].to_s
290
+ end
291
+ pattern = escape_xml(pattern)
292
+ return [uname, pattern, users, insert_location]
293
+ end
294
+
295
+ def buffer_userjoin(line, pattern, uname, users, insert_location)
296
+ set_status(NEWDATA) if insert_location == BUFFER_END
297
+ pattern = @config.get_pattern('userjoin')
298
+ pattern['%c'] = line[CHANNEL]
299
+ pattern = escape_xml(pattern)
300
+ return [uname, pattern, users, insert_location]
301
+ end
302
+
303
+ def buffer_part(line, pattern, uname, users, insert_location)
304
+ return true if @config['dropjoinpart']
305
+ set_status(NEWDATA) if insert_location == BUFFER_END
306
+ pattern = @config.get_pattern(line[:type])
307
+ pattern.sub!('%u', line[PRESENCE])
308
+ users.push(line[PRESENCE])
309
+ pattern.sub!('%r', line[REASON].to_s)
310
+ pattern.sub!('%c', line[CHANNEL])
311
+ if user = @users[line[PRESENCE]] and user.hostname
312
+ pattern.sub!('%h', user.hostname)
313
+ else
314
+ pattern.sub!('%h', line[ADDRESS].to_s)
315
+ end
316
+ pattern = escape_xml(pattern)
317
+ return [uname, pattern, users, insert_location]
318
+ end
319
+
320
+ def buffer_userpart(line, pattern, uname, users, insert_location)
321
+ set_status(NEWDATA) if insert_location == BUFFER_END
322
+ pattern = @config.get_pattern('user'+line[:type])
323
+ pattern.sub!('%c', line[CHANNEL])
324
+ pattern = escape_xml(pattern)
325
+ return [uname, pattern, users, insert_location]
326
+ end
327
+
328
+ def buffer_error(line, pattern, uname, users, insert_location)
329
+ set_status(NEWDATA) if insert_location == BUFFER_END
330
+ pattern = @config.get_pattern('error')
331
+ pattern['%m'] = line[ERR]
332
+ pattern = escape_xml(pattern)
333
+ return [uname, pattern, users, insert_location]
334
+ end
335
+
336
+ def buffer_notice(line, pattern, uname, users, insert_location)
337
+ set_status(NEWDATA) if insert_location == BUFFER_END
338
+ pattern = @config.get_pattern('notice')
339
+ pattern['%m'] = line[MSG]
340
+ pattern = escape_xml(pattern)
341
+ return [uname, pattern, users, insert_location]
342
+ end
343
+
344
+ def buffer_topic(line, pattern, uname, users, insert_location)
345
+ set_status(NEWDATA) if insert_location == BUFFER_END
346
+ if line['init'] and line['line'] == 2
347
+ pattern += @config.get_pattern('topic_setby')
348
+ pattern['%c'] = line[CHANNEL]
349
+ pattern['%u'] = line[TOPIC_SET_BY].to_s
350
+ pattern['%a'] = Time.at(line[TOPIC_TIMESTAMP].to_i).strftime('%c')
351
+ users.push(line[TOPIC_SET_BY])
352
+ elsif line['init'] and line['line'] == 1
353
+ pattern += @config.get_pattern('topic')
354
+ pattern['%c'] = line[CHANNEL]
355
+ pattern['%t'] = line[TOPIC]
356
+ elsif line[TOPIC]
357
+ pattern += @config.get_pattern('topic_change')
358
+ pattern['%t'] = line[TOPIC].to_s
359
+ pattern['%u'] = line[TOPIC_SET_BY].to_s
360
+ users.push(line[TOPIC_SET_BY])
361
+ end
362
+ pattern = escape_xml(pattern)
363
+ return [uname, pattern, users, insert_location]
364
+ end
365
+
366
+ def buffer_modechange(line, pattern, uname, users, insert_location)
367
+ if line[ADD]
368
+ pattern = @config.get_pattern('add_mode')
369
+ pattern['%m'] = line[ADD]
370
+ elsif line[REMOVE]
371
+ pattern = @config.get_pattern('remove_mode')
372
+ pattern['%m'] = line[REMOVE]
373
+ else
374
+ return
375
+ end
376
+ if line[SOURCE_PRESENCE]
377
+ pattern['%s'] = line[SOURCE_PRESENCE]
378
+ elsif line[:irc_source_nick]
379
+ pattern['%s'] = line[:irc_source_nick]
380
+ end
381
+ #pattern['%m'] = line['mode']
382
+ pattern['%u'] = line[PRESENCE]
383
+ users.push(line[SOURCE_PRESENCE], line[PRESENCE])
384
+ pattern = escape_xml(pattern)
385
+ return [uname, pattern, users, insert_location]
386
+ end
387
+
388
+ def buffer_nickchange(line, pattern, uname, users, insert_location)
389
+ pattern = @config.get_pattern('nickchange')
390
+
391
+ pattern['%u'] = line[PRESENCE].to_s
392
+ pattern['%n'] = line[NAME].to_s
393
+
394
+ users.push(line[NAME])
395
+ pattern = escape_xml(pattern)
396
+ return [uname, pattern, users, insert_location]
397
+ end
398
+
399
+ def buffer_usernickchange(line, pattern, uname, users, insert_location)
400
+ pattern = @config.get_pattern('usernickchange')
401
+
402
+ pattern['%n'] = line[NAME].to_s
403
+
404
+ users.push(line[NAME])
405
+ pattern = escape_xml(pattern)
406
+ return [uname, pattern, users, insert_location]
407
+ end
408
+ end
409
+ end