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,288 @@
1
+ #
2
+ # Copyright (c) 2004-2005 SASADA Koichi <ko1 at atdot.net>
3
+ #
4
+ # This program is free software with ABSOLUTELY NO WARRANTY.
5
+ # You can re-distribute and/or modify this program under
6
+ # the same terms of the Ruby's license.
7
+ #
8
+ #
9
+ # $Id$
10
+ # Create : K.S. 04/04/17 16:50:10
11
+ #
12
+
13
+ require 'thread'
14
+
15
+ module Nadoka
16
+ class NDK_Client
17
+ def initialize config, sock, manager
18
+ @sock = sock
19
+
20
+ @config = config
21
+ @logger = config.logger
22
+ @manager= manager
23
+ @state = manager.state
24
+
25
+ @queue = Queue.new
26
+ @remote_host = @sock.peeraddr[2]
27
+ @thread = Thread.current
28
+ @connected = false
29
+
30
+ # client information
31
+ @realname = nil
32
+ @hostname = nil
33
+ end
34
+
35
+ attr_writer :logger
36
+ attr_reader :remote_host
37
+
38
+ def start
39
+ send_thread = Thread.new{
40
+ begin
41
+ while q = @queue.pop
42
+ begin
43
+ send_to_client q
44
+ end
45
+ end
46
+ rescue Exception => e
47
+ @manager.ndk_error e
48
+ end
49
+ }
50
+ begin
51
+ if login
52
+ @connected = true
53
+ begin
54
+ @manager.invoke_event :leave_away, @manager.client_count
55
+ @manager.invoke_event :invoke_bot, :client_login, @manager.client_count, self
56
+ while msg = recv_from_client
57
+ send_from_client msg, self
58
+ end
59
+ rescue NDK_QuitClient
60
+ # finish
61
+ ensure
62
+ @manager.invoke_event :invoke_bot, :client_logout, @manager.client_count, self
63
+ end
64
+ end
65
+ rescue Exception
66
+ @manager.ndk_error $!
67
+ ensure
68
+ @logger.slog "Client #{@realname}@#{@remote_host} disconnected."
69
+ @sock.close
70
+ send_thread.kill if send_thread && send_thread.alive?
71
+ end
72
+ end
73
+
74
+ def kill
75
+ @thread && @thread.kill
76
+ end
77
+
78
+ def recv_from_client
79
+ while !@sock.closed?
80
+ begin
81
+ str = @sock.gets
82
+ if str
83
+ msg = ::RICE::Message::parse str
84
+
85
+ case msg.command
86
+ when 'PING'
87
+ send_msg Cmd.pong(*msg.params[0]), false
88
+ when 'PONG'
89
+ # ignore
90
+ else
91
+ @logger.dlog "[C>] #{str}"
92
+ return msg
93
+ end
94
+ else
95
+ break
96
+ end
97
+ rescue ::RICE::UnknownCommand, ::RICE::InvalidMessage
98
+ @logger.slog "Invalid Message: #{str}"
99
+ rescue Exception => e
100
+ @manager.ndk_error e
101
+ break
102
+ end
103
+ end
104
+ end
105
+
106
+ def push msg
107
+ if @connected
108
+ @queue << msg
109
+ end
110
+ end
111
+ alias << push
112
+
113
+ def login
114
+ pass = nil
115
+ nick = nil
116
+ @username = nil
117
+
118
+ while (nick == nil) || (@username == nil)
119
+ msg = recv_from_client
120
+ return nil if msg == nil
121
+
122
+ case msg.command
123
+ when 'USER'
124
+ @username, @hostname, @servername, @realname = msg.params
125
+ when 'NICK'
126
+ nick = msg.params[0]
127
+ when 'PASS'
128
+ pass = msg.params[0]
129
+ else
130
+ raise "Illegal login sequence: #{msg}"
131
+ end
132
+ end
133
+
134
+ if @config.client_server_pass && (@config.client_server_pass != pass)
135
+ send_reply Rpl.err_passwdmismatch(nick, "Password Incorrect.")
136
+ return false
137
+ end
138
+
139
+ send_reply Rpl.rpl_welcome( nick,
140
+ 'Welcome to the Internet Relay Network '+"#{nick}!#{@username}@#{@remote_host}")
141
+ send_reply Rpl.rpl_yourhost(nick, "Your host is nadoka, running version #{NDK_Version}")
142
+ send_reply Rpl.rpl_created( nick, 'This server was created ' + NDK_Created.asctime)
143
+ send_reply Rpl.rpl_myinfo( nick, "nadoka #{NDK_Version} aoOirw abeiIklmnoOpqrstv")
144
+
145
+ send_motd(nick)
146
+
147
+ send_command Cmd.nick(@state.nick), nick
148
+ nick = @manager.state.nick
149
+
150
+ @manager.state.current_channels.each{|ch, chs|
151
+ send_command Cmd.join(chs.name)
152
+ if chs.topic
153
+ send_reply Rpl.rpl_topic(@state.nick, chs.name, chs.topic)
154
+ else
155
+ send_reply Rpl.rpl_notopic(@state.nick, chs.name, "No topic is set.")
156
+ end
157
+ send_reply Rpl.rpl_namreply( @state.nick, chs.state, chs.name, chs.names.join(' '))
158
+ send_reply Rpl.rpl_endofnames(@state.nick, chs.name, "End of NAMES list.")
159
+ }
160
+
161
+ @logger.slog "Client #{@realname}@#{@remote_host} connected."
162
+ true
163
+ end
164
+
165
+ def send_motd nick
166
+ send_reply Rpl.rpl_motdstart(nick, "- Nadoka Message of the Day - ")
167
+ send_reply Rpl.rpl_motd(nick, "- Enjoy IRC chat with Nadoka chan!")
168
+ send_reply Rpl.rpl_motd(nick, "- ")
169
+ send_reply Rpl.rpl_endofmotd(nick, "End of MOTD command.")
170
+ end
171
+
172
+ # :who!~username@host CMD ..
173
+ def send_command cmd, nick = @manager.state.nick
174
+ msg = add_prefix(cmd, "#{nick}!#{@username}@#{@remote_host}")
175
+ send_msg msg
176
+ end
177
+
178
+ # :serverinfo REPL ...
179
+ def send_reply repl
180
+ msg = add_prefix(repl, @config.nadoka_server_name)
181
+ send_msg msg
182
+ end
183
+
184
+ def send_msg msg, logging=true
185
+ @logger.dlog "[C<] #{msg}" if logging
186
+ unless @sock.closed?
187
+ begin
188
+ @sock.write msg.to_s
189
+ rescue Exception => e
190
+ @manager.ndk_error e
191
+ end
192
+ end
193
+ end
194
+
195
+ def send_to_client msg
196
+ if /^\d+/ =~ msg.command
197
+ send_reply msg
198
+ else
199
+ send_msg msg
200
+ end
201
+ end
202
+
203
+ def add_prefix cmd, prefix = "#{@manager.state.nick}!#{@username}@#{@remote_host}"
204
+ cmd.prefix = prefix
205
+ cmd
206
+ end
207
+
208
+ def add_prefix2 cmd, nick
209
+ cmd.prefix = "#{nick}!#{@username}@#{@remote_host}"
210
+ cmd
211
+ end
212
+
213
+
214
+ ::RICE::Command.regist_command('NADOKA')
215
+
216
+ # client -> server handling
217
+ def send_from_client msg, from
218
+ until @manager.connected
219
+ # ignore
220
+ return
221
+ end
222
+
223
+ case msg.command
224
+ when 'NADOKA'
225
+ nadoka_client_command msg.params[0], msg.params[1..-1]
226
+ return
227
+ when 'QUIT'
228
+ raise NDK_QuitClient
229
+ when 'PRIVMSG', 'NOTICE'
230
+ if /^\/nadoka/ =~ msg.params[1]
231
+ _, cmd, *args = msg.params[1].split(/\s+/)
232
+ nadoka_client_command cmd, args
233
+ return
234
+ end
235
+ if @manager.send_to_bot(:client_privmsg, self, msg.params[0], msg.params[1])
236
+ @manager.send_to_server msg
237
+ @manager.send_to_clients_otherwise msg, from
238
+ end
239
+ else
240
+ @manager.send_to_server msg
241
+ end
242
+ end
243
+
244
+ def client_notice msg
245
+ self << Cmd.notice(@state.nick, msg)
246
+ end
247
+
248
+ def state
249
+ "client from #{@remote_host}(#{@username}, #{@hostname}, #{@servername}, #{@realname})"
250
+ end
251
+
252
+ NdkCommandDescription = {
253
+ #
254
+ 'QUIT' => 'quite nadoka program',
255
+ 'RESTART' => 'restart nadoka program(not relaod *.rb programs)',
256
+ 'RELOAD' => 'reload configurations and bot programs(*.nb)',
257
+ 'RECONNECT' => 'reconnect next server',
258
+ 'STATUS' => 'show nadoka running status',
259
+ }
260
+ def nadoka_client_command cmd, args
261
+ cmd ||= ''
262
+ case cmd.upcase
263
+ when 'QUIT'
264
+ @manager.invoke_event :quit_program
265
+ client_notice 'nadoka will be quit. bye!'
266
+ when 'RESTART'
267
+ @manager.invoke_event :restart_program, self
268
+ client_notice 'nadoka will be restart. see you later.'
269
+ when 'RELOAD'
270
+ @manager.invoke_event :reload_config, self
271
+ when 'RECONNECT'
272
+ @manager.invoke_event :reconnect_to_server, self
273
+ when 'STATUS'
274
+ @manager.ndk_status.each{|msg| client_notice msg}
275
+ when 'HELP'
276
+ self << Cmd.notice(@state.nick, 'available: ' + NdkCommandDescription.keys.join(', '))
277
+ if args[1]
278
+ self << Cmd.notice(@state.nick, "#{args[1]}: #{NdkCommandDescription[args[1].upcase]}")
279
+ end
280
+ else
281
+ if @manager.send_to_bot :nadoka_command, self, cmd, *args
282
+ self << Cmd.notice(@state.nick, 'No such command. Use /NADOKA HELP.')
283
+ end
284
+ end
285
+ end
286
+ end
287
+ end
288
+
@@ -0,0 +1,571 @@
1
+ #
2
+ # Copyright (c) 2004-2005 SASADA Koichi <ko1 at atdot.net>
3
+ #
4
+ # This program is free software with ABSOLUTELY NO WARRANTY.
5
+ # You can re-distribute and/or modify this program under
6
+ # the same terms of the Ruby's license.
7
+ #
8
+ #
9
+ # $Id$
10
+ # Create : K.S. 04/04/17 16:50:33
11
+ #
12
+ #
13
+ # You can check RCFILE with following command:
14
+ #
15
+ # ruby ndk_config.rb [RCFILE]
16
+ #
17
+
18
+ require 'uri'
19
+ require 'socket'
20
+ require 'kconv'
21
+
22
+ require 'ndk/logger'
23
+
24
+ module Nadoka
25
+
26
+ class NDK_ConfigBase
27
+ # system
28
+ # 0: quiet, 1: normal, 2: system, 3: debug
29
+ Loglevel = 2
30
+ Setting_name = nil
31
+
32
+ # client server
33
+ Client_server_port = 6667 # or nil (no listen)
34
+ Client_server_host = nil
35
+ Client_server_pass = 'NadokaPassWord' # or nil
36
+ Client_server_acl = nil
37
+ ACL_Object = nil
38
+
39
+ #
40
+ Server_list = [
41
+ # { :host => '127.0.0.1', :port => 6667, :pass => nil }
42
+ ]
43
+ Servers = []
44
+
45
+ Reconnect_delay = 150
46
+
47
+ Default_channels = []
48
+ Login_channels = []
49
+
50
+ #
51
+ User = ENV['USER'] || ENV['USERNAME'] || 'nadokatest'
52
+ Nick = 'ndkusr'
53
+ Hostname = Socket.gethostname
54
+ Servername = '*'
55
+ Realname = 'nadoka user'
56
+ Mode = nil
57
+
58
+ Away_Message = 'away'
59
+ Away_Nick = nil
60
+
61
+ Quit_Message = "Quit Nadoka #{::Nadoka::NDK_Version} - http://www.atdot.net/nadoka/"
62
+
63
+ #
64
+ Channel_info = {}
65
+ # log
66
+
67
+ Default_log = {
68
+ :file => '${setting_name}-${channel_name}/%y%m%d.log',
69
+ :time_format => '%H:%M:%S',
70
+ :message_format => {
71
+ 'PRIVMSG' => '<{nick}> {msg}',
72
+ 'NOTICE' => '{{nick}} {msg}',
73
+ 'JOIN' => '+ {nick} ({prefix:user}@{prefix:host})',
74
+ 'NICK' => '* {nick} -> {newnick}',
75
+ 'QUIT' => '- {nick} (QUIT: {msg}) ({prefix:user}@{prefix:host})',
76
+ 'PART' => '- {nick} (PART: {msg}) ({prefix:user}@{prefix:host})',
77
+ 'KICK' => '- {nick} kicked by {kicker} ({msg})',
78
+ 'MODE' => '* {nick} changed mode ({msg})',
79
+ 'TOPIC' => '<{ch} TOPIC> {msg} (by {nick})',
80
+ 'SYSTEM' => '[NDK] {orig}',
81
+ 'OTHER' => '{orig}',
82
+ 'SIMPLE' => '{orig}',
83
+ },
84
+ }
85
+
86
+ System_log = {
87
+ :file => '${setting_name}-system.log',
88
+ :time_format => '%y/%m/%d-%H:%M:%S',
89
+ :message_format => {
90
+ 'PRIVMSG' => '{ch} <{nick}> {msg}',
91
+ 'NOTICE' => '{ch} {{nick}} {msg}',
92
+ 'JOIN' => '{ch} + {nick} ({prefix:user}@{prefix:host})',
93
+ 'NICK' => '{ch} * {nick} -> {newnick}',
94
+ 'QUIT' => '{ch} - {nick} (QUIT: {msg}) ({prefix:user}@{prefix:host})',
95
+ 'PART' => '{ch} - {nick} (PART: {msg}) ({prefix:user}@{prefix:host})',
96
+ 'KICK' => '{ch} - {nick} kicked by {kicker} ({msg})',
97
+ 'MODE' => '{ch} * {nick} changed mode ({msg})',
98
+ 'TOPIC' => '{ch} <{ch} TOPIC> {msg} (by {nick})',
99
+ 'SYSTEM' => '[NDK] {orig}',
100
+ 'OTHER' => nil,
101
+ 'SIMPLE' => nil,
102
+ },
103
+ }
104
+
105
+ Debug_log = {
106
+ :io => $stdout,
107
+ :time_format => '%y/%m/%d-%H:%M:%S',
108
+ :message_format => System_log[:message_format],
109
+ }
110
+
111
+ Talk_log = {
112
+ :file => '${setting_name}-talk/%y%m%d.log',
113
+ :time_format => Default_log[:time_format],
114
+ :message_format => {
115
+ 'PRIVMSG' => '[{sender} => {receiver}] {msg}',
116
+ 'NOTICE' => '{{sender} -> {receiver}} {msg}',
117
+ }
118
+ }
119
+
120
+ System_Logwriter = nil
121
+ Debug_Logwriter = nil
122
+ Default_Logwriter = nil
123
+ Talk_Logwriter = nil
124
+
125
+ BackLog_Lines = 20
126
+
127
+ # file name encoding setting
128
+ # 'euc' or 'sjis' or 'jis' or 'utf8'
129
+ FilenameEncoding =
130
+ case RUBY_PLATFORM
131
+ when /mswin/, /cygwin/, /mingw/
132
+ 'sjis'
133
+ else
134
+ if /UTF-?8/i =~ ENV['LANG']
135
+ 'utf8'
136
+ else
137
+ 'euc'
138
+ end
139
+ end
140
+
141
+ # dirs
142
+ Default_Plugins_dir = File.expand_path('../../plugins', __FILE__)
143
+ Plugins_dir = './plugins'
144
+ Log_dir = './log'
145
+
146
+ # bots
147
+ BotConfig = []
148
+
149
+ # filters
150
+ Privmsg_Filter = []
151
+ Notice_Filter = []
152
+ Primitive_Filters = {}
153
+
154
+ # ...
155
+ Privmsg_Filter_light = []
156
+ Nadoka_server_name = 'NadokaProgram'
157
+
158
+ def self.inherited subklass
159
+ ConfigClass << subklass
160
+ end
161
+ end
162
+ ConfigClass = [NDK_ConfigBase]
163
+
164
+ class NDK_Config
165
+ NDK_ConfigBase.constants.each{|e|
166
+ eval %Q{
167
+ def #{e.downcase}
168
+ @config['#{e.downcase}'.intern]
169
+ end
170
+ }
171
+ }
172
+
173
+ def initialize manager, rcfile = nil
174
+ @manager = manager
175
+ @bots = []
176
+ load_config(rcfile || './nadokarc')
177
+ end
178
+ attr_reader :config, :bots, :logger
179
+
180
+ def remove_previous_setting
181
+ # remove setting class
182
+ klass = ConfigClass.shift
183
+ while k = ConfigClass.shift
184
+ Object.module_eval{
185
+ remove_const(k.name)
186
+ }
187
+ end
188
+ ConfigClass.push(klass)
189
+
190
+
191
+ # clear required files
192
+ RequiredFiles.replace []
193
+
194
+ # remove current NadokaBot
195
+ Object.module_eval %q{
196
+ remove_const :NadokaBot
197
+ module NadokaBot
198
+ def self.included mod
199
+ Nadoka::NDK_Config::BotClasses['::' + mod.name.downcase] = mod
200
+ end
201
+ end
202
+ }
203
+
204
+ # clear bot class
205
+ BotClasses.each{|k, v|
206
+ Object.module_eval{
207
+ if /\:\:/ !~ k.to_s && const_defined?(v.name)
208
+ remove_const(v.name)
209
+ end
210
+ }
211
+ }
212
+ BotClasses.clear
213
+
214
+ # destruct bot instances
215
+ @bots.each{|bot|
216
+ bot.bot_destruct
217
+ }
218
+ @bots = []
219
+
220
+ GC.start
221
+ end
222
+
223
+ def load_bots
224
+ # for compatibility
225
+ return load_bots_old if @config[:botconfig].kind_of? Hash
226
+ @bots = @config[:botconfig].map{|bot|
227
+ if bot.kind_of? Hash
228
+ next nil if bot[:disable]
229
+ name = bot[:name]
230
+ cfg = bot
231
+ raise "No bot name specified. Check rcfile." unless name
232
+ else
233
+ name = bot
234
+ cfg = nil
235
+ end
236
+ load_botfile name.to_s.downcase
237
+ make_bot_instance name, cfg
238
+ }.compact
239
+ end
240
+
241
+ # for compatibility
242
+ def load_bots_old
243
+ (@config[:botfiles] + (@config[:defaultbotfiles]||[])).each{|file|
244
+ load_botfile file
245
+ }
246
+
247
+ @config[:botconfig].keys.each{|bk|
248
+ bkn = bk.to_s
249
+ bkni= bkn.intern
250
+
251
+ unless BotClasses.any?{|n, c| n == bkni}
252
+ if @config[:botfiles]
253
+ raise "No such BotClass: #{bkn}"
254
+ else
255
+ load_botfile "#{bkn.downcase}.nb"
256
+ end
257
+ end
258
+ }
259
+
260
+ @bots = BotClasses.map{|bkname, bk|
261
+ if @config[:botconfig].has_key? bkname
262
+ if (cfg = @config[:botconfig][bkname]).kind_of? Array
263
+ cfg.map{|c|
264
+ make_bot_instance bk, c
265
+ }
266
+ else
267
+ make_bot_instance bk, cfg
268
+ end
269
+ else
270
+ make_bot_instance bk, nil
271
+ end
272
+ }.flatten
273
+ end
274
+
275
+ def server_setting
276
+ if svrs = @config[:servers]
277
+ svl = []
278
+ svrs.each{|si|
279
+ ports = si[:port] || 6667
280
+ host = si[:host]
281
+ pass = si[:pass]
282
+ ssl_params = si[:ssl_params]
283
+ if ports.respond_to? :each
284
+ ports.each{|port|
285
+ svl << {:host => host, :port => port, :pass => pass, :ssl_params => ssl_params}
286
+ }
287
+ else
288
+ svl << {:host => host, :port => ports, :pass => pass, :ssl_params => ssl_params}
289
+ end
290
+ }
291
+ @config[:server_list] = svl
292
+ end
293
+ end
294
+
295
+ def make_logwriter log
296
+ return unless log
297
+
298
+ case log
299
+ when Hash
300
+ if log.has_key?(:logwriter)
301
+ return log[:logwriter]
302
+ elsif log.has_key?(:logwriterclass)
303
+ klass = log[:logwriterclass]
304
+ elsif log.has_key?(:io)
305
+ klass = IOLogWriter
306
+ elsif log.has_key?(:file)
307
+ klass = FileLogWriter
308
+ else
309
+ klass = FileLogWriter
310
+ end
311
+ opts = @config[:default_log].merge(log)
312
+ klass.new(self, opts)
313
+
314
+ when String
315
+ opts = @config[:default_log].dup
316
+ opts[:file] = log
317
+ FileLogWriter.new(self, opts)
318
+
319
+ when IO
320
+ opts = @config[:default_log].dup
321
+ opts[:io] = log
322
+ IOLogWriter.new(self, opts)
323
+
324
+ else
325
+ raise "Unknown LogWriter setting"
326
+ end
327
+ end
328
+
329
+ def make_default_logwriter
330
+ if @config[:default_log].kind_of? Hash
331
+ dl = @config[:default_log]
332
+ else
333
+ # defult_log must be Hash
334
+ dl = @config[:default_log]
335
+ @config[:default_log] = NDK_ConfigBase::Default_log.dup
336
+ end
337
+
338
+ @config[:default_logwriter] ||= make_logwriter(dl)
339
+ @config[:system_logwriter] ||= make_logwriter(@config[:system_log])
340
+ @config[:debug_logwriter] ||= make_logwriter(@config[:debug_log])
341
+ @config[:talk_logwriter] ||= make_logwriter(@config[:talk_log])
342
+ end
343
+
344
+ def channel_setting
345
+ # treat with channel information
346
+ if chs = @config[:channel_info]
347
+ dchs = []
348
+ lchs = []
349
+ cchs = {}
350
+
351
+ chs.each{|ch, setting|
352
+ ch = identical_channel_name(ch)
353
+ setting = {} unless setting.kind_of?(Hash)
354
+
355
+ if !setting[:timing] || setting[:timing] == :startup
356
+ dchs << ch
357
+ elsif setting[:timing] == :login
358
+ lchs << ch
359
+ end
360
+
361
+ # log writer
362
+ setting[:logwriter] ||= make_logwriter(setting[:log]) || @config[:default_logwriter]
363
+
364
+ cchs[ch] = setting
365
+ }
366
+ chs.replace cchs
367
+ @config[:default_channels] = dchs
368
+ @config[:login_channels] = lchs
369
+ end
370
+ end
371
+
372
+ def acl_setting
373
+ if @config[:client_server_acl] && !@config[:acl_object]
374
+ require 'drb/acl'
375
+
376
+ acl = @config[:client_server_acl].strip.split(/\s+/)
377
+ @config[:acl_object] = ACL.new(acl)
378
+ @logger.slog "ACL: #{acl.join(' ')}"
379
+ end
380
+ end
381
+
382
+ def load_config(rcfile)
383
+ load(rcfile) if rcfile
384
+
385
+ @config = {}
386
+ klass = ConfigClass.last
387
+
388
+ klass.ancestors[0..-3].reverse_each{|kl|
389
+ kl.constants.each{|e|
390
+ @config[e.downcase.intern] = klass.const_get(e)
391
+ }
392
+ }
393
+
394
+ @config[:setting_name] ||= File.basename(@manager.rc).sub(/\.?rc$/, '')
395
+
396
+ if $NDK_Debug
397
+ @config[:loglevel] = 3
398
+ end
399
+
400
+ make_default_logwriter
401
+ @logger = NDK_Logger.new(@manager, self)
402
+ @logger.slog "load config: #{rcfile}"
403
+
404
+ server_setting
405
+ channel_setting
406
+ acl_setting
407
+ load_bots
408
+ end
409
+
410
+ def ch_config ch, key
411
+ channel_info[ch] && channel_info[ch][key]
412
+ end
413
+
414
+ def canonical_channel_name ch
415
+ ch = ch.sub(/^\!.{5}/, '!')
416
+ identical_channel_name ch
417
+ end
418
+
419
+ def identical_channel_name ch
420
+ # use 4 gsub() because of the compatibility of RFC2813(3.2)
421
+ ch.toeuc.downcase.tr('[]\\~', '{}|^').tojis.force_encoding('ASCII-8BIT')
422
+ end
423
+
424
+ RName = { # ('&','#','+','!')
425
+ '#' => 'CS-',
426
+ '&' => 'CA-',
427
+ '+' => 'CP-',
428
+ '!' => 'CE-',
429
+ }
430
+
431
+ def make_logfilename tmpl, rch, cn
432
+ unless cn
433
+ cn = rch.sub(/^\!.{5}/, '!')
434
+
435
+ case @config[:filenameencoding].to_s.downcase[0]
436
+ when ?e # EUC
437
+ cn = cn.toeuc.downcase
438
+ when ?s # SJIS
439
+ cn = cn.tosjis.downcase
440
+ when ?u # utf-8
441
+ cn = cn.toutf8.downcase
442
+ else # JIS
443
+ cn = cn.toeuc.downcase.tojis
444
+ cn = URI.encode(cn)
445
+ end
446
+
447
+ # escape
448
+ cn = cn.sub(/^[\&\#\+\!]|/){|c|
449
+ RName[c]
450
+ }
451
+ cn = cn.tr("*:/", "__I")
452
+ end
453
+
454
+ # format
455
+ str = Time.now.strftime(tmpl)
456
+ str.gsub(/\$\{setting_name\}/, setting_name).
457
+ gsub(/\$\{channel_name\}|\{ch\}/, cn)
458
+ end
459
+
460
+ def log_format timefmt, msgfmts, msgobj
461
+ text = log_format_message(msgfmts, msgobj)
462
+
463
+ if timefmt && !msgobj[:nostamp]
464
+ text = "#{msgobj[:time].strftime(timefmt)} #{text}"
465
+ end
466
+
467
+ text
468
+ end
469
+
470
+ def log_format_message msgfmts, msgobj
471
+ type = msgobj[:type]
472
+ format = msgfmts.fetch(type, @config[:default_log][:message_format][type])
473
+
474
+ if format.kind_of? Proc
475
+ text = format.call(params)
476
+ elsif format
477
+ text = format.gsub(/\{([a-z]+)\}|\{prefix\:([a-z]+)\}/){|key|
478
+ if $2
479
+ method = $2.intern
480
+ if msgobj[:orig].respond_to?(:prefix)
481
+ (msgobj[:orig].prefix || '') =~ /^(.+?)\!(.+?)@(.+)/
482
+ case method
483
+ when :nick
484
+ $1
485
+ when :user
486
+ $2
487
+ when :host
488
+ $3
489
+ else
490
+ "!!unknown prefix attribute: #{method}!!"
491
+ end
492
+ end
493
+ else
494
+ if m = msgobj[$1.intern]
495
+ m
496
+ else
497
+ "!!unknown attribute: #{$1}!!"
498
+ end
499
+ end
500
+ }
501
+ else
502
+ text = msgobj[:orig].to_s
503
+ end
504
+ end
505
+
506
+ def make_bot_instance bk, cfg
507
+ bk = BotClasses[bk.to_s.downcase.intern] unless bk.kind_of? Class
508
+ bot = bk.new @manager, self, cfg || {}
509
+ @logger.slog "bot instance: #{bot.bot_state}"
510
+ bot
511
+ end
512
+
513
+ def load_botfile file
514
+ loaded = false
515
+
516
+ if @config[:plugins_dir].respond_to? :each
517
+ @config[:plugins_dir].each{|dir|
518
+ if load_file File.expand_path("#{file}.nb", dir)
519
+ loaded = true
520
+ break
521
+ end
522
+ }
523
+ else
524
+ loaded = load_file File.expand_path("#{file}.nb", @config[:plugins_dir])
525
+ end
526
+
527
+ unless loaded
528
+ raise "No such bot file: #{file}"
529
+ end
530
+ end
531
+
532
+ def load_file file
533
+ if FileTest.exist? file
534
+ Nadoka.require_bot file
535
+ true
536
+ else
537
+ false
538
+ end
539
+ end
540
+
541
+ RequiredFiles = []
542
+ BotClasses = {}
543
+ end
544
+
545
+ def self.require_bot file
546
+ return if NDK_Config::RequiredFiles.include? file
547
+
548
+ NDK_Config::RequiredFiles.push file
549
+ begin
550
+ ret = ::Kernel.load(file)
551
+ rescue
552
+ NDK_Config::RequiredFiles.pop
553
+ raise
554
+ end
555
+ ret
556
+ end
557
+ end
558
+
559
+ module NadokaBot
560
+ # empty module for bot namespace
561
+ # this module is reloadable
562
+ def self.included mod
563
+ Nadoka::NDK_Config::BotClasses['::' + mod.name.downcase] = mod
564
+ end
565
+ end
566
+
567
+ if $0 == __FILE__
568
+ require 'pp'
569
+ pp Nadoka::NDK_Config.new(nil, ARGV.shift)
570
+ end
571
+