From ca0bc6afbf8a59e8668bd96b9236d89f6ff58265 Mon Sep 17 00:00:00 2001 From: shun <1ntegrale9uation@gmail.com> Date: Tue, 3 Oct 2023 20:42:55 +0900 Subject: [PATCH] =?UTF-8?q?=E6=8A=95=E7=A5=A8=E3=82=92=E3=83=AA=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3&=E3=83=9A=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=83=8D=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=8B?= =?UTF-8?q?=E3=82=89ui.Select=E3=81=AB=E7=A7=BB=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/pagenator.py | 109 --------------------------- extensions/change_date.py | 59 +++++++++++++++ extensions/{status.py => play.py} | 54 ++++++++++++-- extensions/players.py | 48 ------------ extensions/vote.py | 119 ++++++------------------------ extensions/vote_fortune.py | 40 ++++++++++ extensions/vote_raid.py | 40 ++++++++++ 7 files changed, 209 insertions(+), 260 deletions(-) delete mode 100644 application/pagenator.py create mode 100644 extensions/change_date.py rename extensions/{status.py => play.py} (52%) delete mode 100644 extensions/players.py create mode 100644 extensions/vote_fortune.py create mode 100644 extensions/vote_raid.py diff --git a/application/pagenator.py b/application/pagenator.py deleted file mode 100644 index 4d74ae2..0000000 --- a/application/pagenator.py +++ /dev/null @@ -1,109 +0,0 @@ -import discord - -colmn_reactions = [ - '\N{DIGIT ONE}\N{COMBINING ENCLOSING KEYCAP}', - '\N{DIGIT TWO}\N{COMBINING ENCLOSING KEYCAP}', - '\N{DIGIT THREE}\N{COMBINING ENCLOSING KEYCAP}', - '\N{DIGIT FOUR}\N{COMBINING ENCLOSING KEYCAP}', - '\N{DIGIT FIVE}\N{COMBINING ENCLOSING KEYCAP}', - '\N{DIGIT SIX}\N{COMBINING ENCLOSING KEYCAP}', - '\N{DIGIT SEVEN}\N{COMBINING ENCLOSING KEYCAP}', - '\N{DIGIT EIGHT}\N{COMBINING ENCLOSING KEYCAP}', -] -back_reaction = '\U00002b05' -go_reaction = '\U000027a1' - - -class Pagenator: - def __init__(self, bot, target_user, channel, data, title, desc): - self.channel = channel - self.target_user = target_user - self.bot = bot - self.title = title - self.desc = desc - - # [value, value2, value3] - self.data = data - self.page = 0 - - self.message = None - - # 1ページのデータの個数 - self.columns = 8 - - self.max_page = len(self.data) / 8 if not len(self.data) % 8 else len(self.data) // 8 + 1 - - async def reflesh_message(self): - if self.message: - self.message = await self.channel.fetch_message(self.message.id) - - async def loop(self): - def check(reaction, user): - if str(reaction.emoji) not in colmn_reactions + [back_reaction, go_reaction]: - return False - return user.id == self.target_user.id and isinstance(reaction.message.channel, discord.DMChannel) - - while not self.bot.is_closed(): - reaction, user = await self.bot.wait_for('reaction_add', check=check, timeout=None) - emoji = str(reaction.emoji) - if emoji in colmn_reactions: - p = colmn_reactions.index(emoji) - embeded_data = self.data[self.page * 8:self.page * 8 + 8] - if p >= len(embeded_data): - continue - - selected = embeded_data[p] - return selected - - elif emoji in [back_reaction, go_reaction]: - if emoji == back_reaction: - self.back_page() - else: - self.go_page() - - await self.edit_embed(self.get_embed()) - await self.reflesh_message() - continue - - async def add_reactions(self): - if self.message is None: - return - for x in colmn_reactions: - await self.message.add_reaction(x) - await self.message.add_reaction(back_reaction) - await self.message.add_reaction(go_reaction) - - async def start(self): - """処理を開始し、結果を返す""" - if self.message is not None: - return - - message = await self.channel.send(embed=self.get_embed()) - self.message = message - await self.add_reactions() - return await self.loop() - - async def edit_embed(self, embed): - if self.message is None: - return - await self.message.edit(embed=embed) - - def get_embed(self): - embed = discord.Embed(title=self.title, description=self.desc) - embeded_data = self.data[self.page * 8:self.page * 8 + 8] - for i, column in enumerate(embeded_data, start=1): - embed.add_field(name=str(i), value=str(column), inline=False) - - return embed - - def go_page(self): - """ページを進める""" - if self.page == self.max_page: - return - self.page += 1 - - def back_page(self): - """ページを戻す""" - if self.page == 0: - return - self.page -= 1 diff --git a/extensions/change_date.py b/extensions/change_date.py new file mode 100644 index 0000000..5c7282b --- /dev/null +++ b/extensions/change_date.py @@ -0,0 +1,59 @@ +import discord +from discord import app_commands +from discord.ext import commands +from application.game import Game + + +class ChangeDateCog(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name='日付変更', description='投票&役職選択完了を確認して日付変更処理を行います') + @app_commands.guild_only() + async def change_date(self, interaction: discord.Interaction): + game: Game = self.bot.game + + if not game.is_set_target(): + return + + guild = game.channel.guild + + game.execute() + + executed = guild.get_member(game.executed.id) + text = f'投票の結果 {executed.display_name} さんが処刑されました' + await game.channel.send(text) + + if game.is_village_win(): + text = 'ゲームが終了しました。人狼が全滅したため村人陣営の勝利です!' + await game.channel.send(text) + game = Game() + return + + game.raid() + + if game.raided is not None: + raided = guild.get_member(game.raided.id) + text = f'{raided.display_name} さんが無残な姿で発見されました' + await game.channel.send(text) + + if game.is_werewolf_win(): + text = 'ゲームが終了しました。村人陣営の数が人狼陣営の数以下になったため人狼陣営の勝利です!' + await game.channel.send(text) + game = Game() + return + + game.fortune() + + if game.fortuned is not None: + text = f'占い結果は {game.fortuned} です。' + await guild.get_member(game.players.fortuneteller.id).send(text) + + for p in game.players.alives: + p.clear_vote().clear_raid().clear_fortune() + + game.days += 1 + + +async def setup(bot: commands.Bot) -> None: + await bot.add_cog(ChangeDateCog(bot)) diff --git a/extensions/status.py b/extensions/play.py similarity index 52% rename from extensions/status.py rename to extensions/play.py index 3d2d061..72d84fd 100644 --- a/extensions/status.py +++ b/extensions/play.py @@ -1,11 +1,11 @@ -import random import discord from discord import app_commands from discord.ext import commands -from constants.roles import simple +from application.player import Player +from application.game import Game -class GameStatus(commands.Cog): +class PlayCog(commands.Cog): def __init__(self, bot): self.bot = bot @@ -51,6 +51,51 @@ async def _start_game_app_command(self, interaction: discord.Interaction): self.bot.game.status = 'playing' await interaction.response.send_message('ゲームが開始されました。それぞれの役職にあった行動をとってください。') + @app_commands.command(name='参加', description='人狼ゲームに参加する') + @app_commands.guild_only() + async def _join_app_command(self, interaction: discord.Interaction): + match self.bot.game.status: + case 'nothing': + await interaction.response.send_message('現在ゲームはありません。', ephemeral=True) + return + case 'playing': + await interaction.response.send_message('現在ゲーム進行中です。', ephemeral=True) + return + for player in self.bot.game.players: + if interaction.user.id == player.id: + await interaction.response.send_message('既にゲームに参加しています。', ephemeral=True) + return + player = Player(interaction.user.id) + self.bot.game.players.append(player) + await interaction.response.send_message(f'{interaction.user.mention} さんが参加しました。') + + @app_commands.command(name='退出', description='人狼ゲームから退出する') + @app_commands.guild_only() + async def _left_app_command(self, interaction: discord.Interaction): + match self.bot.game.status: + case 'nothing': + await interaction.response.send_message('現在募集中のゲームはありません。', ephemeral=True) + return + case 'playing': + await interaction.response.send_message('既にゲームが進行中のため退出できません。', ephemeral=True) + return + for player in self.bot.game.players: + if interaction.user.id == player.id: + self.bot.game.players.remove(player) + await interaction.response.send_message(f'{interaction.user.mention} さんが退出しました。') + return + await interaction.response.send_message('ゲームに参加していません。', ephemeral=True) + + @app_commands.command(name='仲間の人狼を表示', description='仲間の人狼を表示します') + @app_commands.guild_only() + async def _show_werewolfs_app_command(self, interaction: discord.Interaction): + game: Game = self.bot.game + if game.players.get(interaction.user.id).role != '狼': + await interaction.response.send_message('あなたは人狼ではありません', ephemeral=True) + return + werewolfs = ' '.join(interaction.guild.get_member(player.id).mention for player in self.bot.game.players.werewolfs) + await interaction.response.send_message(f'この村の人狼は {werewolfs} です。', ephemeral=True) + @app_commands.command(name='ステータス確認', description='現在の人狼ゲームのステータスを確認します') @app_commands.guild_only() async def _show_game_status_app_command(self, interaction: discord.Interaction): @@ -65,6 +110,5 @@ async def _show_game_status_app_command(self, interaction: discord.Interaction): await interaction.response.send_message('人狼ゲームが進行中です', ephemeral=True) return - async def setup(bot: commands.Bot) -> None: - await bot.add_cog(GameStatus(bot)) + await bot.add_cog(PlayCog(bot)) diff --git a/extensions/players.py b/extensions/players.py deleted file mode 100644 index 83db803..0000000 --- a/extensions/players.py +++ /dev/null @@ -1,48 +0,0 @@ -import discord -from discord import app_commands -from discord.ext import commands -from application.player import Player - - -class PlayersCog(commands.Cog): - def __init__(self, bot: commands.Bot): - self.bot = bot - - @app_commands.command(name='参加', description='人狼ゲームに参加する') - @app_commands.guild_only() - async def _join_app_command(self, interaction: discord.Interaction): - match self.bot.game.status: - case 'nothing': - await interaction.response.send_message('現在ゲームはありません。', ephemeral=True) - return - case 'playing': - await interaction.response.send_message('現在ゲーム進行中です。', ephemeral=True) - return - for player in self.bot.game.players: - if interaction.user.id == player.id: - await interaction.response.send_message('既にゲームに参加しています。', ephemeral=True) - return - player = Player(interaction.user.id) - self.bot.game.players.append(player) - await interaction.response.send_message(f'{interaction.user.mention} さんが参加しました。') - - @app_commands.command(name='退出', description='人狼ゲームから退出する') - @app_commands.guild_only() - async def _left_app_command(self, interaction: discord.Interaction): - match self.bot.game.status: - case 'nothing': - await interaction.response.send_message('現在募集中のゲームはありません。', ephemeral=True) - return - case 'playing': - await interaction.response.send_message('既にゲームが進行中のため退出できません。', ephemeral=True) - return - for player in self.bot.game.players: - if interaction.user.id == player.id: - self.bot.game.players.remove(player) - await interaction.response.send_message(f'{interaction.user.mention} さんが退出しました。') - return - await interaction.response.send_message('ゲームに参加していません。', ephemeral=True) - - -async def setup(bot: commands.Bot) -> None: - await bot.add_cog(PlayersCog(bot)) diff --git a/extensions/vote.py b/extensions/vote.py index 2305b0b..dc3fd43 100644 --- a/extensions/vote.py +++ b/extensions/vote.py @@ -2,115 +2,38 @@ from discord import app_commands from discord.ext import commands from application.game import Game -from application.player import Players -from application.pagenator import Pagenator -class VoteCog(commands.Cog): - def __init__(self, bot): - self.bot = bot - - async def change_date(self, interaction: discord.Interaction): - """日付変更処理""" - if not self.bot.game.is_set_target(): - return - - guild = self.bot.game.channel.guild - - self.bot.game.execute() - - executed = guild.get_member(self.bot.game.executed.id) - text = f'投票の結果 {executed.display_name} さんが処刑されました' - await self.bot.game.channel.send(text) - - if self.bot.game.is_village_win(): - text = 'ゲームが終了しました。人狼が全滅したため村人陣営の勝利です!' - await self.bot.game.channel.send(text) - self.bot.game = Game() - return - - self.bot.game.raid() - - if self.bot.game.raided is not None: - raided = guild.get_member(self.bot.game.raided.id) - text = f'{raided.display_name} さんが無残な姿で発見されました' - await self.bot.game.channel.send(text) - - if self.bot.game.is_werewolf_win(): - text = 'ゲームが終了しました。村人陣営の数が人狼陣営の数以下になったため人狼陣営の勝利です!' - await self.bot.game.channel.send(text) - self.bot.game = Game() - return - - self.bot.game.fortune() +class VoteDropdown(discord.ui.Select): + def __init__(self, members: list[discord.Member]): + super().__init__( + placeholder='処刑したいプレイヤー', + min_values=1, + max_values=1, + options=[discord.SelectOption(label=member.display_name, value=str(member.id)) for member in members] + ) - if self.bot.game.fortuned is not None: - text = f'占い結果は {self.bot.game.fortuned} です。' - await guild.get_member(self.bot.game.players.fortuneteller.id).send(text) + async def callback(self, interaction: discord.Interaction): + game: Game = interaction.client.game + game.players.get(interaction.user.id).set_vote(game.players.get(int(self.values[0]))) + await interaction.response.send_message('処刑希望投票が完了しました。', ephemeral=True) - for p in self.bot.game.players.alives: - p.clear_vote().clear_raid().clear_fortune() - self.bot.game.days += 1 +class VoteView(discord.ui.View): + def __init__(self, members: list[discord.Member]): + super().__init__() + self.add_item(VoteDropdown(members)) - async def select(self, interaction: discord.Interaction, players: Players, set_method, action: str): - d = {self.bot.get_user(p.id).mention: p.id for p in players if p.id != interaction.user.id} - target = await Pagenator( - self.bot, - interaction.user, - interaction.user, - list(d.keys()), - f'{action}対象に指定するユーザーを選びます', - f'{action}対象に指定するユーザーの番号のリアクションを押してください。\n左右矢印リアクションでページを変更できます。' - ).start() - set_method(self.bot.game.players.get(d[target])) - await interaction.response.send_message(f'{action}指定完了しました。', ephemeral=True) - async def select_vote(self, interaction: discord.Interaction): - set_method = self.bot.game.players.get(interaction.author.id).set_vote - await self.select(interaction, self.bot.game.players.alives, set_method, '処刑') - - async def select_raid(self, interaction: discord.Interaction): - set_method = self.bot.game.players.get(interaction.author.id).set_raid - await self.select(interaction, self.bot.game.players.alives.werewolfs, set_method, '襲撃') - - async def select_fortune(self, interaction: discord.Interaction): - set_method = self.bot.game.players.get(interaction.author.id).set_fortune - await self.select(interaction, self.bot.game.players.alives, set_method, '占い') +class VoteCog(commands.Cog): + def __init__(self, bot): + self.bot = bot @app_commands.command(name='投票', description='処刑したいプレイヤーに投票します') @app_commands.guild_only() async def _vote_app_command(self, interaction: discord.Interaction): - await self.select_vote(interaction) - await self.change_date(interaction) - - @app_commands.command(name='襲撃', description='襲撃したいプレイヤーに投票します') - @app_commands.guild_only() - async def _raid_app_command(self, interaction: discord.Interaction): - if self.bot.game.players.get(interaction.user.id).role != '狼': - await interaction.response.send_message('あなたは人狼ではないので、襲撃することはできません。', ephemeral=True) - return - await self.select_raid(interaction) - await self.change_date(interaction) - - @app_commands.command(name='占い', description='プレイヤーを一人占います') - @app_commands.guild_only() - async def _fortune_app_command(self, interaction: discord.Interaction): - if self.bot.game.players.get(interaction.user.id).role != '占': - await interaction.response.send_message('あなたは占い師ではないので、占うことはできません。', ephemeral=True) - return - await self.select_fortune(interaction) - await self.change_date(interaction) - - @app_commands.command(name='仲間の人狼を表示', description='仲間の人狼を表示します') - @app_commands.guild_only() - async def _show_werewolfs_app_command(self, interaction: discord.Interaction): - if self.bot.game.players.get(interaction.user.id).role != '狼': - await interaction.response.send_message('あなたは人狼ではありません', ephemeral=True) - return - guild = self.bot.game.channel.guild - werewolfs = ' '.join(guild.get_member(w.id).display_name for w in self.bot.game.players.alives.werewolfs) - await interaction.response.send_message(f'この村の人狼は {werewolfs} です。', ephemeral=True) + members = [interaction.guild.get_member(player.id) for player in self.bot.game.players.alives] + await interaction.response.send_message('処刑したいプレイヤーを選択してください', view=VoteView(members), ephemeral=True) async def setup(bot: commands.Bot) -> None: diff --git a/extensions/vote_fortune.py b/extensions/vote_fortune.py new file mode 100644 index 0000000..743e1a3 --- /dev/null +++ b/extensions/vote_fortune.py @@ -0,0 +1,40 @@ +import discord +from discord import app_commands +from discord.ext import commands +from application.game import Game + + +class VoteFortuneDropdown(discord.ui.Select): + def __init__(self, members: list[discord.Member]): + super().__init__( + placeholder='占いたいプレイヤー', + min_values=1, + max_values=1, + options=[discord.SelectOption(label=member.display_name, value=str(member.id)) for member in members] + ) + + async def callback(self, interaction: discord.Interaction): + game: Game = interaction.client.game + game.players.get(interaction.user.id).set_fortune(game.players.get(int(self.values[0]))) + await interaction.response.send_message('占い先選択が完了しました。', ephemeral=True) + + +class VoteFortuneView(discord.ui.View): + def __init__(self, members: list[discord.Member]): + super().__init__() + self.add_item(VoteFortuneDropdown(members)) + + +class VoteFortuneCog(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name='占い', description='占いたいプレイヤーを選択します') + @app_commands.guild_only() + async def _vote_app_command(self, interaction: discord.Interaction): + members = [interaction.guild.get_member(player.id) for player in self.bot.game.players.alives] + await interaction.response.send_message('占いたいプレイヤーを選択してください', view=VoteFortuneView(members), ephemeral=True) + + +async def setup(bot: commands.Bot) -> None: + await bot.add_cog(VoteFortuneCog(bot)) diff --git a/extensions/vote_raid.py b/extensions/vote_raid.py new file mode 100644 index 0000000..73c5df4 --- /dev/null +++ b/extensions/vote_raid.py @@ -0,0 +1,40 @@ +import discord +from discord import app_commands +from discord.ext import commands +from application.game import Game + + +class VoteRaidDropdown(discord.ui.Select): + def __init__(self, members: list[discord.Member]): + super().__init__( + placeholder='襲撃したいプレイヤー', + min_values=1, + max_values=1, + options=[discord.SelectOption(label=member.display_name, value=str(member.id)) for member in members] + ) + + async def callback(self, interaction: discord.Interaction): + game: Game = interaction.client.game + game.players.get(interaction.user.id).set_raid(game.players.get(int(self.values[0]))) + await interaction.response.send_message('襲撃希望投票が完了しました。', ephemeral=True) + + +class VoteRaidView(discord.ui.View): + def __init__(self, members: list[discord.Member]): + super().__init__() + self.add_item(VoteRaidDropdown(members)) + + +class VoteRaidCog(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name='襲撃', description='襲撃したいプレイヤーに投票します') + @app_commands.guild_only() + async def _vote_app_command(self, interaction: discord.Interaction): + members = [interaction.guild.get_member(player.id) for player in self.bot.game.players.alives] + await interaction.response.send_message('襲撃したいプレイヤーを選択してください', view=VoteRaidView(members), ephemeral=True) + + +async def setup(bot: commands.Bot) -> None: + await bot.add_cog(VoteRaidCog(bot))