butler 1.8.0 → 1.8.1
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/CHANGELOG +40 -0
- data/README +9 -9
- data/Rakefile +15 -71
- data/bin/botcontrol +151 -146
- data/data/butler/dialogs/botcontrol.rb +8 -3
- data/data/butler/dialogs/create.rb +18 -18
- data/data/butler/dialogs/create_config.rb +8 -0
- data/data/butler/dialogs/en/create_config.yaml +2 -0
- data/data/butler/dialogs/en/list.yaml +2 -1
- data/data/butler/dialogs/info.rb +2 -2
- data/data/butler/dialogs/list.rb +13 -8
- data/data/butler/plugins/games/countdown.rb +41 -0
- data/data/butler/plugins/games/roll.rb +59 -0
- data/lib/access.rb +6 -107
- data/lib/access/admin.rb +3 -0
- data/lib/access/role.rb +37 -2
- data/lib/access/savable.rb +5 -0
- data/lib/access/user.rb +21 -2
- data/lib/access/yamlbase.rb +4 -0
- data/lib/butler.rb +4 -4
- data/lib/butler/bot.rb +13 -13
- data/lib/butler/irc/client.rb +10 -2
- data/lib/butler/irc/parser.rb +7 -2
- data/lib/butler/irc/parser/commands.rb +24 -7
- data/lib/butler/irc/parser/generic.rb +27 -315
- data/lib/butler/irc/parser/rfc2812.rb +328 -0
- data/lib/butler/irc/socket.rb +1 -1
- data/lib/butler/irc/user.rb +13 -0
- data/lib/butler/plugin.rb +1 -1
- data/lib/butler/plugin/configproxy.rb +4 -4
- data/lib/butler/plugins.rb +1 -1
- data/lib/butler/version.rb +1 -1
- data/lib/configuration.rb +22 -71
- data/lib/dialogline.rb +12 -0
- data/lib/event.rb +5 -2
- data/lib/installer.rb +52 -24
- data/lib/iterator.rb +17 -7
- data/lib/log.rb +32 -5
- data/lib/log/comfort.rb +33 -16
- data/lib/log/entry.rb +25 -5
- data/lib/log/fakeio.rb +1 -0
- data/lib/log/splitter.rb +6 -2
- data/lib/ostructfixed.rb +5 -0
- data/lib/ruby/exception/detailed.rb +3 -3
- data/lib/scheduler.rb +19 -4
- data/lib/scriptfile.rb +9 -2
- data/lib/string.rb +176 -0
- data/lib/string/ascii.rb +31 -0
- data/lib/string/mbencoded.rb +79 -0
- data/lib/string/sbencoded.rb +77 -0
- data/lib/string/utf8.rb +157 -0
- data/lib/templater.rb +68 -10
- data/lib/w3validator.rb +86 -0
- data/test/irc/serverlistings/test_rpl_hiddenhost.txt +60 -0
- data/test/test_access.rb +101 -0
- data/test/test_configuration.rb +63 -0
- metadata +19 -2
data/lib/access/savable.rb
CHANGED
@@ -7,8 +7,13 @@
|
|
7
7
|
|
8
8
|
|
9
9
|
class Access
|
10
|
+
# Mixin for classes representing records
|
10
11
|
module Savable
|
12
|
+
|
13
|
+
# The Access instance that manages this records base
|
11
14
|
attr_accessor :access
|
15
|
+
|
16
|
+
# The "Base" instance that manages this record
|
12
17
|
attr_accessor :base
|
13
18
|
|
14
19
|
def save
|
data/lib/access/user.rb
CHANGED
@@ -48,11 +48,19 @@ class Access
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
# The record-id
|
51
52
|
attr_reader :id
|
53
|
+
|
54
|
+
# The credentials of the user (password)
|
52
55
|
attr_reader :credentials
|
56
|
+
|
57
|
+
# Privileges of the user (Privileges instance)
|
53
58
|
attr_reader :privileges
|
59
|
+
|
60
|
+
# Roles the user is in (Roles instance)
|
54
61
|
attr_reader :roles
|
55
62
|
|
63
|
+
# Metadata - anything you want
|
56
64
|
attr_accessor :meta
|
57
65
|
|
58
66
|
# The data needed to restore a user.
|
@@ -87,6 +95,10 @@ class Access
|
|
87
95
|
extend(Admin) if @admin
|
88
96
|
end
|
89
97
|
|
98
|
+
# Set the credentials of the user
|
99
|
+
# pass in nil (revokes the password, which will lead to no password being
|
100
|
+
# accepted as correct) or a plaintext password, the method uses
|
101
|
+
# Access#hash_credentials to hash the passed in value.
|
90
102
|
def credentials=(value)
|
91
103
|
@credentials = value ? access.hash_credentials(value, @id) : "*"
|
92
104
|
save
|
@@ -139,32 +151,39 @@ class Access
|
|
139
151
|
save
|
140
152
|
end
|
141
153
|
|
154
|
+
# Set the logged? status of the user to true
|
142
155
|
def login
|
143
156
|
@logged = true
|
144
157
|
end
|
145
158
|
|
159
|
+
# Set the logged? status of the user to false
|
146
160
|
def logout
|
147
161
|
@logged = false
|
148
162
|
end
|
149
163
|
|
164
|
+
# Set the logged? status of the user
|
150
165
|
def logged=(value)
|
151
|
-
@logged = value
|
166
|
+
@logged = !!value
|
152
167
|
end
|
153
168
|
|
169
|
+
# Whether the user is logged in or not
|
154
170
|
def logged?
|
155
171
|
@logged
|
156
172
|
end
|
157
173
|
|
174
|
+
# :nodoc:
|
158
175
|
def eql?(other)
|
159
176
|
self.class == other.class && @id.eql?(other.id)
|
160
177
|
end
|
161
178
|
alias == eql? # in ruby1.8 that's unecessary as == uses eql? per default
|
162
179
|
|
180
|
+
# :nodoc:
|
163
181
|
def hash
|
164
182
|
@id.hash
|
165
183
|
end
|
166
184
|
|
167
|
-
|
185
|
+
# :nodoc:
|
186
|
+
def inspect
|
168
187
|
"#<%s:0x%08x base: %s id: %s credentials: %s %s%s%s>" % [
|
169
188
|
self.class,
|
170
189
|
object_id << 1,
|
data/lib/access/yamlbase.rb
CHANGED
@@ -12,6 +12,7 @@ require 'yaml'
|
|
12
12
|
|
13
13
|
|
14
14
|
class Access
|
15
|
+
|
15
16
|
# An Access compatible storage backend
|
16
17
|
class YAMLBase
|
17
18
|
include Enumerable
|
@@ -37,10 +38,12 @@ class Access
|
|
37
38
|
"#{@path}/#{escape(id)}.yaml"
|
38
39
|
end
|
39
40
|
|
41
|
+
# Escapes path names
|
40
42
|
def escape(id)
|
41
43
|
id.gsub(/[\x00-\x1f.%]/) { |m| "%%%02x"%m }.gsub("/", ".")
|
42
44
|
end
|
43
45
|
|
46
|
+
# Extracts the record-id from a path
|
44
47
|
def path2id(path)
|
45
48
|
path[(@path.length+1)..-6].gsub(".", "/").gsub(/%([\dA-Fa-f]{2})/) { $1.to_i(16).chr }
|
46
49
|
end
|
@@ -84,6 +87,7 @@ class Access
|
|
84
87
|
File.delete(filename(id))
|
85
88
|
end
|
86
89
|
|
90
|
+
# All record id's
|
87
91
|
def keys
|
88
92
|
Dir.glob("#{@path}/*.yaml").map { |path|
|
89
93
|
path2id(path)
|
data/lib/butler.rb
CHANGED
@@ -20,8 +20,8 @@ require 'yaml'
|
|
20
20
|
|
21
21
|
|
22
22
|
class Butler
|
23
|
-
@path
|
24
|
-
@
|
23
|
+
@path = nil
|
24
|
+
@logger = nil
|
25
25
|
extend Log::Comfort
|
26
26
|
|
27
27
|
class <<self
|
@@ -53,7 +53,7 @@ class Butler
|
|
53
53
|
daemonize(path.base) { }
|
54
54
|
begin
|
55
55
|
File.write(pidfile, $$)
|
56
|
-
butler = Bot.new(path, botname)
|
56
|
+
butler = Bot.new(path, botname, opts)
|
57
57
|
path = butler.path
|
58
58
|
$stderr = File.open(path.log+"/error.log", "w")
|
59
59
|
$stdout = File.open(path.log+"/out.log", "w")
|
@@ -81,7 +81,7 @@ class Butler
|
|
81
81
|
pidfile = test_pidfile(path.run, botname)
|
82
82
|
File.write(pidfile, $$)
|
83
83
|
info("Running #{botname} with PID #{$$} interactively")
|
84
|
-
butler = Bot.new(path, botname)
|
84
|
+
butler = Bot.new(path, botname, opts)
|
85
85
|
butler.plugins.load_all
|
86
86
|
butler.login
|
87
87
|
if block then butler.event_loop(&block) else sleep end
|
data/lib/butler/bot.rb
CHANGED
@@ -27,7 +27,7 @@ class Butler
|
|
27
27
|
attr_reader :access
|
28
28
|
attr_reader :base
|
29
29
|
attr_reader :config
|
30
|
-
attr_reader :
|
30
|
+
attr_reader :logger
|
31
31
|
attr_reader :path
|
32
32
|
attr_reader :plugins
|
33
33
|
attr_reader :scheduler
|
@@ -46,7 +46,7 @@ class Butler
|
|
46
46
|
:plugins => @base+'/plugins',
|
47
47
|
:strings => @base+'/strings'
|
48
48
|
)
|
49
|
-
@
|
49
|
+
@logger = $stderr
|
50
50
|
@config = Configuration.new(@base+'/config')
|
51
51
|
@scheduler = Scheduler.new
|
52
52
|
@access = Access.new(
|
@@ -62,9 +62,9 @@ class Butler
|
|
62
62
|
opts.delete(:reconnect_tries) || -1
|
63
63
|
]
|
64
64
|
|
65
|
-
super(@config["connections
|
66
|
-
:
|
67
|
-
:
|
65
|
+
super(@config["connections/main/server"], {
|
66
|
+
:host => @config["connections/main/host"],
|
67
|
+
:port => @config["connections/main/port"],
|
68
68
|
}.merge(opts))
|
69
69
|
|
70
70
|
# { lang => { trigger => SortedSet[ *commands ] } }
|
@@ -140,12 +140,12 @@ class Butler
|
|
140
140
|
@access.default_user = @access["default_user"]
|
141
141
|
@access.default_user.login
|
142
142
|
|
143
|
-
nick = @config["connections
|
144
|
-
pass = @config["connections
|
143
|
+
nick = @config["connections/main/nick"]
|
144
|
+
pass = @config["connections/main/password"]
|
145
145
|
super(
|
146
146
|
nick,
|
147
|
-
@config["connections
|
148
|
-
@config["connections
|
147
|
+
@config["connections/main/user"],
|
148
|
+
@config["connections/main/real"]
|
149
149
|
)
|
150
150
|
info("Logged in")
|
151
151
|
# FIXME, use #same_nick?
|
@@ -155,7 +155,7 @@ class Butler
|
|
155
155
|
@irc.nick(nick)
|
156
156
|
end
|
157
157
|
@irc.identify(pass) if pass
|
158
|
-
join(*@config["connections
|
158
|
+
join(*@config["connections/main/channels"])
|
159
159
|
end
|
160
160
|
|
161
161
|
def on_disconnect(reason)
|
@@ -170,9 +170,9 @@ class Butler
|
|
170
170
|
|
171
171
|
def output_to_logfiles
|
172
172
|
# Errors still go to $stderr, $stdin handles puts as "info" level $stderr prints
|
173
|
-
@
|
174
|
-
$stdout
|
175
|
-
$stderr
|
173
|
+
@logger = Log.file(@path.log+'/error.log')
|
174
|
+
$stdout = Log.forward(@logger, :warn)
|
175
|
+
$stderr = Log.forward(@logger, :info)
|
176
176
|
end
|
177
177
|
|
178
178
|
def inspect # :nodoc:
|
data/lib/butler/irc/client.rb
CHANGED
@@ -35,6 +35,8 @@ class Butler
|
|
35
35
|
# puts "If this point is reached, client has ended"
|
36
36
|
#
|
37
37
|
class Client
|
38
|
+
include Log::Comfort
|
39
|
+
|
38
40
|
class Terminate < RuntimeError; end
|
39
41
|
# Created by Butler::IRC::Client#subscribe() and similar methods
|
40
42
|
Listener = Struct.new(:priority, :callback, :args, :unsubscriber) unless defined? Listener
|
@@ -101,11 +103,12 @@ class Butler
|
|
101
103
|
|
102
104
|
def initialize(server, options={}, &on_disconnect)
|
103
105
|
options = DefaultOptions.merge(options)
|
106
|
+
@logger = nil
|
104
107
|
@users = Users.new(self)
|
105
108
|
@channels = Channels.new(self)
|
106
109
|
@users.channels = @channels
|
107
110
|
@channels.users = @users
|
108
|
-
@parser = Parser.new(self, @users, @channels)
|
111
|
+
@parser = Parser.new(self, @users, @channels, "rfc2812", "generic")
|
109
112
|
|
110
113
|
@client_charset = options.delete(:client_charset)
|
111
114
|
@server_charset = options.delete(:server_charset)
|
@@ -114,7 +117,7 @@ class Butler
|
|
114
117
|
|
115
118
|
@timeout = DefaultTimeout.merge(options.delete(:timeout))
|
116
119
|
|
117
|
-
@irc = Socket.new(server, options) # the socket, all methods to the socket are wrapped
|
120
|
+
@irc = Socket.new(options.delete(:server) || server, options) # the socket, all methods to the socket are wrapped
|
118
121
|
|
119
122
|
@listen = Hash.new { |h,k| h[k] = [] }
|
120
123
|
@listener = {}
|
@@ -126,6 +129,11 @@ class Butler
|
|
126
129
|
subscribe(:PING, 100) { |listener, message| @irc.pong(message.pong) }
|
127
130
|
end
|
128
131
|
|
132
|
+
# Load an additional command-set for @parser
|
133
|
+
def load_command_set(*sets)
|
134
|
+
@parser.commands.load(*sets)
|
135
|
+
end
|
136
|
+
|
129
137
|
# callback is called whenever a message with Message#symbol == symbol
|
130
138
|
# (or on every message if symbol is nil)
|
131
139
|
# priority may be any numeric, higher priority is dispatched to first,
|
data/lib/butler/irc/parser.rb
CHANGED
@@ -50,6 +50,10 @@ class Butler
|
|
50
50
|
class UnknownCommand < RuntimeError; end
|
51
51
|
module ParseError
|
52
52
|
include Exception::Detailed
|
53
|
+
|
54
|
+
def self.extended(obj)
|
55
|
+
obj.initialize_details
|
56
|
+
end
|
53
57
|
end
|
54
58
|
|
55
59
|
RMessage = /\A(?::([^ \0]+) )?([A-Za-z\d]+|\d{3})(?: (.*))?\z/
|
@@ -58,12 +62,13 @@ class Butler
|
|
58
62
|
attr_reader :client
|
59
63
|
attr_reader :users
|
60
64
|
attr_reader :channels
|
65
|
+
attr_reader :commands
|
61
66
|
|
62
|
-
def initialize(client, users, channels)
|
67
|
+
def initialize(client, users, channels, *command_sets)
|
63
68
|
@client = client
|
64
69
|
@users = users
|
65
70
|
@channels = channels
|
66
|
-
@commands = Commands.new
|
71
|
+
@commands = Commands.new(*command_sets)
|
67
72
|
end
|
68
73
|
|
69
74
|
# parses an incomming message and returns a Message object from which you
|
@@ -7,6 +7,7 @@
|
|
7
7
|
|
8
8
|
|
9
9
|
require 'butler/irc/parser'
|
10
|
+
require 'log/comfort'
|
10
11
|
|
11
12
|
|
12
13
|
|
@@ -14,10 +15,22 @@ class Butler
|
|
14
15
|
module IRC
|
15
16
|
class Parser
|
16
17
|
class Commands
|
17
|
-
|
18
|
+
include Log::Comfort
|
19
|
+
|
20
|
+
# enables chdirs
|
21
|
+
BasePath = File.expand_path(File.dirname(__FILE__))
|
22
|
+
|
23
|
+
def initialize(*files)
|
18
24
|
@commands = Hash.new { |h,k| raise IndexError, "Unknown command #{k}" }
|
19
|
-
|
20
|
-
|
25
|
+
load(*files)
|
26
|
+
end
|
27
|
+
|
28
|
+
def load(*files)
|
29
|
+
raise ArgumentError, "Requires at least one argument." if files.empty?
|
30
|
+
files.each { |file|
|
31
|
+
file = "#{BasePath}/#{file}.rb" unless file.include?('/')
|
32
|
+
instance_eval(File.read(file), file)
|
33
|
+
}
|
21
34
|
end
|
22
35
|
|
23
36
|
def add(raw, *args, &proc)
|
@@ -67,10 +80,14 @@ class Butler
|
|
67
80
|
end
|
68
81
|
|
69
82
|
def create_fields(message)
|
70
|
-
if @matcher
|
71
|
-
@
|
72
|
-
|
73
|
-
|
83
|
+
if @matcher then
|
84
|
+
if match = @matcher.match(message.params) then
|
85
|
+
@mapping.zip(match.captures) { |name, value|
|
86
|
+
message.alter_member(name, value, true)
|
87
|
+
}
|
88
|
+
else
|
89
|
+
warn "Matching failed in #{symbol} for #{message.params.inspect}"
|
90
|
+
end
|
74
91
|
end
|
75
92
|
end
|
76
93
|
|
@@ -6,338 +6,50 @@
|
|
6
6
|
|
7
7
|
|
8
8
|
|
9
|
-
# A list with all
|
10
|
-
#
|
9
|
+
# A list with all common, but non-rfc2812 IRC-Commands and their parsing
|
10
|
+
# instructions.
|
11
11
|
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
add("invite", :INVITE, /^([^\s]*) :(.*)/, [:invited, :channel])
|
16
|
-
|
17
|
-
add("join", :JOIN, /^:(.*)/, [:channel]) { |message, parser|
|
18
|
-
if message.from && message.channel then
|
19
|
-
message.from.add_channel(message.channel, :join)
|
20
|
-
message.channel.add_user(message.from, :join)
|
21
|
-
end
|
22
|
-
}
|
23
|
-
|
24
|
-
add("kick", :KICK, /^([^\s]*) ([^\s]*) :(.*)/, [:channel, :for, :text]) { |message, parser|
|
25
|
-
parser.leave_channel(message, :kick, :kicked)
|
26
|
-
}
|
27
|
-
|
28
|
-
add("kill", :KILL, /^([^\s]*) ([^\s]*) (.*)/, [:channel, :for, :text]) { |message, parser|
|
29
|
-
if message.for then
|
30
|
-
message.for.kill
|
31
|
-
parser.channels.delete_user(message.for, :kill)
|
32
|
-
end
|
33
|
-
}
|
34
|
-
|
35
|
-
# FIXME, really ':?' or just plain ':'?
|
36
|
-
add("mode", :MODE, /^([^\s]*) :?(.*)/, [:for, :arguments]) { |message, parser|
|
37
|
-
modifiers = message[:arguments].split(" ")
|
38
|
-
modes = modifiers.shift.split("")
|
39
|
-
flags = {"o" => User::Flags::OP, "v" => User::Flags::VOICE, "u" => User::Flags::UOP}
|
40
|
-
message.create_member(:modes, [])
|
41
|
-
argumentIndex = 0
|
42
|
-
direction = "+"
|
43
|
-
|
44
|
-
modes.each { |mode|
|
45
|
-
# MODEs direction
|
46
|
-
if (["+", "-"].include?(mode)) then
|
47
|
-
direction = mode
|
48
|
-
# MODEs taking an argument
|
49
|
-
elsif (["k", "o", "v", "u"].include?(mode) || (mode == "l" && direction == "+")) then
|
50
|
-
if ["o", "v", "u"].include?(mode) then
|
51
|
-
if (direction == "+") then
|
52
|
-
parser.users[modifiers[argumentIndex]].add_flags(message.channel, flags[mode]) #adding flags to user
|
53
|
-
else
|
54
|
-
parser.users[modifiers[argumentIndex]].delete_flags(message.channel, flags[mode]) #removing flags from user
|
55
|
-
end
|
56
|
-
end
|
57
|
-
message.modes << [direction, mode, modifiers[argumentIndex]]
|
58
|
-
argumentIndex = argumentIndex+1
|
59
|
-
# MODEs without argument
|
60
|
-
else
|
61
|
-
message.modes << [direction, mode, nil]
|
62
|
-
end
|
63
|
-
}
|
64
|
-
}
|
65
|
-
add("nick", :NICK, /^:(.*)/, [:nick]) { |message, parser|
|
66
|
-
message.create_member(:old_nick, message.from.nick)
|
67
|
-
message.from.nick = message.nick if message.from
|
68
|
-
}
|
69
|
-
|
70
|
-
add("notice", :NOTICE, /([^\s]+) :(.*)/, [:for, :text]) { |message, parser|
|
71
|
-
if message.channel then
|
72
|
-
message.create_member(:realm, :public)
|
73
|
-
message.create_method(:private?) { false }
|
74
|
-
message.create_method(:public?) { true }
|
75
|
-
else
|
76
|
-
message.create_member(:realm, :private)
|
77
|
-
message.create_method(:private?) { true }
|
78
|
-
message.create_method(:public?) { false }
|
79
|
-
end
|
80
|
-
}
|
81
|
-
|
82
|
-
add("part", :PART, /^([^\x00\x07\x10\x0D\x20,:]+)(?: :(.*))?/, [:channel, :reason]) { |message, parser|
|
83
|
-
parser.leave_channel(message, :part, :parted)
|
84
|
-
}
|
85
|
-
|
86
|
-
add("ping", :PING, /:(.*)/, [:pong])
|
87
|
-
|
88
|
-
add("pong", :PONG)
|
89
|
-
|
90
|
-
add("privmsg", :PRIVMSG, /([^\s]+) :(.*)/, [:for, :text]) { |message, parser|
|
91
|
-
if message.channel then
|
92
|
-
message.create_member(:realm, :public)
|
93
|
-
message.create_method(:private?) { false }
|
94
|
-
message.create_method(:public?) { true }
|
95
|
-
else
|
96
|
-
message.create_member(:realm, :private)
|
97
|
-
message.create_method(:private?) { true }
|
98
|
-
message.create_method(:public?) { false }
|
99
|
-
end
|
100
|
-
}
|
101
|
-
|
102
|
-
add("quit", :QUIT, /(.*)/, [:text]) { |message, parser|
|
103
|
-
if message.from then
|
104
|
-
message.from.quit
|
105
|
-
parser.channels.delete_user(message.from, :quit)
|
106
|
-
parser.users.delete(message.from, :quit)
|
107
|
-
end
|
108
|
-
}
|
109
|
-
add("topic", :TOPIC, /([^\s]+) :(.*)/, [:channel, :text])
|
110
|
-
|
111
|
-
#Unknown/Non-RFC codes
|
12
|
+
# Seen:
|
13
|
+
# - ConferenceRoom (irc.bluewin.ch)
|
112
14
|
add("007", :UNK_007)
|
15
|
+
|
16
|
+
# Seen:
|
17
|
+
# - ConferenceRoom (irc.bluewin.ch)
|
113
18
|
add("008", :UNK_008)
|
114
|
-
add("009", :UNK_009)
|
115
|
-
add("307", :RPL_REGISTERED_INFO) #FIXME non2812 "ConferenceRoom" bluewin, sent if a nick is registered
|
116
|
-
add("329", :RPL_CHANNEL_INFO) #channel creation time
|
117
|
-
add("377", :UNK_377)
|
118
19
|
|
119
|
-
|
120
|
-
|
121
|
-
add("
|
122
|
-
add("003", :RPL_CREATED)
|
123
|
-
add("004", :RPL_MYINFO)
|
124
|
-
add("005", :RPL_ISUPPORT)
|
20
|
+
# Seen:
|
21
|
+
# - ConferenceRoom (irc.bluewin.ch)
|
22
|
+
add("009", :UNK_009)
|
125
23
|
|
126
|
-
#2** Codes
|
127
|
-
add("200", :RPL_TRACELINK)
|
128
|
-
add("201", :RPL_TRACECONNECTING)
|
129
|
-
add("202", :RPL_TRACEHANDSHAKE)
|
130
|
-
add("203", :RPL_TRACEUNKNOWN)
|
131
|
-
add("204", :RPL_TRACEOPERATOR)
|
132
|
-
add("205", :RPL_TRACEUSER)
|
133
|
-
add("206", :RPL_TRACESERVER)
|
134
|
-
add("207", :RPL_TRACESERVICE)
|
135
|
-
add("208", :RPL_TRACENEWTYPE)
|
136
|
-
add("209", :RPL_TRACECLASS)
|
137
|
-
add("210", :RPL_TRACERECONNECT)
|
138
|
-
add("211", :RPL_STATSLINKINFO)
|
139
|
-
add("212", :RPL_STATSCOMMANDS)
|
140
|
-
add("213", :RPL_STATSCLINE)
|
141
|
-
add("214", :RPL_STATSNLINE)
|
142
|
-
add("215", :RPL_STATSILINE)
|
143
|
-
add("216", :RPL_STATSKLINE)
|
144
|
-
add("217", :RPL_STATSQLINE)
|
145
|
-
add("218", :RPL_STATSYLINE)
|
146
|
-
add("219", :RPL_ENDOFSTATS)
|
147
|
-
add("221", :RPL_UMODEIS)
|
148
|
-
add("231", :RPL_SERVICEINFO)
|
149
|
-
add("232", :RPL_ENDOFSERVICES)
|
150
|
-
add("233", :RPL_SERVICE)
|
151
|
-
add("234", :RPL_SERVLIST)
|
152
|
-
add("235", :RPL_SERVLISTEND)
|
153
|
-
add("240", :RPL_STATSVLINE)
|
154
|
-
add("241", :RPL_STATSLLINE)
|
155
|
-
add("242", :RPL_STATSUPTIME)
|
156
|
-
add("243", :RPL_STATSOLINE)
|
157
|
-
add("244", :RPL_STATSHLINE)
|
158
|
-
add("245", :RPL_STATSSLINE) # RFC 2812 seems to be erroneous, it assigns 244 double
|
159
|
-
add("246", :RPL_STATSPING)
|
160
|
-
add("247", :RPL_STATSBLINE)
|
161
|
-
add("250", :RPL_STATSCONN)
|
162
|
-
add("251", :RPL_LUSERCLIENT)
|
163
|
-
add("252", :RPL_LUSEROP)
|
164
|
-
add("253", :RPL_LUSERUNKNOWN)
|
165
|
-
add("254", :RPL_LUSERCHANNELS)
|
166
|
-
add("255", :RPL_LUSERME)
|
167
|
-
add("256", :RPL_ADMINME)
|
168
|
-
add("257", :RPL_ADMINLOC1)
|
169
|
-
add("258", :RPL_ADMINLOC2)
|
170
|
-
add("259", :RPL_ADMINEMAIL)
|
171
|
-
add("261", :RPL_TRACELOG)
|
172
|
-
add("262", :RPL_TRACEEND)
|
173
|
-
add("263", :RPL_TRYAGAIN)
|
174
24
|
add("265", :RPL_LOCALUSERS)
|
175
25
|
add("266", :RPL_GLOBALUSERS)
|
176
|
-
add("290", :RPL_IDENTIFY_MSG) # FIXME non2812 "hyperion-1.0.2b"
|
177
26
|
|
178
|
-
|
179
|
-
|
180
|
-
add("
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
add("
|
185
|
-
add("311", :RPL_WHOISUSER, /^([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) \* :(.*)/, [:for, :nick, :user, :host, :real])
|
186
|
-
add("312", :RPL_WHOISSERVER)
|
187
|
-
add("313", :RPL_WHOISOPERATOR)
|
188
|
-
add("314", :RPL_WHOWASUSER)
|
189
|
-
add("315", :RPL_ENDOFWHO)
|
190
|
-
add("316", :RPL_WHOISCHANOP)
|
191
|
-
add("317", :RPL_WHOISIDLE, /^([^\s]+) ([^\s]+) ([^:]+) :(.*)/, [:for, :nick, :values, :descriptions]) { |message, parser|
|
192
|
-
values = message[:values].split(" ");
|
193
|
-
descriptions = message[:descriptions].split(", ").map { |desc| desc.gsub(" ", "_").to_sym };
|
194
|
-
message.delete(:values)
|
195
|
-
message.delete(:descriptions)
|
196
|
-
0.upto([values.length, descriptions.length].min-1) { |index|
|
197
|
-
message[descriptions[index]] = values[index]
|
198
|
-
}
|
199
|
-
}
|
200
|
-
add("318", :RPL_ENDOFWHOIS)
|
201
|
-
add("319", :RPL_WHOISCHANNELS, /^([^\s]+) ([^\s]+) :(.*)/, [:for, :nick, :channels]) { |message, parser|
|
202
|
-
# FIXME
|
203
|
-
message.alter_member(:channels, message.channels.split(" ").map { |channel| parser.channels.create(channel) })
|
204
|
-
}
|
27
|
+
# Seen:
|
28
|
+
# - hyperion-1.0.2b (irc.freenode.net)
|
29
|
+
add("290", :RPL_IDENTIFY_MSG)
|
30
|
+
|
31
|
+
# Seen:
|
32
|
+
# - ConferenceRoom (irc.bluewin.ch), sent if a nick is registered
|
33
|
+
add("307", :RPL_REGISTERED_INFO)
|
205
34
|
add("320", :RPL_IDENTIFIED_TO_SERVICES) # possibly hyperion only
|
206
|
-
add("
|
207
|
-
add("322", :RPL_LIST, /^([^\s]+) ([^\s]+) (\d+) :(.*)/, [:for, :channelname, :usercount, :topic])
|
208
|
-
add("323", :RPL_LISTEND)
|
209
|
-
add("324", :RPL_CHANNELMODEIS)
|
210
|
-
add("325", :RPL_UNIQOPIS)
|
211
|
-
add("331", :RPL_NOTOPIC)
|
212
|
-
# :irc.server.net 332 YourNickname #channel :Topic
|
213
|
-
add("332", :RPL_TOPIC, /^([^\s]+) ([^\s]+) :(.*)/, [:for, :channel, :topic]) { |message, parser|
|
214
|
-
message.channel.topic.text = message[:topic]
|
215
|
-
}
|
35
|
+
add("329", :RPL_CHANNEL_INFO) #channel creation time
|
216
36
|
|
217
|
-
|
218
|
-
add("333", :RPL_TOPIC_INFO, /^(
|
37
|
+
# :irc.server.net 333 YourNickname #channel SetByNick 1139902138
|
38
|
+
add("333", :RPL_TOPIC_INFO, /^(\S+) (\S+) (\S+) (\d+)/, [:for, :channel, :set_by, :set_at]) { |message, parser|
|
219
39
|
topic = message.channel.topic
|
220
40
|
topic.set_by = message[:set_by]
|
221
41
|
topic.set_at = message[:set_at]
|
222
42
|
}
|
223
|
-
add("341", :RPL_INVITING)
|
224
|
-
add("342", :RPL_SUMMONING)
|
225
43
|
add("343", :RPL__MAINTENANCE) #mainenance notice?
|
226
|
-
add("
|
227
|
-
|
228
|
-
add("348", :RPL_EXCEPTLIST)
|
229
|
-
add("349", :RPL_ENDOFEXCEPTLIST)
|
230
|
-
add("351", :RPL_VERSION)
|
231
|
-
# :irc.server.net 352 YourNickname <channel> <user> <host> <server> <nick> ( "H" / "G" > ["*"] [ ( "@" / "+" ) ] :<hopcount> <real name>
|
232
|
-
add("352", :RPL_WHOREPLY,
|
233
|
-
/([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([HG])[*%]{0,2}([@+-]{0,3}) :(\d+) (.*)/,
|
234
|
-
[:for, :channel, :user, :host, :server, :nick, :status, :flags, :hopcount, :real]) { |message, parser|
|
235
|
-
#"for", "channel", "user", "host", "server", "nick", "status", "flags", "hopcount", "real"
|
236
|
-
user = parser.users.create(message[:nick])
|
237
|
-
user.update(message[:user], message[:host], message[:real])
|
238
|
-
user.away = message[:status] == "G"
|
239
|
-
user.add_channel(message.channel, :joined)
|
240
|
-
message.channel.add_user(user, :joined)
|
241
|
-
user.add_flags(message.channel, message[:flags].to_flags)
|
242
|
-
}
|
243
|
-
add("353", :RPL_NAMEREPLY, /([^\s]+) [=@] ([^\s]+) :(.*)/, [:for, :channel, :users]) { |message, parser|
|
244
|
-
users = message[:users]
|
245
|
-
message.alter_member(:users, [])
|
246
|
-
users.split(/ /).each { |nick|
|
247
|
-
user = parser.users.create(nick)
|
248
|
-
message[:users] << user
|
249
|
-
user.add_channel(message.channel, :joined)
|
250
|
-
message.channel.add_user(user, :joined)
|
251
|
-
}
|
252
|
-
}
|
253
|
-
add("361", :RPL_KILLDONE)
|
254
|
-
add("362", :RPL_CLOSING)
|
255
|
-
add("363", :RPL_CLOSEEND)
|
256
|
-
add("364", :RPL_LINKS)
|
257
|
-
add("365", :RPL_ENDOFLINKS)
|
258
|
-
add("366", :RPL_ENDOFNAMES)
|
259
|
-
add("367", :RPL_BANLIST,
|
260
|
-
/([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) (\d+)/,
|
261
|
-
[:for, :channel, :banmask, :banned_by, :bantime]
|
262
|
-
) { |message, parser| #367 nickname #channel nick!user@host nickname 1140125288
|
263
|
-
message[:bantime] = Time.at(message[:bantime].to_i)
|
264
|
-
message[:banmask] = Hostmask.new(message[:banmask])
|
265
|
-
}
|
44
|
+
add("377", :UNK_377)
|
45
|
+
|
266
46
|
|
267
|
-
add("368", :RPL_ENDOFBANLIST)
|
268
|
-
add("369", :RPL_ENDOFWHOWAS)
|
269
|
-
add("371", :RPL_INFO)
|
270
|
-
add("372", :RPL_MOTD)
|
271
|
-
add("373", :RPL_INFOSTART)
|
272
|
-
add("374", :RPL_ENDOFINFO)
|
273
|
-
add("375", :RPL_MOTDSTART)
|
274
|
-
add("376", :RPL_ENDOFMOTD)
|
275
|
-
add("381", :RPL_YOUREOPER)
|
276
|
-
add("382", :RPL_REHASHING)
|
277
|
-
add("383", :RPL_YOURESERVICE)
|
278
|
-
add("384", :RPL_MYPORTIS)
|
279
47
|
add("386", :RPL_PASSWORDACCEPTED) # Custom (chatroom)
|
280
|
-
add("391", :RPL_TIME)
|
281
|
-
add("392", :RPL_USERSSTART)
|
282
|
-
add("393", :RPL_USERS)
|
283
|
-
add("394", :RPL_ENDOFUSERS)
|
284
|
-
add("395", :RPL_NOUSERS)
|
285
48
|
|
286
|
-
#
|
287
|
-
|
288
|
-
add("
|
289
|
-
|
290
|
-
|
291
|
-
add("405", :ERR_TOOMANYCHANNELS)
|
292
|
-
add("406", :ERR_WASNOSUCHNICK)
|
293
|
-
add("407", :ERR_TOOMANYTARGETS)
|
294
|
-
add("408", :ERR_NOSUCHSERVICE)
|
295
|
-
add("409", :ERR_NOORIGIN)
|
296
|
-
add("411", :ERR_NORECIPIENT)
|
297
|
-
add("412", :ERR_NOTEXTTOSEND)
|
298
|
-
add("413", :ERR_NOTOPLEVEL)
|
299
|
-
add("414", :ERR_WILDTOPLEVEL)
|
300
|
-
add("415", :ERR_BADMASK)
|
301
|
-
add("421", :ERR_UNKNOWNCOMMAND)
|
302
|
-
add("422", :ERR_NOMOTD)
|
303
|
-
add("423", :ERR_NOADMININFO)
|
304
|
-
add("424", :ERR_FILEERROR)
|
305
|
-
add("431", :ERR_NONICKNAMEGIVEN)
|
306
|
-
add("432", :ERR_ERRONEUSNICKNAME)
|
307
|
-
add("433", :ERR_NICKNAMEINUSE)
|
308
|
-
add("436", :ERR_NICKCOLLISION)
|
309
|
-
add("437", :ERR_UNAVAILRESOURCE)
|
310
|
-
add("441", :ERR_USERNOTINCHANNEL)
|
311
|
-
add("442", :ERR_NOTONCHANNEL)
|
312
|
-
add("443", :ERR_USERONCHANNEL)
|
313
|
-
add("444", :ERR_NOLOGIN)
|
314
|
-
add("445", :ERR_SUMMONDISABLED)
|
315
|
-
add("446", :ERR_USERSDISABLED)
|
316
|
-
add("451", :ERR_NOTREGISTERED)
|
317
|
-
add("461", :ERR_NEEDMOREPARAMS)
|
318
|
-
add("462", :ERR_ALREADYREGISTRED)
|
319
|
-
add("463", :ERR_NOPERMFORHOST)
|
320
|
-
add("464", :ERR_PASSWDMISMATCH)
|
321
|
-
add("465", :ERR_YOUREBANNEDCREEP)
|
322
|
-
add("466", :ERR_YOUWILLBEBANNED)
|
323
|
-
add("467", :ERR_KEYSET)
|
324
|
-
add("471", :ERR_CHANNELISFULL)
|
325
|
-
add("472", :ERR_UNKNOWNMODE)
|
326
|
-
add("473", :ERR_INVITEONLYCHAN)
|
327
|
-
add("474", :ERR_BANNEDFROMCHAN)
|
328
|
-
add("475", :ERR_BADCHANNELKEY)
|
329
|
-
add("476", :ERR_BADCHANMASK)
|
330
|
-
add("477", :ERR_NOCHANMODES)
|
331
|
-
add("478", :ERR_BANLISTFULL)
|
332
|
-
add("481", :ERR_NOPRIVILEGES)
|
333
|
-
add("482", :ERR_CHANOPRIVSNEEDED)
|
334
|
-
add("483", :ERR_CANTKILLSERVER)
|
335
|
-
add("484", :ERR_RESTRICTED)
|
336
|
-
add("485", :ERR_UNIQOPPRIVSNEEDED)
|
337
|
-
add("491", :ERR_NOOPERHOST)
|
338
|
-
add("492", :ERR_NOSERVICEHOST)
|
49
|
+
# Seen:
|
50
|
+
# - ircu (undernet)
|
51
|
+
add("396", :RPL_HOSTHIDDEN, /^(\S+) (?:(.*?)@)?([:\S]+) :(.*)/, [:nick, :user, :displayed_host, :text]) { |message, parser|
|
52
|
+
parser.users.myself.force_update(nil, message.displayed_host, nil)
|
53
|
+
}
|
339
54
|
|
340
|
-
#5** Codes
|
341
|
-
add("501", :ERR_UMODEUNKNOWNFLAG)
|
342
|
-
add("502", :ERR_USERSDONTMATCH)
|
343
55
|
add("505", :ERR__NOPRIVMSG)
|