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 CHANGED
@@ -24,33 +24,39 @@ Installation
24
24
 
25
25
  You can install the latest Cinch gem using RubyGems
26
26
 
27
- gem install cinch
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
- git clone http://github.com/cinchrb/cinch.git
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
- require 'cinch'
44
+ ```ruby
45
+ require 'cinch'
41
46
 
42
- bot = Cinch::Bot.new do
43
- configure do |c|
44
- c.server = "irc.freenode.org"
45
- c.channels = ["#cinch-bots"]
46
- end
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
- on :message, "hello" do |m|
49
- m.reply "Hello, #{m.user.nick}"
50
- end
51
- end
53
+ on :message, "hello" do |m|
54
+ m.reply "Hello, #{m.user.nick}"
55
+ end
56
+ end
52
57
 
53
- bot.start
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
- require 'cinch'
107
+ ```ruby
108
+ require 'cinch'
102
109
 
103
- class Hello
104
- include Cinch::Plugin
110
+ class Hello
111
+ include Cinch::Plugin
105
112
 
106
- match "hello"
113
+ match "hello"
107
114
 
108
- def execute(m)
109
- m.reply "Hello, #{m.user.nick}"
110
- end
111
- end
115
+ def execute(m)
116
+ m.reply "Hello, #{m.user.nick}"
117
+ end
118
+ end
112
119
 
113
- bot = Cinch::Bot.new do
114
- configure do |c|
115
- c.server = "irc.freenode.org"
116
- c.channels = ["#cinch-bots"]
117
- c.plugins.plugins = [Hello]
118
- end
119
- end
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
- bot.start
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
- on :message, /hello/ do |m|
144
- debug "Someone said hello"
145
- end
153
+ ```ruby
154
+ on :message, /hello/ do |m|
155
+ debug "Someone said hello"
156
+ end
157
+ ```
146
158
 
147
159
  Authors
148
160
  -------
@@ -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
 
@@ -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
@@ -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
+
@@ -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::Loggers::FormattedLogger.new(File.open("/tmp/log.log", "a"))
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
 
@@ -8,7 +8,7 @@ class RandomNumberGenerator
8
8
  def start
9
9
  while true
10
10
  sleep 5 # pretend that we are waiting for some kind of entropy
11
- @bot.dispatch(:random_number, nil, Kernel.rand)
11
+ @bot.handlers.dispatch(:random_number, nil, Kernel.rand)
12
12
  end
13
13
  end
14
14
  end
@@ -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>] *args Arguments that should be passed to
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] *args each capture group of the regex will
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
  #
@@ -13,8 +13,8 @@ module Cinch
13
13
  end
14
14
 
15
15
  # (see Bot#synchronize)
16
- def synchronize(*args, &block)
17
- @bot.synchronize(*args, &block)
16
+ def synchronize(name, &block)
17
+ @bot.synchronize(name, &block)
18
18
  end
19
19
  end
20
20
  end
@@ -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 && !synced?(: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)
@@ -70,7 +70,7 @@ module Cinch
70
70
  :reset => 15.chr,
71
71
  }
72
72
 
73
- # @param [Array<Symbol>] *settings The colors and attributes to apply.
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.
@@ -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 = {:group => nil, :execute_in_callback => false, :args => []}.merge(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 [void]
88
+ # @return [Thread]
78
89
  def call(message, captures, arguments)
79
90
  bargs = captures + arguments
80
91
 
81
- @thread_group.add Thread.new {
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]
@@ -19,7 +19,7 @@ module Cinch
19
19
  end
20
20
  end
21
21
 
22
- # @param [Handler, Array<Handler>] *handlers The handlers to unregister
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] *arguments A list of additional arguments to pass
52
+ # @param [Array] arguments A list of additional arguments to pass
53
53
  # to event handlers
54
- # @return [void]
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
@@ -179,8 +179,8 @@ module Cinch
179
179
  # @group Formatting
180
180
 
181
181
  # (see Formatting.format)
182
- def Format(*args)
183
- Formatting.format(*args)
182
+ def Format(*settings, string)
183
+ Formatting.format(*settings, string)
184
184
  end
185
185
  alias_method :Color, :Format
186
186
 
@@ -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])
@@ -55,8 +55,8 @@ module Cinch
55
55
  end
56
56
 
57
57
  # (see Logger#exception)
58
- def exception(message)
59
- each {|l| l.exception(message)}
58
+ def exception(e)
59
+ each {|l| l.exception(e)}
60
60
  end
61
61
  end
62
62
  end
@@ -50,7 +50,7 @@ module Cinch
50
50
  @mask.dup
51
51
  end
52
52
 
53
- # @param [String, #mask]
53
+ # @param [String, #mask] target
54
54
  # @return [target] if already a Mask
55
55
  # @return [Mask]
56
56
  # @version 2.0.0
@@ -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
- if type == :ctcp
140
- @matches[:ctcp][regexp] ||= ctcp_message.match(regexp)
141
- elsif type == :action
142
- @matches[:action][regexp] ||= action_message.match(regexp)
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
- @matches[:other][regexp] ||= message.to_s.match(regexp)
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]
@@ -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, :use_prefix, :use_suffix, :method, :group, :prefix, :suffix, :react_on)
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 = {:use_prefix => true, :use_suffix => true, :method => :execute, :group => nil, :prefix => nil, :suffix => nil, :react_on => nil}.merge(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, :use_suffix, :method, :group, :prefix, :suffix, :react_on))
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 (true) The method to execute.
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
- return hooks.select { |hook| (events & hook.for).size > 0 }
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, react_on, pattern_to_register, group: matcher.group) do |message, *args|
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(*args, &block)
458
- @bot.synchronize(*args, &block)
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
- while password.size > 0 do
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)
@@ -9,7 +9,7 @@ module Cinch
9
9
  attr = attr.to_sym
10
10
  waited = 0
11
11
  while true
12
- return if synced?(attr)
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 synced?(attribute)
41
+ def attribute_synced?(attribute)
42
42
  @synced_attributes.include?(attribute)
43
43
  end
44
44
 
@@ -134,7 +134,7 @@ module Cinch
134
134
  self == other
135
135
  end
136
136
 
137
- # @param [Target, String]
137
+ # @param [Target, String] other
138
138
  # @return [-1, 0, 1, nil]
139
139
  def <=>(other)
140
140
  casemapping = @bot.irc.isupport["CASEMAPPING"]
@@ -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 [Array<Channel>] channels All channels the user is in.
14
- # @attr_reader [String, nil] away The user's away message, or
15
- # `nil` if not away.
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
- alias_method :nick, :name
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
- attr_writer :in_whois
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
- self.unknown?
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
- self.online?
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
- self.secure?
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
- # By default, you can use methods like User#user, User#host and
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 synced?(attr)
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 => 0,
201
- :secure? => false,
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
@@ -8,6 +8,10 @@ module Cinch
8
8
  def self.filter_string(string)
9
9
  string.gsub(/[\x00-\x1f]/, '')
10
10
  end
11
+
12
+ def self.strip_colors(string)
13
+ string.gsub(/[\x02\x0f\x16\x1f\x12]|\x03(\d{1,2}(,\d{1,2})?)?/, '')
14
+ end
11
15
  end
12
16
  end
13
17
  end
@@ -1,4 +1,4 @@
1
1
  module Cinch
2
2
  # Version of the library
3
- VERSION = '2.0.12'
3
+ VERSION = '2.1.0'
4
4
  end
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.12
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-01-30 00:00:00.000000000 Z
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
-