nadoka 0.8.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/.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
data/ndk/client.rb
ADDED
@@ -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
|
+
|
data/ndk/config.rb
ADDED
@@ -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
|
+
|