cinch 0.3.5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +192 -0
  3. data/Rakefile +53 -43
  4. data/examples/basic/autovoice.rb +32 -0
  5. data/examples/basic/google.rb +35 -0
  6. data/examples/basic/hello.rb +15 -0
  7. data/examples/basic/join_part.rb +38 -0
  8. data/examples/basic/memo.rb +39 -0
  9. data/examples/basic/msg.rb +16 -0
  10. data/examples/basic/seen.rb +36 -0
  11. data/examples/basic/urban_dict.rb +35 -0
  12. data/examples/basic/url_shorten.rb +35 -0
  13. data/examples/plugins/autovoice.rb +40 -0
  14. data/examples/plugins/custom_prefix.rb +23 -0
  15. data/examples/plugins/google.rb +37 -0
  16. data/examples/plugins/hello.rb +22 -0
  17. data/examples/plugins/join_part.rb +42 -0
  18. data/examples/plugins/memo.rb +50 -0
  19. data/examples/plugins/msg.rb +22 -0
  20. data/examples/plugins/multiple_matches.rb +41 -0
  21. data/examples/plugins/seen.rb +45 -0
  22. data/examples/plugins/urban_dict.rb +30 -0
  23. data/examples/plugins/url_shorten.rb +32 -0
  24. data/lib/cinch.rb +7 -20
  25. data/lib/cinch/ban.rb +41 -0
  26. data/lib/cinch/bot.rb +479 -0
  27. data/lib/cinch/callback.rb +11 -0
  28. data/lib/cinch/channel.rb +419 -0
  29. data/lib/cinch/constants.rb +369 -0
  30. data/lib/cinch/exceptions.rb +25 -0
  31. data/lib/cinch/helpers.rb +21 -0
  32. data/lib/cinch/irc.rb +344 -38
  33. data/lib/cinch/isupport.rb +96 -0
  34. data/lib/cinch/logger/formatted_logger.rb +80 -0
  35. data/lib/cinch/logger/logger.rb +44 -0
  36. data/lib/cinch/logger/null_logger.rb +18 -0
  37. data/lib/cinch/mask.rb +46 -0
  38. data/lib/cinch/message.rb +183 -0
  39. data/lib/cinch/message_queue.rb +62 -0
  40. data/lib/cinch/plugin.rb +205 -0
  41. data/lib/cinch/rubyext/infinity.rb +1 -0
  42. data/lib/cinch/rubyext/module.rb +18 -0
  43. data/lib/cinch/rubyext/queue.rb +19 -0
  44. data/lib/cinch/rubyext/string.rb +24 -0
  45. data/lib/cinch/syncable.rb +55 -0
  46. data/lib/cinch/user.rb +325 -0
  47. data/spec/bot_spec.rb +5 -0
  48. data/spec/channel_spec.rb +5 -0
  49. data/spec/cinch_spec.rb +5 -0
  50. data/spec/irc_spec.rb +5 -0
  51. data/spec/message_spec.rb +5 -0
  52. data/spec/plugin_spec.rb +5 -0
  53. data/spec/{helper.rb → spec_helper.rb} +0 -0
  54. data/spec/user_spec.rb +5 -0
  55. metadata +69 -51
  56. data/README.rdoc +0 -195
  57. data/examples/autovoice.rb +0 -32
  58. data/examples/custom_patterns.rb +0 -19
  59. data/examples/custom_prefix.rb +0 -25
  60. data/examples/google.rb +0 -31
  61. data/examples/hello.rb +0 -13
  62. data/examples/join_part.rb +0 -26
  63. data/examples/memo.rb +0 -40
  64. data/examples/msg.rb +0 -14
  65. data/examples/named-param-types.rb +0 -19
  66. data/examples/seen.rb +0 -41
  67. data/examples/urban_dict.rb +0 -31
  68. data/examples/url_shorten.rb +0 -34
  69. data/lib/cinch/base.rb +0 -368
  70. data/lib/cinch/irc/message.rb +0 -135
  71. data/lib/cinch/irc/parser.rb +0 -141
  72. data/lib/cinch/irc/socket.rb +0 -329
  73. data/lib/cinch/names.rb +0 -54
  74. data/lib/cinch/rules.rb +0 -171
  75. data/spec/base_spec.rb +0 -94
  76. data/spec/irc/helper.rb +0 -8
  77. data/spec/irc/message_spec.rb +0 -61
  78. data/spec/irc/parser_spec.rb +0 -103
  79. data/spec/irc/socket_spec.rb +0 -90
  80. data/spec/names_spec.rb +0 -393
  81. data/spec/options_spec.rb +0 -45
  82. data/spec/rules_spec.rb +0 -109
@@ -0,0 +1,80 @@
1
+ require "cinch/logger/logger"
2
+ module Cinch
3
+ module Logger
4
+ # A formatted logger that will colorize individual parts of IRC
5
+ # messages.
6
+ class FormattedLogger < Cinch::Logger::Logger
7
+ COLORS = {
8
+ :reset => "\e[0m",
9
+ :bold => "\e[1m",
10
+ :red => "\e[31m",
11
+ :green => "\e[32m",
12
+ :yellow => "\e[33m",
13
+ :blue => "\e[34m",
14
+ }
15
+
16
+ # @param [IO] output An IO to log to.
17
+ def initialize(output = STDERR)
18
+ @output = output
19
+ @mutex = Mutex.new
20
+ end
21
+
22
+ # (see Logger::Logger#debug)
23
+ def debug(messages)
24
+ log(messages, :debug)
25
+ end
26
+
27
+ # (see Logger::Logger#log)
28
+ def log(messages, kind = :generic)
29
+ @mutex.synchronize do
30
+ messages = [messages].flatten.map {|s| s.chomp}
31
+ # message = message.to_s.chomp # don't want to tinker with the original string
32
+
33
+ messages.each do |message|
34
+ if kind == :debug
35
+ prefix = colorize("!! ", :yellow)
36
+ message = prefix + message
37
+ else
38
+ pre, msg = message.split(" :", 2)
39
+ pre_parts = pre.split(" ")
40
+
41
+ if kind == :incoming
42
+ prefix = colorize(">> ", :green)
43
+
44
+ if pre_parts.size == 1
45
+ pre_parts[0] = colorize(pre_parts[0], :bold)
46
+ else
47
+ pre_parts[0] = colorize(pre_parts[0], :blue)
48
+ pre_parts[1] = colorize(pre_parts[1], :bold)
49
+ end
50
+
51
+ elsif kind == :outgoing
52
+ prefix = colorize("<< ", :red)
53
+ pre_parts[0] = colorize(pre_parts[0], :bold)
54
+ end
55
+
56
+ message = prefix + pre_parts.join(" ")
57
+ message << colorize(" :#{msg}", :yellow) if msg
58
+ end
59
+ @output.puts message.encode
60
+ end
61
+ end
62
+ end
63
+
64
+ # @api private
65
+ # @param [String] text text to colorize
66
+ # @param [Array<Symbol>] codes array of colors to apply
67
+ # @return [String] colorized string
68
+ def colorize(text, *codes)
69
+ COLORS.values_at(*codes).join + text + COLORS[:reset]
70
+ end
71
+
72
+ # (see Logger::Logger#log_exception)
73
+ def log_exception(e)
74
+ lines = ["#{e.backtrace.first}: #{e.message} (#{e.class})"]
75
+ lines.concat e.backtrace[1..-1].map {|s| "\t" + s}
76
+ debug(lines)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,44 @@
1
+ module Cinch
2
+ module Logger
3
+ # This is an abstract class describing the logger interface. All
4
+ # loggers should inherit from this class and provide all necessary
5
+ # methods.
6
+ #
7
+ # Note: You cannot initialize this class directly.
8
+ #
9
+ # @abstract
10
+ class Logger
11
+ def initialize(output)
12
+ raise
13
+ end
14
+
15
+ # This method can be used by plugins to log custom messages.
16
+ #
17
+ # @param [String] message The message to log
18
+ # @return [void]
19
+ def debug(message)
20
+ raise
21
+ end
22
+
23
+ # This method is used by {#debug} and {#log_exception} to log
24
+ # messages, and also by the IRC parser to log incoming and
25
+ # outgoing messages. You should not have to call this.
26
+ #
27
+ # @param [String] message The message to log
28
+ # @param [Symbol<:debug, :generic, :incoming, :outgoing>] kind
29
+ # The kind of message to log
30
+ # @return [void]
31
+ def log(message, kind = :generic)
32
+ raise
33
+ end
34
+
35
+ # This method is used for logging messages.
36
+ #
37
+ # @param [Exception] e The exception to log
38
+ # @return [void]
39
+ def log_exception(e)
40
+ raise
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,18 @@
1
+ require "cinch/logger/logger"
2
+ module Cinch
3
+ module Logger
4
+ class NullLogger < Cinch::Logger::Logger
5
+ def initialize(output = nil)
6
+ end
7
+
8
+ def debug(message)
9
+ end
10
+
11
+ def log(message, kind = :generic)
12
+ end
13
+
14
+ def log_exception(e)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,46 @@
1
+ module Cinch
2
+ class Mask
3
+ # @return [String]
4
+ attr_reader :nick
5
+ # @return [String]
6
+ attr_reader :user
7
+ # @return [String]
8
+ attr_reader :host
9
+ # @return [String]
10
+ attr_reader :mask
11
+ def initialize(mask)
12
+ @mask = mask
13
+ @nick, @user, @host = mask.match(/(.+)!(.+)@(.+)/)[1..-1]
14
+ @regexp = Regexp.new(Regexp.escape(mask).gsub("\\*", ".*"))
15
+ end
16
+
17
+ # @return [Boolean]
18
+ def match(user)
19
+ mask = "%s!%s@%s" % [nick, user, host]
20
+ return mask =~ @regexp
21
+
22
+ # TODO support CIDR (freenode)
23
+ end
24
+ alias_method :=~, :match
25
+
26
+ # @return [String]
27
+ def to_s
28
+ @mask.dup
29
+ end
30
+
31
+ # @param [Ban, Mask, User, String]
32
+ # @return [Mask]
33
+ def self.from(target)
34
+ case target
35
+ when User, Ban
36
+ target.mask
37
+ when String
38
+ Mask.new(target)
39
+ when Mask
40
+ target
41
+ else
42
+ raise ArgumentError
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,183 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Cinch
3
+ class Message
4
+ # @return [String]
5
+ attr_accessor :raw
6
+ # @return [String]
7
+ attr_accessor :prefix
8
+ # @return [String]
9
+ attr_accessor :command
10
+ # @return [Array<String>]
11
+ attr_accessor :params
12
+ attr_reader :events
13
+
14
+ def initialize(msg, bot)
15
+ @raw = msg
16
+ @bot = bot
17
+ @matches = {:ctcp => {}, :other => {}}
18
+ @events = []
19
+ parse if msg
20
+ end
21
+
22
+
23
+
24
+ # @return [Boolean] true if the message is an numeric reply (as
25
+ # opposed to a command)
26
+ def numeric_reply?
27
+ !!(@numeric_reply ||= @command.match(/^\d{3}$/))
28
+ end
29
+
30
+ # @api private
31
+ # @return [void]
32
+ def parse
33
+ match = @raw.match(/(^:(\S+) )?(\S+)(.*)/)
34
+ _, @prefix, @command, raw_params = match.captures
35
+
36
+ raw_params.strip!
37
+ if match = raw_params.match(/(?:^:| :)(.*)$/)
38
+ @params = match.pre_match.split(" ")
39
+ @params << match[1]
40
+ else
41
+ @params = raw_params.split(" ")
42
+ end
43
+ end
44
+
45
+ # @return [User] The user who sent this message
46
+ def user
47
+ return unless @prefix
48
+ nick = @prefix[/^(\S+)!/, 1]
49
+ user = @prefix[/^\S+!(\S+)@/, 1]
50
+ host = @prefix[/@(\S+)$/, 1]
51
+
52
+ return nil if nick.nil?
53
+ @user ||= User.find_ensured(user, nick, host, @bot)
54
+ end
55
+
56
+ # @return [String, nil]
57
+ def server
58
+ return unless @prefix
59
+ return if @prefix.match(/[@!]/)
60
+ @server ||= @prefix[/^(\S+)/, 1]
61
+ end
62
+
63
+ # @return [Boolean] true if the message describes an error
64
+ def error?
65
+ !!error
66
+ end
67
+
68
+ # @return [Number, nil] the numeric error code, if any
69
+ def error
70
+ @error ||= (command.to_i if numeric_reply? && command[/[45]\d\d/])
71
+ end
72
+
73
+ # @return [Boolean] true if this message was sent in a channel
74
+ def channel?
75
+ !!channel
76
+ end
77
+
78
+ # @return [Boolean] true if the message is an CTCP message
79
+ def ctcp?
80
+ params.last =~ /\001.+\001/
81
+ end
82
+
83
+ # @return [String, nil] the command part of an CTCP message
84
+ def ctcp_command
85
+ return unless ctcp?
86
+ ctcp_message.split(" ").first
87
+ end
88
+
89
+ # @return [Channel] The channel in which this message was sent
90
+ def channel
91
+ @channel ||= begin
92
+ case command
93
+ when "INVITE", RPL_CHANNELMODEIS.to_s, RPL_BANLIST.to_s
94
+ Channel.find_ensured(params[1], @bot)
95
+ when RPL_NAMEREPLY.to_s
96
+ Channel.find_ensured(params[2], @bot)
97
+ else
98
+ if params.first.start_with?("#")
99
+ Channel.find_ensured(params.first, @bot)
100
+ elsif numeric_reply? and params[1].start_with?("#")
101
+ Channel.find_ensured(params[1], @bot)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ # @api private
108
+ # @return [MatchData]
109
+ def match(regexp, type)
110
+ if type == :ctcp
111
+ @matches[:ctcp][regexp] ||= ctcp_message.match(regexp)
112
+ else
113
+ @matches[:other][regexp] ||= message.to_s.match(regexp)
114
+ end
115
+ end
116
+
117
+ # @return [String, nil] the CTCP message, without \001 control characters
118
+ def ctcp_message
119
+ return unless ctcp?
120
+ params.last =~ /\001(.+)\001/
121
+ $1
122
+ end
123
+
124
+ def ctcp_args
125
+ return unless ctcp?
126
+ ctcp_message.split(" ")[1..-1]
127
+ end
128
+
129
+ # @return [String, nil]
130
+ def message
131
+ @message ||= begin
132
+ if error?
133
+ error.to_s
134
+ elsif regular_command?
135
+ params.last
136
+ end
137
+ end
138
+ end
139
+
140
+ # Replies to a message, automatically determining if it was a
141
+ # channel or a private message.
142
+ #
143
+ # @param [String] text the message
144
+ # @param [Boolean] prefix if prefix is true and the message was in
145
+ # a channel, the reply will be prefixed by the nickname of whoever
146
+ # send the mesage
147
+ # @return [void]
148
+ def reply(text, prefix = false)
149
+ text = text.to_s
150
+ if channel && prefix
151
+ text = text.split("\n").map {|l| "#{user.nick}: #{l}"}.join("\n")
152
+ end
153
+
154
+ (channel || user).send(text)
155
+ end
156
+
157
+ # Like #reply, but using {Channel#safe_send}/{User#safe_send}
158
+ # instead
159
+ #
160
+ # @param (see #reply)
161
+ # @return (see #reply)
162
+ def safe_reply(text, prefix = false)
163
+ text = text.to_s
164
+ if channel && prefix
165
+ text = "#{user.nick}: #{text}"
166
+ end
167
+ (channel || user).safe_send(text)
168
+ end
169
+
170
+ # Reply to a CTCP message
171
+ #
172
+ # @return [void]
173
+ def ctcp_reply(answer)
174
+ return unless ctcp?
175
+ @bot.raw "NOTICE #{user.nick} :\001#{ctcp_command} #{answer}\001"
176
+ end
177
+
178
+ private
179
+ def regular_command?
180
+ !numeric_reply? # a command can only be numeric or "regular"…
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,62 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "thread"
3
+
4
+ module Cinch
5
+ # @api private
6
+ class MessageQueue
7
+ def initialize(socket, bot)
8
+ @socket = socket
9
+ @queue = Queue.new
10
+ @time_since_last_send = nil
11
+ @bot = bot
12
+
13
+ @log = []
14
+ end
15
+
16
+ # @return [void]
17
+ def queue(message)
18
+ command = message.split(" ").first
19
+
20
+ if command == "PONG"
21
+ @queue.unshift(message)
22
+ else
23
+ @queue << message
24
+ end
25
+ end
26
+
27
+ # @return [void]
28
+ def process!
29
+ while true
30
+ mps = @bot.config.messages_per_second
31
+ max_queue_size = @bot.config.server_queue_size
32
+
33
+ if @log.size > 1
34
+ time_passed = 0
35
+
36
+ @log.each_with_index do |one, index|
37
+ second = @log[index+1]
38
+ time_passed += second - one
39
+ break if index == @log.size - 2
40
+ end
41
+
42
+ messages_processed = (time_passed * mps).floor
43
+ effective_size = @log.size - messages_processed
44
+
45
+ if effective_size <= 0
46
+ @log.clear
47
+ elsif effective_size >= max_queue_size
48
+ sleep 1.0/mps
49
+ end
50
+ end
51
+
52
+ message = @queue.pop.to_s.chomp
53
+
54
+ @log << Time.now
55
+ @bot.logger.log(message, :outgoing) if @bot.config.verbose
56
+
57
+ @time_since_last_send = Time.now
58
+ @socket.print message + "\r\n"
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,205 @@
1
+ module Cinch
2
+ module Plugin
3
+ include Helpers
4
+
5
+ module ClassMethods
6
+ Pattern = Struct.new(:pattern, :use_prefix, :method)
7
+ Listener = Struct.new(:event, :method)
8
+
9
+ # Set a match pattern.
10
+ #
11
+ # @param [Regexp, String] pattern A pattern
12
+ # @option options [Symbol] :method (:execute) The method to execute
13
+ # @option options [Boolean] :use_prefix (true) If true, the
14
+ # plugin prefix will automatically be prepended to the
15
+ # pattern.
16
+ # @return [void]
17
+ def match(pattern, options = {})
18
+ options = {:use_prefix => true, :method => :execute}.merge(options)
19
+ @__cinch_patterns ||= []
20
+ @__cinch_patterns << Pattern.new(pattern, options[:use_prefix], options[:method])
21
+ end
22
+
23
+ # Events to listen to.
24
+ # @overload listen_to(*types, options = {})
25
+ # @param [String, Symbol, Integer] *types Events to listen to. Available
26
+ # events are all IRC commands in lowercase as symbols, all numeric
27
+ # replies, and the following:
28
+ #
29
+ # - :channel (a channel message)
30
+ # - :private (a private message)
31
+ # - :message (both channel and private messages)
32
+ # - :error (IRC errors)
33
+ # - :ctcp (ctcp requests)
34
+ #
35
+ # @param [Hash] options
36
+ # @option options [Symbol] :method (:listen) The method to
37
+ # execute
38
+ # @return [void]
39
+ def listen_to(*types)
40
+ options = {:method => :listen}
41
+ if types.last.is_a?(Hash)
42
+ options.merge!(types.pop)
43
+ end
44
+
45
+ @__cinch_listeners ||= []
46
+
47
+ types.each do |type|
48
+ @__cinch_listeners << Listener.new(type, options[:method])
49
+ end
50
+ end
51
+
52
+ def ctcp(command)
53
+ (@__cinch_ctcps ||= []) << command.to_s.upcase
54
+ end
55
+
56
+ # Define a help message which will be returned on "<prefix>help
57
+ # <pluginname>".
58
+ #
59
+ # @param [String] message
60
+ # @return [void]
61
+ def help(message)
62
+ @__cinch_help_message = message
63
+ end
64
+
65
+ # Set the plugin prefix.
66
+ #
67
+ # @param [String] prefix
68
+ # @return [void]
69
+ def prefix(prefix)
70
+ @__cinch_prefix = prefix
71
+ end
72
+
73
+ # Set which kind of messages to react on (i.e. call {#execute})
74
+ #
75
+ # @param [Symbol<:message, :channel, :private>] target React to all,
76
+ # only public or only private messages?
77
+ # @return [void]
78
+ def react_on(target)
79
+ @__cinch_react_on = target
80
+ end
81
+
82
+ # Define the plugin name.
83
+ #
84
+ # @param [String] name
85
+ # @return [void]
86
+ def plugin(name)
87
+ @__cinch_name = name
88
+ end
89
+
90
+ # @return [String]
91
+ # @api private
92
+ def __plugin_name
93
+ @__cinch_name || self.name.split("::").last.downcase
94
+ end
95
+
96
+ # @return [void]
97
+ # @api private
98
+ def __register_with_bot(bot, instance)
99
+ plugin_name = __plugin_name
100
+
101
+ (@__cinch_listeners || []).each do |listener|
102
+ bot.debug "[plugin] #{plugin_name}: Registering listener for type `#{listener.event}`"
103
+ bot.on(listener.event, [], instance) do |message, plugin|
104
+ plugin.__send__(listener.method, message) if plugin.respond_to?(listener.method)
105
+ end
106
+ end
107
+
108
+ if (@__cinch_patterns ||= []).empty?
109
+ @__cinch_patterns << Pattern.new(plugin_name, true, nil)
110
+ end
111
+
112
+ prefix = @__cinch_prefix || bot.config.plugins.prefix
113
+ if prefix.is_a?(String)
114
+ prefix = Regexp.escape(prefix)
115
+ end
116
+ @__cinch_patterns.each do |pattern|
117
+ if pattern.use_prefix && prefix
118
+ case pattern.pattern
119
+ when Regexp
120
+ pattern.pattern = /^#{prefix}#{pattern.pattern}/
121
+ when String
122
+ pattern.pattern = prefix + pattern.pattern
123
+ end
124
+ end
125
+
126
+ react_on = @__cinch_react_on || :message
127
+
128
+ bot.debug "[plugin] #{plugin_name}: Registering executor with pattern `#{pattern.pattern}`, reacting on `#{react_on}`"
129
+
130
+ bot.on(react_on, pattern.pattern, instance, pattern) do |message, plugin, pattern, *args|
131
+ if plugin.respond_to?(pattern.method)
132
+ method = plugin.method(pattern.method)
133
+ arity = method.arity - 1
134
+ if arity > 0
135
+ args = args[0..arity - 1]
136
+ elsif arity == 0
137
+ args = []
138
+ end
139
+ method.call(message, *args)
140
+ end
141
+ end
142
+ end
143
+
144
+ (@__cinch_ctcps || []).each do |ctcp|
145
+ bot.debug "[plugin] #{plugin_name}: Registering CTCP `#{ctcp}`"
146
+ bot.on(:ctcp, ctcp, instance, ctcp) do |message, plugin, ctcp, *args|
147
+ plugin.__send__("ctcp_#{ctcp.downcase}", message, *args)
148
+ end
149
+ end
150
+
151
+ if @__cinch_help_message
152
+ bot.debug "[plugin] #{plugin_name}: Registering help message"
153
+ bot.on(:message, /#{prefix}help #{Regexp.escape(plugin_name)}/, @__cinch_help_message) do |message, help_message|
154
+ message.reply(help_message)
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ # @return [Bot]
161
+ attr_reader :bot
162
+ # @api private
163
+ def initialize(bot)
164
+ @bot = bot
165
+ self.class.__register_with_bot(bot, self)
166
+ end
167
+
168
+ # @param (see Bot#synchronize)
169
+ # @yield
170
+ # @return (see Bot#synchronize)
171
+ # @see Bot#synchronize
172
+ def synchronize(*args, &block)
173
+ @bot.synchronize(*args, &block)
174
+ end
175
+
176
+ # This method will be executed whenever an event the plugin
177
+ # {Plugin::ClassMethods#listen_to listens to} occurs.
178
+ #
179
+ # @abstract
180
+ # @return [void]
181
+ # @see Plugin::ClassMethods#listen_to
182
+ def listen(*args)
183
+ end
184
+
185
+ # This method will be executed whenever a message matches the
186
+ # {Plugin::ClassMethods#match match pattern} of the plugin.
187
+ #
188
+ # @abstract
189
+ # @return [void]
190
+ # @see Plugin::ClassMethods#match
191
+ def execute(*args)
192
+ end
193
+
194
+ # Provides access to plugin-specific options.
195
+ #
196
+ # @return [Hash] A hash of options
197
+ def config
198
+ @bot.config.plugins.options[self.class]
199
+ end
200
+
201
+ def self.included(by)
202
+ by.extend ClassMethods
203
+ end
204
+ end
205
+ end