#!/usr/bin/env python3 from functools import partial import json from pathlib import Path import random from typing import Literal from xml.dom import minidom import cairosvg import discord from discord.ext import commands from config import * CANTONS = { "AG": "Argovie", "AI": "Appenzell Rhodes-Intérieures", "AR": "Appenzell Rhodes-Extérieures", "BE": "Berne", "BL": "Bâle-Campagne", "BS": "Bâle-Ville", "FR": "Fribourg", "GE": "Genève", "GL": "Glaris", "GR": "Grisons", "JU": "Jura", "LU": "Lucerne", "NE": "Neuchâtel", "NW": "Nidwald", "OW": "Obwald", "SG": "Saint-Gall", "SH": "Schaffhouse", "SO": "Soleure", "SZ": "Schwytz", "TH": "Thurgovie", "TI": "Tessin", "UR": "Uri", "VD": "Vaud", "VS": "Valais", "ZG": "Zoug", "ZH": "Zurich", } CodeCanton = Literal["AG", "AI", "AR", "BE", "BL", "BS", "FR", "GE", "GL", "GR", "JU", "LU", "NE", "NW", "OW", "SG", "SH", "SO", "SZ", "TH", "TI", "UR", "VD", "VS", "ZG", "ZH"] Couleur = Literal["rouge", "vert"] intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix='$', intents=intents) DATA_FILE = Path(__file__).parent / "data.json" if DATA_FILE.exists(): with DATA_FILE.open() as data_file: data = json.load(data_file) else: data = {'equipes': {'rouge': [], 'vert': []}, 'cantons': {code_canton: {'capture': None, 'verrouille': False} for code_canton in CANTONS.keys()}} with DATA_FILE.open('w') as data_file: json.dump(data, data_file, indent=4) def generer_carte(): doc = minidom.parse("map_blank.svg") for code_canton, data_canton in data['cantons'].items(): if data_canton['capture']: path = next(e for e in doc.getElementsByTagName('path') if e.getAttribute('id') == code_canton) couleur = data_canton['capture'] if data_canton['verrouille']: path.setAttribute('fill', f"url(#verrouille-{couleur})") else: path.setAttribute('class', f"capture-{couleur}") with open('map.svg', 'w') as f: doc.writexml(f) cairosvg.svg2png(url='map.svg', write_to='map.png') @bot.command() async def carte(ctx: commands.Context): rouges = list(canton_code for canton_code, canton in data['cantons'].items() if canton['capture'] == "rouge") rouges_verrouilles = list(canton_code for canton_code, canton in data['cantons'].items() if canton['capture'] == "rouge" and canton['verrouille']) noms_rouges = ", ".join(code_canton + (":lock:" if code_canton in rouges_verrouilles else "") for code_canton in rouges) verts = list(canton_code for canton_code, canton in data['cantons'].items() if canton['capture'] == "vert") verts_verrouilles = list(canton_code for canton_code, canton in data['cantons'].items() if canton['capture'] == "vert" and canton['verrouille']) noms_verts = ", ".join(code_canton + (":lock:" if code_canton in verts_verrouilles else "") for code_canton in verts) libres = list(canton_code for canton_code, canton in data['cantons'].items() if canton['capture'] is None) message = f""":red_circle: Équipe rouge : **{len(rouges)} canton{"s" if len(rouges) > 1 else ""}** (dont **{len(rouges_verrouilles)} verrouillé{"s" if len(rouges_verrouilles) > 1 else ""}**) : {noms_rouges} :green_circle: Équipe verte : **{len(verts)} canton{"s" if len(verts) > 1 else ""}** (dont **{len(verts_verrouilles)} verrouillé{"s" if len(verts_verrouilles) > 1 else ""}**) : {noms_verts} :white_circle: **{len(libres)} canton{"s" if len(libres) > 1 else ""}** libre{"s" if len(libres) > 1 else ""} : {", ".join(libres)}""" generer_carte() with open('map.png', 'rb') as f: await ctx.send(message, file=discord.File(f, filename="battle4suisse.png")) @bot.command() async def capturer(ctx: commands.Context, canton: CodeCanton, *, couleur: Couleur | None = None): if couleur is None: author_id = ctx.author.id for couleur, membres_equipe in data['equipes'].items(): if author_id in membres_equipe: break else: raise commands.BadArgument("Vous n'appartez à aucune équipe. Merci de faire `$equipe [rouge|vert]`.") data['cantons'][canton]['capture'] = couleur with DATA_FILE.open('w') as data_file: json.dump(data, data_file, indent=4) await ctx.send(f"@everyone L'équipe {couleur} a capturé le canton de **{CANTONS[canton]}** !") return await carte(ctx) @capturer.error async def capture_error(ctx, error): if isinstance(error, commands.BadLiteralArgument): await ctx.send(f"Canton inconnu : {error.argument}, valeurs possibles : {", ".join(error.literals)}") else: await ctx.send(str(error)) @bot.command() async def verrouiller(ctx: commands.Context, canton: CodeCanton, *, couleur: Couleur | None = None): if couleur is None: author_id = ctx.author.id for couleur, membres_equipe in data['equipes'].items(): if author_id in membres_equipe: break else: raise commands.BadArgument("Vous n'appartez à aucune équipe. Merci de faire `$equipe [rouge|vert]`.") data['cantons'][canton]['capture'] = couleur data['cantons'][canton]['verrouille'] = True with DATA_FILE.open('w') as data_file: json.dump(data, data_file, indent=4) generer_carte() await ctx.send(f"@everyone L'équipe {couleur} a capturé le canton de **{CANTONS[canton]}** !") return await carte(ctx) @bot.command() async def reset(ctx: commands.Context, canton: CodeCanton): data['cantons'][canton]['capture'] = None data['cantons'][canton]['verrouille'] = False with DATA_FILE.open('w') as data_file: json.dump(data, data_file, indent=4) generer_carte() return await carte(ctx) @bot.command() async def equipe(ctx: commands.Context, couleur: Couleur): author_id = ctx.author.id for membres_equipe in data['equipes'].values(): if author_id in membres_equipe: membres_equipe.remove(author_id) data['equipes'][couleur].append(author_id) with DATA_FILE.open('w') as data_file: json.dump(data, data_file, indent=4) await ctx.send(f"Équipe {couleur} rejointe") @equipe.error async def equipe_error(ctx, error): await ctx.send(str(error)) bot.run(DISCORD_TOKEN)