kirby 4.1 → 4.2
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.tar.gz.sig +0 -0
- data/CHANGELOG +2 -0
- data/README +13 -7
- data/bin/kirby +35 -10
- data/kirby.gemspec +4 -4
- data/lib/kirby.rb +102 -49
- metadata +3 -3
- metadata.gz.sig +2 -2
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGELOG
CHANGED
data/README
CHANGED
@@ -25,20 +25,26 @@ Kirby is considered feature-locked.
|
|
25
25
|
|
26
26
|
== Usage
|
27
27
|
|
28
|
-
|
28
|
+
To start a basic Kirby from the command line:
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
cd working/directory
|
31
|
+
kirby mynick mychannel myserver.org [delicious_user] [delicious_pass] [--options]
|
32
|
+
|
33
|
+
Logs and repository histories get saved to the working directory.
|
34
|
+
|
35
|
+
Available post-parameters are:
|
36
|
+
<tt>--daemon</tt>:: Daemonize.
|
33
37
|
<tt>--silent</tt>:: Never speak, even for errors.
|
38
|
+
<tt>--log</tt>:: Log channel chatter to a file.
|
39
|
+
<tt>--debug</tt>:: Debug mode.
|
34
40
|
|
35
|
-
See the Kirby class for commands.
|
41
|
+
See the Kirby class for in-channel commands.
|
36
42
|
|
37
43
|
== Crontab example
|
38
44
|
|
39
|
-
You can use a crontask to keep Kirby alive at all times.
|
45
|
+
You can use a crontask to keep Kirby alive at all times. For example:
|
40
46
|
|
41
|
-
* * * * * bash -c 'cd /working/directory; kirby
|
47
|
+
* * * * * bash -c 'cd /working/directory; kirby mynick mychannel irc.freenode.net --daemon --log --silent &> /dev/null'
|
42
48
|
|
43
49
|
== Reporting problems
|
44
50
|
|
data/bin/kirby
CHANGED
@@ -1,22 +1,47 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
=begin rdoc
|
4
|
-
Run <tt>kirby [nick] [channel] [server] [
|
4
|
+
Run <tt>kirby [nick] [channel] [server] [optional del.icio.us name] [optional del.icio.us password]</tt>.
|
5
5
|
|
6
|
-
Optional parameters:
|
7
|
-
<tt
|
8
|
-
<tt>-no-d</tt>:: Don't daemonize.
|
6
|
+
Optional post-parameters:
|
7
|
+
<tt>--daemon</tt>:: Daemonize.
|
9
8
|
<tt>--silent</tt>:: Never speak, even for errors.
|
9
|
+
<tt>--log</tt>:: Log channel chatter to a file.
|
10
|
+
<tt>--debug</tt>:: Debug mode.
|
10
11
|
=end
|
11
12
|
|
12
13
|
require 'rubygems'
|
13
|
-
require '
|
14
|
+
require 'daemons'
|
14
15
|
|
15
|
-
|
16
|
+
begin
|
17
|
+
require "#{File.dirname(__FILE__)}/../lib/kirby"
|
18
|
+
rescue LoadError
|
19
|
+
require 'kirby'
|
20
|
+
end
|
21
|
+
|
22
|
+
def option?(opt)
|
23
|
+
ARGV.include?("--#{opt}")
|
24
|
+
end
|
25
|
+
|
26
|
+
def param(arg)
|
27
|
+
arg unless arg =~ /^--/
|
28
|
+
end
|
29
|
+
|
30
|
+
@kirby = Kirby.new(
|
31
|
+
:nick => param(ARGV[0]),
|
32
|
+
:channel => param(ARGV[1]),
|
33
|
+
:server => param(ARGV[2]),
|
34
|
+
:delicious_user => param(ARGV[3]),
|
35
|
+
:delicious_pass => param(ARGV[4]),
|
36
|
+
:silent => option?("silent"),
|
37
|
+
:debug => option?("debug"),
|
38
|
+
:log => option?("log")
|
39
|
+
)
|
40
|
+
|
41
|
+
pid = open(@kirby.config[:pidfile]).gets.chomp rescue nil
|
16
42
|
|
17
43
|
if !pid or `ps #{pid}`.split("\n").size < 2
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
Kirby.instance.restart
|
44
|
+
Daemons.daemonize if option? "daemon"
|
45
|
+
open(@kirby.config[:pidfile], 'w') {|f| f.puts $$}
|
46
|
+
@kirby.restart
|
22
47
|
end
|
data/kirby.gemspec
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
|
2
|
-
# Gem::Specification for Kirby-4.
|
2
|
+
# Gem::Specification for Kirby-4.2
|
3
3
|
# Originally generated by Echoe
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = %q{kirby}
|
7
|
-
s.version = "4.
|
7
|
+
s.version = "4.2"
|
8
8
|
|
9
9
|
s.specification_version = 2 if s.respond_to? :specification_version=
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.authors = [""]
|
13
|
-
s.date = %q{2007-12-
|
13
|
+
s.date = %q{2007-12-31}
|
14
14
|
s.default_executable = %q{kirby}
|
15
15
|
s.description = %q{A super-clean IRC bot with sandboxed Ruby evaluation, svn watching, and link-logging to del.icio.us.}
|
16
16
|
s.email = %q{}
|
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.homepage = %q{http://blog.evanweaver.com/files/doc/fauna/kirby/}
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
s.rubyforge_project = %q{fauna}
|
23
|
-
s.rubygems_version = %q{0.
|
23
|
+
s.rubygems_version = %q{1.0.1}
|
24
24
|
s.summary = %q{A super-clean IRC bot with sandboxed Ruby evaluation, svn watching, and link-logging to del.icio.us.}
|
25
25
|
|
26
26
|
s.add_dependency(%q<hpricot>, [">= 0"])
|
data/lib/kirby.rb
CHANGED
@@ -2,60 +2,94 @@
|
|
2
2
|
|
3
3
|
=begin rdoc
|
4
4
|
In-channel commands:
|
5
|
-
<tt>>>
|
6
|
-
<tt>reset_irb</tt>::
|
7
|
-
<tt>add_svn [repository_url]</tt>::
|
5
|
+
<tt>>> CODE</tt>:: evaluate code in IRB.
|
6
|
+
<tt>reset_irb</tt>:: get a clean IRB session.
|
7
|
+
<tt>add_svn [repository_url]</tt>:: watch an SVN repository.
|
8
|
+
<tt>add_atom [atom_feed_url]</tt>:: watch an atom feed, such as a Git repository
|
9
|
+
|
10
|
+
To remove a repository, manually kill the bot and delete the line from <tt>nick.svns</tt> or <tt>nick.atoms</tt> in the bot's working directory. Then restart the bot.
|
8
11
|
=end
|
9
12
|
|
10
13
|
class Kirby
|
11
|
-
include Singleton
|
12
14
|
|
13
|
-
|
14
|
-
STORE = PATH + '/kirby.repositories'
|
15
|
-
ATOM = PATH + '/kirby.atoms'
|
16
|
-
PIDFILE = PATH + '/kirby.pid'
|
15
|
+
attr_reader :config
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
# Make a new Kirby. Will not connect to the server until you call connect().
|
18
|
+
def initialize(opts = {})
|
19
|
+
|
20
|
+
# Defaults
|
21
|
+
path = File.expand_path(".").to_s
|
22
|
+
nick = opts[:nick] || config[:nick] || "kirby-dev"
|
23
|
+
|
24
|
+
@config ||= {
|
25
|
+
:svns => "#{path}/#{nick}.svns",
|
26
|
+
:atoms => "#{path}/#{nick}.atoms",
|
27
|
+
:pidfile => "#{path}/#{nick}.pid",
|
28
|
+
:nick => nick,
|
29
|
+
:channel => 'kirby-dev',
|
30
|
+
:server => "irc.freenode.org",
|
31
|
+
:delicious_user => nil,
|
32
|
+
:delicious_pass => nil,
|
33
|
+
:silent => false,
|
34
|
+
:log => false,
|
35
|
+
:logfile => "#{path}/#{nick}.log",
|
36
|
+
:time_format => '%Y/%m/%d %H:%M:%S',
|
37
|
+
:debug => false
|
38
|
+
}
|
39
|
+
|
40
|
+
# Nicely merge current options
|
41
|
+
opts.each do |key, value|
|
42
|
+
config[key] = value if value
|
43
|
+
end
|
44
|
+
end
|
23
45
|
|
24
46
|
# Connect and reconnect to the server
|
25
|
-
def restart
|
26
|
-
|
27
|
-
|
47
|
+
def restart
|
48
|
+
log "Restarting"
|
49
|
+
puts config.inspect if config[:debug]
|
50
|
+
|
51
|
+
@svns = (YAML.load_file config[:svns] rescue {})
|
52
|
+
@atoms = (YAML.load_file config[:atoms] rescue {})
|
53
|
+
|
28
54
|
@socket.close if @socket
|
29
55
|
connect
|
30
56
|
listen
|
31
57
|
end
|
32
58
|
|
33
59
|
# Connect to the IRC server.
|
34
|
-
def connect
|
35
|
-
|
36
|
-
|
37
|
-
write "
|
38
|
-
write "
|
60
|
+
def connect
|
61
|
+
log "Connecting"
|
62
|
+
@socket = TCPSocket.new(config[:server], 6667)
|
63
|
+
write "USER #{config[:nick]} #{config[:nick]} #{config[:nick]} :#{config[:nick]}"
|
64
|
+
write "NICK #{config[:nick]}"
|
65
|
+
write "JOIN ##{config[:channel]}"
|
39
66
|
end
|
40
67
|
|
41
68
|
# The event loop. Waits for socket traffic, and then responds to it. The server sends <tt>PING</tt> every 3 minutes, which means we don't need a separate thread to check for svn updates. All we do is wake on ping (or channel talking).
|
42
69
|
def listen
|
43
70
|
@socket.each do |line|
|
44
|
-
|
45
|
-
poll
|
71
|
+
puts "GOT: #{line.inspect}" if config[:debug]
|
72
|
+
poll if !config[:silent]
|
46
73
|
case line.strip
|
47
|
-
when /^PING/
|
48
|
-
|
49
|
-
|
50
|
-
|
74
|
+
when /^PING/
|
75
|
+
write line.sub("PING", "PONG")[0..-3]
|
76
|
+
when /^ERROR/, /KICK ##{config[:channel]} #{config[:nick]} /
|
77
|
+
restart unless line =~ /PRIVMSG/
|
78
|
+
when /:(.+?)!.* PRIVMSG ##{config[:channel]} \:\001ACTION (.+)\001/
|
79
|
+
log "* #{$1} #{$2}"
|
80
|
+
when /:(.+?)!.* PRIVMSG ##{config[:channel]} \:(.+)/
|
81
|
+
nick, msg = $1, $2
|
82
|
+
log "<#{nick}> #{msg}"
|
83
|
+
if !config[:silent]
|
51
84
|
case msg
|
52
85
|
when /^>>\s*(.+)/ then try $1.chop
|
53
|
-
when /^#{
|
86
|
+
when /^#{config[:nick]}:/
|
87
|
+
["Usage:", " '>> CODE': evaluate code in IRB", " 'reset_irb': get a clean IRB session", " 'add_svn [repository_url]': watch an SVN repository", " 'add_atom [atom_feed_url]': watch an atom feed, such as a Git repository"].each {|s| say s}
|
54
88
|
when /^reset_irb/ then reset_irb
|
55
|
-
when /^add_svn (.+?)(\s|\r|\n|$)/ then
|
56
|
-
when /^add_atom (.+?)(\s|\r|\n|$)/ then
|
57
|
-
|
58
|
-
|
89
|
+
when /^add_svn (.+?)(\s|\r|\n|$)/ then @svns[$1] = 0 and say @svns.inspect
|
90
|
+
when /^add_atom (.+?)(\s|\r|\n|$)/ then @atoms[$1] = '' and say @atoms.inspect
|
91
|
+
when /(http:\/\/.*?)(\s|\r|\n|$)/ then post($1) if config[:delicious_pass]
|
92
|
+
end
|
59
93
|
end
|
60
94
|
end
|
61
95
|
end
|
@@ -65,60 +99,78 @@ class Kirby
|
|
65
99
|
def write s
|
66
100
|
raise RuntimeError, "No socket" unless @socket
|
67
101
|
@socket.puts s += "\r\n"
|
68
|
-
|
102
|
+
puts "WROTE: #{s.inspect}" if config[:debug]
|
103
|
+
end
|
104
|
+
|
105
|
+
# Write a string to the log, if the logfile is open.
|
106
|
+
def log s
|
107
|
+
# Open log, if necessary
|
108
|
+
if config[:log]
|
109
|
+
puts "LOG: #{s}" if config[:debug]
|
110
|
+
File.open(config[:logfile], 'a') do |f|
|
111
|
+
f.puts "#{Time.now.strftime(config[:time_format])} #{s}"
|
112
|
+
end
|
113
|
+
end
|
69
114
|
end
|
70
115
|
|
71
116
|
# Eval a piece of code in the <tt>irb</tt> environment.
|
72
117
|
def try s
|
73
|
-
reset_irb unless
|
118
|
+
reset_irb unless @session
|
74
119
|
try_eval(s).select{|e| e !~ /^\s+from .+\:\d+(\:|$)/}.each {|e| say e} rescue say "session error"
|
75
120
|
end
|
76
121
|
|
77
122
|
# Say something in the channel.
|
78
123
|
def say s
|
79
|
-
write "PRIVMSG
|
124
|
+
write "PRIVMSG ##{config[:channel]} :#{s[0..450]}"
|
125
|
+
log "<#{config[:nick]}> #{s}"
|
80
126
|
sleep 1
|
81
127
|
end
|
82
128
|
|
83
129
|
# Get a new <tt>irb</tt> session.
|
84
130
|
def reset_irb
|
85
131
|
say "Began new irb session"
|
86
|
-
|
132
|
+
@session = try_eval("!INIT!IRB!")
|
87
133
|
end
|
88
134
|
|
89
135
|
# Inner loop of the try method.
|
90
136
|
def try_eval s
|
91
137
|
reset_irb and return [] if s.strip == "exit"
|
92
138
|
result = open("http://tryruby.hobix.com/irb?cmd=#{CGI.escape(s)}",
|
93
|
-
{'Cookie' => "_session_id=#{
|
139
|
+
{'Cookie' => "_session_id=#{@session}"}).read
|
94
140
|
result[/^Your session has been closed/] ? (reset_irb and try_eval s) : result.split("\n")
|
95
141
|
end
|
96
142
|
|
97
|
-
# Look for
|
143
|
+
# Look for SVN changes. Note that Rubyforge polls much better if you use the http:// protocol instead of the svn:// protocol for your repository.
|
98
144
|
def poll
|
99
|
-
return unless (Time.now - $last_poll >
|
145
|
+
return unless (Time.now - $last_poll > 60 rescue true)
|
100
146
|
$last_poll = Time.now
|
101
|
-
|
147
|
+
@svns.each do |repo, last|
|
148
|
+
puts "POLL: #{repo}" if config[:debug]
|
102
149
|
(Hpricot(`svn log #{repo} -rHEAD:#{last} --limit 10 --xml`)/:logentry).reverse[1..-1].each do |ci|
|
103
|
-
|
104
|
-
|
150
|
+
@svns[repo] = rev = ci.attributes['revision'].to_i
|
151
|
+
project = repo.split(/\.|\//).reject do |path|
|
152
|
+
['trunk', 'rubyforge', 'svn', 'org', 'com', 'net', 'http:', nil].include? path
|
153
|
+
end.last
|
154
|
+
say "Commit #{rev} to #{project || repo} by #{(ci/:author).text}: #{(ci/:msg).text}"
|
105
155
|
end rescue nil
|
106
156
|
end
|
107
|
-
File.open(
|
157
|
+
File.open(config[:svns], 'w') {|f| f.puts YAML.dump(@svns)}
|
108
158
|
|
109
|
-
|
159
|
+
@atoms.each do |feed, last|
|
160
|
+
puts "POLL: #{feed}" if config[:debug]
|
110
161
|
begin
|
111
162
|
e = (Hpricot(open(feed))/:entry).first
|
112
|
-
|
113
|
-
say "#{
|
163
|
+
@atoms[feed] = link = e.at("link")['href']
|
164
|
+
say "Commit #{link} by #{((e/:author)/:name).text}: #{(e/:title).text}" unless link == last
|
114
165
|
rescue
|
115
166
|
end
|
116
167
|
end
|
117
|
-
File.open(
|
168
|
+
File.open(config[:atoms], 'w') {|f| f.puts YAML.dump(@atoms)}
|
118
169
|
end
|
119
170
|
|
120
171
|
# Post a url to the del.icio.us account.
|
121
172
|
def post url
|
173
|
+
puts "POST: #{url}" if config[:debug]
|
122
174
|
query = {:url => url,
|
123
175
|
:description => (((Hpricot(open(url))/:title).first.innerHTML or url) rescue url),
|
124
176
|
:tags => (Hpricot(open("http://del.icio.us/url/check?url=#{CGI.escape(url)}"))/'.alphacloud'/:a).map{|s| s.innerHTML}.join(" "),
|
@@ -126,11 +178,12 @@ class Kirby
|
|
126
178
|
begin
|
127
179
|
http = Net::HTTP.new('api.del.icio.us', 443)
|
128
180
|
http.use_ssl = true
|
129
|
-
http.start do |http|
|
181
|
+
response = http.start do |http|
|
130
182
|
req = Net::HTTP::Get.new('/v1/posts/add?' + query.map{|k,v| "#{k}=#{CGI.escape(v)}"}.join('&'))
|
131
|
-
req.basic_auth
|
183
|
+
req.basic_auth config[:delicious_user], config[:delicious_pass]
|
132
184
|
http.request(req)
|
133
185
|
end.body
|
186
|
+
puts "POST: #{response.inspect}" if config[:debug]
|
134
187
|
end
|
135
188
|
end
|
136
189
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kirby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "4.
|
4
|
+
version: "4.2"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ""
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
yZ0=
|
31
31
|
-----END CERTIFICATE-----
|
32
32
|
|
33
|
-
date: 2007-12-
|
33
|
+
date: 2007-12-31 00:00:00 -05:00
|
34
34
|
default_executable:
|
35
35
|
dependencies:
|
36
36
|
- !ruby/object:Gem::Dependency
|
@@ -88,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
88
|
requirements: []
|
89
89
|
|
90
90
|
rubyforge_project: fauna
|
91
|
-
rubygems_version: 0.
|
91
|
+
rubygems_version: 1.0.1
|
92
92
|
signing_key:
|
93
93
|
specification_version: 2
|
94
94
|
summary: A super-clean IRC bot with sandboxed Ruby evaluation, svn watching, and link-logging to del.icio.us.
|
metadata.gz.sig
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
|
2
|
-
�
|
1
|
+
R�{p^"���p�$|Rx�(���J��%Ȗ���ia�
|
2
|
+
�
|