matchmaking-bot/bot.py

210 lines
10 KiB
Python
Executable File

#!/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())