nadoka 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +5 -0
  2. data/ChangeLog.old +1553 -0
  3. data/Gemfile +4 -0
  4. data/README.org +31 -0
  5. data/Rakefile +1 -0
  6. data/bin/nadoka +13 -0
  7. data/lib/rss_check.rb +206 -0
  8. data/lib/tagparts.rb +206 -0
  9. data/nadoka.gemspec +29 -0
  10. data/nadoka.rb +123 -0
  11. data/nadokarc +267 -0
  12. data/ndk/bot.rb +241 -0
  13. data/ndk/client.rb +288 -0
  14. data/ndk/config.rb +571 -0
  15. data/ndk/error.rb +61 -0
  16. data/ndk/logger.rb +311 -0
  17. data/ndk/server.rb +784 -0
  18. data/ndk/server_state.rb +324 -0
  19. data/ndk/version.rb +44 -0
  20. data/plugins/autoawaybot.nb +66 -0
  21. data/plugins/autodumpbot.nb +227 -0
  22. data/plugins/autoop.nb +56 -0
  23. data/plugins/backlogbot.nb +88 -0
  24. data/plugins/checkbot.nb +64 -0
  25. data/plugins/cronbot.nb +20 -0
  26. data/plugins/dictbot.nb +53 -0
  27. data/plugins/drbcl.rb +39 -0
  28. data/plugins/drbot.nb +93 -0
  29. data/plugins/evalbot.nb +49 -0
  30. data/plugins/gonzuibot.nb +41 -0
  31. data/plugins/googlebot.nb +345 -0
  32. data/plugins/identifynickserv.nb +43 -0
  33. data/plugins/mailcheckbot.nb +0 -0
  34. data/plugins/marldiabot.nb +99 -0
  35. data/plugins/messagebot.nb +96 -0
  36. data/plugins/modemanager.nb +150 -0
  37. data/plugins/opensearchbot.nb +156 -0
  38. data/plugins/opshop.nb +23 -0
  39. data/plugins/pastebot.nb +46 -0
  40. data/plugins/roulettebot.nb +33 -0
  41. data/plugins/rss_checkbot.nb +121 -0
  42. data/plugins/samplebot.nb +24 -0
  43. data/plugins/sendpingbot.nb +17 -0
  44. data/plugins/shellbot.nb +59 -0
  45. data/plugins/sixamobot.nb +77 -0
  46. data/plugins/tenkibot.nb +111 -0
  47. data/plugins/timestampbot.nb +62 -0
  48. data/plugins/titlebot.nb +226 -0
  49. data/plugins/translatebot.nb +301 -0
  50. data/plugins/twitterbot.nb +138 -0
  51. data/plugins/weba.nb +209 -0
  52. data/plugins/xibot.nb +113 -0
  53. data/rice/irc.rb +780 -0
  54. 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
@@ -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
+
@@ -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