]> git.lizzy.rs Git - luairc.git/blob - src/irc/channel.lua
split between internal/public functions in channel.lua
[luairc.git] / src / irc / channel.lua
1 ---
2 -- Implementation of the Channel class
3
4 -- initialization {{{
5 local base =   _G
6 local irc =    require 'irc'
7 local misc =   require 'irc.misc'
8 local socket = require 'socket'
9 local table =  require 'table'
10 -- }}}
11
12 ---
13 -- This module implements a channel object representing a single channel we
14 -- have joined.
15 module 'irc.channel'
16
17 -- object metatable {{{
18 -- TODO: this <br /> shouldn't be necessary - bug in luadoc
19 ---
20 -- An object of the Channel class represents a single joined channel. It has
21 -- several table fields, and can be used in string contexts (returning the
22 -- channel name).<br />
23 -- @class table
24 -- @name Channel
25 -- @field name     Name of the channel (read only)
26 -- @field topic    Channel topic, if set (read/write, writing to this sends a
27 --                 topic change request to the server for this channel)
28 -- @field chanmode Channel mode (public/private/secret) (read only)
29 -- @field members  Array of all members of this channel
30 local mt = {
31     -- __index() {{{
32     __index =    function(self, key)
33                      if key == "name" then
34                          return self._name
35                      elseif key == "topic" then
36                          return self._topic
37                      elseif key == "chanmode" then
38                          return self._chanmode
39                      else
40                          return _M[key]
41                      end
42                  end,
43     -- }}}
44     -- __newindex() {{{
45     __newindex = function(self, key, value)
46                      if key == "name" then
47                          return
48                      elseif key == "topic" then
49                          irc.send("TOPIC", self._name, value)
50                      elseif key == "chanmode" then
51                          return
52                      else
53                          base.rawset(self, key, value)
54                      end
55                  end,
56     -- }}}
57     -- __concat() {{{
58     __concat =   function(first, second)
59                      local first_str, second_str
60
61                      if base.type(first) == "table" then
62                          first_str = first._name
63                      else
64                          first_str = first
65                      end
66                      if base.type(second) == "table" then
67                          second_str = second._name
68                      else
69                          second_str = second
70                      end
71
72                      return first_str .. second_str
73                  end,
74     -- }}}
75     -- __tostring() {{{
76     __tostring = function(self)
77                      return self._name
78                  end
79     -- }}}
80 }
81 -- }}}
82
83 -- private methods {{{
84 -- set_basic_mode {{{
85 --
86 -- Sets a no-arg mode on a channel.
87 -- @name chan:set_basic_mode
88 -- @param self   Channel object
89 -- @param set    True to set the mode, false to unset it
90 -- @param letter Letter of the mode
91 local function set_basic_mode(self, set, letter)
92     if set then
93         irc.send("MODE", self.name, "+" .. letter)
94     else
95         irc.send("MODE", self.name, "-" .. letter)
96     end
97 end
98 -- }}}
99 -- }}}
100
101 -- internal methods {{{
102 -- TODO: is there a better way to do this? also, storing op/voice as initial
103 -- substrings of the username is just ugly
104 -- _add_user {{{
105 --
106 -- Add a user to the channel's internal user list.
107 -- @param self Channel object
108 -- @param user Nick of the user to add
109 -- @param mode Mode (op/voice) of the user, in symbolic form (@/+)
110 function _add_user(self, user, mode)
111     mode = mode or ''
112     self._members[user] = mode .. user
113 end
114 -- }}}
115
116 -- _remove_user {{{
117 --
118 -- Remove a user from the channel's internal user list.
119 -- @param self Channel object
120 -- @param user Nick of the user to remove
121 function _remove_user(self, user)
122     self._members[user] = nil
123 end
124 -- }}}
125
126 -- _change_status {{{
127 --
128 -- Change the op/voice status of a user in the channel's internal user list.
129 -- @param self Channel object
130 -- @param user Nick of the user to affect
131 -- @param on   True if the mode is being set, false if it's being unset
132 -- @param mode 'o' for op, 'v' for voice
133 function _change_status(self, user, on, mode)
134     if on then
135         if mode == 'o' then
136             self._members[user] = '@' .. user
137         elseif mode == 'v' then
138             self._members[user] = '+' .. user
139         end
140     else
141         if (mode == 'o' and self._members[user]:sub(1, 1) == '@') or
142            (mode == 'v' and self._members[user]:sub(1, 1) == '+') then
143             self._members[user] = user
144         end
145     end
146 end
147 -- }}}
148
149 -- _change_nick {{{
150 --
151 -- Change the nick of a user in the channel's internal user list.
152 -- @param self     Channel object
153 -- @param old_nick User's old nick
154 -- @param new_nick User's new nick
155 function _change_nick(self, old_nick, new_nick)
156     for member in self:each_member() do
157         local member_nick = member:gsub('@+', '')
158         if member_nick == old_nick then
159             local mode = self._members[old_nick]:sub(1, 1)
160             if mode ~= '@' and mode ~= '+' then mode = "" end
161             self._members[old_nick] = nil
162             self._members[new_nick] = mode .. new_nick
163             break
164         end
165     end
166 end
167 -- }}}
168 -- }}}
169
170 -- constructor {{{
171 ---
172 -- Creates a new Channel object.
173 -- @param chan Name of the new channel
174 -- @return The new channel instance
175 function new(chan)
176     return base.setmetatable({_name = chan, _topic = {}, _chanmode = "",
177                               _members = {}}, mt)
178 end
179 -- }}}
180
181 -- public methods {{{
182 -- iterators {{{
183 -- each_op {{{
184 ---
185 -- Iterator over the ops in the channel
186 -- @param self Channel object
187 function each_op(self)
188     return function(state, arg)
189                return misc._value_iter(state, arg,
190                                        function(v)
191                                            return v:sub(1, 1) == "@"
192                                        end)
193            end,
194            self._members,
195            nil
196 end
197 -- }}}
198
199 -- each_voice {{{
200 ---
201 -- Iterator over the voiced users in the channel
202 -- @param self Channel object
203 function each_voice(self)
204     return function(state, arg)
205                return misc._value_iter(state, arg,
206                                        function(v)
207                                            return v:sub(1, 1) == "+"
208                                        end)
209            end,
210            self._members,
211            nil
212 end
213 -- }}}
214
215 -- each_user {{{
216 ---
217 -- Iterator over the normal users in the channel
218 -- @param self Channel object
219 function each_user(self)
220     return function(state, arg)
221                return misc._value_iter(state, arg,
222                                        function(v)
223                                            return v:sub(1, 1) ~= "@" and
224                                                   v:sub(1, 1) ~= "+"
225                                        end)
226            end,
227            self._members,
228            nil
229 end
230 -- }}}
231
232 -- each_member {{{
233 ---
234 -- Iterator over all users in the channel
235 -- @param self Channel object
236 function each_member(self)
237     return misc._value_iter, self._members, nil
238 end
239 -- }}}
240 -- }}}
241
242 -- return tables of users {{{
243 -- ops {{{
244 ---
245 -- Gets an array of all the ops in the channel.
246 -- @param self Channel object
247 -- @return Array of channel ops
248 function ops(self)
249     local ret = {}
250     for nick in self:each_op() do
251         table.insert(ret, nick)
252     end
253     return ret
254 end
255 -- }}}
256
257 -- voices {{{
258 ---
259 -- Gets an array of all the voiced users in the channel.
260 -- @param self Channel object
261 -- @return Array of channel voiced users
262 function voices(self)
263     local ret = {}
264     for nick in self:each_voice() do
265         table.insert(ret, nick)
266     end
267     return ret
268 end
269 -- }}}
270
271 -- users {{{
272 ---
273 -- Gets an array of all the normal users in the channel.
274 -- @param self Channel object
275 -- @return Array of channel normal users
276 function users(self)
277     local ret = {}
278     for nick in self:each_user() do
279         table.insert(ret, nick)
280     end
281     return ret
282 end
283 -- }}}
284
285 -- members {{{
286 ---
287 -- Gets an array of all the users in the channel.
288 -- @param self Channel object
289 -- @return Array of channel users
290 function members(self)
291     local ret = {}
292     -- not just returning self._members, since the return value shouldn't be
293     -- modifiable
294     for nick in self:each_member() do
295         table.insert(ret, nick)
296     end
297     return ret
298 end
299 -- }}}
300 -- }}}
301
302 -- setting modes {{{
303 -- ban {{{
304 -- TODO: hmmm, this probably needs an appropriate mask, rather than a nick
305 ---
306 -- Ban a user from a channel.
307 -- @param self Channel object
308 -- @param name User to ban
309 function ban(self, name)
310     irc.send("MODE", self.name, "+b", name)
311 end
312 -- }}}
313
314 -- unban {{{
315 -- TODO: same here
316 ---
317 -- Remove a ban on a user.
318 -- @param self Channel object
319 -- @param name User to unban
320 function unban(self, name)
321     irc.send("MODE", self.name, "-b", name)
322 end
323 -- }}}
324
325 -- voice {{{
326 ---
327 -- Give a user voice on a channel.
328 -- @param self Channel object
329 -- @param name User to give voice to
330 function voice(self, name)
331     irc.send("MODE", self.name, "+v", name)
332 end
333 -- }}}
334
335 -- devoice {{{
336 ---
337 -- Remove voice from a user.
338 -- @param self Channel object
339 -- @param name User to remove voice from
340 function devoice(self, name)
341     irc.send("MODE", self.name, "-v", name)
342 end
343 -- }}}
344
345 -- op {{{
346 ---
347 -- Give a user ops on a channel.
348 -- @param self Channel object
349 -- @param name User to op
350 function op(self, name)
351     irc.send("MODE", self.name, "+o", name)
352 end
353 -- }}}
354
355 -- deop {{{
356 ---
357 -- Remove ops from a user.
358 -- @param self Channel object
359 -- @param name User to remove ops from
360 function deop(self, name)
361     irc.send("MODE", self.name, "-o", name)
362 end
363 -- }}}
364
365 -- set_limit {{{
366 ---
367 -- Set a channel limit.
368 -- @param self      Channel object
369 -- @param new_limit New value for the channel limit (optional; limit is unset
370 --                  if this argument isn't passed)
371 function set_limit(self, new_limit)
372     if new_limit then
373         irc.send("MODE", self.name, "+l", new_limit)
374     else
375         irc.send("MODE", self.name, "-l")
376     end
377 end
378 -- }}}
379
380 -- set_key {{{
381 ---
382 -- Set a channel password.
383 -- @param self Channel object
384 -- @param key  New channel password (optional; password is unset if this
385 --             argument isn't passed)
386 function set_key(self, key)
387     if key then
388         irc.send("MODE", self.name, "+k", key)
389     else
390         irc.send("MODE", self.name, "-k")
391     end
392 end
393 -- }}}
394
395 -- set_private() {{{
396 ---
397 -- Set the private state of a channel.
398 -- @param self Channel object
399 -- @param set  True to set the channel as private, false to unset it
400 function set_private(self, set)
401     set_basic_mode(self, set, "p")
402 end
403 -- }}}
404
405 -- set_secret {{{
406 ---
407 -- Set the secret state of a channel.
408 -- @param self Channel object
409 -- @param set  True to set the channel as secret, false to unset it
410 function set_secret(self, set)
411     set_basic_mode(self, set, "s")
412 end
413 -- }}}
414
415 -- set_invite_only {{{
416 ---
417 -- Set whether joining the channel requires an invite.
418 -- @param self Channel object
419 -- @param set  True to set the channel invite only, false to unset it
420 function set_invite_only(self, set)
421     set_basic_mode(self, set, "i")
422 end
423 -- }}}
424
425 -- set_topic_lock {{{
426 ---
427 -- If true, the topic can only be changed by an op.
428 -- @param self Channel object
429 -- @param set  True to lock the topic, false to unlock it
430 function set_topic_lock(self, set)
431     set_basic_mode(self, set, "t")
432 end
433 -- }}}
434
435 -- set_no_outside_messages {{{
436 ---
437 -- If true, users must be in the channel to send messages to it.
438 -- @param self Channel object
439 -- @param set  True to require users to be in the channel to send messages to
440 --             it, false to remove this restriction
441 function set_no_outside_messages(self, set)
442     set_basic_mode(self, set, "n")
443 end
444 -- }}}
445
446 -- set moderated {{{
447 ---
448 -- Set whether voice is required to speak.
449 -- @param self Channel object
450 -- @param set  True to set the channel as moderated, false to unset it
451 function set_moderated(self, set)
452     set_basic_mode(self, set, "m")
453 end
454 -- }}}
455 -- }}}
456
457 -- accessors {{{
458 -- contains {{{
459 ---
460 -- Test if a user is in the channel.
461 -- @param self Channel object
462 -- @param nick Nick to search for
463 -- @return True if the nick is in the channel, false otherwise
464 function contains(self, nick)
465     for member in self:each_member() do
466         local member_nick = member:gsub('@+', '')
467         if member_nick == nick then
468             return true
469         end
470     end
471     return false
472 end
473 -- }}}
474 -- }}}
475 -- }}}