From c1972bd5da081b0bb61bbb9e7fc250a121a5d1f4 Mon Sep 17 00:00:00 2001 From: Zevaryx Date: Wed, 10 Aug 2022 14:31:51 -0600 Subject: [PATCH] Add redditor following --- jarvis/cogs/reddit.py | 145 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 136 insertions(+), 9 deletions(-) diff --git a/jarvis/cogs/reddit.py b/jarvis/cogs/reddit.py index ed93c54..c8b8333 100644 --- a/jarvis/cogs/reddit.py +++ b/jarvis/cogs/reddit.py @@ -9,7 +9,18 @@ from asyncpraw.models.reddit.submission import Submission from asyncpraw.models.reddit.submission import Subreddit as Sub from asyncprawcore.exceptions import Forbidden, NotFound, Redirect from jarvis_core.db import q -from jarvis_core.db.models import Subreddit, SubredditFollow, UserSetting +from jarvis_core.db.models import ( + Redditor, + RedditorFollow, + Subreddit, + SubredditFollow, + UserSetting, +) + +from jarvis import const +from jarvis.config import JarvisConfig +from jarvis.utils import build_embed +from jarvis.utils.permissions import admin_or_permissions from naff import Client, Extension, InteractionContext, Permissions from naff.client.utils.misc_utils import get from naff.models.discord.channel import ChannelTypes, GuildText @@ -23,13 +34,9 @@ from naff.models.naff.application_commands import ( ) from naff.models.naff.command import check -from jarvis import const -from jarvis.config import JarvisConfig -from jarvis.utils import build_embed -from jarvis.utils.permissions import admin_or_permissions - DEFAULT_USER_AGENT = f"python:JARVIS:{const.__version__} (by u/zevaryx)" sub_name = re.compile(r"\A[A-Za-z0-9][A-Za-z0-9_]{2,20}\Z") +user_name = re.compile(r"[A-Za-z0-9_-]+") image_link = re.compile(r"https?://(?:www)?\.?preview\.redd\.it\/(.*\..*)\?.*") @@ -124,8 +131,126 @@ class RedditCog(Extension): return embeds reddit = SlashCommand(name="reddit", description="Manage Reddit follows") + follow = reddit.group(name="follow", description="Add a follow") + unfollow = reddit.group(name="unfollow", description="Remove a follow") - @reddit.subcommand(sub_cmd_name="follow", sub_cmd_description="Follow a Subreddit") + @follow.subcommand(sub_cmd_name="redditor", sub_cmd_description="Follow a Redditor") + @slash_option( + name="name", + description="Redditor name", + opt_type=OptionTypes.STRING, + required=True, + ) + @slash_option( + name="channel", + description="Channel to post to", + opt_type=OptionTypes.CHANNEL, + channel_types=[ChannelTypes.GUILD_TEXT], + required=True, + ) + @check(admin_or_permissions(Permissions.MANAGE_GUILD)) + async def _redditor_follow( + self, ctx: InteractionContext, name: str, channel: GuildText + ) -> None: + if not user_name.match(name): + await ctx.send("Invalid Redditor name", ephemeral=True) + return + + if not isinstance(channel, GuildText): + await ctx.send("Channel must be a text channel", ephemeral=True) + return + + try: + redditor = await self.api.redditor(name) + await redditor.load() + except (NotFound, Forbidden, Redirect) as e: + self.logger.debug(f"Redditor {name} raised {e.__class__.__name__} on add") + await ctx.send("Redditor may be deleted or nonexistent.", ephemeral=True) + return + + exists = await RedditorFollow.find_one(q(name=redditor.name, guild=ctx.guild.id)) + if exists: + await ctx.send("Redditor already being followed in this guild", ephemeral=True) + return + + count = len([i async for i in SubredditFollow.find(q(guild=ctx.guild.id))]) + if count >= 12: + await ctx.send("Cannot follow more than 12 Redditors", ephemeral=True) + return + + sr = await Redditor.find_one(q(name=redditor.name)) + if not sr: + sr = Redditor(name=redditor.name) + await sr.commit() + + srf = RedditorFollow( + name=redditor.name, + channel=channel.id, + guild=ctx.guild.id, + admin=ctx.author.id, + ) + await srf.commit() + + await ctx.send(f"Now following `u/{name}` in {channel.mention}") + + @unfollow.subcommand(sub_cmd_name="redditor", sub_cmd_description="Unfollow Redditor") + @check(admin_or_permissions(Permissions.MANAGE_GUILD)) + async def _redditor_unfollow(self, ctx: InteractionContext) -> None: + subs = RedditorFollow.find(q(guild=ctx.guild.id)) + redditors = [] + async for sub in subs: + redditors.append(sub) + if not redditors: + await ctx.send("You need to follow a redditor first", ephemeral=True) + return + + options = [] + names = [] + for idx, redditor in enumerate(redditors): + sub = await Redditor.find_one(q(name=redditor.name)) + names.append(sub.name) + option = SelectOption(label=sub.name, value=str(idx)) + options.append(option) + + select = Select( + options=options, custom_id="to_delete", min_values=1, max_values=len(redditors) + ) + + components = [ActionRow(select)] + block = "\n".join(x for x in names) + message = await ctx.send( + content=f"You are following the following redditors:\n```\n{block}\n```\n\n" + "Please choose redditors to unfollow", + components=components, + ) + + try: + context = await self.bot.wait_for_component( + check=lambda x: ctx.author.id == x.context.author.id, + messages=message, + timeout=60 * 5, + ) + for to_delete in context.context.values: + follow = get(redditors, guild=ctx.guild.id, name=names[int(to_delete)]) + try: + await follow.delete() + except Exception: + self.logger.debug("Ignoring deletion error") + for row in components: + for component in row.components: + component.disabled = True + + block = "\n".join(names[int(x)] for x in context.context.values) + await context.context.edit_origin( + content=f"Unfollowed the following:\n```\n{block}\n```", components=components + ) + except asyncio.TimeoutError: + for row in components: + for component in row.components: + component.disabled = True + await message.edit(components=components) + + @follow.subcommand(sub_cmd_name="subreddit", sub_cmd_description="Follow a Subreddit") @slash_option( name="name", description="Subreddit display name", @@ -140,7 +265,9 @@ class RedditCog(Extension): required=True, ) @check(admin_or_permissions(Permissions.MANAGE_GUILD)) - async def _reddit_follow(self, ctx: InteractionContext, name: str, channel: GuildText) -> None: + async def _subreddit_follow( + self, ctx: InteractionContext, name: str, channel: GuildText + ) -> None: if not sub_name.match(name): await ctx.send("Invalid Subreddit name", ephemeral=True) return @@ -191,7 +318,7 @@ class RedditCog(Extension): await ctx.send(f"Now following `r/{name}` in {channel.mention}") - @reddit.subcommand(sub_cmd_name="unfollow", sub_cmd_description="Unfollow Subreddits") + @unfollow.subcommand(sub_cmd_name="subreddit", sub_cmd_description="Unfollow Subreddits") @check(admin_or_permissions(Permissions.MANAGE_GUILD)) async def _subreddit_unfollow(self, ctx: InteractionContext) -> None: subs = SubredditFollow.find(q(guild=ctx.guild.id))