cinch 0.3.5 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|