rubeechat 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+