diff --git a/README.md b/README.md index 338a926..c59d850 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,9 @@ When you need statistics about your discord server * arguments: * - top channels, default is 10 * all - include bots messages +* %react - rank users by their reactions + * arguments: + * - top messages, default is 10 * %cancel - cancel current analysis * Common arguments: diff --git a/src/main.py b/src/main.py index 9a27e73..c82122a 100644 --- a/src/main.py +++ b/src/main.py @@ -12,6 +12,7 @@ from scanners import ( MentionedScanner, MessagesScanner, ChannelsScanner, + ReactionsScanner, ) from logs import GuildLogs @@ -53,6 +54,12 @@ bot.register_command( "emojis: rank emojis by their usage", EmotesScanner.help(), ) +bot.register_command( + "(react(ions?)?)", + lambda *args: ReactionsScanner().compute(*args), + "react: rank users by their reactions", + ReactionsScanner.help(), +) bot.register_command( "(channels?|chan)", lambda *args: ChannelsScanner().compute(*args), diff --git a/src/scanners/__init__.py b/src/scanners/__init__.py index 495dfaf..510c886 100644 --- a/src/scanners/__init__.py +++ b/src/scanners/__init__.py @@ -7,3 +7,4 @@ from .mentions_scanner import MentionsScanner from .mentioned_scanner import MentionedScanner from .messages_scanner import MessagesScanner from .channels_scanner import ChannelsScanner +from .reactions_scanner import ReactionsScanner diff --git a/src/scanners/reactions_scanner.py b/src/scanners/reactions_scanner.py new file mode 100644 index 0000000..b8c7280 --- /dev/null +++ b/src/scanners/reactions_scanner.py @@ -0,0 +1,82 @@ +from typing import Dict, List +from collections import defaultdict +import discord + + +# Custom libs + +from logs import ChannelLogs, MessageLog +from .scanner import Scanner +from data_types import Counter +from utils import COMMON_HELP_ARGS, mention, channel_mention + + +class ReactionsScanner(Scanner): + @staticmethod + def help() -> str: + return ( + "```\n" + + "%react: Rank users by their reactions\n" + + "arguments:\n" + + COMMON_HELP_ARGS + + "* - top , default is 10\n" + + "Example: %react 10 #channel\n" + + "```" + ) + + def __init__(self): + super().__init__( + has_digit_args=True, + help=ReactionsScanner.help(), + intro_context="Reactions", + ) + + async def init(self, message: discord.Message, *args: str) -> bool: + # get max emotes to view + self.top = 10 + for arg in args: + if arg.isdigit(): + self.top = int(arg) + # Create mentions dict + self.messages = defaultdict(Counter) + return True + + def compute_message(self, channel: ChannelLogs, message: MessageLog): + return ReactionsScanner.analyse_message( + channel.id, + message, + self.messages, + self.raw_members, + ) + + def get_results(self, intro: str) -> List[str]: + names = [name for name in self.messages] + names.sort(key=lambda name: self.messages[name].score(), reverse=True) + names = names[: self.top] + usage_count = Counter.total(self.messages) + res = [intro] + res += [ + self.messages[name].to_string( + names.index(name), + mention(name), + total_usage=usage_count, + counted="reactions", + transform=lambda id: f" in {channel_mention(id)}", + ) + for name in names + ] + return res + + @staticmethod + def analyse_message( + channel_id: int, + message: MessageLog, + messages: Dict[str, Counter], + raw_members: List[int], + ) -> bool: + impacted = True + for reaction in message.reactions: + for member_id in message.reactions[reaction]: + if len(raw_members) == 0 or member_id in raw_members: + messages[member_id].update_use(1, message.created_at, channel_id) + return impacted