rubeechat 0.1.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.
@@ -0,0 +1,28 @@
1
+ # The Mohel cuts our gems
2
+ def define_mohel_task
3
+ require 'rubygems'
4
+ gem 'mohel', '>= 0.4.10'
5
+ require 'mohel'
6
+
7
+ Mohel::Task.new do |t|
8
+ s = t.gem_spec
9
+ s.name = 'rubeechat'
10
+ s.summary = "Rubeechat: little ruby helpers for scripting WeeChat"
11
+ s.has_rdoc = true
12
+ s.authors = ['Jonathan D. Simms']
13
+ s.email = 'slyphon@gmail.com'
14
+ s.files = FileList["Rakefile", '{lib,plugins}/**/*']
15
+ s.executables = 'rubeechat-install-plugins'
16
+ yield t if block_given?
17
+
18
+ end
19
+ rescue LoadError => e
20
+
21
+ $stderr.puts %Q[#{e.class}: #{e.message}\n#{e.backtrace.map { |l| "\t#{l}" }.join("\n")}]
22
+
23
+ $stderr.puts "You must do 'gem install mohel' to create rubeechat gems"
24
+ end
25
+
26
+ define_mohel_task
27
+
28
+
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'fileutils'
5
+
6
+ RUBEECHAT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) unless defined?(RUBEECHAT_ROOT)
7
+
8
+ module Rubeechat
9
+ class Installer
10
+ include FileUtils
11
+
12
+ BANNER = <<-EOS
13
+ Usage: #{File.basename($0)} [opts]
14
+
15
+ Installs symlinks to the packaged plugins in your $HOME/.weechat/ruby directory (or in
16
+ the --plugins-dir directory, if given).
17
+
18
+ Will not overwrite existing links or files unless --force option is given.
19
+
20
+ Options:
21
+
22
+ EOS
23
+
24
+ def self.run(*argv)
25
+ self.new.run(*argv)
26
+ end
27
+
28
+ def initialize
29
+ @plugins_dir = File.expand_path('~/.weechat/ruby')
30
+ @plugins = Dir[File.join(RUBEECHAT_ROOT, 'plugins', '*.rb')]
31
+ @force = false
32
+ end
33
+
34
+ def optparser
35
+ @optparser ||= OptionParser.new do |o|
36
+ o.banner = BANNER
37
+ o.on('-f', '--force', 'forces overwriting of existing symlinks in plugin-dir') { @force = true }
38
+ o.on('-D', '--plugin-dir DIR', 'path to the .weechat/ruby plugins dir to install to') { |d| @plugins_dir = File.expand_path(d) }
39
+ o.on('-h', '--help', "you're reading it") { help! }
40
+ end
41
+ end
42
+
43
+ def help!
44
+ $stderr.puts optparser
45
+ exit 1
46
+ end
47
+
48
+ def run(*argv)
49
+ optparser.parse!(argv)
50
+
51
+ @plugins.each do |p|
52
+ target = File.expand_path(File.join(@plugins_dir, File.basename(p)))
53
+
54
+ if File.exists?(target)
55
+ if @force
56
+ rm_f(target)
57
+ else
58
+ $stderr.puts "file #{target} already exists, not clobbering without --force option"
59
+ next
60
+ end
61
+ end
62
+
63
+ $stderr.puts "installing symlink #{target} -> #{p}"
64
+
65
+ ln_s(p, target)
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ Rubeechat::Installer.run(*ARGV)
72
+
73
+ # vim:ft=ruby:ts=2:sw=2:et
@@ -0,0 +1,13 @@
1
+ RUBEECHAT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
2
+
3
+ $LOAD_PATH.unshift(File.join(RUBEECHAT_ROOT, 'lib'))
4
+ $LOAD_PATH.uniq!
5
+
6
+ module Rubeechat
7
+ end
8
+
9
+ require 'rubeechat/info_list'
10
+ require 'rubeechat/privmsg'
11
+ require 'rubeechat/config'
12
+ require 'rubeechat/installer'
13
+
@@ -0,0 +1,12 @@
1
+ module Rubeechat
2
+ class Base
3
+ attr_accessor :name, :author, :license, :description, :shutdown_function, :charset
4
+
5
+
6
+ def register!
7
+ ::Weechat.register
8
+ end
9
+
10
+ end
11
+ end
12
+
@@ -0,0 +1,33 @@
1
+ module Rubeechat
2
+ module Config
3
+ class OptionNotFoundError < StandardError; end
4
+ class GenericOptionError < StandardError; end
5
+
6
+ include Weechat
7
+ extend Weechat
8
+
9
+ def self.[](key)
10
+ key = key.to_s
11
+ return nil unless has_key?(key)
12
+ config_get_plugin(key)
13
+ end
14
+
15
+ def self.[]=(key,value)
16
+ case config_set_plugin(key.to_s, value.to_s)
17
+ when WEECHAT_CONFIG_OPTION_SET_OK_CHANGED
18
+ value
19
+ when WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE
20
+ value
21
+ when WEECHAT_CONFIG_OPTION_SET_OPTION_NOT_FOUND
22
+ raise OptionNotFoundError, key.to_s
23
+ when WEECHAT_CONFIG_OPTION_SET_ERROR
24
+ raise GenericOptionError, key.to_s
25
+ end
26
+ end
27
+
28
+ def self.has_key?(key)
29
+ config_is_set_plugin(key) > 0
30
+ end
31
+ end
32
+ end
33
+
@@ -0,0 +1,106 @@
1
+ module Rubeechat
2
+ class InfoList
3
+ include Enumerable
4
+
5
+ # a very cheezy implemenation of a buffer object
6
+ class Buffer
7
+ def initialize(ptr, hash)
8
+ @p = ptr
9
+ @hash = hash
10
+ end
11
+
12
+ # print a message to this buffer
13
+ def print(msg)
14
+ Weechat.print(@p, msg)
15
+ end
16
+
17
+ def server
18
+ name.split('.').first
19
+ end
20
+
21
+ def current_buffer?
22
+ current_buffer == 1
23
+ end
24
+
25
+ private
26
+ def method_missing(name, *a, &b)
27
+ name = name.to_sym
28
+ if @hash.has_key?(name)
29
+ @hash[name]
30
+ else
31
+ super(name, *a, &b)
32
+ end
33
+ end
34
+ end
35
+
36
+ attr_reader :p
37
+
38
+ def self.open(name, ptr='')
39
+ obj = self.new(name, ptr)
40
+ yield obj
41
+ ensure
42
+ obj.close
43
+ end
44
+
45
+ # return a buffer object from information in infolist pointed to by ptr
46
+ def self.buffer(ptr)
47
+ open(:buffer, ptr) do |info|
48
+ Buffer.new(ptr, info.first)
49
+ end
50
+ end
51
+
52
+ def initialize(name, ptr='')
53
+ @name = name.to_s
54
+ @p = Weechat.infolist_get(@name, ptr, '')
55
+ end
56
+
57
+ def close
58
+ return unless @p
59
+ i, @p = @p , nil
60
+ Weechat.infolist_free(i)
61
+ nil
62
+ end
63
+
64
+ def each
65
+ while Weechat.infolist_next(@p) > 0
66
+ hash = Weechat.infolist_fields(@p).split(',').inject({}) do |memo,str|
67
+ ftype, name = str.split(':')
68
+
69
+ next memo if name =~ /^localvar/
70
+
71
+ if val = fetch_item(ftype, name)
72
+ memo[name.to_sym] = val
73
+ end
74
+
75
+ memo
76
+ end
77
+
78
+ yield hash
79
+ end
80
+ end
81
+
82
+ # returns the first struct in the infolist
83
+ def first
84
+ to_a.first # inefficient!
85
+ end
86
+
87
+ private
88
+ def fetch_item(ftype, name)
89
+ case ftype
90
+ when 'i'
91
+ Weechat.infolist_integer(@p, name)
92
+ when 's'
93
+ Weechat.infolist_string(@p, name)
94
+ when 'p'
95
+ Weechat.infolist_pointer(@p, name)
96
+ when 'b'
97
+ nil
98
+ when 't'
99
+ Weechat.infolist_time(@p, name)
100
+ else
101
+ raise "Wtf? unknown field type string: #{ftype}"
102
+ end
103
+ end
104
+ end
105
+ end
106
+
@@ -0,0 +1,72 @@
1
+ module Rubeechat
2
+
3
+ class Privmsg
4
+ def self.parse(signal, data)
5
+ server, _ = signal.split(',')
6
+
7
+ if data =~ /:([^ ]+) PRIVMSG (#?[^ ]+) :(.*)/
8
+ self.new(server, $1, $2, $3)
9
+ end
10
+ end
11
+
12
+ attr_reader :server, :user, :channel, :message
13
+
14
+ def initialize(server, user, channel, message)
15
+ @server, @user, @channel, @message = server, user, channel, message
16
+ end
17
+
18
+ # :trptcolin!~trptcolin@c-24-15-225-118.hsd1.il.comcast.net
19
+
20
+ def nick
21
+ @nick ||= user[/^([^!]+)!/, 1]
22
+ end
23
+
24
+ def login
25
+ @login ||= user[/!~([^@]+)@/, 1]
26
+ end
27
+
28
+ def hostname
29
+ @hostname ||= user[/@(.*)$/, 1]
30
+ end
31
+
32
+ # the buffer name that is related to this message
33
+ def buffer_name
34
+ "#{server}.#{channel}"
35
+ end
36
+
37
+ # lazily construct a buffer object associated with this privmsg
38
+ def buffer
39
+ unless @buffer
40
+ if ptr = Weechat.buffer_search('irc', buffer_name)
41
+ @buffer = InfoList.buffer(ptr)
42
+ else
43
+ raise "Could not find buffer named: #{buffer_name.inspect}"
44
+ end
45
+ end
46
+ @buffer
47
+ end
48
+
49
+ def about_me?
50
+ message =~ /#{nick_ptrn_str}/
51
+ end
52
+
53
+ def to_me?
54
+ message =~ /^#{nick_ptrn_str}/
55
+ end
56
+
57
+ def involves_me?
58
+ about_me? or to_me?
59
+ end
60
+
61
+ def in_current_buffer?
62
+ buffer.current_buffer?
63
+ end
64
+
65
+ private
66
+ # regular expression string for our nick
67
+ def nick_ptrn_str
68
+ @nick_ptrn ||= Regexp.new(Regexp.escape(Weechat.info_get('irc_nick', server)))
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,376 @@
1
+ #growl notifications for weechat
2
+ require 'time'
3
+ require 'date'
4
+ require 'md5'
5
+ require 'socket'
6
+
7
+ #------------------------------------------------------------------------------
8
+ # taken from ruby-growl-1.0.1 verbatim
9
+
10
+ class Growl
11
+
12
+ ##
13
+ # The Ruby that ships with Tiger has a broken #pack, so 'v' means network
14
+ # byte order instead of 'n'.
15
+
16
+ BROKEN_PACK = [1].pack("n") != "\000\001" # :nodoc:
17
+
18
+ ##
19
+ # ruby-growl Version
20
+
21
+ VERSION = "1.0.1"
22
+
23
+ ##
24
+ # Growl Network Registration Packet +pack+ Format
25
+ #--
26
+ # Format:
27
+ #
28
+ # struct GrowlNetworkRegistration {
29
+ # struct GrowlNetworkPacket {
30
+ # unsigned char version;
31
+ # unsigned char type;
32
+ # } __attribute__((packed));
33
+ # unsigned short appNameLen;
34
+ # unsigned char numAllNotifications;
35
+ # unsigned char numDefaultNotifications;
36
+ # /*
37
+ # * Variable sized. Format:
38
+ # * <application name><all notifications><default notifications><checksum>
39
+ # * where <all notifications> is of the form (<length><name>){num} and
40
+ # * <default notifications> is an array of indices into the all notifications
41
+ # * array, each index being 8 bits.
42
+ # */
43
+ # unsigned char data[];
44
+ # } __attribute__((packed));
45
+
46
+ GNR_FORMAT = "CCnCCa*"
47
+
48
+ GNR_FORMAT.gsub!(/n/, 'v') if BROKEN_PACK
49
+
50
+ ##
51
+ # Growl Network Notification Packet +pack+ Format
52
+ #--
53
+ # Format:
54
+ #
55
+ # struct GrowlNetworkNotification {
56
+ # struct GrowlNetworkPacket {
57
+ # unsigned char version;
58
+ # unsigned char type;
59
+ # } __attribute__((packed));
60
+ # struct GrowlNetworkNotificationFlags {
61
+ # unsigned reserved: 12;
62
+ # signed priority: 3;
63
+ # unsigned sticky: 1;
64
+ # } __attribute__((packed)) flags; //size = 16 (12 + 3 + 1)
65
+ # unsigned short nameLen;
66
+ # unsigned short titleLen;
67
+ # unsigned short descriptionLen;
68
+ # unsigned short appNameLen;
69
+ # /*
70
+ # * Variable sized. Format:
71
+ # * <notification name><title><description><application name><checksum>
72
+ # */
73
+ # unsigned char data[];
74
+ # } __attribute__((packed));
75
+
76
+ GNN_FORMAT = "CCnnnnna*"
77
+
78
+ GNN_FORMAT.gsub!(/n/, 'v') if BROKEN_PACK
79
+
80
+ ##
81
+ # Growl UDP Port
82
+
83
+ GROWL_UDP_PORT = 9887
84
+
85
+ ##
86
+ # Growl Protocol Version
87
+
88
+ GROWL_PROTOCOL_VERSION = 1
89
+
90
+ ##
91
+ # Growl Registration Packet Id
92
+
93
+ GROWL_TYPE_REGISTRATION = 0
94
+
95
+ ##
96
+ # Growl Notification Packet Id
97
+
98
+ GROWL_TYPE_NOTIFICATION = 1
99
+
100
+ ##
101
+ # Creates a new Growl notifier and automatically registers any notifications
102
+ # with the remote machine.
103
+ #
104
+ # +host+ is the host to contact.
105
+ #
106
+ # +app_name+ is the name of the application sending the notifications.
107
+ #
108
+ # +all_notifies+ is a list of notification types your application sends.
109
+ #
110
+ # +default_notifies+ is a list of notification types that are turned on by
111
+ # default.
112
+ #
113
+ # I'm not sure about what +default_notifies+ is supposed to be set to, since
114
+ # there is a comment that says "not a subset of all_notifies" in the code.
115
+ #
116
+ # +password+ is the password needed to send notifications to +host+.
117
+
118
+ def initialize(host, app_name, all_notifies, default_notifies = nil,
119
+ password = nil)
120
+ @socket = UDPSocket.open
121
+ # FIXME This goes somewhere else
122
+ @socket.connect host, GROWL_UDP_PORT
123
+ @app_name = app_name
124
+ @all_notifies = all_notifies
125
+ @default_notifies = default_notifies.nil? ? all_notifies : default_notifies
126
+ @password = password
127
+
128
+ register
129
+ end
130
+
131
+ ##
132
+ # Sends a notification.
133
+ #
134
+ # +notify_type+ is the type of notification to send.
135
+ #
136
+ # +title+ is a title for the notification.
137
+ #
138
+ # +message+ is the body of the notification.
139
+ #
140
+ # +priority+ is the priorty of message to send.
141
+ #
142
+ # +sticky+ makes the notification stick until clicked.
143
+
144
+ def notify(notify_type, title, message, priority = 0, sticky = false)
145
+ raise "Unknown Notification" unless @all_notifies.include? notify_type
146
+ raise "Invalid Priority" unless priority >= -2 and priority <= 2
147
+
148
+ send notification_packet(notify_type, title, message, priority, sticky)
149
+ end
150
+
151
+ # really? i had to add this?
152
+ def close
153
+ @socket.close
154
+ end
155
+
156
+ private unless $TESTING
157
+
158
+ ##
159
+ # Registers the notification types with +host+.
160
+
161
+ def register
162
+ send registration_packet
163
+ end
164
+
165
+ ##
166
+ # Sends a Growl packet
167
+
168
+ def send(packet)
169
+ set_sndbuf packet.length
170
+ @socket.send packet, 0
171
+ @socket.flush
172
+ end
173
+
174
+ ##
175
+ # Builds a Growl registration packet
176
+
177
+ def registration_packet
178
+ length = 0
179
+ data = []
180
+ data_format = ""
181
+
182
+ packet = [
183
+ GROWL_PROTOCOL_VERSION,
184
+ GROWL_TYPE_REGISTRATION
185
+ ]
186
+
187
+ packet << @app_name.length
188
+ packet << @all_notifies.length
189
+ packet << @default_notifies.length
190
+
191
+ data << @app_name
192
+ data_format = "a#{@app_name.length}"
193
+
194
+ @all_notifies.each do |notify|
195
+ data << notify.length
196
+ data << notify
197
+ data_format << "na#{notify.length}"
198
+ end
199
+
200
+ @default_notifies.each do |notify|
201
+ data << @all_notifies.index(notify) if @all_notifies.include? notify
202
+ data_format << "C"
203
+ end
204
+
205
+ data_format.gsub!(/n/, 'v') if BROKEN_PACK
206
+
207
+ data = data.pack data_format
208
+
209
+ packet << data
210
+
211
+ packet = packet.pack GNR_FORMAT
212
+
213
+ checksum = MD5.new packet
214
+ checksum.update @password unless @password.nil?
215
+
216
+ packet << checksum.digest
217
+
218
+ return packet
219
+ end
220
+
221
+ ##
222
+ # Builds a Growl notification packet
223
+
224
+ def notification_packet(name, title, description, priority, sticky)
225
+ flags = 0
226
+ data = []
227
+
228
+ packet = [
229
+ GROWL_PROTOCOL_VERSION,
230
+ GROWL_TYPE_NOTIFICATION,
231
+ ]
232
+
233
+ flags = 0
234
+ flags |= ((0x7 & priority) << 1) # 3 bits for priority
235
+ flags |= 1 if sticky # 1 bit for sticky
236
+
237
+ packet << flags
238
+ packet << name.length
239
+ packet << title.length
240
+ packet << description.length
241
+ packet << @app_name.length
242
+
243
+ data << name
244
+ data << title
245
+ data << description
246
+ data << @app_name
247
+
248
+ packet << data.join
249
+ packet = packet.pack GNN_FORMAT
250
+
251
+ checksum = MD5.new packet
252
+ checksum.update @password unless @password.nil?
253
+
254
+ packet << checksum.digest
255
+
256
+ return packet
257
+ end
258
+
259
+ ##
260
+ # Set the size of the send buffer
261
+ #--
262
+ # Is this truly necessary?
263
+
264
+ def set_sndbuf(length)
265
+ @socket.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDBUF, length
266
+ end
267
+
268
+ end
269
+
270
+ # end ruby-growl
271
+ #-----------------------------------------------------------------------------
272
+
273
+ require 'rubygems'
274
+ require 'rubeechat'
275
+
276
+ require 'logger'
277
+ require 'ostruct'
278
+
279
+ require 'rubygems'
280
+ require 'ruby-debug'
281
+
282
+ # Debugger.start_remote('127.0.0.1', [Debugger::PORT, Debugger::PORT + 1], false) do
283
+ # # load initrc script
284
+ # Debugger.run_init_script(StringIO.new)
285
+ # end
286
+
287
+ L = Logger.new('/dev/null')
288
+ L.level = Logger::DEBUG
289
+
290
+ class Privmsg < Rubeechat::Privmsg
291
+ end
292
+
293
+ def disabled?
294
+ Rubeechat::Config['disabled'] == 'true'
295
+ end
296
+
297
+ GROWLER = Growl.new('127.0.0.1', 'weechat', ['message'])
298
+
299
+ def inactivity
300
+ Weechat.info_get('inactivity', '').to_i
301
+ end
302
+
303
+ def growl_notify(title, message, priority=0, sticky=false)
304
+ L.debug "sending growl notification, type: 'message', title: %p, message: %p, priority: %p, sticky: %p" % [title, message, priority, sticky]
305
+ GROWLER.notify('message', title, message, priority, sticky)
306
+ end
307
+
308
+ def ok
309
+ Weechat::WEECHAT_RC_OK
310
+ end
311
+
312
+ def growl_cmd(_, buf_ptr, args)
313
+ buf = Rubeechat::InfoList.buffer(buf_ptr)
314
+
315
+ cmd, args = args.split(' ', 2)
316
+
317
+ case cmd
318
+ when 'test'
319
+ growl_notify('test', args)
320
+ when 'disable'
321
+ Rubeechat::Config['disabled'] = 'true'
322
+ when 'enable'
323
+ Rubeechat::Config['disabled'] = 'false'
324
+ when 'break'
325
+ # debugger
326
+ end
327
+
328
+ ok
329
+ end
330
+
331
+ def privmsg_cb(_, signal, signal_data)
332
+ p = Rubeechat::Privmsg.parse(signal, signal_data)
333
+
334
+ if p.involves_me?
335
+ # if the message is in a different channel than the current buffer, or
336
+ # we've been inactive for more then 30 seconds
337
+ if !p.in_current_buffer? or (inactivity > 30) and not disabled?
338
+
339
+ msg_type_str = p.to_me? ? "#{p.nick} is talking about you" : "#{p.nick} is talking to you"
340
+ growl_notify("#{msg_type_str} in #{p.channel}", p.message)
341
+ end
342
+ end
343
+
344
+ ok
345
+ end
346
+
347
+ DEFAULT_OPTS = {
348
+ 'disabled' => 'false'
349
+ }
350
+
351
+ ARG_INFO = <<-EOS
352
+ test: send a test message to growl daemon
353
+ enable: unsilence the growler (default)
354
+ disable: silence the growler
355
+ EOS
356
+
357
+ ARG_FORM = '[test|enable|disable]'
358
+
359
+ def weechat_destructor
360
+ L.close
361
+ GROWLER.close
362
+ ok
363
+ end
364
+
365
+ def weechat_init
366
+ Weechat.register('growler', 'slyphon', '0.1', 'GPL3', 'Growl notify when mentioned', 'weechat_destructor', '')
367
+ Weechat.hook_signal('*,irc_in_PRIVMSG', 'privmsg_cb', '')
368
+ Weechat.hook_command('growl', 'control the growler', ARG_FORM, ARG_INFO, '', 'growl_cmd', '')
369
+
370
+ DEFAULT_OPTS.each do |k,v|
371
+ Rubeechat::Config[k] = v unless Rubeechat::Config.has_key?(k)
372
+ end
373
+
374
+ ok
375
+ end
376
+
@@ -0,0 +1,220 @@
1
+ require 'rubygems'
2
+ require 'rubeechat'
3
+
4
+ require 'logger'
5
+ require 'ostruct'
6
+
7
+ ## start remote debugger
8
+
9
+ # require 'rubygems'
10
+ # require 'ruby-debug'
11
+
12
+ # Debugger.start_remote('127.0.0.1', [Debugger::PORT, Debugger::PORT + 1], false) do
13
+ # # load initrc script
14
+ # Debugger.run_init_script(StringIO.new)
15
+ # end
16
+
17
+ L = Logger.new('/dev/null')
18
+ L.level = Logger::DEBUG
19
+
20
+ class ::Symbol
21
+ include Comparable
22
+
23
+ def <=>(other)
24
+ to_s <=> other.to_s
25
+ end
26
+ end
27
+
28
+ class UrlRingBuffer
29
+ include Enumerable
30
+
31
+ DEFAULT_SIZE = 20
32
+
33
+ attr_reader :current_index, :array
34
+
35
+ def initialize(size=nil)
36
+ @max_size ||= DEFAULT_SIZE
37
+
38
+ @array = Array.new(@max_size)
39
+
40
+ @current_index = -1 # initial state
41
+ end
42
+
43
+ def [](idx)
44
+ @array[idx]
45
+ end
46
+
47
+ def replace(other)
48
+ unless other.kind_of?(UrlRingBuffer)
49
+ raise ArgumentError, "argument to replace must be a UrlRingBuffer, not: #{other.inspect}"
50
+ end
51
+
52
+ @array.replace(other.array)
53
+ self
54
+ end
55
+
56
+ def empty?
57
+ (@current_index == -1)
58
+ end
59
+
60
+ def clear
61
+ @array.clear
62
+ @current_index = -1
63
+ end
64
+
65
+ def add(obj)
66
+ @current_index += 1
67
+
68
+ if @current_index >= @max_size # if we are over the max # of items to store
69
+ puts "resetting index to 0"
70
+ @current_index = 0 # start again at 0
71
+ end
72
+
73
+ @array[@current_index] = obj
74
+ end
75
+
76
+ def current_item
77
+ (@current_index >= 0) ? @array[@current_index] : nil
78
+ end
79
+
80
+ # yields [idx, obj] to the block given
81
+ def each
82
+ @array.each_with_index do |obj,i|
83
+ next unless obj
84
+ yield i, obj
85
+ end
86
+ end
87
+ end
88
+
89
+ Rubeechat::InfoList::Buffer.class_eval do
90
+ def urls
91
+ RING_BUFFERS[name]
92
+ end
93
+
94
+ def current_url
95
+ RING_BUFFERS[name].current_item
96
+ end
97
+ end
98
+
99
+ class Privmsg < Rubeechat::Privmsg
100
+ def urls
101
+ unless @urls
102
+ @urls, str = [], message
103
+ while str =~ %r%([a-z0-9]+://[^ ]+)%
104
+ @urls << $1
105
+ str = $~.post_match
106
+ end
107
+ end
108
+ @urls
109
+ end
110
+ end
111
+
112
+
113
+ def ok
114
+ Weechat::WEECHAT_RC_OK
115
+ end
116
+
117
+ # --- plugin implementation ---------------------------------
118
+
119
+ RING_BUFFERS = Hash.new { |n,q| n[q] = UrlRingBuffer.new } unless defined?(RING_BUFFERS)
120
+
121
+ def _debug(m=nil, &b)
122
+ L.debug(m=nil, &b)
123
+ end
124
+
125
+ def open_url(buffer, i=nil)
126
+ url = i.nil? ? buffer.current_url : buffer.urls[i]
127
+
128
+ if url
129
+ cmd = Rubeechat::Config['command'] % [url]
130
+ system(cmd)
131
+ else
132
+ msg = i.nil? ? "url ring empty" : "no url # #{i} for buffer #{buffer.name}"
133
+ buffer.print("urlopen: #{msg}")
134
+ end
135
+ end
136
+
137
+ def open_cmd(data, buf_ptr, args)
138
+ buf = Rubeechat::InfoList.buffer(buf_ptr)
139
+
140
+ cmd, args = args.split(' ', 2)
141
+
142
+ case cmd
143
+ when 'list'
144
+ if buf.urls.empty?
145
+ buf.print("urlopen: no saved urls!")
146
+ else
147
+ buf.urls.each do |idx, url|
148
+ next unless url
149
+ buf.print("[%d]: %s" % [idx,url])
150
+ end
151
+ end
152
+ when 'set-command'
153
+ Rubeechat::Config['command'] = args
154
+ buf.print("urlopen: command is now #{args}")
155
+ when 'clear'
156
+ buf.urls.clear
157
+ buf.print("urlopen: cleared url list")
158
+ when 'clobber'
159
+ RING_BUFFERS.clear
160
+ buf.print("urlopen: clobbered url list (all saved urls)")
161
+ when /(\d+)/
162
+ open_url(buf, $1.to_i)
163
+ when 'debug'
164
+ debugger
165
+ else
166
+ open_url(buf)
167
+ end
168
+
169
+ ok
170
+ end
171
+
172
+ def privmsg_cb(data, signal, signal_data)
173
+ _debug { "privmsg_cb signal: #{signal}, #{signal_data}" }
174
+ pm = Privmsg.parse(signal, signal_data)
175
+
176
+ pm.urls.each do |url|
177
+ _debug { "found url: #{url} in buffer: #{pm.buffer_name}" }
178
+
179
+ _debug { "pm.buffer.urls: #{pm.buffer.urls.inspect}, pm.buffer.name #{pm.buffer.name}" }
180
+
181
+ pm.buffer.urls.add(url) # only does the expensive buffer loading if we found somethin
182
+
183
+ _debug { "pm.buffer.urls: #{pm.buffer.urls.inspect}, pm.buffer.name #{pm.buffer.name}" }
184
+
185
+ pm.buffer.print("[%d]: %s" % [pm.buffer.urls.current_index, pm.buffer.current_url])
186
+ end
187
+
188
+ ok
189
+ end
190
+
191
+ ARG_INFO = <<-EOS
192
+ [1-20]: enter a url number from the current buffer
193
+ list: list all urls we know about
194
+ clear: forget all urls for this buffer
195
+ dump: display all urls we know about
196
+ EOS
197
+
198
+ ARG_FORM = '[1-20] | [list|clear|dump]'
199
+
200
+ DEFAULT_OPTS = {
201
+ 'command' => %q[/usr/bin/open '%s'],
202
+ }
203
+
204
+ def weechat_destructor
205
+ L.close
206
+ ok
207
+ end
208
+
209
+ def weechat_init
210
+ Weechat.register('urlopen', 'slyphon', '0.1', 'GPL3', 'Open urls pasted in chat', 'weechat_destructor', '')
211
+ Weechat.hook_signal('*,irc_in_PRIVMSG', 'privmsg_cb', '')
212
+ Weechat.hook_command('open', 'open a url', ARG_FORM, ARG_INFO, '', 'open_cmd', '')
213
+
214
+ DEFAULT_OPTS.each do |k,v|
215
+ Rubeechat::Config[k] = v unless Rubeechat::Config.has_key?(k)
216
+ end
217
+
218
+ ok
219
+ end
220
+
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubeechat
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Jonathan D. Simms
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-23 00:00:00 +00:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description:
23
+ email: slyphon@gmail.com
24
+ executables:
25
+ - rubeechat-install-plugins
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - Rakefile
32
+ - lib/rubeechat/base.rb
33
+ - lib/rubeechat/config.rb
34
+ - lib/rubeechat/info_list.rb
35
+ - lib/rubeechat/privmsg.rb
36
+ - lib/rubeechat.rb
37
+ - plugins/growl.rb
38
+ - plugins/urlopen.rb
39
+ - bin/rubeechat-install-plugins
40
+ has_rdoc: true
41
+ homepage:
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ hash: 3
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.3.7
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: "Rubeechat: little ruby helpers for scripting WeeChat"
74
+ test_files: []
75
+