def m_privmsg_client(info): ctx = get_context() if ctx.conf.name == info["target"].servername: msg = RFC1459Message.from_data( "PRIVMSG", source=info["source"].hostmask, params=[info["target_name"], info["message"]] ) info["target"].dump_message(msg)
def m_away_notify(info): cli = info['source'] params = cli.metadata.get('away', None) if params: params = [params] msg = RFC1459Message.from_data('AWAY', source=cli.hostmask, params=params) cli.sendto_common_peers(msg, exclude=[cli], cap='away-notify')
def recv(self, number_of_bytes=4096): """Receive bytes from the IRC server.""" try: new_data = self.sock.recv(number_of_bytes).decode( 'utf-8', 'replace') except socket.timeout as ex: error = ex.args[0] if error == 'timed out': return [] else: raise ex self._new_data += new_data raw_messages = [] message_buffer = '' for char in self._new_data: if char in ('\r', '\n'): if len(message_buffer): print(' ->', message_buffer) raw_messages.append(message_buffer) message_buffer = '' continue message_buffer += char # convert to actual rfc1459 messages messages = [] for msg in raw_messages: messages.append(RFC1459Message.from_message(msg)) return messages
def recv(self, number_of_bytes=4096): """Receive bytes from the IRC server.""" try: new_data = self.sock.recv(number_of_bytes).decode('utf-8', 'replace') except socket.timeout as ex: error = ex.args[0] if error == 'timed out': return [] else: raise ex self._new_data += new_data raw_messages = [] message_buffer = '' for char in self._new_data: if char in ('\r', '\n'): if len(message_buffer): print(' ->', message_buffer) raw_messages.append(message_buffer) message_buffer = '' continue message_buffer += char # convert to actual rfc1459 messages messages = [] for msg in raw_messages: messages.append(RFC1459Message.from_message(msg)) return messages
def m_privmsg_channel(info): msg = RFC1459Message.from_data( 'PRIVMSG', source=info['source'], params=[info['target_name'], info['message']]) # XXX - when we have s2s, make sure we only dump messages to local clients here or in dump_message info['target'].dump_message(msg, exclusion_list=[info['source']])
def m_PING(cli, ev_msg): reply = ev_msg['params'][0] if ev_msg['params'] else cli.ctx.conf.name msg = RFC1459Message.from_data('PONG', source=cli.ctx.conf.name, params=[reply]) cli.dump_message(msg) cli.update_pings()
def m_away_notify(info): cli = info['source'] params = cli.metadata.get('away', None) if params: params = [params] msg = RFC1459Message.from_data('AWAY', source=cli, params=params) cli.sendto_common_peers(msg, exclude=[cli], cap='away-notify')
def m_privmsg_client(info): ctx = get_context() if ctx.conf.name == info['target'].servername: msg = RFC1459Message.from_data( 'PRIVMSG', source=info['source'], params=[info['target_name'], info['message']]) info['target'].dump_message(msg)
def dump_verb(self, verb, params, source=None, unprefixed=False): """Dump a verb to a connected client.""" # unprefixed is kind of a hack, but some clients fall over when # prefixes are presented with messages like PING if source is None and not unprefixed: source = self.ctx.conf.name msg = RFC1459Message.from_data(verb, source=source, params=params) self.dump_message(msg)
def flush_legacy_mode_change(self, cli, before, after, before_users, after_users): out = str() args = [] mod = 0 for i in channel_property_items.keys(): if before.get(i, False) and not after.get(i, False): if mod == 1: if i == 'ban' or i == 'quiet' or i == 'invite-exemption' or i == 'exemption': for j in before.get(i, []): if j not in after.get(i): out += channel_property_items[i] args.append(j) continue out += channel_property_items[i] if before.get(i, False) != True: args.append(before.get(i)) else: mod = 1 out += '-' if i == 'ban' or i == 'quiet' or i == 'invite-exemption' or i == 'exemption': for j in before.get(i, []): if j not in after.get(i): out += channel_property_items[i] args.append(j) continue out += channel_property_items[i] if before.get(i, False) != True: args.append(before.get(i)) elif not before.get(i, False) and after.get(i, False): if mod == 2: if i == 'ban' or i == 'quiet' or i == 'invite-exemption' or i == 'exemption': for j in after.get(i): if j not in before.get(i, []): out += channel_property_items[i] args.append(j) continue out += channel_property_items[i] if after.get(i, False) != True: args.append(after.get(i)) else: mod = 2 out += '+' if i == 'ban' or i == 'quiet' or i == 'invite-exemption' or i == 'exemption': for j in after.get(i): if j not in before.get(i, []): out += channel_property_items[i] args.append(j) continue out += channel_property_items[i] if after.get(i, False) != True: args.append(after.get(i)) if len(out) > 0: msg = RFC1459Message.from_data('MODE', source=cli, params=[self.name, out] + args) self.dump_message(msg)
def quit(self, message): eventmgr_core.dispatch('client quit', { 'client': self, 'message': message, }) m = RFC1459Message.from_data('QUIT', source=self.hostmask, params=[message]) self.sendto_common_peers(m) self.exit()
def m_join_channel(info): ch = info['channel'] cli = info['client'] message = info['message'] ctx = get_context() ch.dump_message(RFC1459Message.from_data('PART', source=cli.hostmask, params=[ch.name, message])) ch.part(cli)
def dump_numeric(self, numeric, params, add_target=True): """Dump a numeric to a connected client. This includes the `target` field that numerics have for routing. You do *not* need to include it.""" if add_target: params = [self.nickname] + params msg = RFC1459Message.from_data(numeric, source=self.ctx.conf.name, params=params) self.dump_message(msg)
def quit(self, message): eventmgr_core.dispatch('client quit', { 'client': self, 'message': message, }) m = RFC1459Message.from_data('QUIT', source=self, params=[message]) self.sendto_common_peers(m) self.exit()
def m_NOTICE(cli, ev_msg): targetlist = ev_msg["params"][0].split(",") message = ev_msg["params"][1] for target in targetlist: if target[0] != "#": cli_tg = cli.ctx.clients.get(target, None) if not cli_tg: continue msg = RFC1459Message.from_data("NOTICE", source=cli.hostmask, params=[cli_tg.nickname, message]) cli_tg.dump_message(msg) continue ch = cli.ctx.chmgr.get(target) if not ch or not ch.can_send(cli): continue msg = RFC1459Message.from_data("NOTICE", source=cli.hostmask, params=[ch.name, message]) ch.dump_message(msg, exclusion_list=[cli])
def kill(self, source, reason): eventmgr_core.dispatch('client killed', { 'source': source, 'client': self, 'reason': reason, }) m = RFC1459Message.from_data('KILL', source=source.hostmask, params=[self.nickname, reason]) self.dump_message(m) self.quit('Killed ({source} ({reason}))'.format(source=source.nickname, reason=reason))
def flush_legacy_mode_change(self, cli, before, after, before_users, after_users): out = str() args = [] mod = 0 for i in channel_property_items.keys(): if before.get(i, False) and not after.get(i, False): if mod == 1: if i == 'ban' or i == 'quiet' or i == 'invite-exemption' or i == 'exemption': for j in before.get(i, []): if j not in after.get(i): out += channel_property_items[i] args.append(j) continue out += channel_property_items[i] if before.get(i, False) != True: args.append(before.get(i)) else: mod = 1 out += '-' if i == 'ban' or i == 'quiet' or i == 'invite-exemption' or i == 'exemption': for j in before.get(i, []): if j not in after.get(i): out += channel_property_items[i] args.append(j) continue out += channel_property_items[i] if before.get(i, False) != True: args.append(before.get(i)) elif not before.get(i, False) and after.get(i, False): if mod == 2: if i == 'ban' or i == 'quiet' or i == 'invite-exemption' or i == 'exemption': for j in after.get(i): if j not in before.get(i, []): out += channel_property_items[i] args.append(j) continue out += channel_property_items[i] if after.get(i, False) != True: args.append(after.get(i)) else: mod = 2 out += '+' if i == 'ban' or i == 'quiet' or i == 'invite-exemption' or i == 'exemption': for j in after.get(i): if j not in before.get(i, []): out += channel_property_items[i] args.append(j) continue out += channel_property_items[i] if after.get(i, False) != True: args.append(after.get(i)) if len(out) > 0: msg = RFC1459Message.from_data('MODE', source=cli.hostmask, params=[self.name, out] + args) self.dump_message(msg)
def m_part_channel(info): ch = info['channel'] cli = info['client'] message = info['message'] ctx = get_context() ch.dump_message( RFC1459Message.from_data('PART', source=cli, params=[ch.name, message])) ch.part(cli)
def kill(self, source, reason): eventmgr_core.dispatch('client killed', { 'source': source, 'client': self, 'reason': reason, }) m = RFC1459Message.from_data('KILL', source=source, params=[self.nickname, reason]) self.dump_message(m) self.quit('Killed ({source} ({reason}))'.format(source=source.nickname, reason=reason))
def m_TOPIC(cli, ev_msg): chanlist = ev_msg['params'][0].split(',') for chan in chanlist: if not validate_chan(chan): cli.dump_numeric('479', [chan, 'Illegal channel name']) return ch = cli.ctx.chmgr.get(chan, create=False) if not ch: cli.dump_numeric('403', [chan, 'No such channel']) continue if not ch.has_member(cli): cli.dump_numeric('442', [ch.name, "You're not on that channel"]) continue # handle inquiry if len(ev_msg['params']) == 1: if ch.topic: cli.dump_numeric('332', [ch.name, ch.topic]) cli.dump_numeric('333', [ch.name, ch.topic_setter, ch.topic_ts]) continue cli.dump_numeric('331', [ch.name, 'No topic is set']) continue # XXX - if not ch.get_member(cli).props.get('topic-change', False) and if not ch.props.get('op-topic'): cli.dump_numeric('482', [ch.name, 'You\'re not a channel operator']) continue topic = ev_msg['params'][1] # restrict length if we have it defined topiclen = cli.ctx.conf.limits.get('topic', None) if topiclen and len(ev_msg['params'][1]) > topiclen: topic = topic[:topiclen] # handle setting ch.topic = topic ch.topic_setter = cli.hostmask ch.topic_ts = cli.ctx.current_ts # distribute new topic to peers ch.dump_message( RFC1459Message.from_data('TOPIC', source=cli, params=[ch.name, ch.topic]))
def m_NOTICE(cli, ev_msg): targetlist = ev_msg['params'][0].split(',') message = ev_msg['params'][1] for target in targetlist: if target[0] != '#': cli_tg = cli.ctx.clients.get(target, None) if not cli_tg: continue msg = RFC1459Message.from_data('NOTICE', source=cli, params=[cli_tg.nickname, message]) cli_tg.dump_message(msg) continue ch = cli.ctx.chmgr.get(target) if not ch or not ch.can_send(cli): continue msg = RFC1459Message.from_data('NOTICE', source=cli, params=[ch.name, message]) ch.dump_message(msg, exclusion_list=[cli])
def m_join_channel(info): ch = info['channel'] cli = info['client'] ctx = get_context() ch.join(cli) ch.dump_message(RFC1459Message.from_data('JOIN', source=cli, params=[ch.name]), exclude_cap='extended-join') ch.dump_message(RFC1459Message.from_data( 'JOIN', source=cli, params=[ ch.name, '*' if cli.account is None else cli.account, cli.realname ]), cap='extended-join') if cli.servername != ctx.conf.name: return cli.handle_side_effect('TOPIC', params=[ch.name]) cli.handle_side_effect('NAMES', params=[ch.name])
def m_join_channel(info): ch = info['channel'] cli = info['client'] ctx = get_context() ch.join(cli) ch.dump_message(RFC1459Message.from_data('JOIN', source=cli.hostmask, params=[ch.name])) if cli.servername != ctx.conf.name: return if ch.topic: cli.handle_side_effect('TOPIC', params=[ch.name]) cli.handle_side_effect('NAMES', params=[ch.name])
def m_TOPIC(cli, ev_msg): chanlist = ev_msg['params'][0].split(',') for chan in chanlist: if not validate_chan(chan): cli.dump_numeric('479', [chan, 'Illegal channel name']) return ch = cli.ctx.chmgr.get(chan, create=False) if not ch: cli.dump_numeric('403', [chan, 'No such channel']) continue if not ch.has_member(cli): cli.dump_numeric('442', [ch.name, "You're not on that channel"]) continue # XXX - if not ch.get_member(cli).props.get('topic-change', False) and if not ch.props.get('op-topic'): cli.dump_numeric('482', [ch.name, 'You\'re not a channel operator']) continue # handle inquiry if len(ev_msg['params']) == 1: if ch.topic: cli.dump_numeric('332', [ch.name, ch.topic]) cli.dump_numeric('333', [ch.name, ch.topic_setter, ch.topic_ts]) continue cli.dump_numeric('331', [ch.name, 'No topic is set']) continue topic = ev_msg['params'][1] # restrict length if we have it defined topiclen = cli.ctx.conf.limits.get('topic', None) if topiclen and len(ev_msg['params'][1]) > topiclen: topic = topic[:topiclen] # handle setting ch.topic = topic ch.topic_setter = cli.hostmask ch.topic_ts = cli.ctx.current_ts # distribute new topic to peers ch.dump_message(RFC1459Message.from_data('TOPIC', source=cli.hostmask, params=[ch.name, ch.topic]))
def message_received(self, data): data = data.decode('UTF-8', 'replace').strip('\r\n') linelen = self.ctx.conf.limits.get('line', None) if linelen and len(data) > linelen: data = data[:linelen] m = RFC1459Message.from_message(data) m.client = self # logging.debug('client {0} --> {1}'.format(repr(self.__dict__), repr(m.serialize()))) if len(self.recvq) > self.ctx.conf.recvq_len: self.quit('Excess flood') return self.recvq.append(m) # XXX - drain_queue should be called on all objects at once to enforce recvq limits self.drain_queue()
def m_NICK(cli, ev_msg): new_nickname = ev_msg["params"][0] nicklen = cli.ctx.conf.limits.get("nick", None) if nicklen and len(new_nickname) > nicklen: new_nickname = new_nickname[:nicklen] if not validate_nick(new_nickname): cli.dump_numeric("432", [new_nickname, "Erroneous nickname"]) return if new_nickname in cli.ctx.clients: cli.dump_numeric("433", [new_nickname, "Nickname already in use"]) return msg = RFC1459Message.from_data("NICK", source=cli.hostmask, params=[new_nickname]) if cli.registered: if cli.nickname in cli.ctx.clients: cli.ctx.clients.pop(cli.nickname) cli.ctx.clients[new_nickname] = cli cli.sendto_common_peers(msg) cli.nickname = new_nickname cli.release_registration_lock("NICK")
def m_NICK(cli, ev_msg): new_nickname = ev_msg['params'][0] nicklen = cli.ctx.conf.limits.get('nick', None) if nicklen and len(new_nickname) > nicklen: new_nickname = new_nickname[:nicklen] if not validate_nick(new_nickname): cli.dump_numeric('432', [new_nickname, 'Erroneous nickname']) return with cli.ctx.prereg_nicks_lock: if new_nickname in cli.ctx.clients or new_nickname in cli.ctx.prereg_nicks: cli.dump_numeric('433', [new_nickname, 'Nickname already in use']) return cli.ctx.prereg_nicks.append(new_nickname) msg = RFC1459Message.from_data('NICK', source=cli, params=[new_nickname]) if cli.registered: if cli.nickname in cli.ctx.clients: cli.ctx.clients.pop(cli.nickname) cli.ctx.clients[new_nickname] = cli cli.sendto_common_peers(msg) cli.nickname = new_nickname cli.release_registration_lock('NICK')
def flush_legacy_mode_change(self, before, after): out = str() mod = 0 for i in user_property_items.keys(): if before.get(i, False) and not after.get(i, False): if mod == 1: out += user_property_items[i] else: mod = 1 out += '-' out += user_property_items[i] elif not before.get(i, False) and after.get(i, False): if mod == 2: out += user_property_items[i] else: mod = 2 out += '+' out += user_property_items[i] msg = RFC1459Message.from_data('MODE', source=self.hostmask, params=[self.nickname, out]) self.dump_message(msg)
def flush_legacy_mode_change(self, before, after): out = str() mod = 0 for i in user_property_items.keys(): if before.get(i, False) and not after.get(i, False): if mod == 1: out += user_property_items[i] else: mod = 1 out += '-' out += user_property_items[i] elif not before.get(i, False) and after.get(i, False): if mod == 2: out += user_property_items[i] else: mod = 2 out += '+' out += user_property_items[i] msg = RFC1459Message.from_data('MODE', source=self, params=[self.nickname, out]) self.dump_message(msg)
def send_message(self, verb, **kwargs): """Send the given message to the IRC server.""" msg = RFC1459Message.from_data(verb, **kwargs) self.send(msg)
def m_privmsg_channel(info): msg = RFC1459Message.from_data( "PRIVMSG", source=info["source"].hostmask, params=[info["target_name"], info["message"]] ) # XXX - when we have s2s, make sure we only dump messages to local clients here or in dump_message info["target"].dump_message(msg, exclusion_list=[info["source"]])
def handle_side_effect(self, msg, params=[]): m = RFC1459Message.from_data(msg, source=self.hostmask, params=params) m.client = self self.eventmgr.dispatch(*m.to_event())
def handle_side_effect(self, msg, params=[]): m = RFC1459Message.from_data(msg, source=self, params=params) m.client = self self.eventmgr.dispatch(*m.to_event())
def data_received(self, data): m = RFC1459Message.from_message(data.decode('UTF-8', 'replace').strip('\r\n')) m.client = self em.dispatch(*m.to_event()) self.transport.write(bytes(m.to_message() + '\r\n', 'UTF-8'))
def dump_numeric(self, numeric, params): """Dump a numeric to a connected client. This includes the `target` field that numerics have for routing. You do *not* need to include it.""" msg = RFC1459Message.from_data(numeric, source=self.ctx.conf.name, params=[self.nickname] + params) self.dump_message(msg)
def dump_verb(self, verb, params): """Dump a verb to a connected client.""" msg = RFC1459Message.from_data(verb, source=self.ctx.conf.name, params=params) self.dump_message(msg)
def m_PING(cli, ev_msg): reply = ev_msg["params"][0] if ev_msg["params"] else cli.ctx.conf.name msg = RFC1459Message.from_data("PONG", source=cli.ctx.conf.name, params=[reply]) cli.dump_message(msg)