PerfectlyNormal-Flexo 0.3.9
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +348 -0
- data/README +5 -0
- data/Rakefile +21 -0
- data/bin/flexo +3 -0
- data/bin/mkflexorc +99 -0
- data/flexo.gemspec +53 -0
- data/lib/caselesshash.rb +113 -0
- data/lib/daemonize.rb +59 -0
- data/lib/flexo/client.rb +30 -0
- data/lib/flexo/config.rb +79 -0
- data/lib/flexo/constants.rb +15 -0
- data/lib/flexo/data.rb +104 -0
- data/lib/flexo/dispatcher.rb +134 -0
- data/lib/flexo/errors.rb +29 -0
- data/lib/flexo/event.rb +31 -0
- data/lib/flexo/events/join.rb +13 -0
- data/lib/flexo/events/kick.rb +29 -0
- data/lib/flexo/events/mode.rb +23 -0
- data/lib/flexo/events/nick.rb +14 -0
- data/lib/flexo/events/notice.rb +12 -0
- data/lib/flexo/events/part.rb +14 -0
- data/lib/flexo/events/ping.rb +16 -0
- data/lib/flexo/events/pong.rb +14 -0
- data/lib/flexo/events/privmsg.rb +46 -0
- data/lib/flexo/events/quit.rb +12 -0
- data/lib/flexo/events/reply.rb +45 -0
- data/lib/flexo/events/topic.rb +18 -0
- data/lib/flexo/events/unknown.rb +11 -0
- data/lib/flexo/handler.rb +30 -0
- data/lib/flexo/logger.rb +86 -0
- data/lib/flexo/manager.rb +152 -0
- data/lib/flexo/numerics.rb +207 -0
- data/lib/flexo/plugin.rb +69 -0
- data/lib/flexo/pluginmanager.rb +106 -0
- data/lib/flexo/sender.rb +601 -0
- data/lib/flexo/server.rb +130 -0
- data/lib/flexo/trigger.rb +44 -0
- data/plugins/auth.rb +256 -0
- data/plugins/control.rb +36 -0
- data/plugins/dbus.rb +39 -0
- data/plugins/pstore.rb +34 -0
- data/plugins/svnlookup.rb +63 -0
- metadata +93 -0
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'caselesshash'
|
3
|
+
|
4
|
+
module Flexo
|
5
|
+
# Here's the interesting parts. This class is responsible for...
|
6
|
+
# Managing plugins! Who would've thought that...
|
7
|
+
class PluginManager
|
8
|
+
# As usual. Set up some things. Create some arrays, some hashes,
|
9
|
+
# get a mutex. All that
|
10
|
+
def initialize(mutex)
|
11
|
+
@manager = Flexo::Manager.instance
|
12
|
+
@plugins_available = []
|
13
|
+
@plugins_loaded = CaselessHash.new
|
14
|
+
@plugins_path = @manager.config['plugin.path'].to_a
|
15
|
+
|
16
|
+
find_plugins
|
17
|
+
end
|
18
|
+
|
19
|
+
# Goes through the folders sent to initialize and looks for
|
20
|
+
# anything resembling a plugin. Then shove them into a hash
|
21
|
+
# so that we can find them later
|
22
|
+
def find_plugins
|
23
|
+
@manager.logger.debug " Going to search for plugins (in #{@plugins_path.join(',')}"
|
24
|
+
@plugins_path.each do |dir|
|
25
|
+
Dir["#{dir}/*.rb"].each do |file|
|
26
|
+
begin
|
27
|
+
require file
|
28
|
+
rescue => e
|
29
|
+
@manager.logger.warn(e.message)
|
30
|
+
next
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
ObjectSpace.each_object(Class) do |klass|
|
35
|
+
if klass < Flexo::Plugin
|
36
|
+
@manager.logger.debug("#{klass} looks like a valid Flexo-plugin")
|
37
|
+
unless klass::NAME
|
38
|
+
raise PluginNameError, "The plugin hasn't specified a name!"
|
39
|
+
end
|
40
|
+
|
41
|
+
if @plugins_available.any? { |plugin|
|
42
|
+
plugin::NAME.downcase == klass::NAME.downcase }
|
43
|
+
raise PluginConflictError,
|
44
|
+
"The plugin name #{klass::NAME} is already being used by another plugin."
|
45
|
+
end
|
46
|
+
@plugins_available << klass
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Find a plugin and its class by its name
|
53
|
+
def get_plugin_class(name)
|
54
|
+
@plugins_available.find { |plugin|
|
55
|
+
plugin::NAME.downcase == name.downcase
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
# Load a plugin
|
60
|
+
def load_plugin(name)
|
61
|
+
@manager.logger.debug("Going to load plugin #{name}")
|
62
|
+
klass = get_plugin_class(name)
|
63
|
+
raise Flexo::PluginNotFoundError, "The #{name} plugin could not be found." unless klass
|
64
|
+
|
65
|
+
if !@plugins_loaded.key?(klass::NAME)
|
66
|
+
begin
|
67
|
+
klass::DEPENDS.each do |dependency|
|
68
|
+
if(dependency[0..4] == "ruby-")
|
69
|
+
begin
|
70
|
+
require dependency[5..-1]
|
71
|
+
rescue LoadError
|
72
|
+
raise PluginDependencyError, "Plugin requires #{dependency[5..-1]}, which can't be found"
|
73
|
+
end
|
74
|
+
|
75
|
+
next
|
76
|
+
end
|
77
|
+
|
78
|
+
dep = get_plugin_class(dependency)
|
79
|
+
if !@plugins_loaded.key?(dep::NAME)
|
80
|
+
load_plugin(dep::NAME)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
plugin = klass.new(@plugins_loaded)
|
85
|
+
rescue Flexo::PluginError => e
|
86
|
+
puts "Plugin encountered an error (#{e.class}): #{e.message}"
|
87
|
+
puts e.backtrace
|
88
|
+
rescue Flexo::PluginDependencyError => e
|
89
|
+
@manager.logger.warn e.message
|
90
|
+
return nil
|
91
|
+
rescue Flexo::PluginNotFoundError => e
|
92
|
+
@manager.logger.warn e.message
|
93
|
+
return nil
|
94
|
+
end
|
95
|
+
|
96
|
+
@manager.mutex { @plugins_loaded[klass::NAME] = plugin }
|
97
|
+
@manager.logger.debug " Loaded #{plugin.class}"
|
98
|
+
else
|
99
|
+
@manager.logger.debug " Already loaded #{klass.class}"
|
100
|
+
plugin = @plugins_loaded[klass::NAME]
|
101
|
+
end
|
102
|
+
|
103
|
+
plugin
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/lib/flexo/sender.rb
ADDED
@@ -0,0 +1,601 @@
|
|
1
|
+
module Flexo
|
2
|
+
# One of the important classes. This contains convenience-functions for
|
3
|
+
# sending almost every IRC command imaginable. And if that's not enough,
|
4
|
+
# it'll also have a raw()-function. This is the only class that's supposed
|
5
|
+
# to interact with Server, to make sure that only sanitized content is sent
|
6
|
+
# out from Flexo.
|
7
|
+
#
|
8
|
+
# Most documentation for the functions are grabbed from RFC2812
|
9
|
+
# available at http://tools.ietf.org/html/rfc2812.
|
10
|
+
class Sender
|
11
|
+
# Sets up the link to the manager, and creates a send queue.
|
12
|
+
def initialize
|
13
|
+
@manager = Flexo::Manager.instance
|
14
|
+
@queue = Queue.new
|
15
|
+
|
16
|
+
@manager.thread do
|
17
|
+
while cmd = @queue.pop
|
18
|
+
@manager.server.quote cmd
|
19
|
+
sleep 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Shoves the command into the queue
|
25
|
+
def quote(line)
|
26
|
+
@manager.logger.debug("Adding \"#{line.rstrip}\" to the outgoing queue")
|
27
|
+
@queue << line.rstrip
|
28
|
+
end
|
29
|
+
|
30
|
+
# 3.1.1 Password message
|
31
|
+
# Command: PASS
|
32
|
+
# Paramteres: <password>
|
33
|
+
#
|
34
|
+
# The PASS command is used to set a 'connection password'.
|
35
|
+
# The optional password can and MUST be set before any
|
36
|
+
# attempt to register when the connection is made.
|
37
|
+
# Currently this requires that user send a PASS command
|
38
|
+
# before sending the NICK/USER combination.
|
39
|
+
#
|
40
|
+
# Numeric Replies:
|
41
|
+
# ERR_NEEDMOREPARAMS
|
42
|
+
# ERR_ALREADYREGISTRED
|
43
|
+
#
|
44
|
+
# Example:
|
45
|
+
# PASS secretpasswordhere
|
46
|
+
def pass(password)
|
47
|
+
quote "PASS #{password.to_s}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# 3.1.2 Nick message
|
51
|
+
# Command: NICK
|
52
|
+
# Parameters: <nickname>
|
53
|
+
#
|
54
|
+
# NICK message is used to give the user a nickname if connecting,
|
55
|
+
# or change the previous one if already connected
|
56
|
+
#
|
57
|
+
# Numeric Replies:
|
58
|
+
# ERR_NONICKNAMEGIVEN
|
59
|
+
# ERR_ERRONEUSNICKNAME
|
60
|
+
# ERR_NICKNAMEINUSE
|
61
|
+
# ERR_NICKCOLLISION
|
62
|
+
#
|
63
|
+
# Example:
|
64
|
+
# NICK Wiz # Introducing new nick "Wiz".
|
65
|
+
# :WiZ NICK Kilroy # WiZ changed his nickname to Kilroy.
|
66
|
+
def nick(nickname)
|
67
|
+
quote "NICK #{nickname.to_s}"
|
68
|
+
end
|
69
|
+
|
70
|
+
# 3.1.3 User message
|
71
|
+
# Command: USER
|
72
|
+
# Parameters: <user> <mode> <unused> <realname>
|
73
|
+
#
|
74
|
+
# The USER command is used at the beginning of connection to specify
|
75
|
+
# the username, hostname and realname of a new user.
|
76
|
+
#
|
77
|
+
# The <mode> parameter should be a numeric, and can be used to
|
78
|
+
# automatically set user modes when registering with the server.
|
79
|
+
# This parameter is a bitmask, with only 2 bits having any
|
80
|
+
# signification: if the bit 2 is set, the user mode 'w' will
|
81
|
+
# be set and if the bit 3 is set, the user mode 'i' will be set.
|
82
|
+
# (See Section 3.1.5 "User Modes").
|
83
|
+
# The <realname> may contain space characters.
|
84
|
+
#
|
85
|
+
# Numeric Replies:
|
86
|
+
# ERR_NEEDMOREPARAMS
|
87
|
+
# ERR_ALREADYREGISTRED
|
88
|
+
#
|
89
|
+
# Example:
|
90
|
+
# USER guest 0 * :Ronnie Reagan # User registering themselves with a
|
91
|
+
# # username of "guest" and real name
|
92
|
+
# # "Ronnie Reagan".
|
93
|
+
# USER guest 8 * :Ronnie Reagan # User registering themselves with a
|
94
|
+
# # username of "guest" and real name
|
95
|
+
# # "Ronnie Reagan", and asking to be set
|
96
|
+
# # invisible.
|
97
|
+
def user(user, mode, realname)
|
98
|
+
quote "USER #{user.to_s} #{mode.to_i} * :#{realname.to_s}"
|
99
|
+
end
|
100
|
+
|
101
|
+
# 3.1.4 Oper message
|
102
|
+
# Command: OPER
|
103
|
+
# Parameters: <name> <password>
|
104
|
+
#
|
105
|
+
# A normal user uses the OPER command to obtain operator privileges.
|
106
|
+
# The combination of <name> and <password> are REQUIRED to gain
|
107
|
+
# Operator privileges. Upon success, the user will receive a MODE
|
108
|
+
# message (see section 3.1.5) indicating the new user modes.
|
109
|
+
#
|
110
|
+
# Numeric Replies:
|
111
|
+
# ERR_NEEDMOREPARAMS
|
112
|
+
# RPL_YOUREOPER
|
113
|
+
# ERR_NOOPERHOST
|
114
|
+
# ERR_PASSWDMISMATCH
|
115
|
+
# Example:
|
116
|
+
# OPER foo bar # Attempt to register as an operator
|
117
|
+
# using a username of "foo" and "bar" as the password.
|
118
|
+
def oper(name, password)
|
119
|
+
quote "OPER #{name.to_s} #{password.to_s}"
|
120
|
+
end
|
121
|
+
|
122
|
+
# 3.1.5 User mode message
|
123
|
+
# Command: MODE
|
124
|
+
# Parameters: <nickname> ±<modes>
|
125
|
+
#
|
126
|
+
# The user MODE's are typically changes which affect either how the
|
127
|
+
# client is seen by others or what 'extra' messages the client is sent.
|
128
|
+
# A user MODE command MUST only be accepted if both the sender of the
|
129
|
+
# message and the nickname given as a parameter are both the same. If
|
130
|
+
# no other parameter is given, then the server will return the current
|
131
|
+
# settings for the nick.
|
132
|
+
#
|
133
|
+
# The available modes are as follows:
|
134
|
+
# a - user is flagged as away;
|
135
|
+
# i - marks a users as invisible;
|
136
|
+
# w - user receives wallops;
|
137
|
+
# r - restricted user connection;
|
138
|
+
# o - operator flag;
|
139
|
+
# O - local operator flag;
|
140
|
+
# s - marks a user for receipt of server notices.
|
141
|
+
#
|
142
|
+
# Additional modes may be available later on.
|
143
|
+
#
|
144
|
+
# The flag 'a' SHALL NOT be toggled by the user using the MODE command,
|
145
|
+
# instead use of the AWAY command is REQUIRED.
|
146
|
+
#
|
147
|
+
# If a user attempts to make themselves an operator using the "+o" or
|
148
|
+
# "+O" flag, the attempt SHOULD be ignored as users could bypass the
|
149
|
+
# authentication mechanisms of the OPER command. There is no
|
150
|
+
# restriction, however, on anyone `deopping' themselves (using "-o" or
|
151
|
+
# "-O").
|
152
|
+
#
|
153
|
+
# On the other hand, if a user attempts to make themselves unrestricted
|
154
|
+
# using the "-r" flag, the attempt SHOULD be ignored. There is no
|
155
|
+
# restriction, however, on anyone `deopping' themselves (using "+r").
|
156
|
+
# This flag is typically set by the server upon connection for
|
157
|
+
# administrative reasons. While the restrictions imposed are left up
|
158
|
+
# to the implementation, it is typical that a restricted user not be
|
159
|
+
# allowed to change nicknames, nor make use of the channel operator
|
160
|
+
# status on channels.
|
161
|
+
#
|
162
|
+
# The flag 's' is obsolete but MAY still be used.
|
163
|
+
#
|
164
|
+
# Numeric Replies:
|
165
|
+
# ERR_NEEDMOREPARAMS
|
166
|
+
# ERR_USERSDONTMATCH
|
167
|
+
# ERR_UMODEUNKNOWNFLAG
|
168
|
+
# RPL_UMODEIS
|
169
|
+
# Examples:
|
170
|
+
# MODE WiZ -w # Command by WiZ to turn off
|
171
|
+
# # reception of WALLOPS messages.
|
172
|
+
# MODE Angel +i # Command from Angel to make herself invisible.
|
173
|
+
# MODE WiZ -o # WiZ 'deopping' (removing operator status).
|
174
|
+
def usermode(nickname, mode)
|
175
|
+
quote "MODE #{nickname.to_s} #{mode.to_s}"
|
176
|
+
end
|
177
|
+
|
178
|
+
# 3.1.6 Service message
|
179
|
+
# Command: SERVICE
|
180
|
+
# Parameters: <nickname> <reserved> <distribution> <type>
|
181
|
+
# <reserved> <info>
|
182
|
+
#
|
183
|
+
# The SERVICE command to register a new service. Command parameters
|
184
|
+
# specify the service nickname, distribution, type and info of a new
|
185
|
+
# service.
|
186
|
+
#
|
187
|
+
# The <distribution> parameter is used to specify the visibility of a
|
188
|
+
# service. The service may only be known to servers which have a name
|
189
|
+
# matching the distribution. For a matching server to have knowledge
|
190
|
+
# of the service, the network path between that server and the server
|
191
|
+
# on which the service is connected MUST be composed of servers which
|
192
|
+
# names all match the mask.
|
193
|
+
#
|
194
|
+
# The <type> parameter is currently reserved for future usage.
|
195
|
+
#
|
196
|
+
# Numeric Replies:
|
197
|
+
# ERR_ALREADYREGISTRED
|
198
|
+
# ERR_NEEDMOREPARAMS
|
199
|
+
# ERR_ERRONEUSNICKNAME
|
200
|
+
# RPL_YOURESERVICE
|
201
|
+
# RPL_YOURHOST
|
202
|
+
# RPL_MYINFO
|
203
|
+
# Example:
|
204
|
+
# SERVICE dict * *.fr 0 0 :French Dictionary
|
205
|
+
# # Service registering itself with a name of "dict".
|
206
|
+
# # This service will only be available on servers which
|
207
|
+
# # name matches "*.fr".
|
208
|
+
def service(nickname, distribution, type, info)
|
209
|
+
# Won't be implemented by me
|
210
|
+
end
|
211
|
+
|
212
|
+
# 3.1.7 Quit
|
213
|
+
# Command: QUIT
|
214
|
+
# Parameters: <Quit Message>
|
215
|
+
#
|
216
|
+
# A client session is terminated with a quit message. The server
|
217
|
+
# acknowledges this by sending an ERROR message to the client.
|
218
|
+
#
|
219
|
+
# Numeric Replies:
|
220
|
+
# None.
|
221
|
+
# Example:
|
222
|
+
# QUIT :Gone to have lunch
|
223
|
+
def quit(message="")
|
224
|
+
quote "QUIT :#{message.to_s}"
|
225
|
+
end
|
226
|
+
|
227
|
+
# 3.1.8 Squit
|
228
|
+
# Command: SQUIT
|
229
|
+
# Parameters: <server> <comment>
|
230
|
+
#
|
231
|
+
# The SQUIT command is available only to operators. It is used to
|
232
|
+
# disconnect server links. Also servers can generate SQUIT messages on
|
233
|
+
# error conditions. A SQUIT message may also target a remote server
|
234
|
+
# connection. In this case, the SQUIT message will simply be sent to
|
235
|
+
# the remote server without affecting the servers in between the
|
236
|
+
# operator and the remote server.
|
237
|
+
#
|
238
|
+
# The <comment> SHOULD be supplied by all operators who execute a SQUIT
|
239
|
+
# for a remote server. The server ordered to disconnect its peer
|
240
|
+
# generates a WALLOPS message with <comment> included, so that other
|
241
|
+
# users may be aware of the reason of this action.
|
242
|
+
#
|
243
|
+
# Numeric replies:
|
244
|
+
# ERR_NOPRIVILEGES
|
245
|
+
# ERR_NOSUCHSERVER
|
246
|
+
# ERR_NEEDMOREPARAMS
|
247
|
+
# Examples:
|
248
|
+
# SQUIT tolsun.oulu.fi :Bad Link ? # Command to uplink of the server
|
249
|
+
# # tolson.oulu.fi to terminate its
|
250
|
+
# # connection with comment "Bad Link".
|
251
|
+
# :Trillian SQUIT cm22.eng.umd.edu :Server out of control
|
252
|
+
# # Command from Trillian from to
|
253
|
+
# # disconnect "cm22.eng.umd.edu"
|
254
|
+
# # from the net with comment
|
255
|
+
# # "Server out of control".
|
256
|
+
def squit(server, message="")
|
257
|
+
end
|
258
|
+
|
259
|
+
# 3.2.1 Join message
|
260
|
+
# Command: JOIN
|
261
|
+
# Parameters: <channel>, <key>
|
262
|
+
#
|
263
|
+
# The JOIN command is used by a user to request to start listening to
|
264
|
+
# the specific channel. Servers MUST be able to parse arguments in the
|
265
|
+
# form of a list of target, but SHOULD NOT use lists when sending JOIN
|
266
|
+
# messages to clients.
|
267
|
+
#
|
268
|
+
# Once a user has joined a channel, he receives information about
|
269
|
+
# all commands his server receives affecting the channel. This
|
270
|
+
# includes JOIN, MODE, KICK, PART, QUIT and of course PRIVMSG/NOTICE.
|
271
|
+
# This allows channel members to keep track of the other channel
|
272
|
+
# members, as well as channel modes.
|
273
|
+
#
|
274
|
+
# If a JOIN is successful, the user receives a JOIN message as
|
275
|
+
# confirmation and is then sent the channel's topic (using RPL_TOPIC) and
|
276
|
+
# the list of users who are on the channel (using RPL_NAMREPLY), which
|
277
|
+
# MUST include the user joining.
|
278
|
+
#
|
279
|
+
# Note that this message accepts a special argument ("0"), which is
|
280
|
+
# a special request to leave all channels the user is currently a member
|
281
|
+
# of. The server will process this message as if the user had sent
|
282
|
+
# a PART command (See Section 3.2.2) for each channel he is a member
|
283
|
+
# of.
|
284
|
+
#
|
285
|
+
# Numeric Replies:
|
286
|
+
# ERR_NEEDMOREPARAMS
|
287
|
+
# ERR_BANNEDFROMCHAN
|
288
|
+
# ERR_INVITEONLYCHAN
|
289
|
+
# ERR_BADCHANNELKEY
|
290
|
+
# ERR_CHANNELISFULL
|
291
|
+
# ERR_BADCHANMASK
|
292
|
+
# ERR_NOSUCHCHANNEL
|
293
|
+
# ERR_TOOMANYCHANNELS
|
294
|
+
# ERR_TOOMANYTARGETS
|
295
|
+
# ERR_UNAVAILRESOURCE
|
296
|
+
# RPL_TOPIC
|
297
|
+
# Examples:
|
298
|
+
# JOIN #foobar # Command to join channel #foobar.
|
299
|
+
# JOIN &foo fubar # Command to join channel &foo using
|
300
|
+
# # key "fubar".
|
301
|
+
# JOIN #foo,&bar fubar # Command to join channel #foo using
|
302
|
+
# # key "fubar" and &bar using no key.
|
303
|
+
# JOIN #foo,#bar fubar,foobar # Command to join channel #foo using
|
304
|
+
# # key "fubar", and channel #bar using
|
305
|
+
# # key "foobar".
|
306
|
+
# JOIN #foo,#bar # Command to join channels #foo and
|
307
|
+
# # #bar.
|
308
|
+
# JOIN 0 # Leave all currently joined
|
309
|
+
# # channels.
|
310
|
+
def join(channels, key="")
|
311
|
+
channels.each do |channel|
|
312
|
+
quote "JOIN #{channel.to_s} #{key.to_s}"
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# 3.2.2 Part message
|
317
|
+
# Command: PART
|
318
|
+
# Parameters: <channel> <part message>
|
319
|
+
#
|
320
|
+
# The PART command causes the user sending the message to be removed
|
321
|
+
# from the list of active members for all given channels listed in the
|
322
|
+
# parameter string. If a "Part Message" is given, this will be sent
|
323
|
+
# instead of the default message, the nickname. This request is always
|
324
|
+
# granted by the server.
|
325
|
+
#
|
326
|
+
# Servers MUST be able to parse arguments in the form of a list of
|
327
|
+
# target, but SHOULD NOT use lists when sending PART messages to
|
328
|
+
# clients.
|
329
|
+
#
|
330
|
+
# Numeric Replies:
|
331
|
+
# ERR_NEEDMOREPARAMS
|
332
|
+
# ERR_NOSUCHCHANNEL
|
333
|
+
# ERR_NOTONCHANNEL
|
334
|
+
# Examples:
|
335
|
+
# PART #twilight_zone # Command to leave channel "#twilight_zone"
|
336
|
+
# PART #oz-ops,&group5 # Command to leave both channels
|
337
|
+
# # "&group5" and "#oz-ops".
|
338
|
+
def part(channels, message="")
|
339
|
+
channels.each do |channel|
|
340
|
+
cmd = "PART #{channel.to_s}"
|
341
|
+
cmd += " :#{message}" if message != ""
|
342
|
+
quote cmd
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
# 3.2.3 Channel mode message
|
347
|
+
# Command: MODE
|
348
|
+
# Parameters: <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
|
349
|
+
#
|
350
|
+
# The MODE command is provided so that users may query and change the
|
351
|
+
# characteristics of a channel. Note that there is a maximum limit of three (3) changes per
|
352
|
+
# command for modes that take a parameter.
|
353
|
+
#
|
354
|
+
# Numeric Replies:
|
355
|
+
# ERR_NEEDMOREPARAMS
|
356
|
+
# ERR_KEYSET
|
357
|
+
# ERR_NOCHANMODES
|
358
|
+
# ERR_CHANOPRIVSNEEDED
|
359
|
+
# ERR_USERNOTINCHANNEL
|
360
|
+
# ERR_UNKNOWNMODE
|
361
|
+
# RPL_CHANNELMODEIS
|
362
|
+
# RPL_BANLIST
|
363
|
+
# RPL_ENDOFBANLIST
|
364
|
+
# RPL_EXCEPTLIST
|
365
|
+
# RPL_ENDOFEXCEPTLIST
|
366
|
+
# RPL_INVITELIST
|
367
|
+
# RPL_ENDOFINVITELIST
|
368
|
+
# RPL_UNIQOPIS
|
369
|
+
#
|
370
|
+
# Information about channel modes and how they work can be found
|
371
|
+
# in RFC2811 (http://tools.ietf.org/html/rfc2811)
|
372
|
+
def chanmode(channel, *modes)
|
373
|
+
quote "MODE #{channel.to_s} #{modes.join(' ')}"
|
374
|
+
end
|
375
|
+
|
376
|
+
# 3.2.4 Topic message
|
377
|
+
# Command: TOPIC
|
378
|
+
# Parameters: <channel> [ <topic> ]
|
379
|
+
#
|
380
|
+
# The TOPIC command is used to change or view the topic of a channel.
|
381
|
+
# The topic for channel <channel> is returned if there is no <topic>
|
382
|
+
# given. If the <topic> parameter is present, the topic for that
|
383
|
+
# channel will be changed, if this action is allowed for the user
|
384
|
+
# requesting it. If the <topic> parameter is an empty string, the
|
385
|
+
# topic for that channel will be removed.
|
386
|
+
#
|
387
|
+
# Numeric Replies:
|
388
|
+
# ERR_NEEDMOREPARAMS
|
389
|
+
# ERR_NOTONCHANNEL
|
390
|
+
# RPL_NOTOPIC
|
391
|
+
# RPL_TOPIC
|
392
|
+
# ERR_CHANOPRIVSNEEDED
|
393
|
+
# ERR_NOCHANMODES
|
394
|
+
# Examples:
|
395
|
+
# :WiZ!jto@tolsun.oulu.fi TOPIC #test :New # User Wiz setting the topic.
|
396
|
+
# TOPIC #test :another topic # Command to set the topic on #test
|
397
|
+
# # to "another topic".
|
398
|
+
# TOPIC #test : # Command to clear the topic on #test.
|
399
|
+
# TOPIC #test # Command to check the topic for #test.
|
400
|
+
def topic(channel, topic="")
|
401
|
+
cmd = "TOPIC #{channel.to_s} "
|
402
|
+
cmd += ":#{topic.to_s}" if topic != ""
|
403
|
+
quote cmd
|
404
|
+
end
|
405
|
+
|
406
|
+
# FIXME: 3.2.5 Names message
|
407
|
+
# FIXME: 3.2.6 List message
|
408
|
+
|
409
|
+
# 3.2.7 Invite message
|
410
|
+
# Command: INVITE
|
411
|
+
# Parameters: <nickname> <channel>
|
412
|
+
#
|
413
|
+
# The INVITE command is used to invite a user to a channel. The
|
414
|
+
# parameter <nickname> is the nickname of the person to be invited to
|
415
|
+
# the target channel <channel>. There is no requirement that the
|
416
|
+
# channel the target user is being invited to must exist or be a valid
|
417
|
+
# channel. However, if the channel exists, only members of the channel
|
418
|
+
# are allowed to invite other users. When the channel has invite-only
|
419
|
+
# flag set, only channel operators may issue INVITE command.
|
420
|
+
#
|
421
|
+
# Only the user inviting and the user being invited will receive
|
422
|
+
# notification of the invitation. Other channel members are not
|
423
|
+
# notified. (This is unlike the MODE changes, and is occasionally the
|
424
|
+
# source of trouble for users.)
|
425
|
+
#
|
426
|
+
# Numeric Replies:
|
427
|
+
# ERR_NEEDMOREPARAMS
|
428
|
+
# ERR_NOSUCHNICK
|
429
|
+
# ERR_NOTONCHANNEL
|
430
|
+
# ERR_USERONCHANNEL
|
431
|
+
# ERR_CHANOPRIVSNEEDED
|
432
|
+
# RPL_INVITING
|
433
|
+
# RPL_AWAY
|
434
|
+
# Examples:
|
435
|
+
# :Angel!wings@irc.org INVITE Wiz #Dust # Message to WiZ when he has
|
436
|
+
# # been invited by user Angel
|
437
|
+
# # to channel #Dust
|
438
|
+
# INVITE Wiz #Twilight_Zone # Command to invite WiZ to
|
439
|
+
# # #Twilight_zone
|
440
|
+
def invite(nick, channel)
|
441
|
+
quote "INVITE #{nick.to_s} #{channel.to_s}"
|
442
|
+
end
|
443
|
+
|
444
|
+
# 3.2.8 Kick command
|
445
|
+
# Command: KICK
|
446
|
+
# Parameters: <channel> <user> [<comment>]
|
447
|
+
#
|
448
|
+
# The KICK command can be used to request the forced removal of a user
|
449
|
+
# from a channel. It causes the <user> to PART from the <channel> by
|
450
|
+
# force. For the message to be syntactically correct, there MUST be
|
451
|
+
# either one channel parameter and multiple user parameter, or as many
|
452
|
+
# channel parameters as there are user parameters. If a "comment" is
|
453
|
+
# given, this will be sent instead of the default message, the nickname
|
454
|
+
# of the user issuing the KICK.
|
455
|
+
#
|
456
|
+
# Numeric Replies:
|
457
|
+
# ERR_NEEDMOREPARAMS
|
458
|
+
# ERR_NOSUCHCHANNEL
|
459
|
+
# ERR_BADCHANMASK
|
460
|
+
# ERR_CHANOPRIVSNEEDED
|
461
|
+
# ERR_USERNOTINCHANNEL
|
462
|
+
# ERR_NOTONCHANNEL
|
463
|
+
def kick(channel, user, message="")
|
464
|
+
quote "KICK #{channel} #{user} :#{message}"
|
465
|
+
end
|
466
|
+
|
467
|
+
# 3.3.1 Private messages
|
468
|
+
# Command: PRIVMSG
|
469
|
+
# Parameters: <target> <text to be sent>
|
470
|
+
#
|
471
|
+
# PRIVMSG is used to send private messages between users, as well as to
|
472
|
+
# send messages to channels. <msgtarget> is usually the nickname of
|
473
|
+
# the recipient of the message, or a channel name.
|
474
|
+
#
|
475
|
+
# The <msgtarget> parameter may also be a host mask (#<mask>) or server
|
476
|
+
# mask ($<mask>). In both cases the server will only send the PRIVMSG
|
477
|
+
# to those who have a server or host matching the mask. The mask MUST
|
478
|
+
# have at least 1 (one) "." in it and no wildcards following the last
|
479
|
+
# ".". This requirement exists to prevent people sending messages to
|
480
|
+
# "#*" or "$*", which would broadcast to all users. Wildcards are the
|
481
|
+
# '*' and '?' characters. This extension to the PRIVMSG command is
|
482
|
+
# only available to operators.
|
483
|
+
#
|
484
|
+
# Numeric Replies:
|
485
|
+
# ERR_NORECIPIENT
|
486
|
+
# ERR_NOTEXTTOSEND
|
487
|
+
# ERR_CANNOTSENDTOCHAN
|
488
|
+
# ERR_NOTOPLEVEL
|
489
|
+
# ERR_WILDTOPLEVEL
|
490
|
+
# ERR_TOOMANYTARGETS
|
491
|
+
# ERR_NOSUCHNICK
|
492
|
+
# RPL_AWAY
|
493
|
+
def privmsg(target, text)
|
494
|
+
quote "PRIVMSG #{target.to_s} :#{text.to_s}"
|
495
|
+
end
|
496
|
+
|
497
|
+
# 3.3.2 Notice
|
498
|
+
# Command: NOTICE
|
499
|
+
# Parameters: <target> <text>
|
500
|
+
#
|
501
|
+
# The NOTICE command is used similarly to PRIVMSG. The difference
|
502
|
+
# between NOTICE and PRIVMSG is that automatic replies MUST NEVER be
|
503
|
+
# sent in response to a NOTICE message. This rule applies to servers
|
504
|
+
# too - they MUST NOT send any error reply back to the client on
|
505
|
+
# receipt of a notice. The object of this rule is to avoid loops
|
506
|
+
# between clients automatically sending something in response to
|
507
|
+
# something it received.
|
508
|
+
#
|
509
|
+
# This command is available to services as well as users.
|
510
|
+
# This is typically used by services, and automatons (clients with
|
511
|
+
# either an AI or other interactive program controlling their actions).
|
512
|
+
#
|
513
|
+
# See PRIVMSG for more details on replies.
|
514
|
+
def notice(target, text)
|
515
|
+
quote "NOTICE #{target.to_s} :#{text.to_s}"
|
516
|
+
end
|
517
|
+
|
518
|
+
# 3.4.1 Motd message
|
519
|
+
# Command: MOTD
|
520
|
+
# Parameters: [ <target> ]
|
521
|
+
#
|
522
|
+
# The MOTD command is used to get the "Message Of The Day" of the given
|
523
|
+
# server, or current server if <target> is omitted.
|
524
|
+
#
|
525
|
+
# Wildcards are allowed in the <target> parameter.
|
526
|
+
#
|
527
|
+
# Numeric Replies:
|
528
|
+
# RPL_MOTDSTART
|
529
|
+
# RPL_MOTD
|
530
|
+
# RPL_ENDOFMOTD
|
531
|
+
# ERR_NOMOTD
|
532
|
+
def motd(target="")
|
533
|
+
quote "MOTD #{target}"
|
534
|
+
end
|
535
|
+
|
536
|
+
# FIXME: 3.4.2 Lusers message
|
537
|
+
# FIXME: 3.4.3 Version message
|
538
|
+
# FIXME: 3.4.4 Stats message
|
539
|
+
# FIXME: 3.4.5 Links message
|
540
|
+
# FIXME: 3.4.6 Time message
|
541
|
+
# FIXME: 3.4.7 Connect message
|
542
|
+
# FIXME: 3.4.8 Trace message
|
543
|
+
# FIXME: 3.4.9 Admin command
|
544
|
+
# FIXME: 3.4.10 Info command
|
545
|
+
# FIXME: 3.5 Service Query and Commands
|
546
|
+
# FIXME: 3.6 User based queries
|
547
|
+
# FIXME: 3.7.1 KILL
|
548
|
+
|
549
|
+
# 3.7.2 Ping message
|
550
|
+
#
|
551
|
+
# Command: PING
|
552
|
+
# Parameters: <server1> [ <server2> ]
|
553
|
+
#
|
554
|
+
# The PING command is used to test the presence of an active client or
|
555
|
+
# server at the other end of the connection. Servers send a PING
|
556
|
+
# message at regular intervals if no other activity detected coming
|
557
|
+
# from a connection. If a connection fails to respond to a PING
|
558
|
+
# message within a set amount of time, that connection is closed. A
|
559
|
+
# PING message MAY be sent even if the connection is active.
|
560
|
+
#
|
561
|
+
# When a PING message is received, the appropriate PONG message MUST be
|
562
|
+
# sent as reply to <server1> (server which sent the PING message out)
|
563
|
+
# as soon as possible. If the <server2> parameter is specified, it
|
564
|
+
# represents the target of the ping, and the message gets forwarded
|
565
|
+
# there.
|
566
|
+
#
|
567
|
+
# Numeric Replies:
|
568
|
+
# ERR_NOORIGIN
|
569
|
+
# ERR_NOSUCHSERVER
|
570
|
+
# Examples:
|
571
|
+
# PING tolsun.oulu.fi # Command to send a PING message to server
|
572
|
+
# PING :irc.funet.fi # Ping message sent by server "irc.funet.fi"
|
573
|
+
def ping(server, server2="")
|
574
|
+
quote "PING #{server.to_s} #{server2.to_s}"
|
575
|
+
end
|
576
|
+
|
577
|
+
# 3.7.3 Pong message
|
578
|
+
#
|
579
|
+
# Command: PONG
|
580
|
+
# Parameters: <server> [ <server2> ]
|
581
|
+
#
|
582
|
+
# PONG message is a reply to ping message. If parameter <server2> is
|
583
|
+
# given, this message MUST be forwarded to given target. The <server>
|
584
|
+
# parameter is the name of the entity who has responded to PING message
|
585
|
+
# and generated this message.
|
586
|
+
#
|
587
|
+
# Numeric Replies:
|
588
|
+
# ERR_NOORIGIN
|
589
|
+
# ERR_NOSUCHSERVER
|
590
|
+
# Example:
|
591
|
+
# PONG csd.bu.edu tolsun.oulu.fi # PONG message from csd.bu.edu to
|
592
|
+
# # tolsun.oulu.fi
|
593
|
+
def pong(server, server2="")
|
594
|
+
quote "PONG #{server.to_s} #{server2.to_s}"
|
595
|
+
end
|
596
|
+
|
597
|
+
# FIXME: 3.7.4 Error
|
598
|
+
|
599
|
+
# FIXME: 4. Optional features
|
600
|
+
end
|
601
|
+
end
|