diff options
Diffstat (limited to 'IRCServer.py')
| -rw-r--r-- | IRCServer.py | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/IRCServer.py b/IRCServer.py new file mode 100644 index 00000000..1f291c23 --- /dev/null +++ b/IRCServer.py @@ -0,0 +1,216 @@ +import collections, socket, ssl, sys, time +import IRCChannel, IRCLineHandler, IRCUser + +class Server(object): + def __init__(self, id, hostname, port, password, ipv4, tls, + nickname, username, realname, bot): + self.connected = False + self.bot = bot + self.id = id + self.target_hostname = hostname + self.port = port + self.tls = tls + self.password = password + self.ipv4 = ipv4 + self.nickname = nickname + self.username = username or nickname + self.realname = realname or nickname + self.write_buffer = b"" + self.read_buffer = b"" + self.users = {} + self.new_users = set([]) + self.nickname_ids = {} + self.channels = {} + self.own_modes = set([]) + self.mode_prefixes = collections.OrderedDict() + self.channel_modes = [] + self.channel_types = [] + self.last_read = None + if ipv4: + self.socket = socket.socket(socket.AF_INET, + socket.SOCK_STREAM) + else: + self.socket = socket.socket(socket.AF_INET6, + socket.SOCK_STREAM) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + if self.tls: + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + self.socket = context.wrap_socket(self.socket) + self.cached_fileno = self.socket.fileno() + def __repr__(self): + return "%s:%s%s" % (self.target_hostname, "+" if self.tls else "", + self.port) + def __str__(self): + return repr(self) + def fileno(self): + fileno = self.socket.fileno() + return self.cached_fileno if fileno == -1 else fileno + def connect(self): + self.socket.connect((self.target_hostname, self.port)) + if self.password: + self.send_pass(self.password) + self.send_user(self.username, self.realname) + self.send_nick(self.nickname) + self.connected = True + def disconnect(self): + self.connected = False + try: + self.socket.shutdown(socket.SHUT_RDWR) + except: + pass + try: + self.socket.close() + except: + pass + def set_setting(self, setting, value): + self.bot.database.set_server_setting(self.id, setting, + value) + def get_setting(self, setting, default=None): + return self.bot.database.get_server_setting(self.id, + setting, default) + def find_settings(self, pattern, default=[]): + return self.bot.database.find_server_settings(self.id, + pattern, default) + def del_setting(self, setting): + self.bot.database.del_server_setting(self.id, setting) + def set_own_nickname(self, nickname): + self.nickname = nickname + self.nickname_lower = nickname.lower() + def is_own_nickname(self, nickname): + return nickname.lower() == self.nickname_lower + def add_own_mode(self, mode): + self.own_modes.add(mode) + def remove_own_mode(self, mode): + self.own_modes.remove(mode) + def has_user(self, nickname): + return nickname.lower() in self.nickname_ids + def get_user(self, nickname): + if not self.has_user(nickname): + new_user = IRCUser.User(nickname, self, self.bot) + self.bot.events.on("new").on("user").call( + user=new_user, server=self) + self.users[new_user.id] = new_user + self.nickname_ids[nickname.lower()] = new_user.id + self.new_users.add(new_user) + return self.users[self.nickname_ids[nickname.lower()]] + def remove_user(self, user): + print("removing %s" % user.nickname) + del self.users[user.id] + del self.nickname_ids[user.nickname_lower] + for channel in user.channels: + channel.remove_user(user) + def change_user_nickname(self, old_nickname, new_nickname): + self.nickname_ids[new_nickname.lower()] = self.nickname_ids.pop(old_nickname.lower()) + def has_channel(self, channel_name): + return channel_name[0] in self.channel_types and channel_name.lower( + ) in self.channels + def get_channel(self, channel_name): + if not self.has_channel(channel_name): + new_channel = IRCChannel.Channel(channel_name, self, + self.bot) + self.bot.events.on("new").on("channel").call( + channel=new_channel, server=self) + self.channels[new_channel.name] = new_channel + return self.channels[channel_name.lower()] + def remove_channel(self, channel): + for users in channel.users: + user.part_channel(channel) + del self.channels[channel.name] + def parse_line(self, line): + if line: + line_split = line.split(" ") + IRCLineHandler.handle(line, line_split, self.bot, self) + self.check_users() + def check_users(self): + for user in self.new_users: + if not len(user.channels): + self.remove_user(user) + self.new_users.clear() + def read(self): + encoding = self.bot.database.get_server_setting(self.id, + "encoding", "utf8") + fallback_encoding = self.bot.database.get_server_setting( + self.id, "fallback-encoding", "latin-1") + data = self.read_buffer + self.socket.recv(4096) + self.read_buffer = b"" + data_lines = [line.strip(b"\r") for line in data.split(b"\n")] + if data_lines[-1]: + self.read_buffer = data_lines[-1] + data_lines.pop(-1) + decoded_lines = [] + for line in data_lines: + try: + line = line.decode(encoding) + except: + try: + line = line.decode(fallback_encoding) + except: + continue + decoded_lines.append(line) + if not decoded_lines: + self.disconnect() + self.last_read = time.time() + return decoded_lines + def send(self, data): + encoded = data.encode("utf8") + if len(encoded) > 450: + encoded = encoded[:450] + self.write_buffer += b"%s\r\n" % encoded + print(encoded.decode("utf8")) + def _send(self): + self.write_buffer = self.write_buffer[self.socket.send( + self.write_buffer):] + def waiting_send(self): + return bool(len(self.write_buffer)) + def send_user(self, username, realname): + self.send("USER %s - - :%s" % (username, realname)) + def send_nick(self, nickname): + self.send("NICK %s" % nickname) + def send_pass(self, password): + self.send("PASS %s" % password) + def send_ping(self, nonce="hello"): + self.send("PING :%s" % nonce) + def send_pong(self, nonce="hello"): + self.send("PONG :%s" % nonce) + def send_join(self, channel_name, key=None): + self.send("JOIN %s%s" % (channel_name, + "" if key == None else " %s" % key)) + def send_part(self, channel_name, reason=None): + self.send("PART %s%s" % (channel_name, + "" if key == None else " %s" % reason)) + def send_quit(self, reason="Leaving"): + self.send("QUIT :%s" % reason) + def send_message(self, target, message): + self.send("PRIVMSG %s :%s" % (target, message)) + if self.has_channel(target): + action = message.startswith("\01ACTION ") and message.endswith( + "\01") + if action: + message = message.split("\01ACTION ", 1)[1][:-1] + self.get_channel(target).log.add_line(None, message, action, True) + def send_notice(self, target, message): + self.send("NOTICE %s :%s" % (target, message)) + def send_mode(self, target, mode=None, args=None): + self.send("MODE %s%s%s" % (target, "" if mode == None else " %s" % mode, + "" if args == None else " %s" % args)) + def send_topic(self, channel_name, topic): + self.send("TOPIC %s :%s" % (channel_name, topic)) + def send_kick(self, channel_name, target, reason=None): + self.send("KICK %s %s%s" % (channel_name, target, + "" if reason == None else " :%s" % reason)) + def send_names(self, channel_name): + self.send("NAMES %s" % channel_name) + def send_list(self, search_for=None): + self.send( + "LIST%s" % "" if search_for == None else " %s" % search_for) + def send_invite(self, target, channel_name): + self.send("INVITE %s %s" % (target, channel_name)) + def send_whois(self, target): + self.send("WHOIS %s" % target) + def send_whowas(self, target, amount=None, server=None): + self.send("WHOWAS %s%s%s" % (target, + "" if amount == None else " %s" % amount, + "" if server == None else " :%s" % server)) + def send_who(self, filter=None): + self.send("WHO%s" % ("" if filter == None else " %s" % filter)) |
