butler 1.8.0 → 1.8.1
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|