Skip to content

Commit

Permalink
Merge pull request #53 from tetsuya-ki/develop
Browse files Browse the repository at this point in the history
コヨーテ機能の改善、ゲーム機能に/roll追加、メッセージ集計機能・リアクション集計機能の追加
  • Loading branch information
tetsuya-ki authored Jan 31, 2021
2 parents b720920 + f9428e2 commit 3e06b82
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 38 deletions.
91 changes: 83 additions & 8 deletions cogs/gamecog.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ async def coyoteGame(self, ctx):
- コヨーテ中に、「コヨーテ!」をしたい場合は、`/cy coyote`を入力してください。
- コヨーテ中に、次の回を始めたい場合は、`/cy deal`を入力してください。
- コヨーテ中に、現在の状況を確認したい場合は、`/cy description`を入力してください。
- コヨーテ中に、カードの能力を確認したい場合は、`/cy card`を入力してください。
上級者向け機能
- 説明を省略して、コヨーテを始める場合は、`/cy startAndNoMessage`を入力してください。
- コヨーテ中に、ネタバレありで現在の状況を確認したい場合は、`/cy descriptionAll`を入力してください。
Expand All @@ -186,7 +187,7 @@ async def start(self, ctx):
await self.coyoteLittleMessage(ctx)
await self.dealAndMessage(ctx)

@coyoteGame.command(aliases=['sa','ina','inia'], description='コヨーテを始めるコマンド(全説明)')
@coyoteGame.command(aliases=['sa','sta','ina','inia'], description='コヨーテを始めるコマンド(全説明)')
async def startAndAllMessage(self, ctx):
"""
コヨーテを始めるコマンド(説明が多いバージョン)
Expand All @@ -204,6 +205,8 @@ async def startAndNoMessage(self, ctx):
- 上級者向けの機能です。ルールを説明されずとも把握している場合にのみ推奨します。
"""
await self.startCoyote(ctx)
msg = self.coyoteGames.create_description(True)
await ctx.send(msg)
await self.dealAndMessage(ctx)

@coyoteGame.command(aliases=['sds','ss','set'], description='デッキを指定して、コヨーテを始めるコマンド(説明なし)')
Expand All @@ -229,6 +232,8 @@ async def setDeckAndStart(self, ctx, *, deck=None):
self.coyoteGames.set(make_team.vc_members)
self.coyoteGames.setDeck(deck)
self.coyoteGames.shuffle()
msg = self.coyoteGames.create_description(True)
await ctx.send(msg)
await self.dealAndMessage(ctx)

@coyoteGame.command(aliases=['c','co','cy','done'], description='コヨーテ!(前プレイヤーの数字がコヨーテの合計数を超えたと思った場合のコマンド)')
Expand All @@ -255,11 +260,16 @@ async def coyote(self, ctx, you_id=None, number=0):
you_id = re.sub(r'[<@!>]', '', you_id)
if you_id.isdecimal():
you_id = int(you_id)
you = ctx.guild.get_member(you_id)
else:
msg = '「コヨーテする相手」(@で指定)と「コヨーテを言われた人の数字」を指定してください。例:`/coyoteGame coyote @you 99`'
await ctx.send(msg)
return
you = ctx.guild.get_member(you_id)
# IDから取得を試みる
keys = [k for k, v in self.coyoteGames.members.items() if v.id == str(you_id).upper()]
if len(keys) == 0:
msg = '「コヨーテする相手」(@で指定するか、IDで指定(aなど))と「コヨーテを言われた人の数字」を指定してください。例:`/coyoteGame coyote @you 99`'
await ctx.send(msg)
return
else:
you = keys.pop()
if you not in self.coyoteGames.members:
msg = 'ゲームに存在する相手を選び、「コヨーテ!」してください(ゲームしている相手にはいません)。'
await ctx.send(msg)
Expand Down Expand Up @@ -301,6 +311,62 @@ async def descriptionAll(self, ctx):
msg = self.coyoteGames.create_description(True)
await ctx.send(msg)

@coyoteGame.command(aliases=['cards','ca'], description='カードの説明')
async def card(self, ctx):
"""
カードの能力を説明します。
"""
msg = self.coyoteGames.create_description_card()
await ctx.send(msg)

@commands.command(aliases=['dice','dices','r'], description='ダイスを振る(さいころを転がす)')
async def roll(self, ctx, diceAndNum=''):
"""
ダイスを振る(さいころを転がす)コマンド
- `/roll 1d6`のように、左側にダイスの数、右側にダイスの種類(最大値)を指定してください
"""
default_error_msg = '`/roll 1d6`のように指定してください。'
if diceAndNum is None:
await ctx.send(default_error_msg)
return
diceAndNum = str(diceAndNum).lower()
if 'd' not in diceAndNum:
msg = 'dが必ず必要です。'
await ctx.send(msg + default_error_msg)
return
list = str(diceAndNum).split('d')
if len(list) != 2:
await ctx.send(default_error_msg)
return
elif len(list) == 2:
msg = ''
sum = 0
# ダイスの数、ダイスの最大値についてのチェックと数値化
if self.coyoteGames.is_num(list[0]):
dice_num = int(list[0])
else:
msg = 'dの左側が数字ではありません。'
await ctx.send(msg + default_error_msg)
return
if self.coyoteGames.is_num(list[1]):
max_num = int(list[1])
else:
msg = 'dの右側が数字ではありません。'
await ctx.send(msg + default_error_msg)
return
if max_num < 1 or dice_num < 1:
msg = 'dの左右は1以上である必要があります。'
await ctx.send(msg + default_error_msg)
return
for i in range(dice_num):
value = random.randint(1, max_num)
msg += f' {value}'
sum += value
else:
if dice_num > 1:
msg += f' → {sum}'
await ctx.send(msg)

async def startCoyote(self, ctx):
make_team = MakeTeam()
make_team.my_connected_vc_only_flg = True
Expand Down Expand Up @@ -335,13 +401,13 @@ async def coyoteAllMessage(self, ctx):
'コヨーテの鳴き声(想像してね)が上手いプレイヤーから始めます。'
await ctx.send(msg1)

msg2 = '最初のプレイヤーはDMに送られる他の人のカードを見て、この場に「少なくとも」何匹のコヨーテがいるか推理し、コヨーテの数を宣言します。\n'\
msg2 = '最初のプレイヤーはDMに送られる他の人のカードを見て、この場に「少なくとも」何匹のコヨーテがいるか(DMを見て数字を加算し)推理し、コヨーテの数を宣言します。\n'\
'★宣言する数に上限はありませんが、**1以上の整数である必要**があります(つまり、0や負数はダメです)\n'\
'ゲームは時計回りに進行(ボイスチャンネルを下に進むこと)します。\n'\
'次のプレイヤーは次のふたつのうち、「どちらか」の行動をとってください。\n'\
'1: 数字を上げる → 前プレイヤーの宣言した数が実際にこの場にいるコヨーテの数**以下(オーバー)していない**と思う場合、**前プレイヤーより大きな数**を宣言します。\n'\
'2: 「コヨーテ!」→ 前プレイヤーの宣言を疑います。つまり、前プレイヤーの宣言した数が実際にこの場にいるコヨーテの数よりも**大きい(オーバーした)**と思う場合、**「コヨーテ!」**と宣言します\n'\
'2の場合、例:`/coyoteGame coyote @you 99`のように**Discordに書き込んで**ください!(Botが結果を判定します!)\n'\
'2の場合、例:`/coyoteGame coyote @you 99`のように(`@you`はidでもOK)**Discordに書き込んで**ください!(Botが結果を判定します!)\n'\
'**誰かが「コヨーテ!」と宣言するまで**、時計回りで順々に交代しながら宣言する数字を上げていきます\n'
await ctx.send(msg2)

Expand All @@ -359,6 +425,11 @@ async def coyoteAllMessage(self, ctx):
'サイト: <http://www.newgamesorder.jp/games/coyote>'
await ctx.send(msg3)

msg4 = self.coyoteGames.create_description(True)
await ctx.send(msg4)
card_msg = self.coyoteGames.create_description_card()
await ctx.send(card_msg)

async def coyoteLittleMessage(self, ctx):
msg = 'コヨーテ:ゲーム目的\n**自分以外のプレイヤーのカード(DMに送られる)を見て、少なくとも何匹のコヨーテがこの場にいるかを推理します。**\n'\
'もしも宣言した数だけ居なかったら......コヨーテに命を奪われてしまいます! インディアン、嘘つかない。コヨーテだって、嘘が大キライなのです。\n'\
Expand All @@ -368,14 +439,18 @@ async def coyoteLittleMessage(self, ctx):
'次のプレイヤー:は次のふたつのうち、「どちらか」の行動をとってください。\n'\
'1: 数字を上げる → 前プレイヤーの宣言した数が実際にこの場にいるコヨーテの数**以下(オーバー)していない**と思う場合、**前プレイヤーより大きな数**を宣言します。\n'\
'2: 「コヨーテ!」→ 前プレイヤーの宣言を疑います。つまり、前プレイヤーの宣言した数が実際にこの場にいるコヨーテの数よりも**大きい(オーバーした)**と思う場合、**「コヨーテ!」**と宣言します\n'\
'2の場合、例:`/coyoteGame coyote @you 99`のように**Discordに書き込んで**ください!(Botが結果を判定します!)\n'\
'2の場合、例:`/coyoteGame coyote @you 99`のように(`@you`はidでもOK)**Discordに書き込んで**ください!(Botが結果を判定します!)\n'\
'**誰かが「コヨーテ!」と宣言するまで**、時計回り(ボイスチャンネルを下に進む)で順々に交代しながら宣言する**数字を上げて**いきます\n'\
'次の回を始めるには、`/coyoteGame deal`をDiscordに書き込んでください(**今回負けた人から開始**します)。\n'\
'負けたプレイヤーがその回を最後に**ゲームから脱落した場合、その回の勝者から**次の回を始めます。\n'\
'ライフが0になったプレイヤーはゲームから脱落します。最後まで生き残ったプレイヤーが勝利です。\n'\
'なお、コヨーテは絶賛販売中です(1,800円くらい)。気に入った方はぜひ買って遊んでみてください(このBotは許可を得て作成したものではありません)。販売:合同会社ニューゲームズオーダー, 作者:Spartaco Albertarelli, 画:TANSANFABRIK\n'\
'サイト: <http://www.newgamesorder.jp/games/coyote>'
await ctx.send(msg)
msg2 = self.coyoteGames.create_description(True)
await ctx.send(msg2)
card_msg = self.coyoteGames.create_description_card()
await ctx.send(card_msg)

async def coyoteStartCheckNG(self, ctx, desc=False):
if self.coyoteGames is None or (len(self.coyoteGames.members) <= 1 and not desc):
Expand Down
145 changes: 145 additions & 0 deletions cogs/messagecog.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from discord.ext import commands # Bot Commands Frameworkのインポート
from .modules.grouping import MakeTeam
from .modules.radiko import Radiko
from .modules import settings
from logging import getLogger

import discord
import re
import time

logger = getLogger(__name__)

Expand All @@ -14,6 +17,9 @@ class MessageCog(commands.Cog, name='通常用'):
"""
コマンドを元に動作する機能のカテゴリ。
"""
DEFAULT_NUMBER = 4000
MAX_NUMBER = 10000
MAX_RANK = 20

# MessageCogクラスのコンストラクタ。Botを受取り、インスタンス変数として保持。
def __init__(self, bot):
Expand Down Expand Up @@ -164,6 +170,145 @@ async def withDate(self, ctx, *args):
else:
await ctx.channel.send(radiko.r_err)

@commands.command(aliases=['cm','countm'], description='メッセージの件数を取得する機能です(けっこう時間かかります)')
async def countMessage(self, ctx, channel_name=None, numbers=None):
"""
ギルドのチャンネルのメッセージを集計する機能です。それぞれのパーセンテージと件数を表示します。
- channel_name: 集計対象のチャンネル(allの場合全部、未指定はコマンド実行チャンネル)
- numbers: 集計件数を指定
"""
start_time = time.time()
# 集計対象のチャンネルを設定
count_channels = self.get_target_channels(ctx, channel_name)

# 集計件数を設定
if numbers is None or not str(numbers).isdecimal():
numbers = self.DEFAULT_NUMBER
elif str(numbers).isdecimal():
numbers = int(numbers)
if numbers > self.MAX_NUMBER:
numbers = self.MAX_NUMBER

# ランキングの数を設定
ranking_num = settings.COUNT_RANK_SETTING
if settings.COUNT_RANK_SETTING > self.MAX_RANK:
ranking_num = self.MAX_RANK

# 集計作業
target = {}
all_num = 0
sep_channels = ''
for count_channel in count_channels:
try:
async for message in count_channel.history(limit=numbers):
all_num = all_num + 1
if message.author in target:
target[message.author] = target[message.author] + 1
else:
target[message.author] = 1
sep_channels += count_channel.name + ','
except:
continue

target_sorted = sorted(target.items(), key=lambda x:x[1], reverse=True)
message = f'`{ctx.message.clean_content}`の結果です(総件数:' + '{:,}'.format(all_num) + ')。\n'
for rank, ranking_target in enumerate(target_sorted):
percent = '{:.2%}'.format(ranking_target[1] / all_num)
message += f'{rank+1}位: {ranking_target[0].display_name}さん {percent}(' + '{:,}'.format(ranking_target[1]) + '件)\n'
if rank + 1 >= ranking_num:
break

sep_channels = re.sub(r',$', '', sep_channels)
message += f'(集計チャンネル: {sep_channels})\n'

elapsed_time = time.time() - start_time
elapsed_time_text = '経過時間:{:.2f}'.format(elapsed_time) + '[sec]'
logger.info(f'{sep_channels}({numbers}件) → {elapsed_time_text}')
message += elapsed_time_text

await ctx.send(message)

@commands.command(aliases=['cr','countr'], description='リアクションの件数を取得する機能です')
async def countReaction(self, ctx, channel_name=None, numbers=None):
"""
ギルドのチャンネルのリアクションを集計する機能です。それぞれのパーセンテージと件数を表示します。
- channel_name: 集計対象のチャンネル(allの場合全部、未指定はコマンド実行チャンネル)
- numbers: 集計件数を指定
"""
start_time = time.time()
# 集計対象のチャンネルを設定
count_channels = self.get_target_channels(ctx, channel_name)

# 集計件数を設定
if numbers is None or not str(numbers).isdecimal():
numbers = self.DEFAULT_NUMBER
elif str(numbers).isdecimal():
numbers = int(numbers)
if numbers > self.MAX_NUMBER:
numbers = self.MAX_NUMBER

# ランキングの数を設定
ranking_num = settings.COUNT_RANK_SETTING
if settings.COUNT_RANK_SETTING > self.MAX_RANK:
ranking_num = self.MAX_RANK

# 集計作業
target = {}
all_num = 0
sep_channels = ''
for count_channel in count_channels:
try:
async for message in count_channel.history(limit=numbers):
for reaction in message.reactions:
all_num = all_num + reaction.count
if reaction.emoji in target:
target[reaction.emoji] = target[reaction.emoji] + reaction.count
else:
target[reaction.emoji] = reaction.count
sep_channels += count_channel.name + ','
except:
continue

target_sorted = sorted(target.items(), key=lambda x:x[1], reverse=True)
message = f'`{ctx.message.clean_content}`の結果です(総件数:' + '{:,}'.format(all_num) + ')。\n'
for rank, ranking_target in enumerate(target_sorted):
percent = '{:.2%}'.format(ranking_target[1] / all_num)
message += f'{rank+1}位: {ranking_target[0]}{percent}(' + '{:,}'.format(ranking_target[1]) + '件)\n'
if rank + 1 >= ranking_num:
break

sep_channels = re.sub(r',$', '', sep_channels)
message += f'(集計チャンネル: {sep_channels})\n'

elapsed_time = time.time() - start_time
elapsed_time_text = '経過時間:{:.2f}'.format(elapsed_time) + '[sec]'
logger.info(f'{sep_channels}({numbers}件) → {elapsed_time_text}')
message += elapsed_time_text

await ctx.send(message)

def get_target_channels(self, ctx, channel_name):
if channel_name is None:
count_channels = [ctx.channel]
elif str(channel_name).lower() == 'all':
count_channels = ctx.guild.text_channels
else:
channel = discord.utils.get(ctx.guild.text_channels, name=channel_name)

# 名前でchannelが取得できなかった場合の処理
if channel is None:
# チャンネルID指定 <#\d+>の場合、IDからチャンネルを取得。それでも無理なら今のチャンネルを指定
channel_id = re.sub(r'[<#>]', '', channel_name)
if channel_id.isdecimal() and '#' in channel_name:
channel_id = int(channel_id)
count_channels = [ctx.guild.get_channel(channel_id)]
else:
count_channels = [ctx.channel]
# 名前で取得できた場合の処理
else:
count_channels = [channel]
return count_channels

@team.error
async def team_error(self, ctx, error):
if isinstance(error, commands.CommandError):
Expand Down
Loading

0 comments on commit 3e06b82

Please sign in to comment.