def on_chghost(cli, rawnick, ident, host): """Handle a user changing host without a quit. Ordering and meaning of arguments for CHGHOST: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick (nick!ident@host) of the user switching 2 - The new ident for the user (or same if unchanged) 3 - The new host for the user (or same if unchanged) """ user = users._get(rawnick) # FIXME new = users._add(cli, nick=user.nick, ident=ident, host=host, realname=user.realname, account=user.account) # FIXME if user is not new: new.channels = user.channels.copy() new.timestamp = user.timestamp # We lie, but it's ok for chan in set(user.channels): chan.remove_user(user) chan.users.add(new) user.swap(new)
def mode_change(cli, rawnick, chan, mode, *targets): """Update the channel and user modes whenever a mode change occurs. Ordering and meaning of arguments for a MODE change: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick of the mode setter/actor 2 - The channel (target) of the mode change 3 - The mode changes * - The targets of the modes (if any) This takes care of properly updating all relevant users and the channel modes to make sure we remain internally consistent. """ actor = users._add(cli, nick=rawnick) # FIXME if chan == users.Bot.nick: # we only see user modes set to ourselves users.Bot.modes.update(mode) return target = channels.add(chan, cli) target.queue("mode_change", { "mode": mode, "targets": targets }, (var, actor, target))
def kicked_from_chan(cli, rawnick, chan, target, reason): """Handle a user being kicked from a channel. Ordering and meaning of arguments for a channel KICK: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick (nick!ident@host) of the user performing the kick 2 - The channel the kick was performed on 3 - The target of the kick 4 - The reason given for the kick (always present) """ ch = channels.add(chan, cli) actor = users._add(cli, nick=rawnick) # FIXME user = users._add(cli, nick=target) # FIXME Event("chan_kick", {}).dispatch(var, ch, actor, user, reason) if user is users.Bot: ch._clear() else: ch.remove_user(user)
def who_reply(cli, bot_server, bot_nick, chan, ident, host, server, nick, status, hopcount_gecos): """Handle WHO replies for servers without WHOX support. Ordering and meaning of arguments for a bare WHO response: 0 - The IRCClient instance (like everywhere else) 1 - The server the requester (i.e. the bot) is on 2 - The nickname of the requester (i.e. the bot) 3 - The channel the request was made on 4 - The ident of the user in this reply 5 - The hostname of the user in this reply 6 - The server the user in this reply is on 7 - The nickname of the user in this reply 8 - The status (H = Not away, G = Away, * = IRC operator, @ = Opped in the channel in 4, + = Voiced in the channel in 4) 9 - The hop count and realname (gecos) This fires off the "who_result" event, and dispatches it with three arguments, the game state namespace, a Channel, and a User. Less important attributes can be accessed via the event.params namespace. """ hop, realname = hopcount_gecos.split(" ", 1) hop = int(hop) # We throw away the information about the operness of the user, but we probably don't need to care about that # We also don't directly pass which modes they have, since that's already on the channel/user is_away = ("G" in status) modes = {Features["PREFIX"].get(s) for s in status} - {None} user = users._add(cli, nick=nick, ident=ident, host=host, realname=realname) # FIXME ch = None if not channels.predicate(chan): # returns True if it's not a channel ch = channels.add(chan, cli) if ch not in user.channels: user.channels[ch] = modes ch.users.add(user) for mode in modes: if mode not in ch.modes: ch.modes[mode] = set() ch.modes[mode].add(user) event = Event("who_result", {}, away=is_away, data=0, ip_address=None, server=server, hop_count=hop, idle_time=None, extended_who=False) event.dispatch(var, ch, user) if ch is channels.Main and not users.exists(nick): # FIXME users.add(nick, ident=ident, host=host, account="*", inchan=True, modes=modes, moded=set())
def on_account_change(cli, rawnick, account): """Handle a user changing accounts, if enabled. Ordering and meaning of arguments for an ACCOUNT change: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick (nick!ident@host) of the user changing accounts 2 - The account the user changed to We don't see our own account changes, so be careful! """ user = users._add(cli, nick=rawnick) # FIXME user.account = account # We don't pass it to add(), since we want to grab the existing one (if any) Event("account_change", {}).dispatch(var, user)
def on_quit(cli, rawnick, reason): """Handle a user quitting the IRC server. Ordering and meaning of arguments for a server QUIT: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick (nick!ident@host) of the user quitting 2 - The reason for the quit (always present) """ user = users._add(cli, nick=rawnick) # FIXME Event("server_quit", {}).dispatch(var, user, reason) for chan in set(user.channels): if user is users.Bot: chan._clear() else: chan.remove_user(user)
def join_chan(cli, rawnick, chan, account=None, realname=None): """Handle a user joining a channel, which may be the bot itself. Ordering and meaning of arguments for a channel JOIN: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick (nick!ident@host) of the user joining the channel 2 - The channel the user joined The following two arguments are optional and only present if the server supports the extended-join capability (we will have requested it when we connected if it was supported): 3 - The account the user is identified to, or "*" if none 4 - The realname (gecos) of the user, or "" if none """ if account == "*": account = None if realname == "": realname = None ch = channels.add(chan, cli) ch.state = channels._States.Joined user = users._add(cli, nick=rawnick, realname=realname, account=account) # FIXME ch.users.add(user) user.channels[ch] = set() # mark the user as here, in case they used to be connected before but left user.disconnected = False if user is users.Bot: ch.mode() ch.mode(Features["CHANMODES"][0]) ch.who() Event("chan_join", {}).dispatch(var, ch, user)
def part_chan(cli, rawnick, chan, reason=""): """Handle a user leaving a channel, which may be the bot itself. Ordering and meaning of arguments for a channel PART: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick (nick!ident@host) of the user leaving the channel 2 - The channel being left The following argument may or may not be present: 3 - The reason the user gave for parting (if any) """ ch = channels.add(chan, cli) user = users._add(cli, nick=rawnick) # FIXME Event("chan_part", {}).dispatch(var, ch, user, reason) if user is users.Bot: # oh snap! we're no longer in the channel! ch._clear() else: ch.remove_user(user)
def extended_who_reply(cli, bot_server, bot_nick, data, chan, ident, ip_address, host, server, nick, status, hop, idle, account, realname): """Handle WHOX responses for servers that support it. An extended WHO (WHOX) is caracterised by a second parameter to the request That parameter must be '%' followed by at least one of 'tcuihsnfdlar' If the 't' specifier is present, the specifiers must be followed by a comma and at most 3 bytes This is the ordering if all parameters are present, but not all of them are required If a parameter depends on a specifier, it will be stated at the front If a specifier is not given, the parameter will be omitted in the reply Ordering and meaning of arguments for an extended WHO (WHOX) response: 0 - - The IRCClient instance (like everywhere else) 1 - - The server the requester (i.e. the bot) is on 2 - - The nickname of the requester (i.e. the bot) 3 - t - The data sent alongside the request 4 - c - The channel the request was made on 5 - u - The ident of the user in this reply 6 - i - The IP address of the user in this reply 7 - h - The hostname of the user in this reply 8 - s - The server the user in this reply is on 9 - n - The nickname of the user in this reply 10 - f - Status (H = Not away, G = Away, * = IRC operator, @ = Opped in the channel in 5, + = Voiced in the channel in 5) 11 - d - The hop count 12 - l - The idle time (or 0 for users on other servers) 13 - a - The services account name (or 0 if none/not logged in) 14 - r - The realname (gecos) This fires off the "who_result" event, and dispatches it with three arguments, the game state namespace, a Channel, and a User. Less important attributes can be accessed via the event.params namespace. """ if account == "0": account = None hop = int(hop) idle = int(idle) is_away = ("G" in status) data = int.from_bytes(data.encode(Features["CHARSET"]), "little") modes = {Features["PREFIX"].get(s) for s in status} - {None} user = users._add(cli, nick=nick, ident=ident, host=host, realname=realname, account=account) # FIXME ch = None if not channels.predicate(chan): ch = channels.add(chan, cli) if ch not in user.channels: user.channels[ch] = modes ch.users.add(user) for mode in modes: if mode not in ch.modes: ch.modes[mode] = set() ch.modes[mode].add(user) event = Event("who_result", {}, away=is_away, data=data, ip_address=ip_address, server=server, hop_count=hop, idle_time=idle, extended_who=True) event.dispatch(var, ch, user) if ch is channels.Main and not users.exists(nick): # FIXME users.add(nick, ident=ident, host=host, account=account, inchan=True, modes=modes, moded=set())