#!/bin/env python3 # SPDX-License-Identifier: MIT import asyncio, configparser from irctokens import build, Line from ircrobots import Bot as BaseBot from ircrobots import Server as BaseServer from ircrobots import ConnectionParams import pickle import json servers = [] match_players = {} connections = {} admins = {} lastPlayed = "" def update_file(name): with open(f"playerlist-{name}.txt", "wb") as file: pickle.dump(match_players, file) def read_file(name): try: with open(f"playerlist-{name}.txt", "rb") as file: temp = pickle.load(file) match_players[name] = temp[name] except EOFError: match_players[name] = {} print("Empty") class Server(BaseServer): async def line_read(self, line: Line): print("Line Params: ", line.params) if "PRIVMSG" in line.command and any(channel in line.params[0] for channel in self.params.autojoin): chan = self.channels[line.params[0].lstrip("@")].name if line.params[1].startswith(".match"): channel = self.channels[line.params[0].lstrip("@")] if len(line.params[1].split(" ")) < 2: await self.send(build("PRIVMSG", [chan, "ERROR: game not specified"])) else: game = " ".join(line.params[1].split(" ")[1:]).lower() ping = "" user = line.source.split('!')[0] if game not in match_players[self.name + '-' + chan]: await self.send(build("PRIVMSG", [chan, "ERROR: no players in "+ game])) else: matches = match_players[self.name + '-' + chan][game] others = list(set(matches) - set([user])) # don't ping the user who .matched global lastPlayed lastPlayed = game if len(others) > 0: for player in others: pfold = self.casefold(player) if pfold in channel.users: ping += f"{channel.users[pfold].nickname} " else: ping += f"{player} " await self.send(build("PRIVMSG", [chan, "Anyone ready for " + game + f" : {ping} ?"])) if connections[self.name]: for connection,address in connections[self.name].items(): if connection in line.params[1]: if ':' in address: realaddress = address.split(':')[0] port = address.split(':')[1] self.send(build("PRIVMSG", [chan, f"Connect using nc {realaddress} {port}"])) else: self.send(build("PRIVMSG", [chan, f"Connect using nc {address} 1234"])) else: await self.send(build("PRIVMSG", [chan, user + ": you're the only one who plays this game :("])) elif line.params[1].startswith(".add"): user = line.source.split('!')[0] if len(line.params[1].split(" ")) < 2: await self.send(build("PRIVMSG", [chan, "ERROR: game not specified"])) else: game = " ".join(line.params[1].split(" ")[1:]).lower() if game not in match_players[self.name + '-' + chan]: match_players[self.name + '-' + chan][game] = set() if user in match_players[self.name + '-' + chan][game]: await self.send(build("PRIVMSG", [chan, "ERROR: player already in " + game + " list."])) else: match_players[self.name + '-' + chan][game].add(user) update_file(self.name + '-' + chan) await self.send(build("PRIVMSG", [chan, "Added " + user + " to the " + game + " match list."])) elif line.params[1].startswith(".last"): await self.send(build("PRIVMSG", [chan, "Last game played: " + lastPlayed])) elif line.params[1].startswith(".list"): user = line.source.split('!')[0] if len(line.params[1].split(" ")) < 2: games = sorted(match_players[self.name + '-' + chan].keys()) while len(games): await self.send(build("PRIVMSG", [chan, "list: " + ', '.join(games[:20])])) del games[:20] else: game = " ".join(line.params[1].split(" ")[1:]).lower() if game not in match_players[self.name + '-' + chan]: await self.send(build("PRIVMSG", [chan, "ERROR: no players in " + game + " list."])) else: await self.send(build("PRIVMSG", [chan, game + " players: " + str(['_' + e for e in match_players[self.name + '-' + chan][game]])])) elif line.params[1].startswith(".remove"): channel = self.channels[line.params[0].lstrip("@")].name if channel in admins[self.name]: fadmins = admins[self.name][channel] else: fadmins = [] user = line.source.split('!')[0] game = " ".join(line.params[1].split(" ")[1:]).lower() if game not in match_players[self.name + '-' + chan]: await self.send(build("PRIVMSG", [chan, "ERROR: game " + game + " not found"])) if user not in fadmins: await self.send(build("PRIVMSG", [chan, "ERROR: user " + user + " is not an admin"])) if user in fadmins and game in match_players[self.name + '-' + chan]: del match_players[self.name + '-' + chan][game] update_file(self.name + '-' + chan) await self.send(build("PRIVMSG", [chan, "Removed " + game])) elif line.params[1].startswith(".del"): user = line.source.split('!')[0] if len(line.params[1].split(" ")) < 2: await self.send(build("PRIVMSG", [chan, "ERROR: game not specified"])) else: game = " ".join(line.params[1].split(" ")[1:]).lower() if game not in match_players[self.name + '-' + chan]: await self.send(build("PRIVMSG", [chan, "ERROR: no players in "+ game])) else: if user not in match_players[self.name + '-' + chan][game]: await self.send(build("PRIVMSG", [chan, "ERROR: player isn't in list."])) else: match_players[self.name + '-' + chan][game].remove(user) if len(match_players[self.name + '-' + chan][game]) == 0: del match_players[self.name + '-' + chan][game] update_file(self.name + '-' + chan) await self.send(build("PRIVMSG", [chan, "Removed " + user + " from the " + game + " match list."])) elif line.params[1].startswith(".help"): await self.send(build("PRIVMSG", [chan, " .add game: Add user to the game list; .del game: Remove user from the game list; .match game: Ping everyone on the game list; .list game: list people added to the game; .list: list games; .remove game: removes the game from the list (admin command); .last: show last played game"])) async def line_send(self, line: Line): print(f"{self.name} > {line.format()}") class Bot(BaseBot): def create_server(self, name: str): return Server(self, name) async def main(): config = configparser.ConfigParser() with open('bot.ini', 'r') as configfile: config.read_file(configfile) for section in config.sections(): if not config[section]['server']: print(f"ERROR: The section {section} on your bot.ini doesn't have a 'server' parameter.") exit(1) elif not config[section]['nickname']: print(f"ERROR: The section {section} has no nickname defined.") exit(1) elif not config[section]['channels']: print(f"ERROR: You didn't define any channels in section {section}.") exit(1) elif not config[section]['admins']: print(f"ERROR: You didn't define any admins in section {section}.") exit(1) server = config[section]['server'] nickname = config[section]['nickname'] channels = set(config[section]['channels'].split()) try: admins[section] = json.loads(config[section]['admins']) except Exception as e: print(f"ERROR: Invalid admins in section {section}.") print(e) exit(1) if config[section]['connections']: if len(config[section]['connections'].split()) % 2 != 0: # If connections isn't even print(f"ERROR: The section {section} has an invalid 'connections' defined.") exit(1) config_connections = config[section]['connections'].split() connections.update({section:{}}) for i in range(0, len(config_connections), 2): connections[section].update({config_connections[i]:config_connections[i+1]}) servers.append({'name':section, 'opts':{'server':server, 'nickname':nickname, 'channels':channels}}) # read_file() bot = Bot() for entry in servers: name = entry['name'] server = entry['opts']['server'] botnick = entry['opts']['nickname'] channels = entry['opts']['channels'] params = ConnectionParams(botnick, server, 6697, True) for channel in channels: params.autojoin.append(channel) print("Reading channel", channel) read_file(name + '-' + channel) print(match_players) await bot.add_server(name, params) print("Match players is: ", match_players) await bot.run() if __name__ == "__main__": asyncio.run(main())