cinch 1.1.3 → 2.0.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -0
- data/README.md +3 -3
- data/docs/bot_options.md +435 -0
- data/docs/changes.md +440 -0
- data/docs/common_mistakes.md +35 -0
- data/docs/common_tasks.md +47 -0
- data/docs/encodings.md +67 -0
- data/docs/events.md +272 -0
- data/docs/logging.md +5 -0
- data/docs/migrating.md +267 -0
- data/docs/readme.md +18 -0
- data/examples/plugins/custom_prefix.rb +1 -1
- data/examples/plugins/dice_roll.rb +38 -0
- data/examples/plugins/lambdas.rb +1 -1
- data/examples/plugins/memo.rb +16 -10
- data/examples/plugins/url_shorten.rb +1 -0
- data/lib/cinch.rb +5 -60
- data/lib/cinch/ban.rb +13 -7
- data/lib/cinch/bot.rb +228 -403
- data/lib/cinch/{cache_manager.rb → cached_list.rb} +5 -1
- data/lib/cinch/callback.rb +3 -0
- data/lib/cinch/channel.rb +119 -195
- data/lib/cinch/{channel_manager.rb → channel_list.rb} +6 -3
- data/lib/cinch/configuration.rb +73 -0
- data/lib/cinch/configuration/bot.rb +47 -0
- data/lib/cinch/configuration/dcc.rb +16 -0
- data/lib/cinch/configuration/plugins.rb +41 -0
- data/lib/cinch/configuration/sasl.rb +17 -0
- data/lib/cinch/configuration/ssl.rb +19 -0
- data/lib/cinch/configuration/storage.rb +37 -0
- data/lib/cinch/configuration/timeouts.rb +14 -0
- data/lib/cinch/constants.rb +531 -369
- data/lib/cinch/dcc.rb +12 -0
- data/lib/cinch/dcc/dccable_object.rb +37 -0
- data/lib/cinch/dcc/incoming.rb +1 -0
- data/lib/cinch/dcc/incoming/send.rb +131 -0
- data/lib/cinch/dcc/outgoing.rb +1 -0
- data/lib/cinch/dcc/outgoing/send.rb +115 -0
- data/lib/cinch/exceptions.rb +8 -1
- data/lib/cinch/formatting.rb +106 -0
- data/lib/cinch/handler.rb +104 -0
- data/lib/cinch/handler_list.rb +86 -0
- data/lib/cinch/helpers.rb +167 -10
- data/lib/cinch/irc.rb +525 -110
- data/lib/cinch/isupport.rb +11 -9
- data/lib/cinch/logger.rb +168 -0
- data/lib/cinch/logger/formatted_logger.rb +72 -55
- data/lib/cinch/logger/zcbot_logger.rb +9 -24
- data/lib/cinch/logger_list.rb +62 -0
- data/lib/cinch/mask.rb +19 -10
- data/lib/cinch/message.rb +94 -28
- data/lib/cinch/message_queue.rb +70 -28
- data/lib/cinch/mode_parser.rb +6 -1
- data/lib/cinch/network.rb +104 -0
- data/lib/cinch/{rubyext/queue.rb → open_ended_queue.rb} +8 -1
- data/lib/cinch/pattern.rb +24 -4
- data/lib/cinch/plugin.rb +352 -177
- data/lib/cinch/plugin_list.rb +35 -0
- data/lib/cinch/rubyext/float.rb +3 -0
- data/lib/cinch/rubyext/module.rb +7 -0
- data/lib/cinch/rubyext/string.rb +9 -0
- data/lib/cinch/sasl.rb +34 -0
- data/lib/cinch/sasl/dh_blowfish.rb +71 -0
- data/lib/cinch/sasl/diffie_hellman.rb +47 -0
- data/lib/cinch/sasl/mechanism.rb +6 -0
- data/lib/cinch/sasl/plain.rb +26 -0
- data/lib/cinch/storage.rb +62 -0
- data/lib/cinch/storage/null.rb +12 -0
- data/lib/cinch/storage/yaml.rb +96 -0
- data/lib/cinch/syncable.rb +13 -1
- data/lib/cinch/target.rb +144 -0
- data/lib/cinch/timer.rb +145 -0
- data/lib/cinch/user.rb +169 -225
- data/lib/cinch/{user_manager.rb → user_list.rb} +7 -2
- data/lib/cinch/utilities/deprecation.rb +12 -0
- data/lib/cinch/utilities/encoding.rb +54 -0
- data/lib/cinch/utilities/kernel.rb +13 -0
- data/lib/cinch/utilities/string.rb +13 -0
- data/lib/cinch/version.rb +4 -0
- metadata +88 -47
- data/lib/cinch/logger/logger.rb +0 -44
- data/lib/cinch/logger/null_logger.rb +0 -18
- data/lib/cinch/rubyext/infinity.rb +0 -1
data/docs/readme.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# @title README
|
2
|
+
|
3
|
+
|
4
|
+
- {file:changes.md Changelog}
|
5
|
+
- {file:migrating.md Migration guides}
|
6
|
+
|
7
|
+
# Important documents
|
8
|
+
|
9
|
+
The following is a list of important documents to read.
|
10
|
+
|
11
|
+
- {file:bot_options.md Bot options}
|
12
|
+
- {file:common_tasks.md Common Tasks}
|
13
|
+
- {file:common_mistakes.md Common mistakes}
|
14
|
+
- {Cinch::DCC DCC}
|
15
|
+
- {file:encodings.md Encodings}
|
16
|
+
- {file:logging.md Logging}
|
17
|
+
- {Cinch::SASL SASL}
|
18
|
+
- {file:events.d Events}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'cinch'
|
2
|
+
|
3
|
+
class DiceRoll
|
4
|
+
include Cinch::Plugin
|
5
|
+
|
6
|
+
# [[<repeats>#]<rolls>]d<sides>[<+/-><offset>]
|
7
|
+
match(/roll (?:(?:(\d+)#)?(\d+))?d(\d+)(?:([+-])(\d+))?/)
|
8
|
+
def execute(m, repeats, rolls, sides, offset_op, offset)
|
9
|
+
repeats = repeats.to_i
|
10
|
+
repeats = 1 if repeats < 1
|
11
|
+
rolls = rolls.to_i
|
12
|
+
rolls = 1 if rolls < 1
|
13
|
+
|
14
|
+
total = 0
|
15
|
+
|
16
|
+
repeats.times do
|
17
|
+
rolls.times do
|
18
|
+
score = rand(sides.to_i) + 1
|
19
|
+
if offset_op
|
20
|
+
score = score.send(offset_op, offset.to_i)
|
21
|
+
end
|
22
|
+
total += score
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
m.reply "Your dice roll was: #{total}", true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
bot = Cinch::Bot.new do
|
31
|
+
configure do |c|
|
32
|
+
c.server = 'irc.freenode.org'
|
33
|
+
c.channels = ['#cinch-bots']
|
34
|
+
c.plugins.plugins = [DiceRoll]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
bot.start
|
data/examples/plugins/lambdas.rb
CHANGED
@@ -9,7 +9,7 @@ class DirectAddressing
|
|
9
9
|
#
|
10
10
|
# The reason we are using a lambda is that the bot's nick can change
|
11
11
|
# and the prefix has to be up to date.
|
12
|
-
prefix lambda{ |m| m.bot.nick + ": " }
|
12
|
+
prefix lambda{ |m| Regexp.new("^" + Regexp.escape(m.bot.nick + ": " ))}
|
13
13
|
|
14
14
|
match "hello", method: :greet
|
15
15
|
def greet(m)
|
data/examples/plugins/memo.rb
CHANGED
@@ -1,39 +1,42 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'cinch'
|
4
|
+
require 'cinch/storage/yaml'
|
4
5
|
|
5
6
|
class Memo
|
6
|
-
class MemoStruct < Struct.new(:
|
7
|
+
class MemoStruct < Struct.new(:user, :channel, :text, :time)
|
7
8
|
def to_s
|
8
|
-
"[#{time.asctime}] <#{channel}/#{
|
9
|
+
"[#{time.asctime}] <#{channel}/#{user}> #{text}"
|
9
10
|
end
|
10
11
|
end
|
11
12
|
|
12
13
|
include Cinch::Plugin
|
13
14
|
|
14
|
-
listen_to :message
|
15
|
-
match /memo (.+?) (.+)/
|
16
|
-
|
17
15
|
def initialize(*args)
|
18
16
|
super
|
19
|
-
|
17
|
+
storage[:memos] ||= {}
|
20
18
|
end
|
21
19
|
|
20
|
+
listen_to :message
|
21
|
+
match /memo (.+?) (.+)/
|
22
|
+
|
22
23
|
def listen(m)
|
23
|
-
if
|
24
|
-
m.user.send
|
24
|
+
if storage[:memos].has_key?(m.user.nick)
|
25
|
+
m.user.send storage[:memos].delete(m.user.nick).to_s
|
26
|
+
storage.save
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
28
30
|
def execute(m, nick, message)
|
29
|
-
if
|
31
|
+
if storage[:memos].key?(nick)
|
30
32
|
m.reply "There's already a memo for #{nick}. You can only store one right now"
|
31
33
|
elsif nick == m.user.nick
|
32
34
|
m.reply "You can't leave memos for yourself.."
|
33
35
|
elsif nick == bot.nick
|
34
36
|
m.reply "You can't leave memos for me.."
|
35
37
|
else
|
36
|
-
|
38
|
+
storage[:memos][nick] = MemoStruct.new(m.user.name, m.channel.name, message, Time.now)
|
39
|
+
storage.save
|
37
40
|
m.reply "Added memo for #{nick}"
|
38
41
|
end
|
39
42
|
end
|
@@ -44,6 +47,9 @@ bot = Cinch::Bot.new do
|
|
44
47
|
c.server = "irc.freenode.org"
|
45
48
|
c.channels = ["#cinch-bots"]
|
46
49
|
c.plugins.plugins = [Memo]
|
50
|
+
c.storage.backend = Cinch::Storage::YAML
|
51
|
+
c.storage.basedir = "./yaml/"
|
52
|
+
c.storage.autosave = true
|
47
53
|
end
|
48
54
|
end
|
49
55
|
|
data/lib/cinch.rb
CHANGED
@@ -1,61 +1,6 @@
|
|
1
|
+
require 'cinch/version'
|
2
|
+
require 'cinch/utilities/kernel'
|
3
|
+
require 'cinch/utilities/string'
|
4
|
+
require 'cinch/utilities/deprecation'
|
5
|
+
require 'cinch/utilities/encoding'
|
1
6
|
require 'cinch/bot'
|
2
|
-
|
3
|
-
module Cinch
|
4
|
-
VERSION = '1.1.3'
|
5
|
-
|
6
|
-
# @return [String]
|
7
|
-
# @todo Handle mIRC color codes more gracefully.
|
8
|
-
# @api private
|
9
|
-
def self.filter_string(string)
|
10
|
-
string.gsub(/[\x00-\x1f]/, '')
|
11
|
-
end
|
12
|
-
|
13
|
-
# @api private
|
14
|
-
def self.encode_incoming(string, encoding)
|
15
|
-
string = string.dup
|
16
|
-
if encoding == :irc
|
17
|
-
# If incoming text is valid UTF-8, it will be interpreted as
|
18
|
-
# such. If it fails validation, a CP1252 -> UTF-8 conversion
|
19
|
-
# is performed. This allows you to see non-ASCII from mIRC
|
20
|
-
# users (non-UTF-8) and other users sending you UTF-8.
|
21
|
-
#
|
22
|
-
# (from http://xchat.org/encoding/#hybrid)
|
23
|
-
string.force_encoding("UTF-8")
|
24
|
-
if !string.valid_encoding?
|
25
|
-
string.force_encoding("CP1252").encode!("UTF-8", {:invalid => :replace, :undef => :replace})
|
26
|
-
end
|
27
|
-
else
|
28
|
-
string.force_encoding(encoding).encode!({:invalid => :replace, :undef => :replace})
|
29
|
-
string = string.chars.select { |c| c.valid_encoding? }.join
|
30
|
-
end
|
31
|
-
|
32
|
-
return string
|
33
|
-
end
|
34
|
-
|
35
|
-
# @api private
|
36
|
-
def self.encode_outgoing(string, encoding)
|
37
|
-
string = string.dup
|
38
|
-
if encoding == :irc
|
39
|
-
# If your text contains only characters that fit inside the CP1252
|
40
|
-
# code page (aka Windows Latin-1), the entire line will be sent
|
41
|
-
# that way. mIRC users should see it correctly. XChat users who
|
42
|
-
# are using UTF-8 will also see it correctly, because it will fail
|
43
|
-
# UTF-8 validation and will be assumed to be CP1252, even by older
|
44
|
-
# XChat versions.
|
45
|
-
#
|
46
|
-
# If the text doesn't fit inside the CP1252 code page, (for eaxmple if you
|
47
|
-
# type Eastern European characters, or Russian) it will be sent as UTF-8. Only
|
48
|
-
# UTF-8 capable clients will be able to see these characters correctly
|
49
|
-
#
|
50
|
-
# (from http://xchat.org/encoding/#hybrid)
|
51
|
-
begin
|
52
|
-
string.encode!("CP1252")
|
53
|
-
rescue Encoding::UndefinedConversionError
|
54
|
-
end
|
55
|
-
else
|
56
|
-
string.encode!(encoding, {:invalid => :replace, :undef => :replace})
|
57
|
-
end
|
58
|
-
|
59
|
-
return string
|
60
|
-
end
|
61
|
-
end
|
data/lib/cinch/ban.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
require "cinch/mask"
|
2
2
|
module Cinch
|
3
|
+
# This class represents channel bans.
|
3
4
|
class Ban
|
4
|
-
# @return [Mask
|
5
|
+
# @return [Mask] A {Mask} object for non-extended bans
|
6
|
+
# @return [String] A String object for extended bans (see {#extended})
|
5
7
|
attr_reader :mask
|
6
8
|
|
7
|
-
#
|
9
|
+
# The user who created the ban. Might be nil on networks that do
|
10
|
+
# not strictly follow the RFCs, for example IRCnet in some(?)
|
11
|
+
# cases.
|
12
|
+
#
|
13
|
+
# @return [User, nil] The user who created the ban
|
8
14
|
attr_reader :by
|
9
15
|
|
10
16
|
# @return [Time]
|
@@ -13,17 +19,17 @@ module Cinch
|
|
13
19
|
# @return [Boolean] whether this is an extended ban (as used by for example Freenode)
|
14
20
|
attr_reader :extended
|
15
21
|
|
16
|
-
# @param [String] mask The mask
|
17
|
-
# @param [User] by The user who created the ban
|
22
|
+
# @param [String, Mask] mask The mask
|
23
|
+
# @param [User, nil] by The user who created the ban.
|
18
24
|
# @param [Time] at The time at which the ban was created
|
19
25
|
def initialize(mask, by, at)
|
20
26
|
@by, @created_at = by, at
|
21
|
-
if mask =~
|
27
|
+
if mask =~ /^[\$~]/
|
22
28
|
@extended = true
|
23
29
|
@mask = mask
|
24
30
|
else
|
25
31
|
@extended = false
|
26
|
-
@mask = Mask.
|
32
|
+
@mask = Mask.from(mask)
|
27
33
|
end
|
28
34
|
end
|
29
35
|
|
@@ -31,7 +37,7 @@ module Cinch
|
|
31
37
|
# @raise [Exceptions::UnsupportedFeature] Cinch does not support
|
32
38
|
# Freenode's extended bans
|
33
39
|
def match(user)
|
34
|
-
raise UnsupportedFeature, "extended bans
|
40
|
+
raise UnsupportedFeature, "extended bans are not supported yet" if @extended
|
35
41
|
@mask =~ user
|
36
42
|
end
|
37
43
|
alias_method :=~, :match
|
data/lib/cinch/bot.rb
CHANGED
@@ -3,20 +3,23 @@ require 'socket'
|
|
3
3
|
require "thread"
|
4
4
|
require "ostruct"
|
5
5
|
require "cinch/rubyext/module"
|
6
|
-
require "cinch/rubyext/queue"
|
7
6
|
require "cinch/rubyext/string"
|
8
|
-
require "cinch/rubyext/
|
7
|
+
require "cinch/rubyext/float"
|
9
8
|
|
10
9
|
require "cinch/exceptions"
|
11
10
|
|
11
|
+
require "cinch/handler"
|
12
12
|
require "cinch/helpers"
|
13
|
-
|
14
|
-
require "cinch/
|
13
|
+
|
14
|
+
require "cinch/logger_list"
|
15
|
+
require "cinch/logger"
|
16
|
+
|
15
17
|
require "cinch/logger/formatted_logger"
|
16
18
|
require "cinch/syncable"
|
17
19
|
require "cinch/message"
|
18
20
|
require "cinch/message_queue"
|
19
21
|
require "cinch/irc"
|
22
|
+
require "cinch/target"
|
20
23
|
require "cinch/channel"
|
21
24
|
require "cinch/user"
|
22
25
|
require "cinch/constants"
|
@@ -27,81 +30,103 @@ require "cinch/isupport"
|
|
27
30
|
require "cinch/plugin"
|
28
31
|
require "cinch/pattern"
|
29
32
|
require "cinch/mode_parser"
|
30
|
-
require "cinch/
|
31
|
-
require "cinch/
|
32
|
-
|
33
|
+
require "cinch/dcc"
|
34
|
+
require "cinch/sasl"
|
35
|
+
|
36
|
+
require "cinch/handler_list"
|
37
|
+
require "cinch/cached_list"
|
38
|
+
require "cinch/channel_list"
|
39
|
+
require "cinch/user_list"
|
40
|
+
require "cinch/plugin_list"
|
41
|
+
|
42
|
+
require "cinch/timer"
|
43
|
+
require "cinch/formatting"
|
44
|
+
|
45
|
+
require "cinch/configuration"
|
46
|
+
require "cinch/configuration/bot"
|
47
|
+
require "cinch/configuration/plugins"
|
48
|
+
require "cinch/configuration/ssl"
|
49
|
+
require "cinch/configuration/timeouts"
|
50
|
+
require "cinch/configuration/storage"
|
51
|
+
require "cinch/configuration/dcc"
|
52
|
+
require "cinch/configuration/sasl"
|
33
53
|
|
34
54
|
module Cinch
|
55
|
+
# @attr nick
|
56
|
+
# @version 2.0.0
|
57
|
+
class Bot < User
|
58
|
+
include Helpers
|
59
|
+
|
35
60
|
|
36
|
-
|
37
|
-
# @
|
61
|
+
# @return [Configuration::Bot]
|
62
|
+
# @version 2.0.0
|
38
63
|
attr_reader :config
|
64
|
+
|
65
|
+
# The underlying IRC connection
|
66
|
+
#
|
39
67
|
# @return [IRC]
|
40
68
|
attr_reader :irc
|
41
|
-
|
42
|
-
|
69
|
+
|
70
|
+
# The logger list containing all loggers
|
71
|
+
#
|
72
|
+
# @return [LoggerList]
|
73
|
+
# @since 2.0.0
|
74
|
+
attr_accessor :loggers
|
75
|
+
|
43
76
|
# @return [Array<Channel>] All channels the bot currently is in
|
44
77
|
attr_reader :channels
|
45
|
-
|
46
|
-
|
47
|
-
# @
|
48
|
-
attr_reader :mask
|
49
|
-
# @return [String]
|
50
|
-
attr_reader :user
|
51
|
-
# @return [String]
|
52
|
-
attr_reader :realname
|
53
|
-
# @return [Time]
|
54
|
-
attr_reader :signed_on_at
|
55
|
-
# @return [Array<Plugin>] All registered plugins
|
78
|
+
|
79
|
+
# @return [PluginList] All registered plugins
|
80
|
+
# @version 2.0.0
|
56
81
|
attr_reader :plugins
|
57
|
-
|
58
|
-
# @api private
|
59
|
-
attr_reader :handler_threads
|
82
|
+
|
60
83
|
# @return [Boolean] whether the bot is in the process of disconnecting
|
61
84
|
attr_reader :quitting
|
62
|
-
|
63
|
-
|
64
|
-
# @
|
65
|
-
|
85
|
+
|
86
|
+
# @return [UserList] All {User users} the bot knows about.
|
87
|
+
# @see UserList
|
88
|
+
# @since 1.1.0
|
89
|
+
attr_reader :user_list
|
90
|
+
|
91
|
+
# @return [ChannelList] All {Channel channels} the bot knows about.
|
92
|
+
# @see ChannelList
|
93
|
+
# @since 1.1.0
|
94
|
+
attr_reader :channel_list
|
95
|
+
|
96
|
+
# @return [PluginList] All loaded plugins.
|
97
|
+
# @version 2.0.0
|
98
|
+
attr_reader :plugins
|
99
|
+
|
66
100
|
# @return [Boolean]
|
67
101
|
# @api private
|
68
102
|
attr_accessor :last_connection_was_successful
|
69
103
|
|
70
|
-
# @
|
104
|
+
# @return [Callback]
|
105
|
+
# @api private
|
106
|
+
attr_reader :callback
|
71
107
|
|
72
|
-
#
|
108
|
+
# The {HandlerList}, providing access to all registered plugins
|
109
|
+
# and plugin manipulation as well as {HandlerList#dispatch calling handlers}.
|
73
110
|
#
|
74
|
-
# @
|
75
|
-
# @
|
76
|
-
# @
|
77
|
-
|
78
|
-
# Channel(target).join
|
79
|
-
# end
|
80
|
-
def Channel(channel)
|
81
|
-
return channel if channel.is_a?(Channel)
|
82
|
-
@channel_manager.find_ensured(channel)
|
83
|
-
end
|
111
|
+
# @return [HandlerList]
|
112
|
+
# @see HandlerList
|
113
|
+
# @since 2.0.0
|
114
|
+
attr_reader :handlers
|
84
115
|
|
85
|
-
#
|
116
|
+
# The bot's modes.
|
86
117
|
#
|
87
|
-
# @
|
88
|
-
# @
|
89
|
-
|
90
|
-
|
91
|
-
#
|
92
|
-
# m.reply "%s is named %s and connects from %s" % [user.nick, user.name, user.host]
|
93
|
-
# end
|
94
|
-
def User(user)
|
95
|
-
return user if user.is_a?(User)
|
96
|
-
@user_manager.find_ensured(user)
|
97
|
-
end
|
118
|
+
# @return [Array<String>]
|
119
|
+
# @since 2.0.0
|
120
|
+
attr_reader :modes
|
121
|
+
|
122
|
+
# @group Helper methods
|
98
123
|
|
99
124
|
# Define helper methods in the context of the bot.
|
100
125
|
#
|
101
126
|
# @yield Expects a block containing method definitions
|
102
127
|
# @return [void]
|
103
128
|
def helpers(&b)
|
104
|
-
|
129
|
+
@callback.instance_eval(&b)
|
105
130
|
end
|
106
131
|
|
107
132
|
# Since Cinch uses threads, all handlers can be run
|
@@ -142,234 +167,47 @@ module Cinch
|
|
142
167
|
semaphore.synchronize(&block)
|
143
168
|
end
|
144
169
|
|
145
|
-
# Stop execution of the current {#on} handler.
|
146
|
-
#
|
147
|
-
# @return [void]
|
148
|
-
def halt
|
149
|
-
throw :halt
|
150
|
-
end
|
151
|
-
|
152
170
|
# @endgroup
|
153
|
-
# @group Sending messages
|
154
171
|
|
155
|
-
# Sends a raw message to the server.
|
156
|
-
#
|
157
|
-
# @param [String] command The message to send.
|
158
|
-
# @return [void]
|
159
|
-
# @see IRC#message
|
160
|
-
def raw(command)
|
161
|
-
@irc.message(command)
|
162
|
-
end
|
163
|
-
|
164
|
-
# Sends a PRIVMSG to a recipient (a channel or user).
|
165
|
-
# You should be using {Channel#send} and {User#send} instead.
|
166
|
-
#
|
167
|
-
# @param [String] recipient the recipient
|
168
|
-
# @param [String] text the message to send
|
169
|
-
# @param [Boolean] notice Use NOTICE instead of PRIVMSG?
|
170
|
-
# @return [void]
|
171
|
-
# @see Channel#send
|
172
|
-
# @see User#send
|
173
|
-
# @see #safe_msg
|
174
|
-
def msg(recipient, text, notice = false)
|
175
|
-
text = text.to_s
|
176
|
-
split_start = @config.message_split_start || ""
|
177
|
-
split_end = @config.message_split_end || ""
|
178
|
-
command = notice ? "NOTICE" : "PRIVMSG"
|
179
|
-
|
180
|
-
text.split(/\r\n|\r|\n/).each do |line|
|
181
|
-
maxlength = 510 - (":" + " #{command} " + " :").size
|
182
|
-
maxlength = maxlength - self.mask.to_s.length - recipient.to_s.length
|
183
|
-
maxlength_without_end = maxlength - split_end.bytesize
|
184
|
-
|
185
|
-
if line.bytesize > maxlength
|
186
|
-
splitted = []
|
187
|
-
|
188
|
-
while line.bytesize > maxlength_without_end
|
189
|
-
pos = line.rindex(/\s/, maxlength_without_end)
|
190
|
-
r = pos || maxlength_without_end
|
191
|
-
splitted << line.slice!(0, r) + split_end.tr(" ", "\u00A0")
|
192
|
-
line = split_start.tr(" ", "\u00A0") + line.lstrip
|
193
|
-
end
|
194
|
-
|
195
|
-
splitted << line
|
196
|
-
splitted[0, (@config.max_messages || splitted.size)].each do |string|
|
197
|
-
string.tr!("\u00A0", " ") # clean string from any non-breaking spaces
|
198
|
-
raw("#{command} #{recipient} :#{string}")
|
199
|
-
end
|
200
|
-
else
|
201
|
-
raw("#{command} #{recipient} :#{line}")
|
202
|
-
end
|
203
|
-
end
|
204
|
-
end
|
205
|
-
alias_method :privmsg, :msg
|
206
|
-
alias_method :send, :msg
|
207
|
-
|
208
|
-
# Sends a NOTICE to a recipient (a channel or user).
|
209
|
-
# You should be using {Channel#notice} and {User#notice} instead.
|
210
|
-
#
|
211
|
-
# @param [String] recipient the recipient
|
212
|
-
# @param [String] text the message to send
|
213
|
-
# @return [void]
|
214
|
-
# @see Channel#notice
|
215
|
-
# @see User#notice
|
216
|
-
# @see #safe_notice
|
217
|
-
def notice(recipient, text)
|
218
|
-
msg(recipient, text, true)
|
219
|
-
end
|
220
|
-
|
221
|
-
# Like {#msg}, but remove any non-printable characters from
|
222
|
-
# `text`. The purpose of this method is to send text of untrusted
|
223
|
-
# sources, like other users or feeds.
|
224
|
-
#
|
225
|
-
# Note: this will **break** any mIRC color codes embedded in the
|
226
|
-
# string.
|
227
|
-
#
|
228
|
-
# @return (see #msg)
|
229
|
-
# @param (see #msg)
|
230
|
-
# @see #msg
|
231
|
-
# @see User#safe_send
|
232
|
-
# @see Channel#safe_send
|
233
|
-
# @todo Handle mIRC color codes more gracefully.
|
234
|
-
def safe_msg(recipient, text)
|
235
|
-
msg(recipient, Cinch.filter_string(text))
|
236
|
-
end
|
237
|
-
alias_method :safe_privmsg, :safe_msg
|
238
|
-
alias_method :safe_send, :safe_msg
|
239
|
-
|
240
|
-
# Like {#safe_msg} but for notices.
|
241
|
-
#
|
242
|
-
# @return (see #safe_msg)
|
243
|
-
# @param (see #safe_msg)
|
244
|
-
# @see #safe_notice
|
245
|
-
# @see #notice
|
246
|
-
# @see User#safe_notice
|
247
|
-
# @see Channel#safe_notice
|
248
|
-
# @todo (see #safe_msg)
|
249
|
-
def safe_notice(recipient, text)
|
250
|
-
msg(recipient, Cinch.filter_string(text), true)
|
251
|
-
end
|
252
|
-
|
253
|
-
# Invoke an action (/me) in/to a recipient (a channel or user).
|
254
|
-
# You should be using {Channel#action} and {User#action} instead.
|
255
|
-
#
|
256
|
-
# @param [String] recipient the recipient
|
257
|
-
# @param [String] text the message to send
|
258
|
-
# @return [void]
|
259
|
-
# @see Channel#action
|
260
|
-
# @see User#action
|
261
|
-
# @see #safe_action
|
262
|
-
def action(recipient, text)
|
263
|
-
raw("PRIVMSG #{recipient} :\001ACTION #{text}\001")
|
264
|
-
end
|
265
|
-
|
266
|
-
# Like {#action}, but remove any non-printable characters from
|
267
|
-
# `text`. The purpose of this method is to send text from
|
268
|
-
# untrusted sources, like other users or feeds.
|
269
|
-
#
|
270
|
-
# Note: this will **break** any mIRC color codes embedded in the
|
271
|
-
# string.
|
272
|
-
#
|
273
|
-
# @param (see #action)
|
274
|
-
# @return (see #action)
|
275
|
-
# @see #action
|
276
|
-
# @see Channel#safe_action
|
277
|
-
# @see User#safe_action
|
278
|
-
# @todo Handle mIRC color codes more gracefully.
|
279
|
-
def safe_action(recipient, text)
|
280
|
-
action(recipient, Cinch.filter_string(text))
|
281
|
-
end
|
282
|
-
|
283
|
-
# @endgroup
|
284
172
|
# @group Events & Plugins
|
285
173
|
|
286
174
|
# Registers a handler.
|
287
175
|
#
|
288
|
-
# @param [String, Symbol, Integer] event the event to match.
|
289
|
-
#
|
290
|
-
#
|
176
|
+
# @param [String, Symbol, Integer] event the event to match. For a
|
177
|
+
# list of available events, check the {file:events.md Events
|
178
|
+
# documentation}.
|
291
179
|
#
|
292
|
-
#
|
293
|
-
# - :private (a private message)
|
294
|
-
# - :message (both channel and private messages)
|
295
|
-
# - :error (handling errors, use a numeric error code as `match`)
|
296
|
-
# - :ctcp (ctcp requests, use a ctcp command as `match`)
|
297
|
-
#
|
298
|
-
# @param [Regexp, String, Integer] match every message of the
|
180
|
+
# @param [Regexp, Pattern, String] regexp every message of the
|
299
181
|
# right event will be checked against this argument and the event
|
300
182
|
# will only be called if it matches
|
301
183
|
#
|
184
|
+
# @param [Array<Object>] *args Arguments that should be passed to
|
185
|
+
# the block, additionally to capture groups of the regexp.
|
186
|
+
#
|
302
187
|
# @yieldparam [String] *args each capture group of the regex will
|
303
|
-
# be one argument to the block.
|
304
|
-
# though
|
188
|
+
# be one argument to the block.
|
305
189
|
#
|
306
|
-
# @return [
|
307
|
-
def on(event,
|
308
|
-
regexps = [*regexps]
|
309
|
-
regexps = [//] if regexps.empty?
|
310
|
-
|
190
|
+
# @return [Handler] The handlers that have been registered
|
191
|
+
def on(event, regexp = //, *args, &block)
|
311
192
|
event = event.to_sym
|
312
193
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
end
|
326
|
-
debug "[on handler] Registering handler with pattern `#{pattern.inspect}`, reacting on `#{event}`"
|
327
|
-
pattern
|
328
|
-
end
|
329
|
-
(@events[event] ||= []) << [regexps, args, block]
|
330
|
-
end
|
194
|
+
pattern = case regexp
|
195
|
+
when Pattern
|
196
|
+
regexp
|
197
|
+
when Regexp
|
198
|
+
Pattern.new(nil, regexp, nil)
|
199
|
+
else
|
200
|
+
if event == :ctcp
|
201
|
+
Pattern.generate(:ctcp, regexp)
|
202
|
+
else
|
203
|
+
Pattern.new(/^/, /#{Regexp.escape(regexp.to_s)}/, /$/)
|
204
|
+
end
|
205
|
+
end
|
331
206
|
|
332
|
-
|
333
|
-
|
334
|
-
# and attached to the event, or nil.
|
335
|
-
# @param [Array] *arguments A list of additional arguments to pass
|
336
|
-
# to event handlers
|
337
|
-
# @return [void]
|
338
|
-
def dispatch(event, msg = nil, *arguments)
|
339
|
-
if handlers = find(event, msg)
|
340
|
-
handlers.each do |handler|
|
341
|
-
regexps, args, block = *handler
|
342
|
-
# calling Message#match multiple times is not a problem
|
343
|
-
# because we cache the result
|
344
|
-
if msg
|
345
|
-
regexp = regexps.find { |rx|
|
346
|
-
msg.match(rx.to_r(msg), event)
|
347
|
-
}
|
348
|
-
captures = msg.match(regexp.to_r(msg), event).captures
|
349
|
-
else
|
350
|
-
captures = []
|
351
|
-
end
|
207
|
+
handler = Handler.new(self, event, pattern, {args: args, execute_in_callback: true}, &block)
|
208
|
+
@handlers.register(handler)
|
352
209
|
|
353
|
-
|
354
|
-
end
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
# Register all plugins from `@config.plugins.plugins`.
|
359
|
-
#
|
360
|
-
# @return [void]
|
361
|
-
def register_plugins
|
362
|
-
@config.plugins.plugins.each do |plugin|
|
363
|
-
register_plugin(plugin)
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
# Registers a plugin.
|
368
|
-
#
|
369
|
-
# @param [Class<Plugin>] plugin The plugin class to register
|
370
|
-
# @return [void]
|
371
|
-
def register_plugin(plugin)
|
372
|
-
@plugins << plugin.new(self)
|
210
|
+
return handler
|
373
211
|
end
|
374
212
|
|
375
213
|
# @endgroup
|
@@ -380,8 +218,8 @@ module Cinch
|
|
380
218
|
#
|
381
219
|
# @yieldparam [Struct] config the bot's config
|
382
220
|
# @return [void]
|
383
|
-
def configure
|
384
|
-
@
|
221
|
+
def configure
|
222
|
+
yield @config
|
385
223
|
end
|
386
224
|
|
387
225
|
# Disconnects from the server.
|
@@ -390,15 +228,11 @@ module Cinch
|
|
390
228
|
# @return [void]
|
391
229
|
def quit(message = nil)
|
392
230
|
@quitting = true
|
393
|
-
command
|
394
|
-
|
231
|
+
command = message ? "QUIT :#{message}" : "QUIT"
|
232
|
+
|
233
|
+
@irc.send command
|
395
234
|
end
|
396
235
|
|
397
|
-
# Connects the bot to a server.
|
398
|
-
#
|
399
|
-
# @param [Boolean] plugins Automatically register plugins from
|
400
|
-
# `@config.plugins.plugins`?
|
401
|
-
# @return [void]
|
402
236
|
# Connects the bot to a server.
|
403
237
|
#
|
404
238
|
# @param [Boolean] plugins Automatically register plugins from
|
@@ -406,21 +240,37 @@ module Cinch
|
|
406
240
|
# @return [void]
|
407
241
|
def start(plugins = true)
|
408
242
|
@reconnects = 0
|
409
|
-
register_plugins if plugins
|
243
|
+
@plugins.register_plugins(@config.plugins.plugins) if plugins
|
410
244
|
|
411
245
|
begin
|
412
|
-
@
|
246
|
+
@user_list.each do |user|
|
413
247
|
user.in_whois = false
|
414
248
|
user.unsync_all
|
415
249
|
end # reset state of all users
|
416
250
|
|
417
|
-
@
|
251
|
+
@channel_list.each do |channel|
|
418
252
|
channel.unsync_all
|
419
253
|
end # reset state of all channels
|
420
254
|
|
421
|
-
@
|
255
|
+
@join_handler.unregister if @join_handler
|
256
|
+
@join_timer.stop if @join_timer
|
257
|
+
|
258
|
+
join_lambda = lambda { @config.channels.each { |channel| Channel(channel).join }}
|
259
|
+
|
260
|
+
if @config.delay_joins.is_a?(Symbol)
|
261
|
+
@join_handler = join_handler = on(@config.delay_joins) {
|
262
|
+
join_handler.unregister
|
263
|
+
join_lambda.call
|
264
|
+
}.first
|
265
|
+
else
|
266
|
+
@join_timer = Timer.new(self, interval: @config.delay_joins, shots: 1) {
|
267
|
+
join_lambda.call
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
@loggers.info "Connecting to #{@config.server}:#{@config.port}"
|
422
272
|
@irc = IRC.new(self)
|
423
|
-
@irc.
|
273
|
+
@irc.start
|
424
274
|
|
425
275
|
if @config.reconnect && !@quitting
|
426
276
|
# double the delay for each unsuccesful reconnection attempt
|
@@ -431,15 +281,26 @@ module Cinch
|
|
431
281
|
@reconnects += 1
|
432
282
|
end
|
433
283
|
|
434
|
-
#
|
435
|
-
# throttled by the IRC server
|
284
|
+
# Throttle reconnect attempts
|
436
285
|
wait = 2**@reconnects
|
437
|
-
@
|
438
|
-
|
286
|
+
wait = @config.max_reconnect_delay if wait > @config.max_reconnect_delay
|
287
|
+
@loggers.info "Waiting #{wait} seconds before reconnecting"
|
288
|
+
start_time = Time.now
|
289
|
+
while !@quitting && (Time.now - start_time) < wait
|
290
|
+
sleep 1
|
291
|
+
end
|
439
292
|
end
|
440
293
|
end while @config.reconnect and not @quitting
|
441
294
|
end
|
442
295
|
|
296
|
+
# TODO document this
|
297
|
+
def stop
|
298
|
+
@plugins.each do |plugin|
|
299
|
+
plugin.storage.save
|
300
|
+
plugin.storage.unload
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
443
304
|
# @endgroup
|
444
305
|
# @group Channel Control
|
445
306
|
|
@@ -447,30 +308,30 @@ module Cinch
|
|
447
308
|
#
|
448
309
|
# @param [String, Channel] channel either the name of a channel or a {Channel} object
|
449
310
|
# @param [String] key optionally the key of the channel
|
450
|
-
# @return [
|
311
|
+
# @return [Channel] The joined channel
|
451
312
|
# @see Channel#join
|
452
313
|
def join(channel, key = nil)
|
453
|
-
Channel(channel)
|
314
|
+
channel = Channel(channel)
|
315
|
+
channel.join(key)
|
316
|
+
|
317
|
+
channel
|
454
318
|
end
|
455
319
|
|
456
320
|
# Part a channel.
|
457
321
|
#
|
458
322
|
# @param [String, Channel] channel either the name of a channel or a {Channel} object
|
459
323
|
# @param [String] reason an optional reason/part message
|
460
|
-
# @return [
|
324
|
+
# @return [Channel] The channel that was left
|
461
325
|
# @see Channel#part
|
462
326
|
def part(channel, reason = nil)
|
463
|
-
Channel(channel)
|
327
|
+
channel = Channel(channel)
|
328
|
+
channel.part(reason)
|
329
|
+
|
330
|
+
channel
|
464
331
|
end
|
465
332
|
|
466
333
|
# @endgroup
|
467
334
|
|
468
|
-
# (see Logger::Logger#debug)
|
469
|
-
# @see Logger::Logger#debug
|
470
|
-
def debug(msg)
|
471
|
-
@logger.debug(msg)
|
472
|
-
end
|
473
|
-
|
474
335
|
# @return [Boolean] True if the bot reports ISUPPORT violations as
|
475
336
|
# exceptions.
|
476
337
|
def strict?
|
@@ -479,66 +340,76 @@ module Cinch
|
|
479
340
|
|
480
341
|
# @yield
|
481
342
|
def initialize(&b)
|
482
|
-
@
|
483
|
-
@
|
484
|
-
@config = OpenStruct.new({
|
485
|
-
:server => "localhost",
|
486
|
-
:port => 6667,
|
487
|
-
:ssl => OpenStruct.new({
|
488
|
-
:use => false,
|
489
|
-
:verify => false,
|
490
|
-
:client_cert => nil,
|
491
|
-
:ca_path => "/etc/ssl/certs",
|
492
|
-
}),
|
493
|
-
:password => nil,
|
494
|
-
:nick => "cinch",
|
495
|
-
:nicks => nil,
|
496
|
-
:realname => "cinch",
|
497
|
-
:user => "cinch",
|
498
|
-
:verbose => true,
|
499
|
-
:messages_per_second => 0.5,
|
500
|
-
:server_queue_size => 10,
|
501
|
-
:strictness => :forgiving,
|
502
|
-
:message_split_start => '... ',
|
503
|
-
:message_split_end => ' ...',
|
504
|
-
:max_messages => nil,
|
505
|
-
:plugins => OpenStruct.new({
|
506
|
-
:plugins => [],
|
507
|
-
:prefix => /^!/,
|
508
|
-
:suffix => nil,
|
509
|
-
:options => Hash.new {|h,k| h[k] = {}},
|
510
|
-
}),
|
511
|
-
:channels => [],
|
512
|
-
:encoding => :irc,
|
513
|
-
:reconnect => true,
|
514
|
-
:local_host => nil,
|
515
|
-
:timeouts => OpenStruct.new({
|
516
|
-
:read => 240,
|
517
|
-
:connect => 10,
|
518
|
-
}),
|
519
|
-
:ping_interval => 120,
|
520
|
-
})
|
343
|
+
@loggers = LoggerList.new
|
344
|
+
@loggers << Logger::FormattedLogger.new($stderr)
|
521
345
|
|
346
|
+
@config = Configuration::Bot.new
|
347
|
+
@handlers = HandlerList.new
|
522
348
|
@semaphores_mutex = Mutex.new
|
523
|
-
@semaphores
|
524
|
-
@
|
525
|
-
@
|
526
|
-
@
|
527
|
-
@
|
528
|
-
@quitting = false
|
349
|
+
@semaphores = Hash.new { |h, k| h[k] = Mutex.new }
|
350
|
+
@callback = Callback.new(self)
|
351
|
+
@channels = []
|
352
|
+
@quitting = false
|
353
|
+
@modes = []
|
529
354
|
|
530
|
-
@
|
531
|
-
@
|
355
|
+
@user_list = UserList.new(self)
|
356
|
+
@channel_list = ChannelList.new(self)
|
357
|
+
@plugins = PluginList.new(self)
|
532
358
|
|
359
|
+
super(nil, self)
|
533
360
|
instance_eval(&b) if block_given?
|
361
|
+
end
|
534
362
|
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
363
|
+
# @since 2.0.0
|
364
|
+
# @return [self]
|
365
|
+
# @api private
|
366
|
+
def bot
|
367
|
+
# This method is needed for the Helpers interface
|
368
|
+
self
|
369
|
+
end
|
370
|
+
|
371
|
+
# Sets a mode on the bot.
|
372
|
+
#
|
373
|
+
# @param [String] mode
|
374
|
+
# @return [void]
|
375
|
+
# @since 2.0.0
|
376
|
+
# @see Bot#modes
|
377
|
+
# @see Bot#unset_mode
|
378
|
+
def set_mode(mode)
|
379
|
+
@modes << mode unless @modes.include?(mode)
|
380
|
+
@irc.send "MODE #{nick} +#{mode}"
|
381
|
+
end
|
382
|
+
|
383
|
+
# Unsets a mode on the bot.
|
384
|
+
#
|
385
|
+
# @param [String] mode
|
386
|
+
# @return [void]
|
387
|
+
# @since 2.0.0
|
388
|
+
def unset_mode(mode)
|
389
|
+
@modes.delete(mode)
|
390
|
+
@irc.send "MODE #{nick} -#{mode}"
|
391
|
+
end
|
392
|
+
|
393
|
+
# @since 2.0.0
|
394
|
+
def modes=(modes)
|
395
|
+
(@modes - modes).each do |mode|
|
396
|
+
unset_mode(mode)
|
397
|
+
end
|
398
|
+
|
399
|
+
(modes - @modes).each do |mode|
|
400
|
+
set_mode(mode)
|
539
401
|
end
|
540
402
|
end
|
541
403
|
|
404
|
+
# Used for updating the bot's nick from within the IRC parser.
|
405
|
+
#
|
406
|
+
# @param [String] nick
|
407
|
+
# @api private
|
408
|
+
# @return [String]
|
409
|
+
def set_nick(nick)
|
410
|
+
@name = nick
|
411
|
+
end
|
412
|
+
|
542
413
|
# The bot's nickname.
|
543
414
|
# @overload nick=(new_nick)
|
544
415
|
# @raise [Exceptions::NickTooLong] Raised if the bot is
|
@@ -548,11 +419,8 @@ module Cinch
|
|
548
419
|
# @overload nick
|
549
420
|
# @return [String]
|
550
421
|
# @return [String]
|
551
|
-
attr_accessor :nick
|
552
|
-
undef_method "nick"
|
553
|
-
undef_method "nick="
|
554
422
|
def nick
|
555
|
-
@
|
423
|
+
@name
|
556
424
|
end
|
557
425
|
|
558
426
|
def nick=(new_nick)
|
@@ -560,7 +428,7 @@ module Cinch
|
|
560
428
|
raise Exceptions::NickTooLong, new_nick
|
561
429
|
end
|
562
430
|
@config.nick = new_nick
|
563
|
-
|
431
|
+
@irc.send "NICK #{new_nick}"
|
564
432
|
end
|
565
433
|
|
566
434
|
# Try to create a free nick, first by cycling through all
|
@@ -568,80 +436,37 @@ module Cinch
|
|
568
436
|
#
|
569
437
|
# @param [String] base The base nick to start trying from
|
570
438
|
# @api private
|
571
|
-
# @return String
|
572
|
-
|
439
|
+
# @return [String]
|
440
|
+
# @since 2.0.0
|
441
|
+
def generate_next_nick!(base = nil)
|
573
442
|
nicks = @config.nicks || []
|
574
443
|
|
575
444
|
if base
|
576
445
|
# if `base` is not in our list of nicks to try, assume that it's
|
577
446
|
# custom and just append an underscore
|
578
447
|
if !nicks.include?(base)
|
579
|
-
|
448
|
+
new_nick = base + "_"
|
580
449
|
else
|
581
450
|
# if we have a base, try the next nick or append an
|
582
451
|
# underscore if no more nicks are left
|
583
452
|
new_index = nicks.index(base) + 1
|
584
453
|
if nicks[new_index]
|
585
|
-
|
454
|
+
new_nick = nicks[new_index]
|
586
455
|
else
|
587
|
-
|
456
|
+
new_nick = base + "_"
|
588
457
|
end
|
589
458
|
end
|
590
459
|
else
|
591
460
|
# if we have no base, try the first possible nick
|
592
461
|
new_nick = @config.nicks ? @config.nicks.first : @config.nick
|
593
462
|
end
|
594
|
-
end
|
595
|
-
|
596
|
-
# @return [Boolean] True if the bot is using SSL to connect to the
|
597
|
-
# server.
|
598
|
-
def secure?
|
599
|
-
@config[:ssl] == true || (@config[:ssl].is_a?(Hash) && @config[:ssl][:use])
|
600
|
-
end
|
601
|
-
|
602
|
-
# This method is only provided in order to give Bot and User a
|
603
|
-
# common interface.
|
604
|
-
#
|
605
|
-
# @return [false] Always returns `false`.
|
606
|
-
# @see User#unknown? See User#unknown? for the method's real use.
|
607
|
-
def unknown?
|
608
|
-
false
|
609
|
-
end
|
610
|
-
|
611
|
-
[:host, :mask, :user, :realname, :signed_on_at, :secure?].each do |attr|
|
612
|
-
define_method(attr) do
|
613
|
-
User(nick).__send__(attr)
|
614
|
-
end
|
615
|
-
end
|
616
463
|
|
617
|
-
|
618
|
-
def find(type, msg = nil)
|
619
|
-
if events = @events[type]
|
620
|
-
if msg.nil?
|
621
|
-
return events
|
622
|
-
end
|
623
|
-
|
624
|
-
events.select { |regexps|
|
625
|
-
regexps.first.any? { |regexp|
|
626
|
-
msg.match(regexp.to_r(msg), type)
|
627
|
-
}
|
628
|
-
}
|
629
|
-
end
|
464
|
+
@config.nick = new_nick
|
630
465
|
end
|
631
466
|
|
632
|
-
|
633
|
-
|
634
|
-
@
|
635
|
-
begin
|
636
|
-
catch(:halt) do
|
637
|
-
@callback.instance_exec(msg, *args, *bargs, &block)
|
638
|
-
end
|
639
|
-
rescue => e
|
640
|
-
@logger.log_exception(e)
|
641
|
-
ensure
|
642
|
-
@handler_threads.delete Thread.current
|
643
|
-
end
|
644
|
-
end
|
467
|
+
# @return [String]
|
468
|
+
def inspect
|
469
|
+
"#<Bot nick=#{@name.inspect}>"
|
645
470
|
end
|
646
471
|
end
|
647
472
|
end
|