ircbot 0.0.2 → 0.1.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/README +43 -21
  2. data/Rakefile +9 -6
  3. data/bin/ircbot +3 -27
  4. data/lib/ircbot.rb +12 -15
  5. data/lib/ircbot/client.rb +32 -0
  6. data/lib/ircbot/client/commands.rb +34 -0
  7. data/lib/ircbot/client/config.rb +55 -0
  8. data/lib/ircbot/client/config/channels.rb +40 -0
  9. data/lib/ircbot/client/config/plugins.rb +16 -0
  10. data/lib/ircbot/client/encoding.rb +17 -0
  11. data/lib/ircbot/client/plugins.rb +64 -0
  12. data/lib/ircbot/core_ext/delegation.rb +135 -0
  13. data/lib/ircbot/core_ext/extending.rb +80 -0
  14. data/lib/ircbot/core_ext/message.rb +18 -0
  15. data/lib/ircbot/framework.rb +22 -40
  16. data/lib/ircbot/plugin.rb +63 -0
  17. data/lib/ircbot/plugins.rb +141 -0
  18. data/lib/ircbot/version.rb +4 -0
  19. data/plugins/echo.rb +14 -0
  20. data/plugins/irc.rb +19 -0
  21. data/plugins/plugins.rb +59 -0
  22. data/spec/config_spec.rb +83 -0
  23. data/spec/fixtures/sama-zu.yml +8 -0
  24. data/spec/framework_spec.rb +18 -0
  25. data/spec/its_helper.rb +15 -0
  26. data/spec/plugin_spec.rb +70 -0
  27. data/spec/plugins_spec.rb +30 -0
  28. data/spec/provide_helper.rb +36 -0
  29. data/spec/spec_helper.rb +16 -0
  30. metadata +40 -19
  31. data/lib/irc/agent.rb +0 -177
  32. data/lib/irc/client.rb +0 -476
  33. data/lib/irc/const.rb +0 -242
  34. data/lib/irc/irc.rb +0 -324
  35. data/lib/irc/localize.rb +0 -260
  36. data/lib/ircbot/agent_manager.rb +0 -89
  37. data/lib/ircbot/config_client.rb +0 -369
  38. data/lib/ircbot/core_ext/digest.rb +0 -14
  39. data/lib/ircbot/core_ext/irc.rb +0 -61
  40. data/lib/ircbot/core_ext/rand-polimorphism.rb +0 -23
  41. data/lib/ircbot/core_ext/writefile.rb +0 -45
  42. data/lib/ircbot/ordered_hash.rb +0 -91
  43. data/lib/ircbot/reply_client.rb +0 -328
data/README CHANGED
@@ -1,47 +1,69 @@
1
1
  ircbot
2
- ===
2
+ ======
3
3
 
4
- old fashioned irc bot
4
+ An irc bot framework that offers easy-to-use by plugins
5
5
 
6
6
 
7
7
  Config
8
8
  ======
9
9
 
10
- Edit "config/xxx.dat" as your environment.
10
+ Edit "config/xxx.yml" as your environment.
11
+
12
+ * host: irc server host
13
+ * port: irc server port
14
+ * nick: nick name for this bot
15
+ * user: user name for this bot
16
+ * real: real name for this bot
17
+ * help: help message for this bot
18
+ * channels: startup channel names
19
+ * plugins: plugin names
11
20
 
12
21
 
13
22
  Usage
14
23
  =====
15
24
 
16
- ircbot -f config/xxx.dat
25
+ ircbot -f config/xxx.yml
17
26
 
18
27
 
19
- Command
20
- =======
28
+ Plugin
29
+ ======
21
30
 
22
- * (nick|realname).COMMAND arg
23
- # same as `arg > (nick|realname).COMMAND'
24
- * (nick|realname).help
25
- * (nick|realname).restart(CPI_NAME = nil)
26
- * (nick|realname).register(CPI_NAME)
27
- * (nick|realname).remove(CPI_NAME)
31
+ In ircbot world, we define functions as plugin (Ircbot::Plugin).
28
32
 
33
+ Instance methods:
34
+ * reply : this method is called when privmsg event is fired,
35
+ and reply message(the returned string) to the channel.
36
+ args: [text, nick, message object]
29
37
 
30
- CPI
31
- ===
38
+ * help : used from "plugins" plugin
32
39
 
33
- In ircbot's world, we define modules as cpi.
40
+ Accessor methods:
34
41
 
42
+ * message : message object same as 3rd argument
43
+ * direct? : whether the message is directly toward to the bot or not
44
+ * config : hash of given config file
35
45
 
36
46
 
37
- Notice
38
- ======
47
+ Example
48
+ =======
39
49
 
40
- Main code and cpi modules should be euc-japan (KCODE=e).
50
+ When you want echo bot, define the function as plugin first.
41
51
 
52
+ plugins/echo.rb:
42
53
 
43
- Author
44
- ======
54
+ class EchoPlugin < Ircbot::Plugin
55
+ def reply(text)
56
+ text
57
+ end
58
+ end
59
+
60
+
61
+
62
+ Required
63
+ ========
64
+
65
+ * irc-net gem
45
66
 
46
- maiha@wota.jp
47
67
 
68
+ Authors: maiha@wota.jp
69
+ Links: http://github.com/maiha/ircbot
data/Rakefile CHANGED
@@ -7,9 +7,11 @@ require 'rake/gempackagetask'
7
7
  GEM_NAME = "ircbot"
8
8
  AUTHOR = "maiha"
9
9
  EMAIL = "maiha@wota.jp"
10
- HOMEPAGE = "http://github.com/maiha/ircbot"
11
- SUMMARY = "old fashioned irc bot"
12
- GEM_VERSION = "0.0.2"
10
+ SUMMARY = "easy irc bot framework"
11
+
12
+ require File.dirname(__FILE__) + '/lib/ircbot/version'
13
+ GEM_VERSION = Ircbot::VERSION
14
+ HOMEPAGE = Ircbot::HOMEPAGE
13
15
 
14
16
  spec = Gem::Specification.new do |s|
15
17
  s.rubyforge_project = 'asakusarb'
@@ -20,13 +22,14 @@ spec = Gem::Specification.new do |s|
20
22
  s.has_rdoc = true
21
23
  s.extra_rdoc_files = ["README", "MIT-LICENSE"]
22
24
  s.summary = SUMMARY
23
- s.description = s.summary
25
+ s.description = "An irc bot framework that offers easy-to-use by plugins"
24
26
  s.author = AUTHOR
25
27
  s.email = EMAIL
26
28
  s.homepage = HOMEPAGE
27
29
  s.require_path = 'lib'
28
- s.add_dependency('activesupport', '>= 2.0.0')
29
- s.files = %w(MIT-LICENSE README Rakefile) + Dir.glob("{lib,spec,app,public,stubs}/**/*")
30
+ s.add_dependency('extlib', '>= 0.9.14')
31
+ s.add_dependency('net-irc', '>= 0.0.9')
32
+ s.files = %w(MIT-LICENSE README Rakefile) + Dir.glob("{lib,spec,plugins}/**/*")
30
33
  end
31
34
 
32
35
  Rake::GemPackageTask.new(spec) do |pkg|
data/bin/ircbot CHANGED
@@ -1,21 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
- # clear ARGV for Irb
3
2
 
4
3
  require 'ircbot'
5
4
 
6
- $KCODE = 'e'
7
-
8
- # Sync
9
- STDOUT.sync = true
10
- STDERR.sync = true
11
-
12
- debug = nil
13
5
  config = nil
14
- cpi = Pathname(Dir.getwd) + "cpi"
6
+ plugin = Pathname(Dir.getwd) + "plugins"
15
7
 
16
8
  while (arg = ARGV.shift)
17
9
  case arg
18
- when /^-d$/ ; debug = true
19
10
  when /^-f$/ ; config = ARGV.shift
20
11
  when /^-c$/ ; cpi = ARGV.shift
21
12
  else
@@ -28,23 +19,8 @@ unless config
28
19
  raise "Specify your config file\nusage: #{$0} -f config/xxx.dat"
29
20
  end
30
21
 
31
- # set cpi directory
32
- Ircbot.push_path :cpi, cpi, "**/*.cpi"
33
-
34
-
35
- module Ircbot
36
- class Kicker < Ircbot::ReplyClient
37
- rescue_from(SocketError) {
38
- raise Ircbot::Recover, 30.minutes
39
- }
40
-
41
- rescue_from(Errno::EPIPE, Errno::ECONNRESET, Errno::ETIMEDOUT, IRC::AbnormalTerminated) {
42
- raise Ircbot::Recover, 300.minutes
43
- }
44
- end
45
- end
46
-
22
+ Ircbot.push_path(:plugin, plugin)
47
23
 
48
- irc = Ircbot::ReplyClient.read_config(config, debug)
24
+ irc = Ircbot::Client.from_file(config)
49
25
  irc.start
50
26
  puts "Bye"
@@ -1,8 +1,8 @@
1
-
2
1
  require 'nkf'
3
2
  require 'pathname'
4
3
  require 'rubygems'
5
- require 'active_support'
4
+ require 'extlib'
5
+ require 'extlib/dictionary'
6
6
 
7
7
  ######################################################################
8
8
  ### Load path
@@ -14,28 +14,25 @@ $LOAD_PATH.unshift __DIR__ unless
14
14
  $LOAD_PATH.include?(__DIR__) ||
15
15
  $LOAD_PATH.include?(File.expand_path(__DIR__))
16
16
 
17
- require 'ircbot/framework'
17
+ require "ircbot/framework"
18
+ require "ircbot/version"
18
19
 
20
+ Ircbot.push_path(:plugin, Ircbot.system_root + 'plugins')
21
+ Ircbot.toplevel_binding = binding
19
22
 
20
23
  ######################################################################
21
24
  ### IRC library
22
25
 
23
- require 'irc/irc'
24
- require 'irc/agent'
25
- require 'irc/client'
26
-
26
+ require "net/irc"
27
27
 
28
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'
29
+ ### Core ext
35
30
 
31
+ require "ircbot/core_ext/delegation" # from activesupport-2.3.5
32
+ require "ircbot/core_ext/extending" # from activesupport-2.3.5
33
+ require "ircbot/core_ext/message"
36
34
 
37
35
  ######################################################################
38
36
  ### Ircbot
39
37
 
40
- require 'ircbot/reply_client'
41
-
38
+ require "ircbot/client"
@@ -0,0 +1,32 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require "ircbot/plugin"
4
+ require "ircbot/plugins"
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
26
+
27
+ require "ircbot/client/encoding"
28
+ require "ircbot/client/commands"
29
+ require "ircbot/client/config"
30
+ require "ircbot/client/config/channels"
31
+ require "ircbot/client/config/plugins"
32
+ require "ircbot/client/plugins"
@@ -0,0 +1,34 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Ircbot
4
+ class Client
5
+ ######################################################################
6
+ ### Convinient access to commands
7
+
8
+ def post(command, *params)
9
+ if params[1]
10
+ params[1] = encode(params[1]).strip
11
+ if config.multiline
12
+ params[1].split(/\n/).compact.each do |text|
13
+ params[1] = text
14
+ super(command, *params)
15
+ end
16
+ return
17
+ end
18
+ end
19
+
20
+ super(command, *params)
21
+ end
22
+
23
+ def notice(channel, text)
24
+ text = encode(text)
25
+ post NOTICE, channel, text
26
+ end
27
+
28
+ def privmsg(channel, text)
29
+ text = encode(text)
30
+ post PRIVMSG, channel, text
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,55 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Ircbot
4
+ class Client
5
+ class Config
6
+ ######################################################################
7
+ ### Reader
8
+ def self.read(path)
9
+ path = Pathname(path)
10
+ ext = path.extname.delete(".")
11
+ ext = "yml" if ext.empty?
12
+
13
+ reader = "read_#{ext}"
14
+ if respond_to?(reader)
15
+ Mash.new(__send__(reader, path))
16
+ else
17
+ raise NotImplementedError, "Cannot read #{path}: Format(#{ext})is not supported"
18
+ end
19
+ end
20
+
21
+ def self.read_yml(path)
22
+ require 'yaml'
23
+ path = Pathname(path)
24
+ YAML.load(path.read{})
25
+ end
26
+
27
+ ######################################################################
28
+ ### Config
29
+ def initialize(obj)
30
+ @obj = obj
31
+ end
32
+
33
+ def [](key)
34
+ __send__(key)
35
+ end
36
+
37
+ private
38
+ def method_missing(*args)
39
+ @obj.__send__(*args)
40
+ end
41
+ end
42
+
43
+ def self.from_file(path)
44
+ new(Config.read(path))
45
+ end
46
+
47
+ def initialize(hash)
48
+ super(hash[:host], hash[:port], hash)
49
+ end
50
+
51
+ def config
52
+ @config ||= Config.new(opts)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,40 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Ircbot
4
+ class Client
5
+
6
+ ######################################################################
7
+ ### Config
8
+
9
+ class Config
10
+ def channels
11
+ case (val = super)
12
+ when Array
13
+ val
14
+ else
15
+ val.to_s.split
16
+ end
17
+ end
18
+ end
19
+
20
+ ######################################################################
21
+ ### Event
22
+
23
+ def on_rpl_welcome(m)
24
+ super
25
+
26
+ config.channels.each do |channel|
27
+ post JOIN, channel
28
+ end
29
+ end
30
+
31
+ ######################################################################
32
+ ### Command
33
+
34
+ def broadcast(text)
35
+ config.channels.each do |channel|
36
+ privmsg channel, text
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,16 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Ircbot
4
+ class Client
5
+ class Config
6
+ def plugins
7
+ case (val = super)
8
+ when Array
9
+ val
10
+ else
11
+ val.to_s.split
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Ircbot
4
+ class Client
5
+ ######################################################################
6
+ ### Character conversions
7
+
8
+ def decode(text)
9
+ NKF.nkf('-w', text.to_s)
10
+ end
11
+
12
+ def encode(text)
13
+ NKF.nkf('-j', text.to_s)
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,64 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Ircbot
4
+ class Client
5
+ ######################################################################
6
+ ### Accessors
7
+
8
+ def plugins
9
+ @plugins ||= Plugins.new(self, config.plugins)
10
+ end
11
+
12
+ delegate :plugin!, :plugin, :to=>"plugins"
13
+
14
+
15
+ ######################################################################
16
+ ### Events
17
+
18
+ def on_privmsg(m)
19
+ super
20
+
21
+ text = decode(m.params[1].to_s)
22
+ args = [text, m.prefix.nick, m]
23
+
24
+ plugins_call_replies(args, m)
25
+ end
26
+
27
+ private
28
+ def plugins_call_replies(args, m)
29
+ text = catch(:halt) do
30
+ plugins.active.each do |plugin|
31
+ plugins_call_action(:reply, plugin, args, m, :reply=>true)
32
+ end
33
+ return true
34
+ end
35
+ m.reply(self, text) if text
36
+ end
37
+
38
+ def plugins_call_logs(args)
39
+ raise NotImplementedError, "obsoleted"
40
+ plugins.active.each do |plugin|
41
+ plugins_call_action(:log, plugin, args)
42
+ end
43
+ end
44
+
45
+ def plugins_call_action(type, plugin, args, m, opts = {})
46
+ plugin.message = m
47
+ arity = plugin.method(type).arity rescue return
48
+ reply = plugin.__send__(type, *args[0,arity])
49
+ m.reply(self, reply) if opts[:reply]
50
+ rescue Exception => e
51
+ type = (e.class == RuntimeError) ? 'Error' : "#{e.class}"
52
+ m.reply(self, "#{type}: #{e.message}")
53
+ plugins_rescue_action type, plugin, e
54
+ end
55
+
56
+ def plugins_rescue_action(type, plugin, e)
57
+ p [e.class, e.message, type, plugin]
58
+ at = e.backtraces rescue '(no backtraces)'
59
+ puts at
60
+ end
61
+
62
+ end
63
+ end
64
+