ircbot 0.1.2 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -17,6 +17,7 @@ Config
17
17
  * help: help message for this bot
18
18
  * channels: startup channel names
19
19
  * plugins: plugin names
20
+ * timeout: bot will exit when no new pings come for this sec
20
21
 
21
22
 
22
23
  Usage
@@ -57,7 +58,30 @@ Example
57
58
  end
58
59
  end
59
60
 
60
-
61
+
62
+ Advanced
63
+ ========
64
+
65
+ Messages are passed into plugins automatically.
66
+ If you don't want this plugin chain, throw :done will help you.
67
+
68
+ config:
69
+ plugins: a b
70
+
71
+ class APlugin < Ircbot::Plugin
72
+ def reply(text)
73
+ throw :done, "This is A"
74
+ end
75
+ end
76
+
77
+ class BPlugin < Ircbot::Plugin
78
+ def reply(text)
79
+ "This is B"
80
+ end
81
+ end
82
+
83
+ BPlugin#reply will be never invoked.
84
+
61
85
 
62
86
  Required
63
87
  ========
data/bin/ircbot CHANGED
@@ -62,6 +62,9 @@ end
62
62
 
63
63
  Ircbot.push_path(:plugin, plugin)
64
64
 
65
+ STDOUT.sync = true
66
+ STDERR.sync = true
67
+
65
68
  irc = Ircbot::Client.from_file(config)
66
69
  irc.start
67
70
  puts "Bye"
@@ -0,0 +1,8 @@
1
+ host: localhost
2
+ port: 6666
3
+ nick: airi
4
+ user: airi
5
+ real: airi
6
+ channels: "#test"
7
+ plugins: irc plugins which reminder tv
8
+ multiline: true
@@ -6,3 +6,4 @@ real: sama~zu
6
6
  channels: "#test"
7
7
  plugins: irc plugins
8
8
  multiline: true
9
+ timeout: 500
@@ -1,8 +1,9 @@
1
1
  host: irc.haun.org
2
2
  port: 6667
3
3
  nick: sama-zu
4
- user: samazu
5
- real: sama~zu
4
+ user: sama-zu
5
+ real: sama-zu
6
6
  channels: "#wota"
7
- plugins: irc plugins which
7
+ plugins: irc plugins which reminder tv
8
8
  multiline: true
9
+ timeout: 600
@@ -6,3 +6,5 @@ real: <%= nick %>
6
6
  channels: "#test"
7
7
  plugins: irc plugins
8
8
  multiline: true
9
+ timeout: 500
10
+ # wait_before_restart: 300
@@ -3,30 +3,17 @@
3
3
  require "ircbot/plugin"
4
4
  require "ircbot/plugins"
5
5
 
6
- module Ircbot
7
- class Client < Net::IRC::Client
8
-
9
- # escape from nil black hole
10
- def method_missing(name, *args)
11
- case name.to_s
12
- when /^on_/
13
- # nop for calling super from subclass
14
- else
15
- raise NameError, "undefined local variable or method `#{name}' for #{self}"
16
- end
17
- end
18
-
19
- class Standalone < Client
20
- def initialize(*)
21
- super({})
22
- end
23
- end
24
- end
25
- end
6
+ ######################################################################
7
+ ### Irctob::Client
26
8
 
9
+ require "ircbot/client/core"
10
+ require "ircbot/client/standalone"
11
+ require "ircbot/client/eventable"
12
+ require "ircbot/client/logger"
27
13
  require "ircbot/client/encoding"
28
14
  require "ircbot/client/commands"
29
15
  require "ircbot/client/config"
30
16
  require "ircbot/client/config/channels"
31
17
  require "ircbot/client/config/plugins"
18
+ require "ircbot/client/timeout"
32
19
  require "ircbot/client/plugins"
@@ -27,16 +27,20 @@ module Ircbot
27
27
  ######################################################################
28
28
  ### Config
29
29
  def initialize(obj)
30
- @obj = obj
30
+ @obj = Mash.new(obj)
31
31
  end
32
32
 
33
33
  def [](key)
34
- __send__(key)
34
+ @obj[key]
35
35
  end
36
36
 
37
37
  private
38
- def method_missing(*args)
39
- @obj.__send__(*args)
38
+ def method_missing(name, *args)
39
+ if args.empty?
40
+ self[name]
41
+ else
42
+ super
43
+ end
40
44
  end
41
45
  end
42
46
 
@@ -46,10 +50,9 @@ module Ircbot
46
50
 
47
51
  def initialize(hash)
48
52
  super(hash[:host], hash[:port], hash)
53
+ @config = Config.new(hash)
49
54
  end
50
55
 
51
- def config
52
- @config ||= Config.new(opts)
53
- end
56
+ attr_reader :config
54
57
  end
55
58
  end
@@ -20,13 +20,11 @@ module Ircbot
20
20
  ######################################################################
21
21
  ### Event
22
22
 
23
- def on_rpl_welcome(m)
24
- super
25
-
23
+ event(:rpl_welcome) {
26
24
  config.channels.each do |channel|
27
25
  post JOIN, channel
28
26
  end
29
- end
27
+ }
30
28
 
31
29
  ######################################################################
32
30
  ### Command
@@ -32,34 +32,6 @@ module Ircbot
32
32
  "unknown"
33
33
  end
34
34
  end
35
- def channels
36
- case (val = super)
37
- when Array
38
- val
39
- else
40
- val.to_s.split
41
- end
42
- end
43
- end
44
-
45
- ######################################################################
46
- ### Event
47
-
48
- def on_rpl_welcome(m)
49
- super
50
-
51
- config.channels.each do |channel|
52
- post JOIN, channel
53
- end
54
- end
55
-
56
- ######################################################################
57
- ### Command
58
-
59
- def broadcast(text)
60
- config.channels.each do |channel|
61
- privmsg channel, text
62
- end
63
35
  end
64
36
  end
65
37
  end
@@ -0,0 +1,4 @@
1
+ module Ircbot
2
+ class Client < Net::IRC::Client
3
+ end
4
+ end
@@ -0,0 +1,21 @@
1
+ module Ircbot
2
+ class Client
3
+ include Extlib::Hook
4
+
5
+ class << self
6
+ def event(name, &block)
7
+ name = "on_#{name}".intern
8
+ unless instance_methods.include?(name.to_s)
9
+ define_method(name){|m|}
10
+ end
11
+ before name, &block
12
+ end
13
+ end
14
+
15
+ # escape from nil black hole
16
+ private
17
+ def method_missing(name, *args)
18
+ raise NameError, "undefined local variable or method `#{name}' for #{self}"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Ircbot
4
+ class Client
5
+ def debug(text)
6
+ p [:debug, text]
7
+ end
8
+ end
9
+ end
@@ -15,18 +15,16 @@ module Ircbot
15
15
  ######################################################################
16
16
  ### Events
17
17
 
18
- def on_privmsg(m)
19
- super
20
-
18
+ event(:privmsg) {|m|
21
19
  text = decode(m.params[1].to_s)
22
20
  args = [text, m.prefix.nick, m]
23
21
 
24
22
  plugins_call_replies(args, m)
25
- end
23
+ }
26
24
 
27
25
  private
28
26
  def plugins_call_replies(args, m)
29
- text = catch(:halt) do
27
+ text = catch(:done) do
30
28
  plugins.active.each do |plugin|
31
29
  plugins_call_action(:reply, plugin, args, m, :reply=>true)
32
30
  end
@@ -0,0 +1,9 @@
1
+ module Ircbot
2
+ class Client
3
+ class Standalone < Client
4
+ def initialize(*)
5
+ super({})
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,32 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Ircbot
4
+ class Client
5
+ ######################################################################
6
+ ### Event
7
+
8
+ event(:ping) {
9
+ if (sec = config.timeout.to_i) > 0
10
+ @kill_myself_at.kill if @kill_myself_at.is_a?(Thread)
11
+ @kill_myself_at = Thread.new { sleep sec; timeouted }
12
+ end
13
+ }
14
+
15
+ class Timeouted < RuntimeError; end
16
+
17
+ def start
18
+ super
19
+ rescue Exception => e
20
+ sec = [config.wait_before_restart.to_i, 10].max
21
+ sleep sec
22
+ $stderr.puts "Restarting after timeouted"
23
+ retry
24
+ end
25
+
26
+ private
27
+ def timeouted
28
+ $stderr.puts "No new pings for #{config.timeout}sec."
29
+ raise Timeouted
30
+ end
31
+ end
32
+ end
@@ -29,6 +29,7 @@ module Ircbot
29
29
  ### Accessors
30
30
 
31
31
  delegate :plugin!, :client, :bot, :config, :to=>"@plugins"
32
+ delegate :debug, :to=>"@plugins"
32
33
 
33
34
  def plugin_name
34
35
  @plugin_name ||= Extlib::Inflection.foreign_key(self.class.name).sub(/(_plugin)?_id$/,'')
@@ -59,5 +60,9 @@ module Ircbot
59
60
  def direct?
60
61
  message.channel == config.nick
61
62
  end
63
+
64
+ def nick
65
+ message.prefix.nick
66
+ end
62
67
  end
63
68
  end
@@ -6,7 +6,7 @@ module Ircbot
6
6
  class Plugins
7
7
  attr_reader :plugins
8
8
  attr_reader :client
9
- delegate :config, :to=>"client"
9
+ delegate :config, :debug, :to=>"client"
10
10
 
11
11
  def initialize(client = nil, plugins = nil)
12
12
  @client = client || Client::Standalone.new
@@ -1,4 +1,4 @@
1
1
  module Ircbot
2
- VERSION = '0.1.2' unless defined?(Ircbot::VERSION)
2
+ VERSION = '0.1.4' unless defined?(Ircbot::VERSION)
3
3
  HOMEPAGE = "http://github.com/maiha/ircbot"
4
4
  end
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env ruby -Ku
2
+ # -*- coding: utf-8 -*-
3
+
4
+ ######################################################################
5
+ # [Install]
6
+ #
7
+ # gem install chawan night-time dm-core dm-migrations dm-timestamps do_sqlite3 data_objects
8
+ #
9
+
10
+ require 'rubygems'
11
+ require 'ircbot'
12
+ require 'chawan'
13
+ require 'night-time'
14
+
15
+ require 'dm-core'
16
+ require 'dm-migrations'
17
+ require 'dm-timestamps'
18
+
19
+ module Reminder
20
+ def self.connect(path = nil)
21
+ @connecteds ||= {}
22
+ @connecteds[path] ||=
23
+ (
24
+ path = Pathname(path || Pathname(Dir.getwd) + "db" + "reminder.db").expand_path
25
+ path.parent.mkpath
26
+ DataMapper.setup(:default, "sqlite3://#{path}")
27
+ Reminder::Event.auto_upgrade!
28
+ )
29
+ end
30
+
31
+ ######################################################################
32
+ ### Exceptions
33
+
34
+ class EventNotFound < RuntimeError; end
35
+ class EventNotSaved < RuntimeError
36
+ attr_accessor :event
37
+ def initialize(event)
38
+ @event = event
39
+ end
40
+ end
41
+ class EventHasDone < EventNotSaved; end
42
+ class StartNotFound < EventNotSaved; end
43
+
44
+ ######################################################################
45
+ ### Event
46
+
47
+ class Event
48
+ include DataMapper::Resource
49
+
50
+ property :id , Serial
51
+ property :st , DateTime # 開始日時
52
+ property :en , DateTime # 終了日時
53
+ property :title , String # 件名
54
+ property :desc , String # 詳細
55
+ property :where , String # 場所
56
+ property :allday , Boolean , :default=>false # 終日フラグ
57
+ property :alerted , Boolean , :default=>false # お知らせ済
58
+ property :alert_at , DateTime # お知らせ日時
59
+
60
+ ######################################################################
61
+ ### Class methods
62
+
63
+ class << self
64
+ def default_storage_name
65
+ "event"
66
+ end
67
+
68
+ def reminders
69
+ all(:alerted=>false, :alert_at.lt=>Time.now, :order=>[:alert_at])
70
+ end
71
+ end
72
+
73
+ ######################################################################
74
+ ### Instance methods
75
+
76
+ def done!
77
+ self.alerted = true
78
+ save
79
+ end
80
+
81
+ def to_s
82
+ desc.to_s
83
+ end
84
+ end
85
+
86
+ module TimeParser
87
+ def parse(text)
88
+ event = Event.new
89
+ event.desc = text
90
+ event.title = text.sub(%r{^[\s\d:-]+}, '')
91
+ event.allday = false
92
+
93
+ t = Date._parse(text)
94
+ # => {:zone=>"-14:55", :year=>2010, :hour=>13, :min=>30, :mday=>4, :offset=>-53700, :mon=>1}
95
+
96
+ if t[:year] && t[:mon] && t[:mday] && t[:hour]
97
+ event.st = Time.mktime(t[:year], t[:mon], t[:mday], t[:hour], t[:min], t[:sec])
98
+ if t[:zone].to_s =~ /^-?(\d+):(\d+)(:(\d+))?$/
99
+ event.en = Time.mktime(t[:year], t[:mon], t[:mday], $1, $2, $4)
100
+ end
101
+ else
102
+ event.allday = true
103
+ event.st = Time.mktime(t[:year], t[:mon], t[:mday]) rescue nil
104
+ end
105
+
106
+ return event
107
+ end
108
+
109
+ def register(text)
110
+ event = parse(text)
111
+ event.st or raise StartNotFound, event
112
+ if event.st.to_time > Time.now
113
+ event.alert_at = Time.at(event.st.to_time.to_i - 30*60)
114
+ event.save or raise EventNotSaved, event
115
+ return event
116
+ else
117
+ raise EventHasDone, event
118
+ end
119
+ end
120
+ end
121
+
122
+ extend TimeParser
123
+ end
124
+
125
+
126
+ class ReminderPlugin < Ircbot::Plugin
127
+ class EventWatcher
128
+ attr_accessor :interval
129
+ attr_accessor :callback
130
+
131
+ def initialize(options = {})
132
+ @interval = options[:interval] || 60
133
+ @callback = options[:callback] || proc{|e| puts e}
134
+ end
135
+
136
+ def start
137
+ loop do
138
+ if callback
139
+ events = Reminder::Event.reminders
140
+ #debug "#{self.class} found #{events.size} events"
141
+ events.each do |event|
142
+ callback.call(event)
143
+ event.done!
144
+ end
145
+ end
146
+ sleep interval
147
+ end
148
+ end
149
+ end
150
+
151
+ def reply(text)
152
+ start_reminder
153
+
154
+ case text
155
+ when %r{^\d{4}.?\d{1,2}.?\d{1,2}}
156
+ event = Reminder.register(text)
157
+ text = "Remind you again at %s" % event.alert_at.strftime("%Y-%m-%d %H:%M")
158
+ throw :done, text
159
+ end
160
+ return nil
161
+
162
+ rescue Reminder::EventNotFound
163
+ return nil
164
+
165
+ rescue Reminder::StartNotFound => e
166
+ return "Reminder cannot detect start: #{e.event.st}"
167
+
168
+ rescue Reminder::EventHasDone => e
169
+ puts "Reminder ignores past event: #{e.event.st}"
170
+ return nil
171
+ end
172
+
173
+ private
174
+ def start_reminder(&callback)
175
+ bot = self.bot
176
+ callback ||= proc{|event| bot.broadcast event.to_s}
177
+ @event_watcher_thread ||=
178
+ (connect
179
+ reminder = EventWatcher.new(:interval=>60, :callback=>callback)
180
+ Thread.new { reminder.start })
181
+ end
182
+
183
+ def reminder_db_path
184
+ Ircbot.root + "db" + "reminder-#{config.nick}.db"
185
+ end
186
+
187
+ def connect
188
+ @connect ||= Reminder.connect(reminder_db_path)
189
+ end
190
+ end
191
+
192
+
193
+ ######################################################################
194
+ ### Setup database
195
+
196
+ if $0 == __FILE__
197
+
198
+ def spec(src, buffer, &block)
199
+ buffer = "require '#{Pathname(src).expand_path}'\n" + buffer
200
+ tmp = Tempfile.new("dynamic-spec")
201
+ tmp.print(buffer)
202
+ tmp.close
203
+ block.call(tmp)
204
+ ensure
205
+ tmp.close(true)
206
+ end
207
+
208
+ spec($0, DATA.read{}) do |tmp|
209
+ system("spec -cfs #{tmp.path}")
210
+ end
211
+ end
212
+
213
+ __END__
214
+
215
+ require 'spec'
216
+ require 'ostruct'
217
+
218
+ module Spec
219
+ module Example
220
+ module Subject
221
+ module ExampleGroupMethods
222
+ def parse(text, &block)
223
+ describe "(#{text})" do
224
+ subject {
225
+ event = Reminder.parse(text)
226
+ hash = {
227
+ :st => (event.st.strftime("%Y-%m-%d %H:%M:%S") rescue nil),
228
+ :en => (event.en.strftime("%Y-%m-%d %H:%M:%S") rescue nil),
229
+ :title => event.title.to_s,
230
+ :desc => event.title.to_s,
231
+ :allday => event.allday,
232
+ }
233
+ OpenStruct.new(hash)
234
+ }
235
+ instance_eval(&block)
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
242
+
243
+ describe "Reminder#parse" do
244
+
245
+ parse '' do
246
+ its(:st) { should == nil }
247
+ end
248
+
249
+ parse '2010-01-04 CX' do
250
+ its(:st) { should == "2010-01-04 00:00:00" }
251
+ its(:en) { should == nil }
252
+ its(:title) { should == "CX" }
253
+ its(:allday) { should == true }
254
+ end
255
+
256
+ parse '2010-01-04 13:30 CX' do
257
+ its(:st) { should == "2010-01-04 13:30:00" }
258
+ its(:en) { should == nil }
259
+ its(:title) { should == "CX" }
260
+ its(:allday) { should == false }
261
+ end
262
+
263
+ parse '2010-01-04 13:30-14:55 CX' do
264
+ its(:st) { should == "2010-01-04 13:30:00" }
265
+ its(:en) { should == "2010-01-04 14:55:00" }
266
+ its(:title) { should == "CX" }
267
+ its(:allday) { should == false }
268
+ end
269
+
270
+ parse '2010-01-18 27:15-27:45 TX' do
271
+ its(:st) { should == "2010-01-19 03:15:00" }
272
+ its(:en) { should == "2010-01-19 03:45:00" }
273
+ its(:title) { should == "TX" }
274
+ its(:allday) { should == false }
275
+ end
276
+ end
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # tv.rb
4
+ # Author:
5
+ # Created: Sun Jan 10 18:44:35 2010
6
+
7
+ require 'rubygems'
8
+ require 'ircbot'
9
+ require '/git/plugins/ircbot-plugins/lib/tvguide'
10
+ require '/git/plugins/ircbot-plugins/lib/tv-cast'
11
+ require 'chawan'
12
+
13
+ class TVGuide::Program
14
+ def id
15
+ "#{st}:#{sn}"
16
+ end
17
+ end
18
+
19
+ class TvPlugin < Ircbot::Plugin
20
+ ######################################################################
21
+ ### Extensions to Plugin
22
+
23
+ class WeightedRandomizer
24
+ def initialize(hash)
25
+ @hash = construct(hash)
26
+ end
27
+
28
+ def construct(obj)
29
+ case obj
30
+ when Hash
31
+ return obj
32
+ when Array
33
+ hash = {}
34
+ obj.reverse.each_with_index do |val,i|
35
+ hash[val] = i+1
36
+ end
37
+ return hash
38
+ else
39
+ raise NotImplementedError, "expected Hash or Array, but got #{hash.class}: #{hash.inspect}"
40
+ end
41
+ end
42
+
43
+ def execute
44
+ maps = []
45
+ stop = 0
46
+ @hash.each_pair do |val, key|
47
+ start = stop
48
+ stop += key
49
+ maps << [start...stop, val]
50
+ end
51
+ hit = rand(stop)
52
+ maps.each do |(range, val)|
53
+ return val if range === hit
54
+ end
55
+ return @hash.keys.first
56
+ end
57
+ end
58
+
59
+ module ReplyApiFu
60
+ class Reply < RuntimeError
61
+ def initialize(text)
62
+ @text = text
63
+ end
64
+
65
+ def text
66
+ case @text
67
+ when Hash
68
+ WeightedRandomizer.new(@text).execute
69
+ else
70
+ @text.to_s
71
+ end
72
+ end
73
+ end
74
+
75
+ class Nop < RuntimeError; end
76
+
77
+ def nop
78
+ raise Nop
79
+ end
80
+ end
81
+
82
+ include ReplyApiFu
83
+
84
+ ######################################################################
85
+ ### TvPlugin
86
+
87
+ STATION_ABBRS = {
88
+ 'NHK' => 'NHK総合',
89
+ 'NHKG' => 'NHK総合',
90
+ 'NHKE' => 'NHK教育',
91
+ 'ETV' => 'NHK教育',
92
+ '日テレ' => '日本テレビ',
93
+ 'NTV' => '日本テレビ',
94
+ 'TBS' => 'TBSテレビ',
95
+ 'フジ' => 'フジテレビ',
96
+ 'CX' => 'フジテレビ',
97
+ 'テレ朝' => 'テレビ朝日',
98
+ 'EX' => 'テレビ朝日',
99
+ 'テレ東' => 'テレビ東京',
100
+ 'TX' => 'テレビ東京',
101
+ 'MX' => 'TOKYO MX',
102
+ 'BS' => 'NHK衛星第一',
103
+ 'BS1' => 'NHK衛星第一',
104
+ 'BS2' => 'NHK衛星第二',
105
+ }
106
+
107
+ class Conversation
108
+ attr_reader :nick
109
+ attr_reader :messages
110
+
111
+ Message = Struct.new(:time, :text)
112
+
113
+ delegate :[], :first, :last, :clear, :to=>"@messages"
114
+
115
+ def initialize(nick)
116
+ @nick = nick
117
+ @messages = []
118
+ end
119
+
120
+ def <<(text)
121
+ messages << Message.new(Time.now, text.to_s)
122
+ end
123
+
124
+ class One < Conversation
125
+ def <<(text)
126
+ clear
127
+ super
128
+ end
129
+ end
130
+ end
131
+
132
+ EagarProgram = Struct.new(:nick, :time, :program)
133
+ class EagarProgram
134
+ def push(program)
135
+ self.time = Time.now
136
+ self.program = program
137
+ end
138
+
139
+ def shift
140
+ self.time = nil
141
+ return program
142
+ end
143
+ end
144
+
145
+ def reply(text)
146
+ respond_to_eagar_program
147
+
148
+ names = extract_station_names(text)
149
+ case text
150
+ when /キャスト/
151
+ do_cast(names.first)
152
+ when /(だれ|誰).*(\?|?)/
153
+ do_who(names.first)
154
+ else
155
+ do_summary(names)
156
+ end
157
+
158
+ nop
159
+ rescue Nop
160
+ return nil
161
+ rescue Reply => reply
162
+ return reply.text
163
+ end
164
+
165
+ def do_summary(names)
166
+ reports = names.map{|name| summary(current[name])}.compact
167
+ raise Reply, reports.join("\n")
168
+ end
169
+
170
+ def do_who(name)
171
+ prog = current[name]
172
+ if prog.casts.size > 0
173
+ raise Reply, "#{name}: #{prog.casts.join(',')}"
174
+ else
175
+ eagar_program_for(nick).push prog
176
+ raise Reply, {
177
+ "知るかよ!" => 80,
178
+ "#{nick}必死w" => 10,
179
+ "EPGには記述されていません" => 1,
180
+ }
181
+ end
182
+ end
183
+
184
+ def do_cast(name)
185
+ p = current[name]
186
+ query = "%s %s" % [p.title, name]
187
+ casts = TvCast.lookup!(query)
188
+ if casts.size > 0
189
+ raise Reply, "#{name}: #{casts.join(',')}"
190
+ else
191
+ raise Reply, "見つかりませんでした"
192
+ end
193
+ end
194
+
195
+ private
196
+
197
+ def extract_station_names(text)
198
+ text = text.sub(/(誰|キャスト)/, ',\1')
199
+ nouns = Chawan.parse(text.gsub(/\s+/,',')).compact.noun.map(&:to_s)
200
+ names = nouns.map{|name| STATION_ABBRS[name] || name} & station_names
201
+ nop if names.empty?
202
+ return names
203
+ end
204
+
205
+ def eagar_program_for(nick)
206
+ @eagar_programs ||= {}
207
+ @eagar_programs[nick] ||= EagarProgram.new(nick)
208
+ end
209
+
210
+ def respond_to_eagar_program
211
+ eager = eagar_program_for(nick)
212
+ if eager.time and Time.now.to_i < eager.time.to_i + 10 and prog = eager.shift
213
+ query = "%s %s" % [prog.title, prog.station_name]
214
+ casts = TvCast.lookup!(query)
215
+ if casts.size > 0
216
+ random = WeightedRandomizer.new(casts)
217
+ name = random.execute
218
+ raise Reply, {
219
+ "#{name}と思われ" => 50,
220
+ "#{name}?" => 50,
221
+ "#{name}の予感!" => 50,
222
+ "#{casts[0,2].join('か')}のどっちか" => 30,
223
+ "ググった限り#{name}ぽい>#{prog.title}" => 20,
224
+ "#{name}に大決定や!(確度#{(100/casts.size).to_i}%)" => 10,
225
+ casts.inspect => 2,
226
+ }
227
+ end
228
+ raise Reply, text
229
+ end
230
+ end
231
+
232
+ def current
233
+ @current ||= TVGuide.current
234
+ end
235
+
236
+ def station_names
237
+ @station_names ||= current.station_names
238
+ end
239
+
240
+ def reporteds
241
+ @reporteds ||= {}
242
+ end
243
+
244
+ def summary(p)
245
+ nop if reporteds[p.id]
246
+ reporteds[p.id] = true
247
+ time1 = p.st.strftime("%H:%M") rescue ''
248
+ time2 = p.en.strftime("%H:%M") rescue ''
249
+ "#{time1}-#{time2} #{p.title} (#{p.sn})"
250
+ rescue
251
+ nop
252
+ end
253
+ end
254
+
255
+
256
+ if $0 == __FILE__
257
+ $KCODE = 'u'
258
+ require 'pp'
259
+
260
+ # pp TVGuide::TXML301PG.new.map
261
+ pp TvPlugin.new.reply('NHK総合')
262
+ end
@@ -33,6 +33,7 @@ describe Ircbot::Client do
33
33
  its(:real) { should == "sama~zu" }
34
34
  its(:host) { should == "localhost" }
35
35
  its(:port) { should == "6667" }
36
+ its(:unknown_key) { should == nil } # should not raise error
36
37
 
37
38
  describe "#channels" do
38
39
  it "should return the array when an array is given" do
@@ -36,6 +36,7 @@ describe Ircbot::Plugin do
36
36
  provide :plugin
37
37
  provide :plugin!
38
38
  provide :direct?
39
+ provide :nick
39
40
 
40
41
  ######################################################################
41
42
  ### Not connected
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ircbot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - maiha
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-10 00:00:00 +09:00
12
+ date: 2010-01-27 00:00:00 +09:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -52,8 +52,13 @@ files:
52
52
  - lib/ircbot/client/config/plugins.rb
53
53
  - lib/ircbot/client/config/generator.rb
54
54
  - lib/ircbot/client/encoding.rb
55
+ - lib/ircbot/client/core.rb
55
56
  - lib/ircbot/client/commands.rb
57
+ - lib/ircbot/client/standalone.rb
56
58
  - lib/ircbot/client/plugins.rb
59
+ - lib/ircbot/client/logger.rb
60
+ - lib/ircbot/client/eventable.rb
61
+ - lib/ircbot/client/timeout.rb
57
62
  - lib/ircbot/core_ext/extending.rb
58
63
  - lib/ircbot/core_ext/message.rb
59
64
  - lib/ircbot/core_ext/delegation.rb
@@ -69,11 +74,14 @@ files:
69
74
  - spec/spec_helper.rb
70
75
  - spec/framework_spec.rb
71
76
  - spec/plugins_spec.rb
77
+ - plugins/tv.rb
78
+ - plugins/reminder.rb
72
79
  - plugins/echo.rb
73
80
  - plugins/plugins.rb
74
81
  - plugins/which.rb
75
82
  - plugins/irc.rb
76
83
  - config/wota.yml
84
+ - config/airi.yml
77
85
  - config/sama-zu.yml
78
86
  - config/yml.erb
79
87
  has_rdoc: true