import disnake from disnake import CategoryChannel, PermissionOverwrite, TextChannel from disnake.ext import commands import logging from orochi import http from orochi.config import Config from orochi.models import Game, GameState, Player, RoundVote, Vote bot = commands.Bot(command_prefix='!') @bot.event async def on_ready(): config: Config = bot.config logger = bot.logger if config.guild is None: config.save() logger.error("The guild ID is missing") exit(1) guild = await bot.fetch_guild(config.guild) if not guild: logger.error("Unknown guild.") exit(1) if config.vote_category is None: category = await guild.create_category("Votes") config.vote_category = category.id config.save() if config.secret_category is None: category = await guild.create_category("Conversation⋅s secrète⋅s") config.secret_category = category.id config.save() vote_category: CategoryChannel = await guild.fetch_channel(config.vote_category) if vote_category is None: config.vote_category = None return await on_ready() secret_category: CategoryChannel = await guild.fetch_channel(config.secret_category) if secret_category is None: config.secret_category = None return await on_ready() await vote_category.set_permissions( guild.default_role, overwrite=PermissionOverwrite(read_message_history=False, read_messages=False) ) await secret_category.set_permissions( guild.default_role, overwrite=PermissionOverwrite(read_message_history=False, read_messages=False) ) for i, player in enumerate(Config.PLAYERS): player_id = player.lower() if player_id not in config.vote_channels: channel: TextChannel = await vote_category.create_text_channel(player_id) config.vote_channels[player_id] = channel.id config.save() channel: TextChannel = await guild.fetch_channel(config.vote_channels[player_id]) if channel is None: del config.vote_channels[player_id] return await on_ready() await channel.edit(name=player_id, category=vote_category, position=i) await channel.set_permissions( guild.default_role, overwrite=PermissionOverwrite(read_message_history=False, read_messages=False) ) if player_id not in config.player_roles: role = await guild.create_role(name=player) config.player_roles[player_id] = role.id config.save() guild = await bot.fetch_guild(guild.id) # update roles role = guild.get_role(config.player_roles[player_id]) if role is None: del config.player_roles[player_id] config.save() return await on_ready() await channel.set_permissions( role, overwrite=PermissionOverwrite(read_message_history=True, read_messages=True) ) game = Game.load('game.save') if not game: game = Game() for player in config.PLAYERS: game.register_player(player, config.vote_channels[player.lower()]) game.save('game.save') # Update private channel id if necessary for player in list(game.players.values()): if player.private_channel_id != config.vote_channels[player.name.lower()]: game.register_player(player.name, config.vote_channels[player.name.lower()]) game.save('game.save') # Setup first round if not exists if not game.rounds: game.rounds.append(game.default_first_round()) game.save('game.save') if not config.telepathy_channel: channel: TextChannel = await secret_category.create_text_channel("bigbrain") config.telepathy_channel = channel.id config.save() telepathy_channel: TextChannel = await guild.fetch_channel(config.telepathy_channel) if not telepathy_channel: config.telepathy_channel = None return await on_ready() await telepathy_channel.edit(name="bigbrain", category=secret_category, position=0, topic="Échanges télépathiques") await telepathy_channel.set_permissions( guild.default_role, overwrite=PermissionOverwrite(read_message_history=False, read_messages=False) ) delphine = guild.get_role(config.player_roles['delphine']) philia = guild.get_role(config.player_roles['philia']) await telepathy_channel.set_permissions( delphine, overwrite=PermissionOverwrite(read_message_history=True, read_messages=True) ) await telepathy_channel.set_permissions( philia, overwrite=PermissionOverwrite(read_message_history=True, read_messages=True) ) if not config.brother_channel: channel: TextChannel = await secret_category.create_text_channel("doliprane") config.brother_channel = channel.id config.save() brother_channel: TextChannel = await guild.fetch_channel(config.brother_channel) if not brother_channel: config.brother_channel = None return await on_ready() await brother_channel.edit(name="doliprane", category=secret_category, position=1, topic="Des voix dans la tête ...") await brother_channel.set_permissions( guild.default_role, overwrite=PermissionOverwrite(read_message_history=False, read_messages=False) ) await brother_channel.set_permissions( philia, overwrite=PermissionOverwrite(read_message_history=True, read_messages=True) ) brother_channel_webhook = None if config.brother_channel_webhook is not None: try: brother_channel_webhook = await bot.fetch_webhook(config.brother_channel_webhook) except disnake.HTTPException | disnake.NotFound | disnake.Forbidden: pass if brother_channel_webhook is None: brother_channel_webhook = await brother_channel.create_webhook(name="???") config.brother_channel_webhook = brother_channel_webhook.id config.save() if not config.backdoor_channel: channel: TextChannel = await secret_category.create_text_channel("backdoor") config.backdoor_channel = channel.id config.save() backdoor_channel: TextChannel = await guild.fetch_channel(config.backdoor_channel) if not backdoor_channel: config.backdoor_channel = None return await on_ready() await backdoor_channel.edit(name="backdoor", category=secret_category, position=2, topic="Panel d'administrati0n du jeu") await backdoor_channel.set_permissions( guild.default_role, overwrite=PermissionOverwrite(read_message_history=False, read_messages=False) ) dan = guild.get_role(config.player_roles['dan']) await backdoor_channel.set_permissions( dan, overwrite=PermissionOverwrite(read_message_history=True, read_messages=True) ) config.save() @bot.command(help="Sauvegarde la partie") @commands.has_permissions(administrator=True) async def save(ctx: commands.Context): Game.INSTANCE.save('game.save') await ctx.reply("La partie a été sauvegardée.") @bot.command(help="Recharger la partie") @commands.has_permissions(administrator=True) async def load(ctx: commands.Context): Game.load('game.save') await ctx.reply("La partie a été rechargée.") @bot.command(help="Envoyer un message en tant qu'Orochi.") @commands.has_permissions(administrator=True) async def send(ctx: commands.Context, *, message: str): await ctx.message.delete() await ctx.send(message) @bot.command(help="Envoyer un message à Philia par la pensée en tant que Brother.") @commands.has_permissions(administrator=True) async def brother(ctx: commands.Context, *, message: str): webhook = await bot.fetch_webhook(bot.config.brother_channel_webhook) await webhook.send(message) await ctx.message.reply("Message envoyé.") @bot.command(help="Ouvrir les votes") async def open(ctx: commands.Context): game: Game = Game.INSTANCE current_round = game.rounds[-1] if game.state == GameState.VOTING: await ctx.reply("Les votes sont déjà ouverts.") return elif game.state == GameState.RESULTS: await ctx.reply("Les votes viennent d'être fermés, merci de démarrer un nouveau tour avec !prepare.") return # Ensure that each room is configured for room in current_round.rooms: if room is None: await ctx.reply("Les salles ne sont pas configurées.") if len(list(room.players)) != 3: await ctx.reply(f"La salle {room.room.value} ne contient pas trois joueurs, merci de la reconfigurer.") # Send messages to players for room in current_round.rooms: votes = list(room.votes) for i, vote in enumerate(votes): players = list(vote.players) other_vote = votes[1 - i] for j, player in enumerate(players): other_player = players[1 - j] if len(players) == 2 else None view = VoteView(timeout=3600) channel_id = player.private_channel_id channel = bot.get_channel(channel_id) message = "Les votes sont ouverts.\n" message += f"Vous devez aller voter en salle **{room.room.value}**.\n" if other_player: message += f"Vous êtes allié⋅e avec **{other_player.name}**.\n" message += f"Vous affrontez {' et '.join(f'**{adv.name}**' for adv in other_vote.players)}.\n" message += "Bonne chance !" await channel.send(message) await channel.send("Pour voter, utilisez l'un des boutons ci-dessous. Vous pouvez appuyer " "sur le bouton plusieurs fois, mais seul le premier vote sera enregistré.", view=view) game.state = GameState.VOTING await ctx.reply("Les salles de vote sont ouvertes, les joueur⋅se⋅s peuvent désormais voter.") class VoteView(disnake.ui.View): @disnake.ui.button(label="S'allier", style=disnake.ButtonStyle.green) async def confirm(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction): await interaction.response.send_message("Votre vote a bien été pris en compte.", ephemeral=True) self.vote(interaction, Vote.ALLY) @disnake.ui.button(label="Trahir", style=disnake.ButtonStyle.red) async def cancel(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction): await interaction.response.send_message("Votre vote a bien été pris en compte.", ephemeral=True) self.vote(interaction, Vote.BETRAY) def vote(self, interaction: disnake.MessageInteraction, vote: Vote) -> None: game = Game.INSTANCE current_round = game.rounds[-1] current_player: Player | None = None for player in game.players.values(): if player.private_channel_id == interaction.channel_id: current_player = player break current_vote: RoundVote | None = None for room in current_round.rooms: for v in room.votes: if current_player in v.players: current_vote = v break else: continue break if current_vote.vote is None: current_vote.vote = vote game.save('game.save') def run(): config = Config.load() http.run_web_server() logger = logging.getLogger('discord') logger.setLevel(logging.DEBUG) handler = logging.FileHandler(filename='../discord.log', encoding='utf-8', mode='w') handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s: %(message)s')) logger.addHandler(handler) bot.config = config bot.logger = logger bot.run(config.discord_token)