ircbot 0.1.5 → 0.2.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.
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