cinch 0.3.5 → 1.0.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.
- data/LICENSE +20 -0
- data/README.md +192 -0
- data/Rakefile +53 -43
- data/examples/basic/autovoice.rb +32 -0
- data/examples/basic/google.rb +35 -0
- data/examples/basic/hello.rb +15 -0
- data/examples/basic/join_part.rb +38 -0
- data/examples/basic/memo.rb +39 -0
- data/examples/basic/msg.rb +16 -0
- data/examples/basic/seen.rb +36 -0
- data/examples/basic/urban_dict.rb +35 -0
- data/examples/basic/url_shorten.rb +35 -0
- data/examples/plugins/autovoice.rb +40 -0
- data/examples/plugins/custom_prefix.rb +23 -0
- data/examples/plugins/google.rb +37 -0
- data/examples/plugins/hello.rb +22 -0
- data/examples/plugins/join_part.rb +42 -0
- data/examples/plugins/memo.rb +50 -0
- data/examples/plugins/msg.rb +22 -0
- data/examples/plugins/multiple_matches.rb +41 -0
- data/examples/plugins/seen.rb +45 -0
- data/examples/plugins/urban_dict.rb +30 -0
- data/examples/plugins/url_shorten.rb +32 -0
- data/lib/cinch.rb +7 -20
- data/lib/cinch/ban.rb +41 -0
- data/lib/cinch/bot.rb +479 -0
- data/lib/cinch/callback.rb +11 -0
- data/lib/cinch/channel.rb +419 -0
- data/lib/cinch/constants.rb +369 -0
- data/lib/cinch/exceptions.rb +25 -0
- data/lib/cinch/helpers.rb +21 -0
- data/lib/cinch/irc.rb +344 -38
- data/lib/cinch/isupport.rb +96 -0
- data/lib/cinch/logger/formatted_logger.rb +80 -0
- data/lib/cinch/logger/logger.rb +44 -0
- data/lib/cinch/logger/null_logger.rb +18 -0
- data/lib/cinch/mask.rb +46 -0
- data/lib/cinch/message.rb +183 -0
- data/lib/cinch/message_queue.rb +62 -0
- data/lib/cinch/plugin.rb +205 -0
- data/lib/cinch/rubyext/infinity.rb +1 -0
- data/lib/cinch/rubyext/module.rb +18 -0
- data/lib/cinch/rubyext/queue.rb +19 -0
- data/lib/cinch/rubyext/string.rb +24 -0
- data/lib/cinch/syncable.rb +55 -0
- data/lib/cinch/user.rb +325 -0
- data/spec/bot_spec.rb +5 -0
- data/spec/channel_spec.rb +5 -0
- data/spec/cinch_spec.rb +5 -0
- data/spec/irc_spec.rb +5 -0
- data/spec/message_spec.rb +5 -0
- data/spec/plugin_spec.rb +5 -0
- data/spec/{helper.rb → spec_helper.rb} +0 -0
- data/spec/user_spec.rb +5 -0
- metadata +69 -51
- data/README.rdoc +0 -195
- data/examples/autovoice.rb +0 -32
- data/examples/custom_patterns.rb +0 -19
- data/examples/custom_prefix.rb +0 -25
- data/examples/google.rb +0 -31
- data/examples/hello.rb +0 -13
- data/examples/join_part.rb +0 -26
- data/examples/memo.rb +0 -40
- data/examples/msg.rb +0 -14
- data/examples/named-param-types.rb +0 -19
- data/examples/seen.rb +0 -41
- data/examples/urban_dict.rb +0 -31
- data/examples/url_shorten.rb +0 -34
- data/lib/cinch/base.rb +0 -368
- data/lib/cinch/irc/message.rb +0 -135
- data/lib/cinch/irc/parser.rb +0 -141
- data/lib/cinch/irc/socket.rb +0 -329
- data/lib/cinch/names.rb +0 -54
- data/lib/cinch/rules.rb +0 -171
- data/spec/base_spec.rb +0 -94
- data/spec/irc/helper.rb +0 -8
- data/spec/irc/message_spec.rb +0 -61
- data/spec/irc/parser_spec.rb +0 -103
- data/spec/irc/socket_spec.rb +0 -90
- data/spec/names_spec.rb +0 -393
- data/spec/options_spec.rb +0 -45
- 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
|
data/lib/cinch/mask.rb
ADDED
@@ -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
|
data/lib/cinch/plugin.rb
ADDED
@@ -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
|