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.
- data/Rakefile +28 -0
- data/bin/rubeechat-install-plugins +73 -0
- data/lib/rubeechat.rb +13 -0
- data/lib/rubeechat/base.rb +12 -0
- data/lib/rubeechat/config.rb +33 -0
- data/lib/rubeechat/info_list.rb +106 -0
- data/lib/rubeechat/privmsg.rb +72 -0
- data/plugins/growl.rb +376 -0
- data/plugins/urlopen.rb +220 -0
- metadata +75 -0
data/Rakefile
ADDED
@@ -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
|
data/lib/rubeechat.rb
ADDED
@@ -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,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
|
+
|
data/plugins/growl.rb
ADDED
@@ -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
|
+
|
data/plugins/urlopen.rb
ADDED
@@ -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
|
+
|