cinch 2.0.12 → 2.1.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/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
|
-
|