ircbot 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,260 @@
1
+ =begin header
2
+ localization extension
3
+ IO supporting coding system conversion
4
+
5
+ $Author: igarashi $
6
+ $Date: 2001/01/02 00:21:18 $
7
+
8
+ Copyright (C) 1998-2001 Hiroshi IGARASHI
9
+ =end
10
+
11
+ require 'kconv'
12
+ #require 'uconv'
13
+
14
+ $vsave, $VERBOSE = $VERBOSE, FALSE
15
+
16
+ module Kernel
17
+ private
18
+
19
+ def println(*args)
20
+ =begin
21
+ print with ln
22
+ =end
23
+ STDOUT.println(*args)
24
+ end
25
+
26
+ def eprint(*args)
27
+ =begin
28
+ print to stderr
29
+ =end
30
+ STDERR.print(*args)
31
+ end
32
+
33
+ def eprintln(*args)
34
+ =begin
35
+ eprint with ln
36
+ =end
37
+ STDERR.println(*args)
38
+ end
39
+ end
40
+
41
+ # IO class extension
42
+ class IO
43
+ def println(*args)
44
+ =begin
45
+ print with ln
46
+ =end
47
+ if args.length > 0
48
+ print(*args)
49
+ end
50
+ print("\n")
51
+ end
52
+ public(:println)
53
+ end
54
+
55
+ class << ARGF
56
+ def lineno
57
+ $.
58
+ end
59
+ def path
60
+ filename
61
+ end
62
+ end
63
+
64
+ ## available only if kconv ext module imported
65
+ if defined?(Kconv)
66
+
67
+ module Kernel
68
+ private
69
+
70
+ # lreadline
71
+ def lreadline(*arg)
72
+ Kconv.tointern(readline(*arg))
73
+ end
74
+
75
+ # lgets
76
+ def lgets(*arg)
77
+ ret = gets(*arg)
78
+ unless ret.nil?
79
+ Kconv.tointern(ret)
80
+ else
81
+ nil
82
+ end
83
+ end
84
+
85
+ def p(*args)
86
+ args.each { |arg|
87
+ println(Kconv.toextern(arg.inspect))
88
+ }
89
+ end
90
+
91
+ # lprint
92
+ def lprint(*args)
93
+ STDOUT.lprint(*args)
94
+ end
95
+
96
+ # lprint with ln
97
+ def lprintln(*args)
98
+ STDOUT.lprintln(*args)
99
+ end
100
+
101
+ # leprint
102
+ def leprint(*args)
103
+ STDERR.lprint(*args)
104
+ end
105
+
106
+ def leprintln(*args)
107
+ =begin
108
+ leprint with ln
109
+ =end
110
+ STDERR.lprintln(*args)
111
+ end
112
+ end
113
+
114
+ # Kconv module extension
115
+ module Kconv
116
+ ## local encoding
117
+ case $KIOCODE
118
+ ## EUC
119
+ when 'EUC'
120
+ alias_method(:toextern, :toeuc)
121
+ ## JIS
122
+ when 'JIS'
123
+ alias_method(:toextern, :tojis)
124
+ ## SJIS
125
+ when 'SJIS'
126
+ alias_method(:toextern, :tosjis)
127
+ else
128
+ case $KCODE
129
+ ## EUC
130
+ when 'EUC'
131
+ alias_method(:toextern, :toeuc)
132
+ ## JIS
133
+ when 'JIS'
134
+ alias_method(:toextern, :tojis)
135
+ ## SJIS
136
+ when 'SJIS'
137
+ alias_method(:toextern, :tosjis)
138
+ when 'NONE'
139
+ def toextern(str)
140
+ str
141
+ end
142
+ else
143
+ raise "Unknown coding system(\"#{$KIOCODE}\")."
144
+ end
145
+ end
146
+
147
+ case $KCODE
148
+ ## EUC
149
+ when 'EUC'
150
+ alias_method(:tointern, :toeuc)
151
+ ## SJIS
152
+ when 'SJIS'
153
+ alias_method(:tointern, :tosjis)
154
+ when 'NONE'
155
+ def tointern(str)
156
+ str
157
+ end
158
+ else
159
+ raise "Unknown coding system(\"#{$KCODE}\")."
160
+ end
161
+
162
+ alias_method(:tolocal, :toextern)
163
+ module_function(:toextern, :tolocal, :tointern)
164
+ end
165
+ end
166
+
167
+ class << ARGF
168
+
169
+ if defined?(Kconv)
170
+ # lreadline
171
+ def lreadline(*arg)
172
+ Kconv.tointern(readline(*arg))
173
+ end
174
+ # lgets
175
+ def lgets(*arg)
176
+ ret = gets(*arg)
177
+ unless ret.nil?
178
+ Kconv.tointern(ret)
179
+ else
180
+ nil
181
+ end
182
+ end
183
+ end
184
+
185
+ end
186
+
187
+ class IO
188
+
189
+ if defined?(Kconv)
190
+
191
+ # lreadline
192
+ def lreadline(*arg)
193
+ Kconv.tointern(readline(*arg))
194
+ end
195
+
196
+ # lgets
197
+ def lgets(*arg)
198
+ ret = gets(*arg)
199
+ unless ret.nil?
200
+ Kconv.tointern(ret)
201
+ else
202
+ nil
203
+ end
204
+ end
205
+
206
+ # convert to local encoding and print
207
+ def lprint(*args)
208
+ args.each { |arg|
209
+ print(Kconv.toextern(arg.to_s))
210
+ }
211
+ end
212
+ def jisprint(*args)
213
+ args.each { |arg|
214
+ print(Kconv.tojis(arg.to_s))
215
+ }
216
+ end
217
+ def eucprint(*args)
218
+ args.each { |arg|
219
+ print(Kconv.toeuc(arg.to_s))
220
+ }
221
+ end
222
+ def sjisprint(*args)
223
+ args.each { |arg|
224
+ print(Kconv.tosjis(arg.to_s))
225
+ }
226
+ end
227
+
228
+ def lprintln(*args)
229
+ lprint(*args)
230
+ print("\n")
231
+ end
232
+
233
+ def set_codesys(codesys_name)
234
+ case codesys_name
235
+ ## JIS
236
+ when 'JIS'
237
+ class << self
238
+ alias_method(:lprint, :jisprint)
239
+ end
240
+ ## EUC
241
+ when 'EUC'
242
+ class << self
243
+ alias_method(:lprint, :eucprint)
244
+ end
245
+ ## SJIS
246
+ when 'SJIS'
247
+ class << self
248
+ alias_method(:lprint, :sjisprint)
249
+ end
250
+ when 'NONE'
251
+ alias_method(:lprint, :print)
252
+ else
253
+ raise "Unknown coding system(\"#{codesys_name}\")."
254
+ end
255
+ end
256
+ public(:lreadline, :lprint, :lprintln)
257
+ end
258
+ end
259
+
260
+ $VERBOSE = $vsave
@@ -0,0 +1,41 @@
1
+
2
+ require 'nkf'
3
+ require 'pathname'
4
+ require 'rubygems'
5
+ require 'active_support'
6
+
7
+ ######################################################################
8
+ ### Load path
9
+
10
+ Thread.abort_on_exception = true
11
+ __DIR__ = File.dirname(__FILE__)
12
+
13
+ $LOAD_PATH.unshift __DIR__ unless
14
+ $LOAD_PATH.include?(__DIR__) ||
15
+ $LOAD_PATH.include?(File.expand_path(__DIR__))
16
+
17
+ require 'ircbot/framework'
18
+
19
+
20
+ ######################################################################
21
+ ### IRC library
22
+
23
+ require 'irc/irc'
24
+ require 'irc/agent'
25
+ require 'irc/client'
26
+
27
+
28
+ ######################################################################
29
+ ### Ircbot library
30
+
31
+ require 'ircbot/core_ext/rand-polimorphism'
32
+ require 'ircbot/core_ext/writefile'
33
+ require 'ircbot/core_ext/digest'
34
+ require 'ircbot/core_ext/irc'
35
+
36
+
37
+ ######################################################################
38
+ ### Ircbot
39
+
40
+ require 'ircbot/reply_client'
41
+
@@ -0,0 +1,89 @@
1
+ # -*- coding: euc-jp -*-
2
+
3
+ module Ircbot
4
+ ######################################################################
5
+ ### agent ��������륯�饹
6
+ ### (ľ�� agent �����Ѥ��ʤ��Τϡ�agent �δ�����ʻ���ƹԤ�����)
7
+ ######################################################################
8
+ class AgentManager
9
+ attr :cpi, true
10
+ attr :name
11
+ attr :created
12
+ attr :alive
13
+
14
+ def initialize (name, obj, client, arg = nil)
15
+ @name = name
16
+ @cpi = obj
17
+ @created = Time.now
18
+ @alive = nil
19
+ @client = client
20
+ @arg = arg
21
+
22
+ method = :do_construct
23
+ if @cpi.respond_to?(method)
24
+ @cpi.send(method, client)
25
+ end
26
+ end
27
+
28
+ def notifyMessage (msg)
29
+ # NOP
30
+ end
31
+
32
+ def start (arg = nil)
33
+ @arg = arg if arg
34
+
35
+ method = :do_start
36
+ if @cpi.respond_to?(method)
37
+ @cpi.send(method, @arg)
38
+ end
39
+
40
+ @alive = true
41
+ end
42
+
43
+ def stop
44
+ method = :do_destruct
45
+ if @cpi.respond_to?(method)
46
+ @cpi.send(method, nil)
47
+ end
48
+ @alive = nil
49
+ end
50
+
51
+ def alive?
52
+ @alive
53
+ end
54
+
55
+ def send (method, *args)
56
+ if alive?
57
+ return @cpi.send(method, *args)
58
+ else
59
+ return nil
60
+ end
61
+ end
62
+
63
+ # ���ꤵ�줿�᥽�åɷ���缡�¹Ԥ���
64
+ # 1�ļ¹Ԥ����齪λ���ɤΥ᥽�åɤ�������Ƥʤ����� nil ���֤���
65
+ def apply_methods (msg, methods)
66
+ methods = [methods] unless methods.is_a? Array
67
+ methods.each do |method|
68
+ next unless @cpi.respond_to?(method)
69
+ begin
70
+ if msg
71
+ if (result = send(method, msg))
72
+ return result
73
+ end
74
+ else
75
+ if (result = send(method))
76
+ return result
77
+ end
78
+ end
79
+ rescue Exception => err
80
+ error = "error: #{err} in #{self.name}"
81
+ @client.syslog(error, :error)
82
+ @client.syslog($@.join("\n"), :error)
83
+ return error
84
+ end
85
+ end
86
+ return nil
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,369 @@
1
+ # -*- coding: euc-jp -*-
2
+
3
+ require 'ircbot/agent_manager'
4
+ require 'ircbot/ordered_hash'
5
+
6
+ module Ircbot
7
+ class ConfigClient < IRC::Client
8
+ include ActiveSupport::Rescuable
9
+
10
+ attr :config
11
+ attr :messages
12
+ attr :agents
13
+
14
+ PARAM_STRING = %w( nick server username realname password port log syslog help pidfile )
15
+ PARAM_ARRAY = %w( agents channels )
16
+ PARAM_KEYS = PARAM_STRING + PARAM_ARRAY
17
+
18
+ class << self
19
+ ######################################################################
20
+ ### config �ե����뤫�������ͤ��ɤ߹��ߡ�Client ���֥������Ȥ����
21
+ ######################################################################
22
+ def read_config(input, debug = nil)
23
+ case input
24
+ when IO # already IO (NOP)
25
+ when String # maybe file
26
+ begin
27
+ input = File.open(input)
28
+ rescue Exception
29
+ $stderr.puts("cannot read config file. (#{input})")
30
+ exit
31
+ end
32
+ else
33
+ $stderr.puts("missing config file.")
34
+ exit
35
+ end
36
+
37
+ params = {}
38
+ param_regexp = self::PARAM_KEYS.join('|')
39
+ while (line = input.gets)
40
+ case line
41
+ when /^#/ # ignore
42
+ when /^(#{param_regexp})\s*=\s*/io
43
+ key, val = $1.downcase, $'.chomp
44
+ symbol = key.intern
45
+ case key
46
+ when *self::PARAM_STRING
47
+ params[symbol] = val
48
+ when *self::PARAM_ARRAY
49
+ params[symbol] = val.split(/[,\s]+/)
50
+ else # IGNORE...
51
+ $stderr.puts "ignore: #{key} = #{params[symbol].inspect}(#{params[symbol].class})"
52
+ end
53
+ if debug
54
+ $stderr.puts "debug: #{key} = #{params[symbol].inspect}(#{params[symbol].class})"
55
+ end
56
+ end
57
+ end
58
+ self.new(params)
59
+ end
60
+ end
61
+
62
+ def initialize (hash = {})
63
+ @config = hash
64
+
65
+ # ɬ�ܹ���
66
+ @nick = hash[:nick] || missing_error(:nick)
67
+ @server = hash[:server] || missing_error(:server)
68
+ @username = hash[:username] || @nick || missing_error(:username)
69
+ @realname = hash[:realname] || @nick || missing_error(:realname)
70
+ @channels = hash[:channels]
71
+
72
+ @port = hash[:port] || 6667
73
+ @password = hash[:password] || 'xxx'
74
+ @log = hash[:log]
75
+ @syslog = hash[:syslog]
76
+ @help = hash[:help] || '�إ�פ�����ޤ���(config �� help �Ԥ򸫤ޤ�)'
77
+ @agents = OrderedHash.new # String��Agent
78
+
79
+ @agents_opt = {} # ����������Ȥε�ư�����Ѥ��륪�ץ����
80
+
81
+ @mynames = [@nick, @realname].compact.sort.uniq
82
+ @cachesize = (hash[:cachesize].to_i < 1) ? 100 : hash[:cachesize].to_i
83
+ @last_message_id = 0
84
+ @messages = []
85
+
86
+ @last_ping_time = nil # �Ǹ�� CMD_PING �������ä�����
87
+ end
88
+
89
+ def each_agent
90
+ @agents.each_pair do |name, agent|
91
+ yield(agent)
92
+ end
93
+ end
94
+
95
+ def missing_error (arg)
96
+ syslog("#{arg}�����ꤵ��Ƥ��ޤ���", :fatal)
97
+ end
98
+
99
+ def syslog (message, level = :normal)
100
+ line = Time.now.strftime("%Y-%m-%d %H:%M:%S #{message}")
101
+ case level
102
+ when :normal
103
+ if @syslog
104
+ File.open!(@syslog, "a+") {|file| file.puts line; file.flush}
105
+ end
106
+ when :error
107
+ syslog(message, :normal)
108
+ $stderr.puts line
109
+ when :fatal
110
+ syslog(message, :error)
111
+ exit 1
112
+ else
113
+ syslog("syslog: wrong level(#{level}): mes=[#{message}]", :error)
114
+ end
115
+ end
116
+
117
+ def write_pid_file
118
+ # write pid information to a file
119
+ filename = config[:pidfile]
120
+ pid = Process::pid
121
+ if filename
122
+ if exist_process?
123
+ raise "another process is running. (pid=#{pid})"
124
+ end
125
+ File::write!(pid.to_s, filename, 'w+')
126
+ File::chmod(0664, filename)
127
+ end
128
+ end
129
+
130
+ def exist_process?
131
+ filename = config[:pidfile] or return nil
132
+ File::exists?(filename) or return nil
133
+ pid = File::open(filename).read or return nil
134
+
135
+ begin
136
+ Process::getpgid(pid.to_i)
137
+ return pid.chomp
138
+ rescue Errno::ESRCH
139
+ return nil
140
+ end
141
+ end
142
+
143
+ def do_log (message)
144
+ begin
145
+ case @log
146
+ when NilClass
147
+ return nil
148
+ when /\/$/
149
+ format = "#{@log}/%Y%m/%d.log"
150
+ when String
151
+ format = @log
152
+ else
153
+ syslog("do_log: unknown type #{@log.class}", :error)
154
+ @log = nil
155
+ return nil
156
+ end
157
+
158
+ now = Time.now
159
+ path = now.strftime(format)
160
+ from = message[:from]
161
+ to = message[:to]
162
+ str = message[:str]
163
+
164
+ File.open!(path, "a+") {|file|
165
+ file.puts now.strftime("%H:%M <#{to}:#{from}> #{str}")
166
+ }
167
+ return nil
168
+ rescue
169
+ syslog("do_log: #{$!}", :error)
170
+ @log = nil
171
+ end
172
+ end
173
+
174
+
175
+ ######################################################################
176
+ ### IRC �Υ��ͥ������ط�
177
+ ######################################################################
178
+
179
+ def start
180
+ write_pid_file
181
+ syslog("#{self.class}: system starts.")
182
+
183
+ create_message_thread
184
+
185
+ # ����³�ΰ٤ˡ�initialize ��Ǥʤ������ǸƤӽФ���
186
+
187
+ connect
188
+ join
189
+
190
+ # ����������Ȥ���Ͽ
191
+ @config[:agents].each do |name|
192
+ registerAgent(name)
193
+ end
194
+ each_agent {|agent| agent.start}
195
+
196
+ startThreads
197
+ @message_thread.join
198
+ @log_thread.raise(Stop.new)
199
+ @log_thread.join
200
+
201
+ rescue Exception => e
202
+ begin
203
+ syslog("catch exception: #{e}(#{e.class})", :error)
204
+ rescue_with_handler(e) or raise
205
+ rescue Recover => recover
206
+ irc.stop
207
+ sleep recover.wait
208
+ retry
209
+ end
210
+ end
211
+
212
+ def ping_timeout
213
+ stop
214
+ exit
215
+ end
216
+
217
+ def startThreads
218
+ super
219
+ @ping_thread = Thread.start {
220
+ eprintln("ping_thread started.") if $DEBUG
221
+ #_putlog("debug", "ping_thread started.")
222
+ sec = 3600 # �����ÿ� PING ���ʤ��� ping_timeout��Ƥ�
223
+ while true
224
+ sleep 60
225
+ case @last_ping_time
226
+ when Time
227
+ if Time.now > @last_ping_time + sec
228
+ ping_timeout
229
+ end
230
+ end
231
+ end
232
+ eprintln("ping_thread stopped.") if $DEBUG
233
+ #_putlog("debug", "ping_thread stopped.")
234
+ }
235
+ eprintln("ping_thread created.") if $DEBUG
236
+ end
237
+
238
+ # derived from original ruby-irc::Client#stop
239
+ def stop
240
+ # Agent�����
241
+ each_agent do |agent|
242
+ agent.stop
243
+ end
244
+
245
+ disconnect
246
+ @message_thread.raise(IRC::Stop.new)
247
+ destroy_message_thread
248
+ end
249
+
250
+ # derived from original ruby-irc::Client's one.
251
+ def connect
252
+ @log_queue = Queue.new
253
+ @connection = IRC::Connection::new(@log_queue)
254
+
255
+ @connection.connect(@server, @port.to_s)
256
+ @connection.sendPASS(@password)
257
+ @connection.sendNICK(@nick)
258
+ @connection.sendUSER(nil, @username, "hostname", "servername", @realname)
259
+ end
260
+
261
+ # derived from original ruby-irc::Client's one.
262
+ def disconnect
263
+ @connection.sendQUIT(@nick, nil)
264
+ @connection.disconnect
265
+ end
266
+
267
+ def join
268
+ channels = @channels.join(',')
269
+ @connection.send(CMD_JOIN, nil, @nick, channels, '')
270
+ end
271
+
272
+ def names (channel = nil)
273
+ @connection.sendNAMES(channel)
274
+ end
275
+
276
+ ######################################################################
277
+ ### Message Handling
278
+ ######################################################################
279
+
280
+ def handlePING(msg)
281
+ # p [:handlePING, Time.now]
282
+ super
283
+ @last_ping_time = Time.now
284
+ end
285
+
286
+ def add_message (msg)
287
+ index = (@last_message_id += 1) % @cachesize
288
+ @messages[index] = msg
289
+
290
+ return index
291
+ end
292
+
293
+ def previous_message (msg)
294
+ index = (@cachesize + msg[:id].to_i - 1) % @cachesize
295
+ @messages[index]
296
+ end
297
+
298
+ def last_messages (size)
299
+ mes = @messages[@last_message_id % @cachesize] or return []
300
+ array = []
301
+ i = 0
302
+ j = 0
303
+
304
+ while (size > 0)
305
+ j += 1
306
+ break if i > @cachesize # maybe looping. give up
307
+ break if j > 100
308
+ break unless mes
309
+
310
+ i += 1
311
+ # p [mes[:from],mes[:to],mes[:str], size, i, mes[:from] == mes[:to]]
312
+ unless mes[:from] == mes[:to]
313
+ array << mes
314
+ size -= 1
315
+ end
316
+ mes = mes.previous
317
+ end
318
+ return array
319
+ end
320
+
321
+ # created by yu-yan@4th.to
322
+ def create_message_thread
323
+ @message_last = nil
324
+ @message_timer = Time.now
325
+ @message_queue = Queue.new
326
+ @message_thread = Thread.new(@message_timer, @message_queue) { |message_timer, message_queue|
327
+ loop {
328
+ now = Time.now
329
+ message_timer = [message_timer, now].max + 2
330
+
331
+ if (message_timer - now) > 15
332
+ sleep(3)
333
+ else
334
+ sleep(0.1)
335
+ end
336
+
337
+ # STDERR.print("timer:#{message_timer.strftime('%H:%M:%S')} - now:#{now.strftime('%H:%M:%S')} = #{message_timer - now}\n")
338
+
339
+ message, to = message_queue.pop
340
+ privmsg(message, to)
341
+ # STDERR.print("MSG:", message, "\n")
342
+ }
343
+ }
344
+ end
345
+
346
+ def destroy_message_thread
347
+ if @message_thread.is_a? Thread
348
+ @message_thread.kill
349
+ end
350
+ end
351
+
352
+ # derived from original ruby-irc::Client's one.
353
+ def handleMessageLoop
354
+ loop do
355
+ msg = @connection.recv
356
+ if msg.nil?
357
+ syslog("handleMessageLoop: Abnormal terminated.", :error)
358
+ abnormal_terminated
359
+ end
360
+ handleMessageInternal(msg)
361
+ distributeMessage(msg)
362
+ end
363
+ end
364
+
365
+ def abnormal_terminated
366
+ raise IRC::AbnormalTerminated
367
+ end
368
+ end
369
+ end