From f9428e29f157971c27e780f25e43a197334488fc Mon Sep 17 00:00:00 2001 From: tetsuya-k <64536338+tetsuya-ki@users.noreply.github.com> Date: Sun, 31 Jan 2021 23:56:46 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E9=9B=86=E8=A8=88=E6=A9=9F=E8=83=BD=E3=80=81=E3=83=AA=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E9=9B=86=E8=A8=88=E6=A9=9F?= =?UTF-8?q?=E8=83=BD=E3=82=92=E8=BF=BD=E5=8A=A0(resolve=20#33)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cogs/messagecog.py | 145 ++++++++++++++++++++++++++ cogs/modules/files/.env-docker.sample | 3 +- cogs/modules/files/.env.sample | 3 +- cogs/modules/settings.py | 15 ++- 4 files changed, 159 insertions(+), 7 deletions(-) diff --git a/cogs/messagecog.py b/cogs/messagecog.py index 48b7add..45dfa76 100644 --- a/cogs/messagecog.py +++ b/cogs/messagecog.py @@ -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__) @@ -14,6 +17,9 @@ class MessageCog(commands.Cog, name='通常用'): """ コマンドを元に動作する機能のカテゴリ。 """ + DEFAULT_NUMBER = 4000 + MAX_NUMBER = 10000 + MAX_RANK = 20 # MessageCogクラスのコンストラクタ。Botを受取り、インスタンス変数として保持。 def __init__(self, bot): @@ -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): diff --git a/cogs/modules/files/.env-docker.sample b/cogs/modules/files/.env-docker.sample index 9b3ae02..ef4f277 100644 --- a/cogs/modules/files/.env-docker.sample +++ b/cogs/modules/files/.env-docker.sample @@ -4,4 +4,5 @@ AUDIT_LOG_SEND_CHANNEL=99999999.99999999;99999999.99999999 IS_HEROKU=False SAVE_FILE_MESSAGE=twitter FIRST_REACTION_CHECK=True -SCRAPBOX_SID_AND_PROJECTNAME=all:scrapbox_sid@projectname1,projectname2;guild1:scrapbox_sid@projectname3 \ No newline at end of file +SCRAPBOX_SID_AND_PROJECTNAME=all:scrapbox_sid@projectname1,projectname2;guild1:scrapbox_sid@projectname3 +COUNT_RANK_SETTING=5 \ No newline at end of file diff --git a/cogs/modules/files/.env.sample b/cogs/modules/files/.env.sample index 04a9363..21f994e 100644 --- a/cogs/modules/files/.env.sample +++ b/cogs/modules/files/.env.sample @@ -4,4 +4,5 @@ AUDIT_LOG_SEND_CHANNEL = "guild1.audit_log_send_channel_id1;guild1.audit_log_sen IS_HEROKU = True SAVE_FILE_MESSAGE = "twitter" FIRST_REACTION_CHECK = True -SCRAPBOX_SID_AND_PROJECTNAME = "all:scrapbox_sid@projectname1,projectname2;guild1:scrapbox_sid@projectname3" \ No newline at end of file +SCRAPBOX_SID_AND_PROJECTNAME = "all:scrapbox_sid@projectname1,projectname2;guild1:scrapbox_sid@projectname3" +COUNT_RANK_SETTING = 5 \ No newline at end of file diff --git a/cogs/modules/settings.py b/cogs/modules/settings.py index a75b29e..64c3c08 100644 --- a/cogs/modules/settings.py +++ b/cogs/modules/settings.py @@ -4,12 +4,10 @@ from logging import DEBUG, INFO, WARN, ERROR def if_env(str): - if str is None: + if str is None or str.upper() != 'TRUE': return False - elif str.upper() == 'TRUE': - return True else: - return False + return True def get_log_level(str): if str is None: @@ -24,6 +22,12 @@ def get_log_level(str): else: return WARN +def num_env(param): + if str is None or not str(param).isdecimal(): + return 5 + else: + return int(param) + load_dotenv(verbose=True) dotenv_path = join(dirname(__file__), 'files' + os.sep + '.env') load_dotenv(dotenv_path) @@ -34,4 +38,5 @@ def get_log_level(str): IS_HEROKU = if_env(os.environ.get('IS_HEROKU')) SAVE_FILE_MESSAGE = os.environ.get('SAVE_FILE_MESSAGE') FIRST_REACTION_CHECK = if_env(os.environ.get('FIRST_REACTION_CHECK')) -SCRAPBOX_SID_AND_PROJECTNAME = os.environ.get('SCRAPBOX_SID_AND_PROJECTNAME') \ No newline at end of file +SCRAPBOX_SID_AND_PROJECTNAME = os.environ.get('SCRAPBOX_SID_AND_PROJECTNAME') +COUNT_RANK_SETTING = num_env(os.environ.get('COUNT_RANK_SETTING')) \ No newline at end of file