From 6a89f72add6cb77789305bf1955277686cf4b750 Mon Sep 17 00:00:00 2001 From: aikaterna <20862007+aikaterna@users.noreply.github.com> Date: Sat, 17 Jul 2021 17:07:27 -0700 Subject: [PATCH] [Tools] Remove old converter, add uid --- tools/converter.py | 72 +++++++++++++++++++--------------------------- tools/tools.py | 40 ++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 44 deletions(-) diff --git a/tools/converter.py b/tools/converter.py index bfc78b4..ae4944f 100644 --- a/tools/converter.py +++ b/tools/converter.py @@ -1,57 +1,45 @@ -""" -Because discord.py rewrite doesn't come with a general channel converter anymore -Written by sitryk -""" import discord -import re +from fuzzywuzzy import fuzz, process +from typing import List +from unidecode import unidecode -from discord.ext.commands import converter, BadArgument +from discord.ext.commands.converter import IDConverter, _get_from_guilds +from discord.ext.commands.errors import BadArgument + +from redbot.core import commands -class GuildChannelConverter(converter.IDConverter, converter.Converter): +class FuzzyMember(IDConverter): """ - Check order is: + Original class written by TrustyJaid#0001 + https://github.com/TrustyJAID/Trusty-cogs/blob/c739903aa2c8111c58b3d5e695a1221cbe1f57d9/serverstats/converters.py - 1. Text Channels - 2. Voice Channels - 3. Categories + This will accept partial names and perform a fuzzy search for + members within the guild and return a list of member objects. + + Guidance code on how to do this from: + https://github.com/Rapptz/discord.py/blob/rewrite/discord/ext/commands/converter.py#L85 + https://github.com/Cog-Creators/Red-DiscordBot/blob/V3/develop/redbot/cogs/mod/mod.py#L24 """ - async def convert(self, ctx, argument): + async def convert(self, ctx: commands.Context, argument: str) -> List[discord.Member]: bot = ctx.bot - match = self._get_id_match(argument) or re.match(r"<#([0-9]+)>$", argument) - result = None guild = ctx.guild + result = [] - if match is None: - order = [ - (discord.TextChannel, guild.text_channels), - (discord.VoiceChannel, guild.voice_channels), - (discord.CategoryChannel, guild.categories), - ] + members = {m: unidecode(m.name) for m in guild.members} + fuzzy_results = process.extract(argument, members, limit=1000, scorer=fuzz.partial_ratio) + matching_names = [m[2] for m in fuzzy_results if m[1] > 90] + for x in matching_names: + result.append(x) - # not a mention - for c_types in order: - if guild: - result = discord.utils.get(c_types[1], name=argument) - if result is not None: - break - else: + nick_members = {m: unidecode(m.nick) for m in guild.members if m.nick and m not in matching_names} + fuzzy_results2 = process.extract(argument, nick_members, limit=50, scorer=fuzz.partial_ratio) + matching_nicks = [m[2] for m in fuzzy_results2 if m[1] > 90] + for x in matching_nicks: + result.append(x) - def check(c): - return isinstance(c, c_types[0]) and c.name == argument - - result = discord.utils.find(check, bot.get_all_channels()) - if result is not None: - break - else: - channel_id = int(match.group(1)) - if guild: - result = guild.get_channel(channel_id) - else: - result = converter._get_from_guilds(bot, "get_channel", channel_id) - - if not isinstance(result, (discord.TextChannel, discord.VoiceChannel, discord.CategoryChannel)): - raise BadArgument('Channel "{}" not found.'.format(argument)) + if not result or result == [None]: + raise BadArgument('Member "{}" not found'.format(argument)) return result diff --git a/tools/tools.py b/tools/tools.py index b9f442a..ff712c4 100644 --- a/tools/tools.py +++ b/tools/tools.py @@ -5,13 +5,17 @@ import inspect import itertools import logging import re + +from contextlib import suppress as sps +from tabulate import tabulate +from typing import Optional + from redbot.core import checks, commands from redbot.core.utils import chat_formatting as cf from redbot.core.utils.common_filters import filter_invites from redbot.core.utils.menus import menu, DEFAULT_CONTROLS, close_menu -from tabulate import tabulate -from contextlib import suppress as sps +from .converter import FuzzyMember log = logging.getLogger("red.aikaterna.tools") @@ -661,6 +665,38 @@ class Tools(commands.Cog): await asyncio.sleep(1) await waiting.edit(content=data) + @commands.guild_only() + @commands.command() + async def uid(self, ctx, partial_name_or_nick: Optional[FuzzyMember]): + """Get a member's id from a fuzzy name search.""" + if partial_name_or_nick is None: + partial_name_or_nick = [ctx.author] + + table = [] + headers = ["ID", "Name", "Nickname"] + for user_obj in partial_name_or_nick: + table.append([user_obj.id, user_obj.name, user_obj.nick if not None else ""]) + msg = tabulate(table, headers, tablefmt="simple") + + embed_list = [] + for page in cf.pagify(msg, delims=["\n"], page_length=1800): + embed = discord.Embed( + description="", + colour=await ctx.embed_colour(), + ) + embed.add_field(name="Matching Users", value=cf.box(page)) + embed_list.append(embed) + + final_embed_list = [] + for i, embed in enumerate(embed_list): + embed.set_footer(text=f"Page {i + 1}/{len(embed_list)}") + final_embed_list.append(embed) + if len(embed_list) == 1: + close_control = {"\N{CROSS MARK}": close_menu} + await menu(ctx, final_embed_list, close_control) + else: + await menu(ctx, final_embed_list, DEFAULT_CONTROLS) + @commands.guild_only() @commands.command() async def uinfo(self, ctx, user: discord.Member = None):