nadoka 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/ChangeLog.old +1553 -0
- data/Gemfile +4 -0
- data/README.org +31 -0
- data/Rakefile +1 -0
- data/bin/nadoka +13 -0
- data/lib/rss_check.rb +206 -0
- data/lib/tagparts.rb +206 -0
- data/nadoka.gemspec +29 -0
- data/nadoka.rb +123 -0
- data/nadokarc +267 -0
- data/ndk/bot.rb +241 -0
- data/ndk/client.rb +288 -0
- data/ndk/config.rb +571 -0
- data/ndk/error.rb +61 -0
- data/ndk/logger.rb +311 -0
- data/ndk/server.rb +784 -0
- data/ndk/server_state.rb +324 -0
- data/ndk/version.rb +44 -0
- data/plugins/autoawaybot.nb +66 -0
- data/plugins/autodumpbot.nb +227 -0
- data/plugins/autoop.nb +56 -0
- data/plugins/backlogbot.nb +88 -0
- data/plugins/checkbot.nb +64 -0
- data/plugins/cronbot.nb +20 -0
- data/plugins/dictbot.nb +53 -0
- data/plugins/drbcl.rb +39 -0
- data/plugins/drbot.nb +93 -0
- data/plugins/evalbot.nb +49 -0
- data/plugins/gonzuibot.nb +41 -0
- data/plugins/googlebot.nb +345 -0
- data/plugins/identifynickserv.nb +43 -0
- data/plugins/mailcheckbot.nb +0 -0
- data/plugins/marldiabot.nb +99 -0
- data/plugins/messagebot.nb +96 -0
- data/plugins/modemanager.nb +150 -0
- data/plugins/opensearchbot.nb +156 -0
- data/plugins/opshop.nb +23 -0
- data/plugins/pastebot.nb +46 -0
- data/plugins/roulettebot.nb +33 -0
- data/plugins/rss_checkbot.nb +121 -0
- data/plugins/samplebot.nb +24 -0
- data/plugins/sendpingbot.nb +17 -0
- data/plugins/shellbot.nb +59 -0
- data/plugins/sixamobot.nb +77 -0
- data/plugins/tenkibot.nb +111 -0
- data/plugins/timestampbot.nb +62 -0
- data/plugins/titlebot.nb +226 -0
- data/plugins/translatebot.nb +301 -0
- data/plugins/twitterbot.nb +138 -0
- data/plugins/weba.nb +209 -0
- data/plugins/xibot.nb +113 -0
- data/rice/irc.rb +780 -0
- metadata +102 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
# -*-ruby-*-
|
2
|
+
# nadoka-twit
|
3
|
+
#
|
4
|
+
# = Usage
|
5
|
+
#
|
6
|
+
# == Get consumer key
|
7
|
+
#
|
8
|
+
# 1. access https://twitter.com/apps/new
|
9
|
+
# 2. register it
|
10
|
+
# 3. memo 'Consumer key' and 'Consumer secret'
|
11
|
+
#
|
12
|
+
# == Get access token
|
13
|
+
#
|
14
|
+
# 1. run this script with consumer key and consumer secret like:
|
15
|
+
# ruby twitterbot.nb <consumer_key> <consumer_secret>
|
16
|
+
# 2. memo access_token and access_token_secret
|
17
|
+
#
|
18
|
+
# == Setting nadokarc
|
19
|
+
#
|
20
|
+
# 1. set :consumer_key, :consumer_secret, :access_token,
|
21
|
+
# and :acccess_token_secret
|
22
|
+
#
|
23
|
+
# = Configuration
|
24
|
+
#
|
25
|
+
# == :ch
|
26
|
+
#
|
27
|
+
# target channel
|
28
|
+
#
|
29
|
+
# == :pattern
|
30
|
+
#
|
31
|
+
# pattern for messages to send twitter
|
32
|
+
#
|
33
|
+
# == :nkf_encoding
|
34
|
+
#
|
35
|
+
# the encoding of messages
|
36
|
+
#
|
37
|
+
# == :consumer_key, :consumer_secret
|
38
|
+
#
|
39
|
+
# Consumer key and consumer secret
|
40
|
+
#
|
41
|
+
# == :access_token, :acccess_token_secret
|
42
|
+
#
|
43
|
+
# Access token and access token secret
|
44
|
+
#
|
45
|
+
require 'time'
|
46
|
+
require 'rubygems'
|
47
|
+
require 'rubytter'
|
48
|
+
require 'json'
|
49
|
+
|
50
|
+
if __FILE__ == $0
|
51
|
+
key = ARGV.shift
|
52
|
+
secret = ARGV.shift
|
53
|
+
unless key && secret
|
54
|
+
puts "Usage: #$0 <consumer_key> <consumer_secret>"
|
55
|
+
end
|
56
|
+
|
57
|
+
oauth = Rubytter::OAuth.new(key, secret)
|
58
|
+
request_token = oauth.get_request_token
|
59
|
+
system('open', request_token.authorize_url) || puts("Access here: #{request_token.authorize_url}\nand...")
|
60
|
+
|
61
|
+
print "Enter PIN: "
|
62
|
+
pin = gets.strip
|
63
|
+
|
64
|
+
access_token = request_token.get_access_token(
|
65
|
+
:oauth_token => request_token.token,
|
66
|
+
:oauth_verifier => pin
|
67
|
+
)
|
68
|
+
puts ":access_token => '#{access_token.token}',"
|
69
|
+
puts ":access_token_secret => '#{access_token.secret}',"
|
70
|
+
exit
|
71
|
+
end
|
72
|
+
|
73
|
+
class TwitterBot < Nadoka::NDK_Bot
|
74
|
+
def bot_initialize
|
75
|
+
@ch = @bot_config.fetch(:ch, nil)
|
76
|
+
@pattern = @bot_config.fetch(:pattern, />tw$/)
|
77
|
+
@nkf_encoding = @bot_config.fetch(:nkf_encoding, nil)
|
78
|
+
|
79
|
+
consumer = OAuth::Consumer.new(
|
80
|
+
@bot_config.fetch(:consumer_key, nil),
|
81
|
+
@bot_config.fetch(:consumer_secret, nil),
|
82
|
+
:site => 'https://api.twitter.com')
|
83
|
+
access_token = OAuth::AccessToken.new(consumer,
|
84
|
+
@bot_config.fetch(:access_token, nil),
|
85
|
+
@bot_config.fetch(:access_token_secret, nil))
|
86
|
+
@rt = OAuthRubytter.new(access_token)
|
87
|
+
@current_id = -1
|
88
|
+
end
|
89
|
+
|
90
|
+
def on_timer(t)
|
91
|
+
@rt.friends_timeline.each do |status|
|
92
|
+
id = status.id.to_i
|
93
|
+
next unless @current_id < id
|
94
|
+
@current_id = id
|
95
|
+
time = Time.parse(status.created_at)
|
96
|
+
next if time + 5 * 60 < Time.now
|
97
|
+
text = status.text.tr("\r\n", ' ')
|
98
|
+
text = NKF.nkf('--ic=UTF-8 --oc=' + @nkf_encoding, text) if @nkf_encoding
|
99
|
+
send_notice @ch, "#{time.strftime('%H:%M')} #{status.user.screen_name}: #{text}"
|
100
|
+
end
|
101
|
+
rescue Errno::ETIMEDOUT, Timeout::Error, SocketError
|
102
|
+
rescue Exception => err
|
103
|
+
puts_error_message(err)
|
104
|
+
end
|
105
|
+
|
106
|
+
def on_client_privmsg(client, ch, message)
|
107
|
+
return unless @ch.nil? or @ch.upcase == ch.upcase
|
108
|
+
unless @pattern =~ message
|
109
|
+
slog 'pattern unmatch, ignored'
|
110
|
+
return
|
111
|
+
end
|
112
|
+
text = message.sub(@pattern, '')
|
113
|
+
text = NKF.nkf('--oc=UTF-8 --ic=' + @nkf_encoding, text) if @nkf_encoding
|
114
|
+
slog((@rt.update(text) ? 'sent to twitter: ' : 'twitter send faild: ') + message)
|
115
|
+
rescue Exception => err
|
116
|
+
puts_error_message(err)
|
117
|
+
end
|
118
|
+
|
119
|
+
def slog(msg, nostamp = false)
|
120
|
+
current_method = caller.first[/:in \`(.*?)\'/, 1].to_s
|
121
|
+
msg.each do |line|
|
122
|
+
@logger.slog "#{self.class.to_s}##{current_method} #{line}", nostamp
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
def puts_error_message(err)
|
128
|
+
if err.is_a?(Rubytter::APIError)
|
129
|
+
@logger.slog "%s: %s (%s) %s" % [err.backtrace[0], err.message, err.class, err.response]
|
130
|
+
elsif err.is_a?(JSON::ParserError)
|
131
|
+
sleep 180
|
132
|
+
return
|
133
|
+
else
|
134
|
+
@logger.slog "%s: %s (%s)" % [err.backtrace[0], err.message, err.class]
|
135
|
+
end
|
136
|
+
@logger.slog err.backtrace.select{|l|/\A\/home/=~l}
|
137
|
+
end
|
138
|
+
end
|
data/plugins/weba.nb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
# -*-ruby-*-
|
2
|
+
#
|
3
|
+
# Copyright (c) 2004-2005 SASADA Koichi <ko1 at atdot.net>
|
4
|
+
#
|
5
|
+
# This program is free software with ABSOLUTELY NO WARRANTY.
|
6
|
+
# You can re-distribute and/or modify this program under
|
7
|
+
# the same terms of the Ruby's license.
|
8
|
+
#
|
9
|
+
#
|
10
|
+
# $Id$
|
11
|
+
#
|
12
|
+
|
13
|
+
=begin
|
14
|
+
|
15
|
+
== Abstract
|
16
|
+
|
17
|
+
WebA: Web Accessor
|
18
|
+
http interface for irc
|
19
|
+
|
20
|
+
You can access IRC via ((<http://host:port/weba>))
|
21
|
+
(by default).
|
22
|
+
|
23
|
+
|
24
|
+
== Configuration
|
25
|
+
|
26
|
+
BotConfig = [
|
27
|
+
{
|
28
|
+
:name => :WebA,
|
29
|
+
:passwd => 'WebAPassWord', # if passwd is specified, use Basic Access Authentication with id
|
30
|
+
:id => 'weba',
|
31
|
+
:port => 12345,
|
32
|
+
:entry => 'weba',
|
33
|
+
# :message_format => ... (see source)
|
34
|
+
}
|
35
|
+
]
|
36
|
+
|
37
|
+
=end
|
38
|
+
|
39
|
+
require 'webrick'
|
40
|
+
require 'lib/tagparts'
|
41
|
+
|
42
|
+
class WebA < Nadoka::NDK_Bot
|
43
|
+
class WebAlet < WEBrick::HTTPServlet::AbstractServlet
|
44
|
+
def initialize server, bot, authorizer
|
45
|
+
super
|
46
|
+
@bot = bot
|
47
|
+
@auth = authorizer
|
48
|
+
end
|
49
|
+
|
50
|
+
def do_GET req, res
|
51
|
+
@auth.authenticate(req, res) if @auth
|
52
|
+
begin
|
53
|
+
res.body = @bot.htmlpage(req.query).to_s
|
54
|
+
res['content-type'] = 'text/html; charset=Shift_JIS'
|
55
|
+
rescue WebARedirect => e
|
56
|
+
res.set_redirect(WEBrick::HTTPStatus::Found, "#{req.path}?ch=#{URI.encode(e.ch.tosjis)}")
|
57
|
+
res.body = 'moved'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class WebARedirect < Exception
|
63
|
+
attr_reader :ch
|
64
|
+
def initialize ch
|
65
|
+
@ch = ch
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
include HTMLParts
|
70
|
+
|
71
|
+
def htmlpage query
|
72
|
+
rch = (query['ch'] || '').tojis
|
73
|
+
ch = ccn(rch)
|
74
|
+
ch = !ch.empty? && (@state.channels.include?(ch) || ch == 'all') && ch
|
75
|
+
|
76
|
+
ttl = ch ? " - #{rch.tosjis}" : ''
|
77
|
+
|
78
|
+
if ch && (msg = query['message']) && !msg.empty?
|
79
|
+
msg = msg.tojis + ' (from WebA)'
|
80
|
+
send_privmsg(ch, msg)
|
81
|
+
raise WebARedirect.new(ch)
|
82
|
+
end
|
83
|
+
|
84
|
+
_html(
|
85
|
+
_head(_title("WebA: IRC Web Accessor#{ttl}")),
|
86
|
+
_body(
|
87
|
+
_h1("WebA#{ttl}"),
|
88
|
+
_p(
|
89
|
+
_a({:href => "./#{@entry}?ch="+URI.encode((rch || '').tosjis)}, 'reload'),
|
90
|
+
_a({:href => "./#{@entry}"}, 'top')
|
91
|
+
),
|
92
|
+
|
93
|
+
case ch
|
94
|
+
when 'command'
|
95
|
+
command_ch
|
96
|
+
else
|
97
|
+
view_ch(rch, ch)
|
98
|
+
select_ch(rch, ch)
|
99
|
+
end
|
100
|
+
))
|
101
|
+
end
|
102
|
+
|
103
|
+
def select_ch rch, ch
|
104
|
+
_p({:class => 'channel-list'},
|
105
|
+
(@state.channel_raw_names.sort + ['all']).map{|e|
|
106
|
+
e = e.tosjis
|
107
|
+
[_a({:href => "./#{@entry}?ch="+ URI.encode(e)}, e), ' ']
|
108
|
+
}
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
def view_ch rch, ch
|
113
|
+
return unless ch
|
114
|
+
|
115
|
+
chs = ch.tosjis
|
116
|
+
|
117
|
+
if ch == 'all'
|
118
|
+
msgs = []
|
119
|
+
@stores.pools.each{|_, store|
|
120
|
+
msgs.concat store.pool
|
121
|
+
}
|
122
|
+
msgs = msgs.sort_by{|msg| msg[:time]}
|
123
|
+
else
|
124
|
+
msgs = (@stores.pools[ch] && @stores.pools[ch].pool) || []
|
125
|
+
end
|
126
|
+
|
127
|
+
_div({:class => 'irc-accessor'},
|
128
|
+
if(ch != 'all')
|
129
|
+
_form({:method => 'get', :action => "./#{@entry}", :class => 'sayform'},
|
130
|
+
"msg: ",
|
131
|
+
_input({:type => 'text', :name => 'message', :class => 'msgbox'}),
|
132
|
+
_input({:type => 'submit', :name => 'say', :value => 'say'}),
|
133
|
+
_input({:type => 'hidden', :name => 'ch', :value => ch})
|
134
|
+
)
|
135
|
+
end,
|
136
|
+
_h2("channel #{ch.tosjis}"),
|
137
|
+
_div({:class => 'messages'},
|
138
|
+
msgs.map{|m|
|
139
|
+
if ch == 'all' && m[:ch]
|
140
|
+
chn = _a({:href => "./#{@entry}?ch=#{URI.encode(m[:ch])}"}, m[:ch].tosjis)
|
141
|
+
msg = @config.log_format_message(@all_message_format, m)
|
142
|
+
elsif m[:type] == 'PRIVMSG'
|
143
|
+
chn = _a({:href => "./#{@entry}?user=#{URI.encode(m[:user])}"}, "<#{m[:user].tosjis}>")
|
144
|
+
msg = @config.log_format_message(@message_format, m)
|
145
|
+
else
|
146
|
+
msg = @config.log_format_message(@message_format, m)
|
147
|
+
chn = ''
|
148
|
+
end
|
149
|
+
|
150
|
+
_div({:class=>'msg'},
|
151
|
+
"#{m[:time].strftime('%H:%M')} ", chn, "#{msg}".tosjis)
|
152
|
+
}.reverse
|
153
|
+
)
|
154
|
+
)
|
155
|
+
end
|
156
|
+
|
157
|
+
def bot_initialize
|
158
|
+
@stores = @logger.message_stores
|
159
|
+
@server = WEBrick::HTTPServer.new({
|
160
|
+
:Port => @bot_config.fetch(:port, 12345),
|
161
|
+
})
|
162
|
+
@entry = @bot_config.fetch(:entry, 'weba')
|
163
|
+
|
164
|
+
auth = nil
|
165
|
+
if passwd = @bot_config.fetch(:passwd, 'WebAPassWord')
|
166
|
+
id = @bot_config.fetch(:id, 'weba')
|
167
|
+
|
168
|
+
userdb = Hash.new
|
169
|
+
userdb.extend(WEBrick::HTTPAuth::UserDB)
|
170
|
+
userdb.auth_type = WEBrick::HTTPAuth::BasicAuth
|
171
|
+
userdb.set_passwd("WebA Authentication", id, passwd)
|
172
|
+
|
173
|
+
auth = WEBrick::HTTPAuth::BasicAuth.new({
|
174
|
+
:Realm => "WebA Authentication",
|
175
|
+
:UserDB => userdb,
|
176
|
+
:Algorithm => 'MD5-sess',
|
177
|
+
:Qop => [ 'auth' ],
|
178
|
+
:UseOpaque => true,
|
179
|
+
:NonceExpirePeriod => 60,
|
180
|
+
:NonceExpireDelta => 5,
|
181
|
+
})
|
182
|
+
end
|
183
|
+
|
184
|
+
@server.mount("/#{@entry}", WebAlet, self, auth)
|
185
|
+
|
186
|
+
@server_thread = Thread.new{
|
187
|
+
begin
|
188
|
+
@server.start
|
189
|
+
rescue Exception => e
|
190
|
+
@manager.ndk_error e
|
191
|
+
end
|
192
|
+
}
|
193
|
+
@message_format = @config.default_log[:message_format].merge(
|
194
|
+
@bot_config.fetch(:message_format, {
|
195
|
+
'PRIVMSG' => '{msg}',
|
196
|
+
'NOTICE' => '{{user}} {msg}',
|
197
|
+
}))
|
198
|
+
|
199
|
+
@all_message_format = @config.default_log[:message_format].merge(
|
200
|
+
@bot_config.fetch(:all_message_format, {}))
|
201
|
+
end
|
202
|
+
|
203
|
+
def bot_destruct
|
204
|
+
@server_thread.kill
|
205
|
+
@server.shutdown
|
206
|
+
sleep 1
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
data/plugins/xibot.nb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
#
|
2
|
+
# Xi Bot
|
3
|
+
#
|
4
|
+
# No rights reserved.
|
5
|
+
#
|
6
|
+
# Synopsis:
|
7
|
+
# xi> 2d10 (two dice of ten)
|
8
|
+
# [2d10] 13 = 7 + 6
|
9
|
+
# xi> 5d
|
10
|
+
# [5d6] 14 = 3 + 1 + 3 + 1 + 6
|
11
|
+
# xi>100
|
12
|
+
# [1d100] 26
|
13
|
+
#
|
14
|
+
|
15
|
+
class XiBot < Nadoka::NDK_Bot
|
16
|
+
def bot_initialize
|
17
|
+
@available_channel = @bot_config[:ch] || /.*/
|
18
|
+
end
|
19
|
+
|
20
|
+
def dice(count=1, max=6)
|
21
|
+
count.times{ count += rand(max) }
|
22
|
+
count
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_privmsg prefix, ch, msg
|
26
|
+
return unless @available_channel === ch
|
27
|
+
return unless /\Axi\s*>\s*/ =~ msg
|
28
|
+
case $~.post_match.downcase
|
29
|
+
when /character/
|
30
|
+
%w/STR DEX CON INT WIS CHA/.each do |name|
|
31
|
+
values = (1..3).map{|i|rand(6)+1}
|
32
|
+
sum = values.inject(0){|s, i|s += i}
|
33
|
+
send_notice(ch, '%s: %2d = %s' % [name, sum, values.join(' + ')])
|
34
|
+
end
|
35
|
+
when /char/
|
36
|
+
values = %w/STR DEX CON INT WIS CHA/.map do |name|
|
37
|
+
'%s: %2d' % [name, (1..4).map{|i|rand(6)+1}.sort.last(3).inject(0){|s, i|s += i}]
|
38
|
+
end
|
39
|
+
send_notice(ch, "#{prefix.nick}: #{values.join(', ')}")
|
40
|
+
when /san/
|
41
|
+
int = dice(2, 6) + 6
|
42
|
+
pow = dice(3, 6)
|
43
|
+
san0 = pow * 5
|
44
|
+
current = san0
|
45
|
+
result = 'int:%d pow:%d san0:%d' % [int, pow, san0]
|
46
|
+
|
47
|
+
case rand(10)
|
48
|
+
when 9
|
49
|
+
result <<= ' you saw Great Cthulhu.'
|
50
|
+
losts = [dice(1, 10), dice(1, 100)]
|
51
|
+
when 7, 8
|
52
|
+
result <<= ' you saw a living dead.'
|
53
|
+
losts = [1, dice(1, 10)]
|
54
|
+
when 4, 5, 6
|
55
|
+
result <<= ' you saw a Dimension-Shambler.'
|
56
|
+
losts = [0, dice(1, 10)]
|
57
|
+
when 2, 3, 4
|
58
|
+
result <<= ' you woke up in the grave.'
|
59
|
+
losts = [0, dice(1, 6)]
|
60
|
+
else
|
61
|
+
result <<= ' you find a dead body.'
|
62
|
+
losts = [0, dice(1, 3)]
|
63
|
+
end
|
64
|
+
|
65
|
+
check = dice(1, 100)
|
66
|
+
result << " check:#{check}"
|
67
|
+
lost = losts[check > current ? 1 : 0]
|
68
|
+
|
69
|
+
insane = false
|
70
|
+
if lost > 0
|
71
|
+
result << " you lost #{lost} SAN point."
|
72
|
+
if lost >= current
|
73
|
+
# eternal insanity
|
74
|
+
result << ' you went mad. (eternal)'
|
75
|
+
insane = true
|
76
|
+
elsif lost * 5 > current
|
77
|
+
# indefinite insanity
|
78
|
+
r = %w/緊張症・痴呆症 記憶喪失 サンチョ・パンザ症、ドンキホーテ症 偏執症
|
79
|
+
恐怖症、フェティッシュ 強迫観念、中毒、けいれん発作 誇大妄想 精神分裂症
|
80
|
+
犯罪性精神異常 多重人格/[rand(10)]
|
81
|
+
result << ' you went mad. (indefinite %s)' % NKF.nkf('-jW', r)
|
82
|
+
insane = true
|
83
|
+
elsif lost >= 5
|
84
|
+
idearoll = dice(1, 100)
|
85
|
+
result << " idearoll:#{idearoll}"
|
86
|
+
if idearoll <= int * 5
|
87
|
+
# temporary insanity
|
88
|
+
result << ' you went mad. (temporary)'
|
89
|
+
insane = true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
result << ' you kept sanity.' unless insane
|
94
|
+
#message = '%s: current: %d check: %d result: %s' % [prefix.nick, current, check, result]
|
95
|
+
message = "#{prefix.nick}: #{result}"
|
96
|
+
send_notice(ch, message)
|
97
|
+
when /(?:(\d+)d)?(\d+)?(?:\*([1-9]))?/
|
98
|
+
count = $1.to_i
|
99
|
+
count = 1 unless (1..100).include? count
|
100
|
+
max = $2.to_i
|
101
|
+
max = 6 unless (1..1_000_000_000).include? max
|
102
|
+
($3 ? $3.to_i : 1).times do
|
103
|
+
values = (1..count).map{|i|rand(max)+1}
|
104
|
+
sum = values.inject(0){|s, i|s += i}
|
105
|
+
if count == 1
|
106
|
+
send_notice(ch, '%s: [%dd%d] %d' % [prefix.nick,count, max, sum])
|
107
|
+
else
|
108
|
+
send_notice(ch, '%s: [%dd%d] %d = %s' % [prefix.nick,count, max, sum, values.join(' + ')])
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|