cinch 2.0.12 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +44 -32
- data/docs/changes.md +61 -1
- data/docs/common_mistakes.md +24 -0
- data/docs/common_tasks.md +9 -0
- data/docs/logging.md +1 -1
- data/examples/plugins/own_events.rb +1 -1
- data/lib/cinch/bot.rb +16 -2
- data/lib/cinch/callback.rb +2 -2
- data/lib/cinch/channel.rb +2 -2
- data/lib/cinch/formatting.rb +1 -1
- data/lib/cinch/handler.rb +17 -3
- data/lib/cinch/handler_list.rb +10 -6
- data/lib/cinch/helpers.rb +2 -2
- data/lib/cinch/irc.rb +7 -1
- data/lib/cinch/logger_list.rb +2 -2
- data/lib/cinch/mask.rb +1 -1
- data/lib/cinch/message.rb +34 -6
- data/lib/cinch/plugin.rb +57 -20
- data/lib/cinch/sasl/dh_blowfish.rb +5 -11
- data/lib/cinch/syncable.rb +2 -2
- data/lib/cinch/target.rb +1 -1
- data/lib/cinch/user.rb +129 -56
- data/lib/cinch/utilities/string.rb +4 -0
- data/lib/cinch/version.rb +1 -1
- metadata +3 -3
- data/examples/plugins/urban_dict.rb +0 -30
data/README.md
CHANGED
@@ -24,33 +24,39 @@ Installation
|
|
24
24
|
|
25
25
|
You can install the latest Cinch gem using RubyGems
|
26
26
|
|
27
|
-
|
27
|
+
```
|
28
|
+
gem install cinch
|
29
|
+
```
|
28
30
|
|
29
31
|
### GitHub
|
30
32
|
|
31
33
|
Alternatively you can check out the latest code directly from Github
|
32
34
|
|
33
|
-
|
35
|
+
```
|
36
|
+
git clone http://github.com/cinchrb/cinch.git
|
37
|
+
```
|
34
38
|
|
35
39
|
Example
|
36
40
|
-------
|
37
41
|
|
38
42
|
Your typical Hello, World application in Cinch would go something like this:
|
39
43
|
|
40
|
-
|
44
|
+
```ruby
|
45
|
+
require 'cinch'
|
41
46
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
+
bot = Cinch::Bot.new do
|
48
|
+
configure do |c|
|
49
|
+
c.server = "irc.freenode.org"
|
50
|
+
c.channels = ["#cinch-bots"]
|
51
|
+
end
|
47
52
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
53
|
+
on :message, "hello" do |m|
|
54
|
+
m.reply "Hello, #{m.user.nick}"
|
55
|
+
end
|
56
|
+
end
|
52
57
|
|
53
|
-
|
58
|
+
bot.start
|
59
|
+
```
|
54
60
|
|
55
61
|
More examples can be found in the `examples` directory.
|
56
62
|
|
@@ -98,27 +104,31 @@ your own plugins for others to use
|
|
98
104
|
|
99
105
|
Want to see the same Hello, World application in plugin form? Sure you do!
|
100
106
|
|
101
|
-
|
107
|
+
```ruby
|
108
|
+
require 'cinch'
|
102
109
|
|
103
|
-
|
104
|
-
|
110
|
+
class Hello
|
111
|
+
include Cinch::Plugin
|
105
112
|
|
106
|
-
|
113
|
+
match "hello"
|
107
114
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
115
|
+
def execute(m)
|
116
|
+
m.reply "Hello, #{m.user.nick}"
|
117
|
+
end
|
118
|
+
end
|
112
119
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
+
bot = Cinch::Bot.new do
|
121
|
+
configure do |c|
|
122
|
+
c.server = "irc.freenode.org"
|
123
|
+
c.channels = ["#cinch-bots"]
|
124
|
+
c.plugins.plugins = [Hello]
|
125
|
+
end
|
126
|
+
end
|
120
127
|
|
121
|
-
|
128
|
+
bot.start
|
129
|
+
```
|
130
|
+
|
131
|
+
Note: Plugins take a default prefix of `/^!/` which means the actual match is `!hello`.
|
122
132
|
|
123
133
|
More information can be found in the {Cinch::Plugin} documentation.
|
124
134
|
|
@@ -140,9 +150,11 @@ terminal, meaning you get some pretty damn awesome readable coloured
|
|
140
150
|
text. Cinch also provides a way for your plugins to log custom
|
141
151
|
messages:
|
142
152
|
|
143
|
-
|
144
|
-
|
145
|
-
|
153
|
+
```ruby
|
154
|
+
on :message, /hello/ do |m|
|
155
|
+
debug "Someone said hello"
|
156
|
+
end
|
157
|
+
```
|
146
158
|
|
147
159
|
Authors
|
148
160
|
-------
|
data/docs/changes.md
CHANGED
@@ -1,6 +1,67 @@
|
|
1
1
|
# @title What has changed?
|
2
2
|
# @markup kramdown
|
3
3
|
|
4
|
+
# What has changed in 2.1?
|
5
|
+
1. Color stripping
|
6
|
+
1. Per group hooks
|
7
|
+
1. API improvements
|
8
|
+
1. New methods
|
9
|
+
1. Changed methods
|
10
|
+
1. New aliases
|
11
|
+
|
12
|
+
## Color stripping
|
13
|
+
|
14
|
+
The new method {Cinch::Utilities::String.strip_colors} allows removal
|
15
|
+
of mIRC color codes from messages.
|
16
|
+
|
17
|
+
Additionally, a new match option called `strip_colors` makes it
|
18
|
+
possible to automatically and temporarily strip color codes before
|
19
|
+
attemping to match a message.
|
20
|
+
|
21
|
+
## Per group hooks
|
22
|
+
|
23
|
+
A new option `group` for hooks allows registering hooks for specific
|
24
|
+
groups.
|
25
|
+
|
26
|
+
## API improvements
|
27
|
+
|
28
|
+
### New methods
|
29
|
+
|
30
|
+
#### {Cinch::Bot}
|
31
|
+
|
32
|
+
- {Cinch::Bot#oper}
|
33
|
+
|
34
|
+
#### {Cinch::User}
|
35
|
+
|
36
|
+
- {Cinch::User#oper?}
|
37
|
+
|
38
|
+
#### {Cinch::Message}
|
39
|
+
|
40
|
+
- {Cinch::Message#action_reply}
|
41
|
+
- {Cinch::Message#safe_action_reply}
|
42
|
+
|
43
|
+
### Changed methods
|
44
|
+
|
45
|
+
#### {Cinch::Handler}
|
46
|
+
|
47
|
+
- {Cinch::Handler#call} now returns the started thread.
|
48
|
+
|
49
|
+
#### {Cinch::HandlerList}
|
50
|
+
|
51
|
+
- {Cinch::HandlerList#dispatch} now returns the started threads.
|
52
|
+
|
53
|
+
### New aliases
|
54
|
+
|
55
|
+
Due to some unfortunate naming mistakes in Cinch 2.0, Cinch 2.1 adds
|
56
|
+
several aliases. All of the new aliases deprecate the original method
|
57
|
+
names, which will be removed in Cinch 3.0.
|
58
|
+
|
59
|
+
#### {Cinch::User}
|
60
|
+
- {Cinch::User#monitored?} for {Cinch::User#monitored}
|
61
|
+
- {Cinch::User#synced?} for {Cinch::User#synced}
|
62
|
+
|
63
|
+
|
64
|
+
|
4
65
|
# What has changed in 2.0?
|
5
66
|
1. Added support for SASL
|
6
67
|
1. Added support for DCC SEND
|
@@ -181,7 +242,6 @@ moved from {Cinch} to {Cinch::Constants}
|
|
181
242
|
- {Cinch::Bot#modes}
|
182
243
|
- {Cinch::Bot#modes=}
|
183
244
|
- {Cinch::Bot#set_mode}
|
184
|
-
- {Cinch::Bot#stop}
|
185
245
|
- {Cinch::Bot#unset_mode}
|
186
246
|
- {Cinch::Bot#user_list}
|
187
247
|
|
data/docs/common_mistakes.md
CHANGED
@@ -34,3 +34,27 @@ The following is not possible:
|
|
34
34
|
# ...
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
# My handlers won't run, even though the Regexp matches.
|
39
|
+
|
40
|
+
Cinch plugins have a default prefix (`/^!/`). This allows for flexible prefixes
|
41
|
+
across the whole bot. The default prefix can be changed in `Bot#configure`:
|
42
|
+
|
43
|
+
x = Cinch::Bot.new do
|
44
|
+
configure do |c|
|
45
|
+
# ...
|
46
|
+
c.plugins.prefix = /^%/
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Alternatively, you can set the prefix for specific plugins only, by calling `set`:
|
51
|
+
|
52
|
+
class MyPlygin
|
53
|
+
include Cinch::Plugin
|
54
|
+
|
55
|
+
set :prefix, /^%/
|
56
|
+
end
|
57
|
+
|
58
|
+
You can also choose to not use a prefix for specific matchers:
|
59
|
+
|
60
|
+
match /hi/, use_prefix: false
|
data/docs/common_tasks.md
CHANGED
@@ -46,3 +46,12 @@ example:
|
|
46
46
|
@bot.loggers.info "I miss my master :("
|
47
47
|
end
|
48
48
|
end
|
49
|
+
|
50
|
+
# Sending messages to users and channels beside `m.user` and `m.channel`
|
51
|
+
|
52
|
+
Cinch provides {Cinch::Helpers helper methods} to get instances of Channel
|
53
|
+
and User objects that you can work with:
|
54
|
+
|
55
|
+
User('user').send("Hello!") # Sends a message to user 'user'
|
56
|
+
Channel('#channel').send("Hello!") # Sends a message to channel '#channel'
|
57
|
+
|
data/docs/logging.md
CHANGED
@@ -41,7 +41,7 @@ The level can be changed for single loggers or all loggers at once, by either us
|
|
41
41
|
Example:
|
42
42
|
|
43
43
|
bot = Cinch::Bot.new { }
|
44
|
-
bot.loggers << Cinch::
|
44
|
+
bot.loggers << Cinch::Logger::FormattedLogger.new(File.open("/tmp/log.log", "a"))
|
45
45
|
bot.loggers.level = :debug
|
46
46
|
bot.loggers.first.level = :info
|
47
47
|
|
data/lib/cinch/bot.rb
CHANGED
@@ -177,10 +177,10 @@ module Cinch
|
|
177
177
|
# right event will be checked against this argument and the event
|
178
178
|
# will only be called if it matches
|
179
179
|
#
|
180
|
-
# @param [Array<Object>]
|
180
|
+
# @param [Array<Object>] args Arguments that should be passed to
|
181
181
|
# the block, additionally to capture groups of the regexp.
|
182
182
|
#
|
183
|
-
# @yieldparam [String]
|
183
|
+
# @yieldparam [Array<String>] args each capture group of the regex will
|
184
184
|
# be one argument to the block.
|
185
185
|
#
|
186
186
|
# @return [Handler] The handlers that have been registered
|
@@ -266,6 +266,8 @@ module Cinch
|
|
266
266
|
}
|
267
267
|
end
|
268
268
|
|
269
|
+
@modes = []
|
270
|
+
|
269
271
|
@loggers.info "Connecting to #{@config.server}:#{@config.port}"
|
270
272
|
@irc = IRC.new(self)
|
271
273
|
@irc.start
|
@@ -424,6 +426,18 @@ module Cinch
|
|
424
426
|
@irc.send "NICK #{new_nick}"
|
425
427
|
end
|
426
428
|
|
429
|
+
# Gain oper privileges.
|
430
|
+
#
|
431
|
+
# @param [String] password
|
432
|
+
# @param [String] user The username to use. Defaults to the bot's
|
433
|
+
# nickname
|
434
|
+
# @since 2.1.0
|
435
|
+
# @return [void]
|
436
|
+
def oper(password, user = nil)
|
437
|
+
user ||= self.nick
|
438
|
+
@irc.send "OPER #{user} #{password}"
|
439
|
+
end
|
440
|
+
|
427
441
|
# Try to create a free nick, first by cycling through all
|
428
442
|
# available alternatives and then by appending underscores.
|
429
443
|
#
|
data/lib/cinch/callback.rb
CHANGED
data/lib/cinch/channel.rb
CHANGED
@@ -59,7 +59,7 @@ module Cinch
|
|
59
59
|
|
60
60
|
@synced_attributes = Set.new
|
61
61
|
@when_requesting_synced_attribute = lambda {|attr|
|
62
|
-
if @in_channel && attr == :topic && !
|
62
|
+
if @in_channel && attr == :topic && !attribute_synced?(:topic)
|
63
63
|
# Even if we are in the channel, if there's no topic set,
|
64
64
|
# the attribute won't be synchronised yet. Explicitly
|
65
65
|
# request the topic.
|
@@ -321,7 +321,7 @@ module Cinch
|
|
321
321
|
# Kicks a user from the channel.
|
322
322
|
#
|
323
323
|
# @param [String, User] user the user to kick
|
324
|
-
# @param [String] a reason for the kick
|
324
|
+
# @param [String] reason a reason for the kick
|
325
325
|
# @raise [Exceptions::KickReasonTooLong]
|
326
326
|
# @return [void]
|
327
327
|
def kick(user, reason = nil)
|
data/lib/cinch/formatting.rb
CHANGED
@@ -70,7 +70,7 @@ module Cinch
|
|
70
70
|
:reset => 15.chr,
|
71
71
|
}
|
72
72
|
|
73
|
-
# @param [Array<Symbol>]
|
73
|
+
# @param [Array<Symbol>] settings The colors and attributes to apply.
|
74
74
|
# When supplying two colors, the first will be used for the
|
75
75
|
# foreground and the second for the background.
|
76
76
|
# @param [String] string The string to format.
|
data/lib/cinch/handler.rb
CHANGED
@@ -19,6 +19,9 @@ module Cinch
|
|
19
19
|
# @return [Symbol]
|
20
20
|
attr_reader :group
|
21
21
|
|
22
|
+
# @return [Boolean]
|
23
|
+
attr_reader :strip_colors
|
24
|
+
|
22
25
|
# @return [ThreadGroup]
|
23
26
|
# @api private
|
24
27
|
attr_reader :thread_group
|
@@ -31,15 +34,23 @@ module Cinch
|
|
31
34
|
# to.
|
32
35
|
# @option options [Boolean] :execute_in_callback (false) Whether
|
33
36
|
# to execute the handler in an instance of {Callback}
|
37
|
+
# @option options [Boolean] :strip_colors (false) Strip colors
|
38
|
+
# from message before attemping match
|
34
39
|
# @option options [Array] :args ([]) Additional arguments to pass
|
35
40
|
# to the block
|
36
41
|
def initialize(bot, event, pattern, options = {}, &block)
|
37
|
-
options = {
|
42
|
+
options = {
|
43
|
+
:group => nil,
|
44
|
+
:execute_in_callback => false,
|
45
|
+
:strip_colors => false,
|
46
|
+
:args => []
|
47
|
+
}.merge(options)
|
38
48
|
@bot = bot
|
39
49
|
@event = event
|
40
50
|
@pattern = pattern
|
41
51
|
@group = options[:group]
|
42
52
|
@execute_in_callback = options[:execute_in_callback]
|
53
|
+
@strip_colors = options[:strip_colors]
|
43
54
|
@args = options[:args]
|
44
55
|
@block = block
|
45
56
|
|
@@ -74,11 +85,11 @@ module Cinch
|
|
74
85
|
# @param [Message] message Message that caused the invocation
|
75
86
|
# @param [Array] captures Capture groups of the pattern that are
|
76
87
|
# being passed as arguments
|
77
|
-
# @return [
|
88
|
+
# @return [Thread]
|
78
89
|
def call(message, captures, arguments)
|
79
90
|
bargs = captures + arguments
|
80
91
|
|
81
|
-
|
92
|
+
thread = Thread.new {
|
82
93
|
@bot.loggers.debug "[New thread] For #{self}: #{Thread.current} -- #{@thread_group.list.size} in total."
|
83
94
|
|
84
95
|
begin
|
@@ -93,6 +104,9 @@ module Cinch
|
|
93
104
|
@bot.loggers.debug "[Thread done] For #{self}: #{Thread.current} -- #{@thread_group.list.size - 1} remaining."
|
94
105
|
end
|
95
106
|
}
|
107
|
+
|
108
|
+
@thread_group.add(thread)
|
109
|
+
thread
|
96
110
|
end
|
97
111
|
|
98
112
|
# @return [String]
|
data/lib/cinch/handler_list.rb
CHANGED
@@ -19,7 +19,7 @@ module Cinch
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
# @param [Handler, Array<Handler>]
|
22
|
+
# @param [Handler, Array<Handler>] handlers The handlers to unregister
|
23
23
|
# @return [void]
|
24
24
|
# @see Handler#unregister
|
25
25
|
def unregister(*handlers)
|
@@ -39,7 +39,7 @@ module Cinch
|
|
39
39
|
end
|
40
40
|
|
41
41
|
handlers = handlers.select { |handler|
|
42
|
-
msg.match(handler.pattern.to_r(msg), type)
|
42
|
+
msg.match(handler.pattern.to_r(msg), type, handler.strip_colors)
|
43
43
|
}.group_by {|handler| handler.group}
|
44
44
|
|
45
45
|
handlers.values_at(*(handlers.keys - [nil])).map(&:first) + (handlers[nil] || [])
|
@@ -49,10 +49,12 @@ module Cinch
|
|
49
49
|
# @param [Symbol] event The event type
|
50
50
|
# @param [Message, nil] msg The message which is responsible for
|
51
51
|
# and attached to the event, or nil.
|
52
|
-
# @param [Array]
|
52
|
+
# @param [Array] arguments A list of additional arguments to pass
|
53
53
|
# to event handlers
|
54
|
-
# @return [
|
54
|
+
# @return [Array<Thread>]
|
55
55
|
def dispatch(event, msg = nil, *arguments)
|
56
|
+
threads = []
|
57
|
+
|
56
58
|
if handlers = find(event, msg)
|
57
59
|
already_run = Set.new
|
58
60
|
handlers.each do |handler|
|
@@ -61,14 +63,16 @@ module Cinch
|
|
61
63
|
# calling Message#match multiple times is not a problem
|
62
64
|
# because we cache the result
|
63
65
|
if msg
|
64
|
-
captures = msg.match(handler.pattern.to_r(msg), event).captures
|
66
|
+
captures = msg.match(handler.pattern.to_r(msg), event, handler.strip_colors).captures
|
65
67
|
else
|
66
68
|
captures = []
|
67
69
|
end
|
68
70
|
|
69
|
-
handler.call(msg, captures, arguments)
|
71
|
+
threads << handler.call(msg, captures, arguments)
|
70
72
|
end
|
71
73
|
end
|
74
|
+
|
75
|
+
threads
|
72
76
|
end
|
73
77
|
|
74
78
|
# @yield [handler] Yields all registered handlers
|
data/lib/cinch/helpers.rb
CHANGED
data/lib/cinch/irc.rb
CHANGED
@@ -493,7 +493,7 @@ module Cinch
|
|
493
493
|
modes = ModeParser.parse_modes(msg.params[1], msg.params[2..-1])
|
494
494
|
modes.each do |direction, mode, _|
|
495
495
|
if direction == :add
|
496
|
-
@bot.modes << mode
|
496
|
+
@bot.modes << mode unless @bot.modes.include?(mode)
|
497
497
|
else
|
498
498
|
@bot.modes.delete(mode)
|
499
499
|
end
|
@@ -629,6 +629,12 @@ module Cinch
|
|
629
629
|
})
|
630
630
|
end
|
631
631
|
|
632
|
+
def on_313(msg, events)
|
633
|
+
# RPL_WHOISOPERATOR
|
634
|
+
user = User(msg.params[1])
|
635
|
+
@whois_updates[user].merge!({:oper? => true})
|
636
|
+
end
|
637
|
+
|
632
638
|
def on_317(msg, events)
|
633
639
|
# RPL_WHOISIDLE
|
634
640
|
user = User(msg.params[1])
|
data/lib/cinch/logger_list.rb
CHANGED
data/lib/cinch/mask.rb
CHANGED
data/lib/cinch/message.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
require "time"
|
3
|
+
require "cinch/utilities/string"
|
3
4
|
|
4
5
|
module Cinch
|
5
6
|
# This class serves two purposes. For one, it simply
|
@@ -135,14 +136,23 @@ module Cinch
|
|
135
136
|
|
136
137
|
# @api private
|
137
138
|
# @return [MatchData]
|
138
|
-
def match(regexp, type)
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
139
|
+
def match(regexp, type, strip_colors)
|
140
|
+
text = ""
|
141
|
+
case type
|
142
|
+
when :ctcp
|
143
|
+
text = ctcp_message
|
144
|
+
when :action
|
145
|
+
text = action_message
|
143
146
|
else
|
144
|
-
|
147
|
+
text = message.to_s
|
148
|
+
type = :other
|
145
149
|
end
|
150
|
+
|
151
|
+
if strip_colors
|
152
|
+
text = Cinch::Utilities::String.strip_colors(text)
|
153
|
+
end
|
154
|
+
|
155
|
+
@matches[type][regexp] ||= text.match(regexp)
|
146
156
|
end
|
147
157
|
|
148
158
|
# @group Replying
|
@@ -176,6 +186,24 @@ module Cinch
|
|
176
186
|
@target.safe_send(text)
|
177
187
|
end
|
178
188
|
|
189
|
+
# Reply to a message with an action.
|
190
|
+
#
|
191
|
+
# @param [String] text the action message
|
192
|
+
# @return [void]
|
193
|
+
def action_reply(text)
|
194
|
+
text = text.to_s
|
195
|
+
@target.action(text)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Like #action_reply, but using {Target#safe_action} instead
|
199
|
+
#
|
200
|
+
# @param (see #action_reply)
|
201
|
+
# @return (see #action_reply)
|
202
|
+
def safe_action_reply(text)
|
203
|
+
text = text.to_s
|
204
|
+
@target.safe_action(text)
|
205
|
+
end
|
206
|
+
|
179
207
|
# Reply to a CTCP message
|
180
208
|
#
|
181
209
|
# @return [void]
|
data/lib/cinch/plugin.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "cinch/helpers"
|
2
|
+
|
1
3
|
# TODO more details in "message dropped" debug output
|
2
4
|
module Cinch
|
3
5
|
# This class represents the core of the plugin functionality of
|
@@ -79,7 +81,15 @@ module Cinch
|
|
79
81
|
# @attr [Boolean] use_suffix
|
80
82
|
# @attr [Symbol] method
|
81
83
|
# @attr [Symbol] group
|
82
|
-
Matcher = Struct.new(:pattern,
|
84
|
+
Matcher = Struct.new(:pattern,
|
85
|
+
:use_prefix,
|
86
|
+
:use_suffix,
|
87
|
+
:method,
|
88
|
+
:group,
|
89
|
+
:prefix,
|
90
|
+
:suffix,
|
91
|
+
:react_on,
|
92
|
+
:strip_colors)
|
83
93
|
|
84
94
|
# Represents a Listener as created by {#listen_to}.
|
85
95
|
#
|
@@ -105,7 +115,7 @@ module Cinch
|
|
105
115
|
# @attr [Symbol] type
|
106
116
|
# @attr [Array<Symbol>] for
|
107
117
|
# @attr [Symbol] method
|
108
|
-
Hook = Struct.new(:type, :for, :method)
|
118
|
+
Hook = Struct.new(:type, :for, :method, :group)
|
109
119
|
|
110
120
|
# @api private
|
111
121
|
def self.extended(by)
|
@@ -179,14 +189,32 @@ module Cinch
|
|
179
189
|
# @option options [Symbol, Fixnum] react_on (:message) The
|
180
190
|
# {file:docs/events.md event} to react on.
|
181
191
|
# @option options [Symbol] :group (nil) The group the match belongs to.
|
192
|
+
# @option options [Boolean] :strip_colors (false) Strip colors
|
193
|
+
# from message before attempting match
|
182
194
|
# @return [Matcher]
|
183
195
|
# @todo Document match/listener grouping
|
184
196
|
def match(pattern, options = {})
|
185
|
-
options = {
|
197
|
+
options = {
|
198
|
+
:use_prefix => true,
|
199
|
+
:use_suffix => true,
|
200
|
+
:method => :execute,
|
201
|
+
:group => nil,
|
202
|
+
:prefix => nil,
|
203
|
+
:suffix => nil,
|
204
|
+
:react_on => nil,
|
205
|
+
:strip_colors => false,
|
206
|
+
}.merge(options)
|
186
207
|
if options[:react_on]
|
187
208
|
options[:react_on] = options[:react_on].to_s.to_sym
|
188
209
|
end
|
189
|
-
matcher = Matcher.new(pattern, *options.values_at(:use_prefix,
|
210
|
+
matcher = Matcher.new(pattern, *options.values_at(:use_prefix,
|
211
|
+
:use_suffix,
|
212
|
+
:method,
|
213
|
+
:group,
|
214
|
+
:prefix,
|
215
|
+
:suffix,
|
216
|
+
:react_on,
|
217
|
+
:strip_colors))
|
190
218
|
@matchers << matcher
|
191
219
|
|
192
220
|
matcher
|
@@ -253,12 +281,15 @@ module Cinch
|
|
253
281
|
# a handler?
|
254
282
|
# @option options [Array<:match, :listen_to, :ctcp>] :for ([:match, :listen_to, :ctcp])
|
255
283
|
# Which kinds of events to run the hook for.
|
256
|
-
# @option options [Symbol] :method (
|
284
|
+
# @option options [Symbol] :method (:hook) The method to execute.
|
285
|
+
# @option options [Symbol] :group (nil) The match group to
|
286
|
+
# execute the hook for. Hooks belonging to the `nil` group
|
287
|
+
# will execute for all matches.
|
257
288
|
# @return [Hook]
|
258
289
|
# @since 1.1.0
|
259
290
|
def hook(type, options = {})
|
260
|
-
options = {:for => [:match, :listen_to, :ctcp], :method => :hook}.merge(options)
|
261
|
-
hook = Hook.new(type, options[:for], options[:method])
|
291
|
+
options = {:for => [:match, :listen_to, :ctcp], :method => :hook, :group => nil}.merge(options)
|
292
|
+
hook = Hook.new(type, options[:for], options[:method], options[:group])
|
262
293
|
__hooks(type) << hook
|
263
294
|
|
264
295
|
hook
|
@@ -266,7 +297,7 @@ module Cinch
|
|
266
297
|
|
267
298
|
# @return [Hash]
|
268
299
|
# @api private
|
269
|
-
def __hooks(type = nil, events = nil)
|
300
|
+
def __hooks(type = nil, events = nil, group = nil)
|
270
301
|
if type.nil?
|
271
302
|
hooks = @hooks
|
272
303
|
else
|
@@ -280,14 +311,16 @@ module Cinch
|
|
280
311
|
if hooks.is_a?(Hash)
|
281
312
|
hooks = hooks.map { |k, v| v }
|
282
313
|
end
|
283
|
-
|
314
|
+
hooks.select! { |hook| (events & hook.for).size > 0 }
|
284
315
|
end
|
316
|
+
|
317
|
+
return hooks.select { |hook| hook.group.nil? || hook.group == group }
|
285
318
|
end
|
286
319
|
|
287
320
|
# @return [Boolean] True if processing should continue
|
288
321
|
# @api private
|
289
|
-
def call_hooks(type, event, instance, args)
|
290
|
-
stop = __hooks(type, event).find { |hook|
|
322
|
+
def call_hooks(type, event, group, instance, args)
|
323
|
+
stop = __hooks(type, event, group).find { |hook|
|
291
324
|
# stop after the first hook that returns false
|
292
325
|
if hook.method.respond_to?(:call)
|
293
326
|
instance.instance_exec(*args, &hook.method) == false
|
@@ -314,9 +347,9 @@ module Cinch
|
|
314
347
|
self.class.listeners.each do |listener|
|
315
348
|
@bot.loggers.debug "[plugin] #{self.class.plugin_name}: Registering listener for type `#{listener.event}`"
|
316
349
|
new_handler = Handler.new(@bot, listener.event, Pattern.new(nil, //, nil)) do |message, *args|
|
317
|
-
if self.class.call_hooks(:pre, :listen_to, self, [message])
|
350
|
+
if self.class.call_hooks(:pre, :listen_to, nil, self, [message])
|
318
351
|
__send__(listener.method, message, *args)
|
319
|
-
self.class.call_hooks(:post, :listen_to, self, [message])
|
352
|
+
self.class.call_hooks(:post, :listen_to, nil, self, [message])
|
320
353
|
else
|
321
354
|
@bot.loggers.debug "[plugin] #{self.class.plugin_name}: Dropping message due to hook"
|
322
355
|
end
|
@@ -332,9 +365,9 @@ module Cinch
|
|
332
365
|
self.class.ctcps.each do |ctcp|
|
333
366
|
@bot.loggers.debug "[plugin] #{self.class.plugin_name}: Registering CTCP `#{ctcp}`"
|
334
367
|
new_handler = Handler.new(@bot, :ctcp, Pattern.generate(:ctcp, ctcp)) do |message, *args|
|
335
|
-
if self.class.call_hooks(:pre, :ctcp, self, [message])
|
368
|
+
if self.class.call_hooks(:pre, :ctcp, nil, self, [message])
|
336
369
|
__send__("ctcp_#{ctcp.downcase}", message, *args)
|
337
|
-
self.class.call_hooks(:post, :ctcp, self, [message])
|
370
|
+
self.class.call_hooks(:post, :ctcp, nil, self, [message])
|
338
371
|
else
|
339
372
|
@bot.loggers.debug "[plugin] #{self.class.plugin_name}: Dropping message due to hook"
|
340
373
|
end
|
@@ -370,7 +403,11 @@ module Cinch
|
|
370
403
|
|
371
404
|
@bot.loggers.debug "[plugin] #{self.class.plugin_name}: Registering executor with pattern `#{pattern_to_register.inspect}`, reacting on `#{react_on}`"
|
372
405
|
|
373
|
-
new_handler = Handler.new(@bot,
|
406
|
+
new_handler = Handler.new(@bot,
|
407
|
+
react_on,
|
408
|
+
pattern_to_register,
|
409
|
+
group: matcher.group,
|
410
|
+
strip_colors: matcher.strip_colors) do |message, *args|
|
374
411
|
method = method(matcher.method)
|
375
412
|
arity = method.arity - 1
|
376
413
|
if arity > 0
|
@@ -378,9 +415,9 @@ module Cinch
|
|
378
415
|
elsif arity == 0
|
379
416
|
args = []
|
380
417
|
end
|
381
|
-
if self.class.call_hooks(:pre, :match, self, [message])
|
418
|
+
if self.class.call_hooks(:pre, :match, matcher.group, self, [message])
|
382
419
|
method.call(message, *args)
|
383
|
-
self.class.call_hooks(:post, :match, self, [message])
|
420
|
+
self.class.call_hooks(:post, :match, matcher.group, self, [message])
|
384
421
|
else
|
385
422
|
@bot.loggers.debug "[plugin] #{self.class.plugin_name}: Dropping message due to hook"
|
386
423
|
end
|
@@ -454,8 +491,8 @@ module Cinch
|
|
454
491
|
end
|
455
492
|
|
456
493
|
# (see Bot#synchronize)
|
457
|
-
def synchronize(
|
458
|
-
@bot.synchronize(
|
494
|
+
def synchronize(name, &block)
|
495
|
+
@bot.synchronize(name, &block)
|
459
496
|
end
|
460
497
|
|
461
498
|
# Provides access to plugin-specific options.
|
@@ -55,18 +55,12 @@ module Cinch
|
|
55
55
|
password << "." * (8 - (password.size % 8))
|
56
56
|
|
57
57
|
crypted = ""
|
58
|
-
cipher = OpenSSL::Cipher.new("BF")
|
58
|
+
cipher = OpenSSL::Cipher.new("BF-ECB")
|
59
|
+
cipher.key_len = 32 # OpenSSL's default of 16 doesn't work
|
60
|
+
cipher.encrypt
|
61
|
+
cipher.key = secret
|
59
62
|
|
60
|
-
|
61
|
-
# We have to reinitialize this every time because for OpenSSL, "BF" is synonynmous with "BF-CBC", and we do not want CBC
|
62
|
-
cipher.reset
|
63
|
-
cipher.key_len = 32 # OpenSSL's default of 16 doesn't work
|
64
|
-
cipher.encrypt
|
65
|
-
cipher.key = secret
|
66
|
-
|
67
|
-
clear = password.slice!(0, 8)
|
68
|
-
crypted << cipher.update(clear) # we do not want the content of cipher.final
|
69
|
-
end
|
63
|
+
crypted = cipher.update(password) # we do not want the content of cipher.final
|
70
64
|
|
71
65
|
answer = [public.bytesize, public, user, crypted].pack("na*Z*a*")
|
72
66
|
Base64.strict_encode64(answer)
|
data/lib/cinch/syncable.rb
CHANGED
@@ -9,7 +9,7 @@ module Cinch
|
|
9
9
|
attr = attr.to_sym
|
10
10
|
waited = 0
|
11
11
|
while true
|
12
|
-
return if
|
12
|
+
return if attribute_synced?(attr)
|
13
13
|
waited += 1
|
14
14
|
|
15
15
|
if waited % 100 == 0
|
@@ -38,7 +38,7 @@ module Cinch
|
|
38
38
|
|
39
39
|
# @return [Boolean]
|
40
40
|
# @api private
|
41
|
-
def
|
41
|
+
def attribute_synced?(attribute)
|
42
42
|
@synced_attributes.include?(attribute)
|
43
43
|
end
|
44
44
|
|
data/lib/cinch/target.rb
CHANGED
data/lib/cinch/user.rb
CHANGED
@@ -3,22 +3,32 @@ require "cinch/target"
|
|
3
3
|
require "timeout"
|
4
4
|
|
5
5
|
module Cinch
|
6
|
-
# @attr_reader [String] user
|
7
|
-
# @attr_reader [String] host
|
8
|
-
# @attr_reader [String] realname
|
9
6
|
# @attr_reader [String] authname
|
7
|
+
# @attr_reader [String, nil] away The user's away message, or
|
8
|
+
# `nil` if not away.
|
9
|
+
# @attr_reader [Array<Channel>] channels All channels the user is
|
10
|
+
# in.
|
11
|
+
# @attr_reader [String] host
|
10
12
|
# @attr_reader [Integer] idle How long this user has been idle, in seconds.
|
11
13
|
# This is a snapshot of the last WHOIS.
|
14
|
+
# @attr_reader [String] nick The user's nickname
|
15
|
+
# @attr_reader [Boolean] online True if the user is online.
|
16
|
+
# @attr_reader [Boolean] oper True if the user is an IRC operator.
|
17
|
+
# @attr_reader [String] realname
|
18
|
+
# @attr_reader [Boolean] secure True if the user is using a secure
|
19
|
+
# connection, i.e. SSL.
|
12
20
|
# @attr_reader [Time] signed_on_at
|
13
|
-
# @attr_reader [
|
14
|
-
#
|
15
|
-
#
|
21
|
+
# @attr_reader [Boolean] unknown True if the instance references an user who
|
22
|
+
# cannot be found on the server.
|
23
|
+
# @attr_reader [String] user
|
16
24
|
#
|
17
25
|
# @version 2.0.0
|
18
26
|
class User < Target
|
19
27
|
include Syncable
|
20
28
|
|
21
|
-
|
29
|
+
def nick
|
30
|
+
name
|
31
|
+
end
|
22
32
|
|
23
33
|
# @return [String]
|
24
34
|
# @since 1.1.0
|
@@ -26,45 +36,126 @@ module Cinch
|
|
26
36
|
|
27
37
|
# @return [Boolean]
|
28
38
|
attr_reader :synced
|
39
|
+
# @since 2.1.0
|
40
|
+
alias_method :synced?, :synced
|
29
41
|
|
30
42
|
# @return [Boolean]
|
31
|
-
attr_reader :in_whois
|
32
|
-
|
33
43
|
# @api private
|
34
|
-
|
44
|
+
attr_accessor :in_whois
|
45
|
+
|
46
|
+
def user
|
47
|
+
attr(:user, true, false)
|
48
|
+
end
|
49
|
+
|
50
|
+
def host
|
51
|
+
attr(:host, true, false)
|
52
|
+
end
|
53
|
+
|
54
|
+
def realname
|
55
|
+
attr(:realname, true, false)
|
56
|
+
end
|
57
|
+
|
58
|
+
def authname
|
59
|
+
attr(:authname, true, false)
|
60
|
+
end
|
61
|
+
|
62
|
+
def idle
|
63
|
+
attr(:idle, true, false)
|
64
|
+
end
|
65
|
+
|
66
|
+
def signed_on_at
|
67
|
+
attr(:signed_on_at, true, false)
|
68
|
+
end
|
35
69
|
|
36
|
-
# @return [Boolean] True if the instance references an user who
|
37
|
-
# cannot be found on the server.
|
38
|
-
attr_reader :unknown
|
39
|
-
alias_method :unknown?, :unknown
|
40
|
-
undef_method "unknown?"
|
41
|
-
undef_method "unknown"
|
42
70
|
def unknown
|
43
|
-
|
71
|
+
attr(:unknown?, true, false)
|
44
72
|
end
|
73
|
+
alias_method :unknown?, :unknown
|
45
74
|
|
46
|
-
# @return [Boolean] True if the user is online.
|
47
75
|
# @note This attribute will be updated by various events, but
|
48
76
|
# unless {#monitor} is being used, this information cannot be
|
49
77
|
# ensured to be always correct.
|
50
|
-
attr_reader :online
|
51
|
-
alias_method :online?, :online
|
52
|
-
undef_method "online?"
|
53
|
-
undef_method "online"
|
54
78
|
def online
|
55
|
-
|
79
|
+
attr(:online?, true, false)
|
80
|
+
end
|
81
|
+
alias_method :online?, :online
|
82
|
+
|
83
|
+
def channels
|
84
|
+
attr(:channels, true, false)
|
56
85
|
end
|
57
86
|
|
58
|
-
# @return [Boolean] True if the user is using a secure connection, i.e. SSL.
|
59
|
-
attr_reader :secure
|
60
|
-
alias_method :secure?, :secure
|
61
|
-
undef_method "secure?"
|
62
|
-
undef_method "secure"
|
63
87
|
def secure
|
64
|
-
|
88
|
+
attr(:secure?, true, false)
|
89
|
+
end
|
90
|
+
alias_method :secure?, :secure
|
91
|
+
|
92
|
+
# @since 2.1.0
|
93
|
+
def oper
|
94
|
+
attr(:oper?, true, false)
|
95
|
+
end
|
96
|
+
alias_method :oper?, :oper
|
97
|
+
|
98
|
+
# @private
|
99
|
+
def user_unsynced
|
100
|
+
attr(:user, true, true)
|
101
|
+
end
|
102
|
+
|
103
|
+
# @private
|
104
|
+
def host_unsynced
|
105
|
+
attr(:host, true, true)
|
106
|
+
end
|
107
|
+
|
108
|
+
# @private
|
109
|
+
def realname_unsynced
|
110
|
+
attr(:realname, true, true)
|
111
|
+
end
|
112
|
+
|
113
|
+
# @private
|
114
|
+
def authname_unynced
|
115
|
+
attr(:authname, true, true)
|
116
|
+
end
|
117
|
+
|
118
|
+
# @private
|
119
|
+
def idle_unsynced
|
120
|
+
attr(:idle, true, true)
|
121
|
+
end
|
122
|
+
|
123
|
+
# @private
|
124
|
+
def signed_on_at_unsynced
|
125
|
+
attr(:signed_on_at, true, true)
|
126
|
+
end
|
127
|
+
|
128
|
+
# @private
|
129
|
+
def unknown_unsynced
|
130
|
+
attr(:unknown?, true, true)
|
131
|
+
end
|
132
|
+
alias_method "unknown?_unsynced", "unknown_unsynced"
|
133
|
+
|
134
|
+
# @private
|
135
|
+
def online_unsynced
|
136
|
+
attr(:online?, true, true)
|
65
137
|
end
|
138
|
+
alias_method "online?_unsynced", "online_unsynced"
|
66
139
|
|
67
|
-
#
|
140
|
+
# @private
|
141
|
+
def channels_unsynced
|
142
|
+
attr(:channels, true, true)
|
143
|
+
end
|
144
|
+
|
145
|
+
# @private
|
146
|
+
def secure_unsynced
|
147
|
+
attr(:secure?, true, true)
|
148
|
+
end
|
149
|
+
alias_method "secure?_unsynced", "secure_unsynced"
|
150
|
+
|
151
|
+
# @private
|
152
|
+
# @since 2.1.0
|
153
|
+
def oper_unsynced
|
154
|
+
attr(:oper?, true, true)
|
155
|
+
end
|
156
|
+
alias_method "oper?_unsynced", "oper_unsynced"
|
157
|
+
|
158
|
+
# By default, you can use methods like {#user}, {#host} and
|
68
159
|
# alike – If you however fear that another thread might change
|
69
160
|
# data while you're using it and if this means a critical issue to
|
70
161
|
# your code, you can store a clone of the result of this method
|
@@ -82,7 +173,10 @@ module Cinch
|
|
82
173
|
# @return [Boolean] True if the user is being monitored
|
83
174
|
# @see #monitor
|
84
175
|
# @see #unmonitor
|
176
|
+
# @note The attribute writer is in fact part of the private API
|
85
177
|
attr_reader :monitored
|
178
|
+
# @since 2.1.0
|
179
|
+
alias_method :monitored?, :monitored
|
86
180
|
|
87
181
|
# @api private
|
88
182
|
attr_writer :monitored
|
@@ -101,6 +195,7 @@ module Cinch
|
|
101
195
|
:channels => [],
|
102
196
|
:secure? => false,
|
103
197
|
:away => nil,
|
198
|
+
:oper? => false,
|
104
199
|
}
|
105
200
|
case args.size
|
106
201
|
when 2
|
@@ -114,7 +209,7 @@ module Cinch
|
|
114
209
|
@synced_attributes = Set.new
|
115
210
|
|
116
211
|
@when_requesting_synced_attribute = lambda {|attr|
|
117
|
-
unless
|
212
|
+
unless attribute_synced?(attr)
|
118
213
|
@data[:unknown?] = false
|
119
214
|
unsync :unknown?
|
120
215
|
|
@@ -197,8 +292,9 @@ module Cinch
|
|
197
292
|
|
198
293
|
{
|
199
294
|
:authname => nil,
|
200
|
-
:idle
|
201
|
-
:secure?
|
295
|
+
:idle => 0,
|
296
|
+
:secure? => false,
|
297
|
+
:oper? => false,
|
202
298
|
:channels => [],
|
203
299
|
}.merge(values).each do |attr, value|
|
204
300
|
sync(attr, value, true)
|
@@ -371,28 +467,5 @@ module Cinch
|
|
371
467
|
unsync(:authname)
|
372
468
|
@bot.user_list.update_nick(self)
|
373
469
|
end
|
374
|
-
|
375
|
-
# Provides synced access to user attributes.
|
376
|
-
def method_missing(m, *args)
|
377
|
-
if m.to_s =~ /^(.+)_unsynced$/
|
378
|
-
m = $1.to_sym
|
379
|
-
unsync = true
|
380
|
-
end
|
381
|
-
|
382
|
-
if @data.has_key?(m)
|
383
|
-
attr(m, true, unsync)
|
384
|
-
else
|
385
|
-
super
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
# @since 1.1.2
|
390
|
-
def respond_to?(m, include_all = false)
|
391
|
-
if m.to_s =~ /^(.+)_unsynced$/
|
392
|
-
m = $1.to_sym
|
393
|
-
end
|
394
|
-
|
395
|
-
return @data.has_key?(m) || super
|
396
|
-
end
|
397
470
|
end
|
398
471
|
end
|
data/lib/cinch/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cinch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-02-27 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: A simple, friendly DSL for creating IRC bots
|
15
15
|
email:
|
@@ -103,7 +103,6 @@ files:
|
|
103
103
|
- examples/plugins/own_events.rb
|
104
104
|
- examples/plugins/seen.rb
|
105
105
|
- examples/plugins/last_nick.rb
|
106
|
-
- examples/plugins/urban_dict.rb
|
107
106
|
- examples/plugins/hooks.rb
|
108
107
|
- examples/plugins/hello.rb
|
109
108
|
- examples/plugins/custom_prefix.rb
|
@@ -142,3 +141,4 @@ signing_key:
|
|
142
141
|
specification_version: 3
|
143
142
|
summary: An IRC Bot Building Framework
|
144
143
|
test_files: []
|
144
|
+
has_rdoc: yard
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'cinch'
|
2
|
-
require 'open-uri'
|
3
|
-
require 'nokogiri'
|
4
|
-
require 'cgi'
|
5
|
-
|
6
|
-
class UrbanDictionary
|
7
|
-
include Cinch::Plugin
|
8
|
-
|
9
|
-
match /urban (.+)/
|
10
|
-
def lookup(word)
|
11
|
-
url = "http://www.urbandictionary.com/define.php?term=#{CGI.escape(word)}"
|
12
|
-
CGI.unescape_html Nokogiri::HTML(open(url)).at("div.definition").text.gsub(/\s+/, ' ') rescue nil
|
13
|
-
end
|
14
|
-
|
15
|
-
def execute(m, word)
|
16
|
-
m.reply(lookup(word) || "No results found", true)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
bot = Cinch::Bot.new do
|
21
|
-
configure do |c|
|
22
|
-
c.server = "irc.freenode.net"
|
23
|
-
c.nick = "MrCinch"
|
24
|
-
c.channels = ["#cinch-bots"]
|
25
|
-
c.plugins.plugins = [UrbanDictionary]
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
bot.start
|
30
|
-
|