ircbot 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +71 -0
  4. data/README +72 -3
  5. data/bin/ircbot +3 -0
  6. data/config/samples/postgres.yml +19 -0
  7. data/config/{sama-zu.yml → samples/sama-zu.yml} +1 -1
  8. data/config/{yml.erb → samples/yml.erb} +0 -0
  9. data/ircbot.gemspec +13 -0
  10. data/lib/ircbot.rb +3 -1
  11. data/lib/ircbot/client.rb +6 -0
  12. data/lib/ircbot/client/config.rb +9 -0
  13. data/lib/ircbot/client/plugins.rb +14 -1
  14. data/lib/ircbot/core_ext/message.rb +4 -1
  15. data/lib/ircbot/plugin.rb +17 -0
  16. data/lib/ircbot/plugins.rb +68 -13
  17. data/lib/ircbot/utils/html_parser.rb +26 -0
  18. data/lib/ircbot/utils/watcher.rb +36 -0
  19. data/lib/ircbot/version.rb +1 -1
  20. data/old/plugins/summary.cpi +267 -0
  21. data/plugins/plugins.rb +1 -1
  22. data/plugins/reminder.rb +79 -175
  23. data/plugins/summary/ch2.rb +272 -0
  24. data/plugins/summary/engines.rb +30 -0
  25. data/plugins/summary/engines/base.rb +105 -0
  26. data/plugins/summary/engines/ch2.rb +14 -0
  27. data/plugins/summary/engines/https.rb +6 -0
  28. data/plugins/summary/engines/none.rb +10 -0
  29. data/plugins/summary/engines/twitter.rb +16 -0
  30. data/plugins/summary/spec/ch2_spec.rb +64 -0
  31. data/plugins/summary/spec/spec_helper.rb +19 -0
  32. data/plugins/summary/spec/summarizers_none_spec.rb +15 -0
  33. data/plugins/summary/spec/summarizers_spec.rb +23 -0
  34. data/plugins/summary/summary.rb +58 -0
  35. data/plugins/watchdog/db.rb +80 -0
  36. data/plugins/watchdog/exceptions.rb +4 -0
  37. data/plugins/watchdog/updater.rb +21 -0
  38. data/plugins/watchdog/watchdog.rb +82 -0
  39. data/spec/plugin_spec.rb +11 -0
  40. data/spec/plugins_spec.rb +35 -1
  41. data/spec/utils/html_parser_spec.rb +30 -0
  42. data/spec/utils/spec_helper.rb +1 -0
  43. metadata +190 -13
@@ -0,0 +1,5 @@
1
+ !.gitignore
2
+ *.gem
3
+ *~
4
+ .bundle
5
+ /vendor
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,71 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ircbot (0.2.0)
5
+ chawan
6
+ data_objects
7
+ dm-core
8
+ dm-migrations
9
+ dm-postgres-adapter
10
+ dm-sqlite-adapter
11
+ dm-timestamps
12
+ do_sqlite3
13
+ dsl_accessor (>= 0.4.0)
14
+ extlib (>= 0.9.14)
15
+ net-irc (>= 0.0.9)
16
+ night-time
17
+ pg
18
+ sqlite3
19
+
20
+ GEM
21
+ remote: https://rubygems.org/
22
+ specs:
23
+ addressable (2.2.8)
24
+ blankslate (2.1.2.4)
25
+ chawan (0.0.4)
26
+ data_objects (0.10.8)
27
+ addressable (~> 2.1)
28
+ diff-lcs (1.1.3)
29
+ dm-core (1.2.0)
30
+ addressable (~> 2.2.6)
31
+ dm-do-adapter (1.2.0)
32
+ data_objects (~> 0.10.6)
33
+ dm-core (~> 1.2.0)
34
+ dm-migrations (1.2.0)
35
+ dm-core (~> 1.2.0)
36
+ dm-postgres-adapter (1.2.0)
37
+ dm-do-adapter (~> 1.2.0)
38
+ do_postgres (~> 0.10.6)
39
+ dm-sqlite-adapter (1.2.0)
40
+ dm-do-adapter (~> 1.2.0)
41
+ do_sqlite3 (~> 0.10.6)
42
+ dm-timestamps (1.2.0)
43
+ dm-core (~> 1.2.0)
44
+ do_postgres (0.10.8)
45
+ data_objects (= 0.10.8)
46
+ do_sqlite3 (0.10.8)
47
+ data_objects (= 0.10.8)
48
+ dsl_accessor (0.4.1)
49
+ blankslate (>= 2.1.2)
50
+ optionize (>= 0.1.0)
51
+ extlib (0.9.15)
52
+ net-irc (0.0.9)
53
+ night-time (1.0.1)
54
+ optionize (0.1.1)
55
+ pg (0.14.0)
56
+ rspec (2.10.0)
57
+ rspec-core (~> 2.10.0)
58
+ rspec-expectations (~> 2.10.0)
59
+ rspec-mocks (~> 2.10.0)
60
+ rspec-core (2.10.1)
61
+ rspec-expectations (2.10.0)
62
+ diff-lcs (~> 1.1.3)
63
+ rspec-mocks (2.10.1)
64
+ sqlite3 (1.3.6)
65
+
66
+ PLATFORMS
67
+ ruby
68
+
69
+ DEPENDENCIES
70
+ ircbot!
71
+ rspec (>= 2.9.0)
data/README CHANGED
@@ -23,7 +23,11 @@ Config
23
23
  Usage
24
24
  =====
25
25
 
26
- ircbot -f config/xxx.yml
26
+ (installed gem)
27
+ % ircbot -f config/xxx.yml
28
+
29
+ (in source dir)
30
+ % ruby -Ku -rubygems -Ilib bin/ircbot -f config/xxx.yml
27
31
 
28
32
 
29
33
  Plugin
@@ -34,9 +38,10 @@ Plugin
34
38
  Instance methods:
35
39
  * reply : this method is called when privmsg event is fired,
36
40
  and reply message(the returned string) to the channel.
37
- args: [text, nick, message object]
38
-
41
+ args: [text, nick, message object]
42
+ * setup : used for initializer
39
43
  * help : used from "plugins" plugin
44
+ * [ANY] : any methods can be invoked by "<NICK>.<PLUGN_NAME>.<METHOD_NAME>"
40
45
 
41
46
  Accessor methods:
42
47
 
@@ -45,6 +50,14 @@ Plugin
45
50
  * config : hash of given config file
46
51
 
47
52
 
53
+ Setup
54
+ =====
55
+
56
+ Install gems via bundler like this.
57
+
58
+ % bundle install --path vendor
59
+
60
+
48
61
  Example
49
62
  =======
50
63
 
@@ -83,6 +96,62 @@ Advanced
83
96
  BPlugin#reply will be never invoked.
84
97
 
85
98
 
99
+ Daemons
100
+ =======
101
+
102
+ Plugin initializer automatically calls "setup" method.
103
+ So you can start daemon in it.
104
+
105
+ plugins/time_signal.rb:
106
+
107
+ class TimeSignalPlugin < Ircbot::Plugin
108
+ def setup
109
+ @daemon = Thread.new {
110
+ sleep 60
111
+ self.bot.broadcast Time.now
112
+ }
113
+ end
114
+
115
+ def stop
116
+ @daemon.kill
117
+ return "Stopped time signal"
118
+ end
119
+ end
120
+
121
+ When the bot name is 'airi', this will act as following.
122
+
123
+ 05:11 <airi> Mon Apr 09 05:11:22 +0900 2012
124
+ 05:12 <airi> Mon Apr 09 05:12:22 +0900 2012
125
+ 05:12 <maiha> airi.time_signal.stop
126
+ 05:12 <airi> Stopped time signal
127
+
128
+
129
+ PluginAttributes
130
+ ================
131
+
132
+ When you want to give options or attributes to plugins,
133
+ write it into config.plugins as Array(Hash)
134
+
135
+ config/xxx.yml
136
+ plugins:
137
+ - irc
138
+ -
139
+ name: watchdog
140
+ db: "postgres://localhost/ircbot"
141
+
142
+ This will generate two plugins
143
+ 1. Ircbot::Plugin(name:irc)
144
+ 2. Ircbot::Plugin(name:watchdog, attrs=>{name=>watchdog,db=>postgres...})
145
+
146
+ And the given options can be found in plugin by [] method.
147
+
148
+ class Watchdog < Ircbot::Plugin
149
+ def setup
150
+ db = self[:db]
151
+ puts "connecting to #{db}"
152
+ ...
153
+
154
+
86
155
  Required
87
156
  ========
88
157
 
data/bin/ircbot CHANGED
@@ -1,5 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'rubygems'
4
+ require "bundler/setup"
5
+
3
6
  require 'ircbot'
4
7
 
5
8
  config = nil
@@ -0,0 +1,19 @@
1
+ host: localhost
2
+ port: 6667
3
+ nick: sama-zu
4
+ user: samazu
5
+ real: sama~zu
6
+ channels: "#test"
7
+ plugins:
8
+ - irc
9
+ - plugins
10
+ -
11
+ name: watchdog
12
+ db: "postgres://localhost/ircbot"
13
+ -
14
+ name: reminder
15
+ db: "postgres://localhost/ircbot"
16
+ - summary
17
+ reminder
18
+ multiline: true
19
+ timeout: 500
@@ -4,6 +4,6 @@ nick: sama-zu
4
4
  user: samazu
5
5
  real: sama~zu
6
6
  channels: "#test"
7
- plugins: irc plugins
7
+ plugins: irc plugins watchdog reminder summary
8
8
  multiline: true
9
9
  timeout: 500
File without changes
@@ -22,6 +22,19 @@ Gem::Specification.new do |s|
22
22
  s.add_dependency "dsl_accessor", ">= 0.4.0"
23
23
  s.add_dependency "extlib", ">= 0.9.14"
24
24
  s.add_dependency "net-irc", ">= 0.0.9"
25
+ s.add_dependency 'chawan'
26
+ s.add_dependency 'night-time'
27
+
28
+ s.add_dependency 'pg'
29
+ s.add_dependency 'sqlite3'
30
+
31
+ s.add_dependency 'dm-core'
32
+ s.add_dependency 'dm-migrations'
33
+ s.add_dependency 'dm-timestamps'
34
+ s.add_dependency 'do_sqlite3'
35
+ s.add_dependency 'data_objects'
36
+ s.add_dependency 'dm-sqlite-adapter'
37
+ s.add_dependency 'dm-postgres-adapter'
25
38
 
26
39
  s.add_development_dependency "rspec", ">= 2.9.0"
27
40
  end
@@ -18,8 +18,10 @@ $LOAD_PATH.unshift __DIR__ unless
18
18
  require "ircbot/framework"
19
19
  require "ircbot/version"
20
20
 
21
- Ircbot.push_path(:plugin, Ircbot.system_root + 'plugins')
22
21
  Ircbot.push_path(:config, Ircbot.system_root + 'config')
22
+ Ircbot.push_path(:plugin, Ircbot.system_root + 'plugins')
23
+ Ircbot.push_path(:plugin, Ircbot.root + 'plugins')
24
+ $:.unshift((Ircbot.root + 'plugins').to_s)
23
25
  Ircbot.toplevel_binding = binding
24
26
 
25
27
  ######################################################################
@@ -17,3 +17,9 @@ require "ircbot/client/config/channels"
17
17
  require "ircbot/client/config/plugins"
18
18
  require "ircbot/client/timeout"
19
19
  require "ircbot/client/plugins"
20
+
21
+ ######################################################################
22
+ ### Irctob::Utils
23
+
24
+ require "ircbot/utils/watcher"
25
+ require "ircbot/utils/html_parser"
@@ -53,6 +53,15 @@ module Ircbot
53
53
  @config = Config.new(hash)
54
54
  end
55
55
 
56
+ def trim(text, size = 120)
57
+ a = text.to_s.split(//)
58
+ if a.size > size
59
+ return a[0..size].join + "..."
60
+ else
61
+ return text.to_s
62
+ end
63
+ end
64
+
56
65
  attr_reader :config
57
66
  end
58
67
  end
@@ -19,10 +19,23 @@ module Ircbot
19
19
  text = decode(m.params[1].to_s)
20
20
  args = [text, m.prefix.nick, m]
21
21
 
22
+ plugins_call_command(args, m)
22
23
  plugins_call_replies(args, m)
23
24
  }
24
25
 
25
26
  private
27
+ def plugins_call_command(args, m)
28
+ case m.message.to_s # text
29
+ when /^#{config.nick}\.(#{plugins.active_names.join('|')})\./
30
+ plugin = plugins[$1]
31
+ command, arg = $'.to_s.split(/\b/, 2)
32
+ args = [arg.to_s.strip]
33
+ if plugin.class.command?(command)
34
+ plugins_call_action(command, plugin, args, m, :reply=>true)
35
+ end
36
+ end
37
+ end
38
+
26
39
  def plugins_call_replies(args, m)
27
40
  text = catch(:done) do
28
41
  plugins.active.each do |plugin|
@@ -54,7 +67,7 @@ module Ircbot
54
67
 
55
68
  def plugins_rescue_action(type, plugin, e)
56
69
  p [e.class, e.message, type, plugin]
57
- at = e.backtraces rescue '(no backtraces)'
70
+ at = e.backtrace rescue '(no backtraces)'
58
71
  puts at
59
72
  end
60
73
 
@@ -12,7 +12,10 @@ class Net::IRC::Message
12
12
 
13
13
  def reply(client, text)
14
14
  to = (client.config.nick == params[0]) ? prefix.nick : params[0]
15
- client.privmsg to, text.to_s unless text.to_s.empty?
15
+ unless text.to_s.empty?
16
+ text = client.trim(text)
17
+ client.privmsg to, text
18
+ end
16
19
  end
17
20
  end
18
21
 
@@ -19,10 +19,19 @@ module Ircbot
19
19
  attr_accessor :running
20
20
  attr_accessor :plugin_name
21
21
 
22
+ def self.command?(name)
23
+ @commands ||= (public_instance_methods - Plugin.public_instance_methods).inject({"setup"=>1, "help"=>1}) {|h,k| h[k.to_s] = 1; h }
24
+ !! @commands[name.to_s]
25
+ end
26
+
22
27
  def initialize(plugins = nil)
23
28
  @plugins = plugins || Plugins.new
24
29
  @message = InitialMessage.new(self.class.name)
25
30
  @running = false
31
+ @attrs = Mash.new
32
+ end
33
+
34
+ def setup
26
35
  end
27
36
 
28
37
  ######################################################################
@@ -31,6 +40,10 @@ module Ircbot
31
40
  delegate :plugin!, :client, :bot, :config, :to=>"@plugins"
32
41
  delegate :debug, :to=>"@plugins"
33
42
 
43
+ def [](key)
44
+ @attrs[key]
45
+ end
46
+
34
47
  def plugin_name
35
48
  @plugin_name ||= Extlib::Inflection.foreign_key(self.class.name).sub(/(_plugin)?_id$/,'')
36
49
  end
@@ -43,6 +56,10 @@ module Ircbot
43
56
  "%s%s" % [running ? '*' : '', plugin_name]
44
57
  end
45
58
 
59
+ def attrs=(hash)
60
+ @attrs = Mash.new(hash)
61
+ end
62
+
46
63
  ######################################################################
47
64
  ### Operations
48
65
 
@@ -11,8 +11,9 @@ module Ircbot
11
11
  def initialize(client = nil, plugins = nil)
12
12
  @client = client || Client::Standalone.new
13
13
  @plugins = Dictionary.new
14
+ @cache = {}
14
15
 
15
- load_plugins Array(plugins)
16
+ load Array(plugins)
16
17
  end
17
18
 
18
19
  ######################################################################
@@ -24,9 +25,35 @@ module Ircbot
24
25
  plugins.values.__send__(:each, &block)
25
26
  end
26
27
 
28
+ ######################################################################
29
+ ### Caching
30
+
31
+ def clear_cache
32
+ @cache = {}
33
+ end
34
+
35
+ def cache(key = nil, &block)
36
+ key = key.to_s
37
+ return @cache ||= {} if key.empty?
38
+
39
+ if @cache.has_key?(key)
40
+ @cache[key]
41
+ else
42
+ @cache[key] = block.call
43
+ end
44
+ end
45
+
27
46
  ######################################################################
28
47
  ### Accessors
29
48
 
49
+ def active_names
50
+ cache(:active_names) { active.map(&:plugin_name) }
51
+ end
52
+
53
+ def active
54
+ cache(:active) { select(&:running) }
55
+ end
56
+
30
57
  def plugin!(plugin)
31
58
  case plugin
32
59
  when String, Symbol
@@ -52,22 +79,29 @@ module Ircbot
52
79
 
53
80
  def start(plugin)
54
81
  plugin!(plugin).running = true
82
+ changed!
55
83
  end
56
84
 
57
85
  def stop(plugin)
58
86
  plugin!(plugin).running = false
87
+ changed!
59
88
  end
60
89
 
61
90
  def delete(plugin)
62
91
  name = plugin.is_a?(Plugin) ? plugin.name : plugin.to_s
63
92
  plugin!(name)
64
93
  @plugins.delete(name)
94
+ changed!
95
+ end
96
+
97
+ def changed!
98
+ clear_cache
65
99
  end
66
100
 
67
101
  ######################################################################
68
102
  ### IO
69
103
 
70
- def load_plugins(plugin)
104
+ def load(plugin)
71
105
  case plugin
72
106
  when Array
73
107
  plugin.each do |name|
@@ -77,6 +111,7 @@ module Ircbot
77
111
  plugin.plugins = self
78
112
  plugins[plugin.plugin_name] = plugin
79
113
  plugin.running = true
114
+ plugin.setup
80
115
  when Class
81
116
  if plugin.ancestors.include?(Ircbot::Plugin)
82
117
  self << plugin.new(self)
@@ -84,44 +119,64 @@ module Ircbot
84
119
  raise ArgumentError, "#{plugin} is not Ircbot::Plugin"
85
120
  end
86
121
  when String, Symbol
122
+ self << {"name" => plugin.to_s}
123
+ when Hash
87
124
  begin
88
- name = plugin.to_s
89
- self << load(name)
125
+ attrs = Mash.new(plugin)
126
+ name = attrs["name"]
127
+ if name.blank?
128
+ raise "no names: #{attrs.inspect}"
129
+ end
130
+ plugin = eval_plugin(name)
131
+ plugin.attrs = attrs
132
+ self << plugin
90
133
  rescue Exception => e
91
- broadcast "Plugin error(#{name}): #{e}[#{e.class}]"
134
+ invalid_plugin_found "Plugin error(#{name}): #{e}[#{e.class}]"
92
135
  end
93
136
  else
94
137
  raise NotImplementedError, "#<< for #{plugin.class}"
95
138
  end
139
+ changed!
96
140
  return self
97
141
  end
98
- alias :<< :load_plugins
142
+ alias :<< :load
143
+
144
+ def eval_plugin(name)
145
+ class_name = plugin_class_name(name)
146
+
147
+ if Object.const_defined?(class_name)
148
+ Object.send(:remove_const, class_name)
149
+ end
99
150
 
100
- def load(name)
101
151
  path = Ircbot.glob_for(:plugin, name).first or
102
152
  raise PluginNotFound, name.to_s
103
153
 
104
154
  script = path.read{}
105
- eval(script, Ircbot.toplevel_binding)
155
+ Dir.chdir(path.parent) do
156
+ eval(script, Ircbot.toplevel_binding)
157
+ end
106
158
 
107
- class_name = Extlib::Inflection.camelize(name) + "Plugin"
108
159
  return Object.const_get(class_name).new
109
160
 
110
161
  rescue NameError => e
111
162
  raise LoadError, "Expected #{path} to define #{class_name} (#{e})"
112
163
  end
113
164
 
114
- def active
115
- select(&:running)
116
- end
117
-
118
165
  def inspect
119
166
  plugins.values.inspect
120
167
  end
121
168
 
169
+ def invalid_plugin_found(text)
170
+ broadcast text
171
+ end
172
+
122
173
  private
123
174
  def broadcast(text)
124
175
  p text
125
176
  end
177
+
178
+ def plugin_class_name(name)
179
+ Extlib::Inflection.camelize(name) + "Plugin"
180
+ end
126
181
  end
127
182
  end