async def nick(self, ctx: commands.Context, *, new_nick: str = ""): guild = session.query(Guild).filter( Guild.guild_id == ctx.guild.id).first() if not guild: guild = Guild(guild_id=ctx.guild.id) session.add(guild) session.commit() if not guild.custom_nick: await ctx.send( f"<{EMOJIS['XMARK']}> This server has disabled custom nicknames!" ) return discord_user = session.query(DiscordUser).filter( DiscordUser.user_id == ctx.author.id).first() verified = [acc.username for acc in discord_user.reddit_accounts] if new_nick != "": end = f"({'unverified' if len(verified) <= 0 else '/u/' + verified[0]})" new_nick = f"{new_nick[:32 - len(end) - 1]} {end}" else: new_nick = f"/u/{verified[0]}" if len( verified) > 0 else ctx.author.username try: await ctx.author.edit(nick=new_nick) except discord.errors.Forbidden: await ctx.send( f"<{EMOJIS['XMARK']}> I don't have the permissions to edit your nickname!" )
async def melonstats(self, ctx, limit=False): guild = self.bot.get_db_guild(ctx.guild.id) if ctx.author.id not in AUTHORIZED_USERS: if not guild.melon_role: await ctx.send( f"<{EMOJIS['XMARK']}> Custom Melons for this guild haven't been enabled!" ) return roles = [role.id for role in ctx.author.roles] if guild.melon_role not in roles: await ctx.send( f"<{EMOJIS['XMARK']}> You aren't authorized to administrate Melons in this guild!" ) return melons = session.query(Melon).filter( or_( Melon.guild_id == guild.guild_id, Melon.category_id.in_(c.category_id for c in guild.categories))).all() else: melons = session.query(Melon).all() stats = [f"{melon.key} [{melon.uses}]" for melon in melons] await ctx.send(f"**Available Melons and their usage:**\n\n{stats}")
async def remdefault(self, ctx: commands.Context, emoji: str): try: await ctx.message.add_reaction(emoji) except BaseException: await ctx.send(f"<{EMOJIS['XMARK']}> Uh-oh, looks like that emoji doesn't work!") return channel = session.query(Channel).filter(Channel.channel_id == ctx.channel.id).first() if not channel or not channel.poll_channel: msg = f"<{EMOJIS['XMARK']}> This channel isn't setup as a poll channel! Please use `!togglepoll` to enable the feature!" await ctx.send(msg) return channel_emoji = session.query(ChannelEmoji).filter( and_( ChannelEmoji.channel_id == ctx.channel.id, ChannelEmoji.emoji == emoji)).first() if channel_emoji: channel_emoji.delete() session.commit() await ctx.send(f"<{EMOJIS['CHECK']}> Removed {emoji} from the list of default emojis for this poll channel!", delete_after=3) await ctx.message.delete() else: await ctx.send(f"<{EMOJIS['XMARK']}> {emoji} isn't a default poll emoji in this channel!")
async def addemoji(self, ctx: commands.Context, emoji: str): try: await ctx.message.add_reaction(emoji) except BaseException: await ctx.send(f"<{EMOJIS['XMARK']}> Uh-oh, looks like that emoji doesn't work!") return channel = session.query(Channel).filter(Channel.channel_id == ctx.channel.id).first() if not channel or not channel.poll_channel: msg = f"<{EMOJIS['XMARK']}> This channel isn't setup as a poll channel! Please use `!togglepoll` to enable the feature!" await ctx.send(msg) return channel_emoji = session.query(ChannelEmoji).filter( and_( ChannelEmoji.channel_id == ctx.channel.id, ChannelEmoji.emoji == emoji)).first() if not channel_emoji: channel_emoji = ChannelEmoji(channel_id=ctx.channel.id, emoji=emoji) session.add(channel_emoji) session.commit() await ctx.send(f"<{EMOJIS['CHECK']}> Successfully added {emoji} to the list of emojis to add in this channel!", delete_after=3) async for msg in ctx.history(limit=None): await msg.add_reaction(emoji) else: await ctx.send(f"<{EMOJIS['XMARK']}> That emoji is already set for this channel!")
async def toggle(self, ctx: commands.Context, toggle: str): guild = session.query(Guild).filter( Guild.guild_id == ctx.guild.id).first() if not guild: guild = Guild(guild_id=ctx.guild.id) session.add(guild) session.commit() if toggle == "username": guild.set_username = not guild.set_username session.commit() if guild.set_username: for member in ctx.guild.members: if discord_user := session.query(DiscordUser).filter( DiscordUser.user_id == member.id).first(): await self.bot.update_guild_user(guild, discord_user) await ctx.send( f"<{EMOJIS['CHECK']}> Reddit usernames enabled for this server!" ) else: await ctx.send( f"<{EMOJIS['CHECK']}> Reddit usernames disabled for this server!" )
async def on_member_join(self, member: discord.Member): guild = session.query(Guild).filter( Guild.guild_id == member.guild.id).first() discord_user = session.query(DiscordUser).filter( DiscordUser.user_id == member.id).first() if guild and discord_user: await self.bot.update_guild_user(guild, discord_user)
async def addtags(self, ctx: commands.Context, *, key: str = ""): guild = self.bot.get_db_guild(ctx.guild.id) if ctx.author.id not in AUTHORIZED_USERS: if not guild.melon_role: await ctx.send( f"<{EMOJIS['XMARK']}> Custom Melons for this guild haven't been enabled!" ) return roles = [role.id for role in ctx.author.roles] if guild.melon_role not in roles: await ctx.send( f"<{EMOJIS['XMARK']}> You aren't authorized to edit Melons in this guild!" ) return if not key: key = await self.ask_prompt( ctx, "❓ What is the key used for the Melon?") if not key: return else: key = key.lower() if ctx.author.id in AUTHORIZED_USERS: melon = session.query(Melon).filter( and_( func.lower(Melon.key) == key, or_(Melon.guild_id == guild.guild_id, Melon.guild_id.is_(None)))).first() else: melon = session.query(Melon).filter( and_( func.lower(Melon.key) == key, Melon.guild_id == guild.guild_id)).first() if not melon: await ctx.send(f"<{EMOJIS['XMARK']}> Melon '{key}' doesn't exist!") tags = await self.ask_prompt( ctx, f"❓ Which tags should be added to the Melon '{key}'?") if not tags: return tags = tags.split(" ") for t in tags: tag = session.query(Tag).filter( func.lower(Tag.value) == t.lower()).first() if not tag: tag = Tag(value=t) session.add(tag) elif tag in melon.tags: continue melon.tags.append(tag) session.commit() await ctx.send( f"<{EMOJIS['CHECK']}> Successfully added tags `{', '.join(tags)}` to Melon '{melon.key}'!" )
async def substats(self, ctx: commands.Context, sub: str = ""): message = await ctx.send("🕑 Please wait while we gather the data...") if sub: subname = sub.replace("r/", "").replace("/", "") else: if channel := session.query(Channel).filter(Channel.channel_id == ctx.channel.id).first(): subname = channel.subreddit elif guild := session.query(Guild).filter(Guild.guild_id == ctx.guild.id).first(): subname = guild.subreddit
def get_db_guild(self, guild_id: int) -> Guild: guild = session.query(Guild).filter(Guild.guild_id == guild_id).first() if not guild: categories = session.query(Category).filter( Category.default == true()).all() guild = Guild(guild_id=guild_id, categories=categories) session.add(guild) session.commit() return guild
async def on_guild_join(self, g: discord.Guild): guild = session.query(Guild).filter(Guild.guild_id == g.id).first() if not guild: guild = Guild(guild_id=g.id) session.add(guild) session.commit()
async def delcat(self, ctx: commands.Context, *, cat: str): category = session.query(Category).filter( func.lower(Category.name) == cat.lower()).first() if not category: await ctx.send( f"<{EMOJIS['XMARK']}> '{cat}' isn't a category in the Melons!") return message = await ctx.send( f"❓ Are you sure you want to remove {cat} from the Melons?") await message.add_reaction(EMOJIS['CHECK']) await message.add_reaction(EMOJIS['XMARK']) try: reaction = await self.bot.wait_for( "reaction_add", check=lambda r, u: u.id == ctx.author.id and r.message.id == message.id, timeout=2 * 60) except asyncio.exceptions.TimeoutError: await ctx.send( f"<{EMOJIS['XMARK']}> That took too long! " + "You can restart the process by calling this command again.") return finally: if reaction[0].custom_emoji and reaction[0].emoji.id == int( EMOJIS['CHECK'].split(":")[2]): session.delete(category) session.commit() await ctx.send( f"<{EMOJIS['CHECK']}> Successfully removed '{cat}' as a category in the Melons!" )
async def on_message(self, msg: discord.Message): if msg.author.bot: return channel = session.query(Channel).filter(Channel.channel_id == msg.channel.id).first() if channel and channel.poll_channel: await self.handle_poll(msg, channel)
async def unverify(self, ctx: commands.Context, user: str): if reddit_user := session.query(RedditUser).filter( and_( func.lower(RedditUser.username) == user, RedditUser.discord_user == ctx.author.id)).first(): session.delete(reddit_user) session.commit() guild = session.query(Guild).filter( Guild.guild_id == ctx.guild.id).first() if not guild: guild = Guild(guild_id=ctx.guild.id) session.add(guild) session.commit() if discord_user := session.query(DiscordUser).filter( DiscordUser.user_id == ctx.author.id).first(): await self.bot.update_guild_user(guild, discord_user)
async def me(self, ctx: commands.Context): if discord_user := session.query(DiscordUser).join(RedditUser).filter( DiscordUser.user_id == ctx.author.id).first(): accounts = [ f"/u/{acc.username}" for acc in discord_user.reddit_accounts if acc.verified ] msg = f"**{ctx.author.mention}'s Accounts**\n\nVerified Accounts: {', '.join(accounts)}" unverified_accounts = [ f"/u/{acc.username}" for acc in discord_user.reddit_accounts if not acc.verified ] if unverified_accounts: msg += f"\nUnverified Account: /u/{unverified_accounts[0]}" await ctx.message.channel.send(msg) guild = session.query(Guild).filter( Guild.guild_id == ctx.guild.id).first() await self.bot.update_guild_user(guild, discord_user)
async def on_raw_message_edit(self, payload: discord.RawMessageUpdateEvent): try: msg = await self.bot.get_channel(int(payload.data["channel_id"])).fetch_message(payload.message_id) except Exception as e: await self.bot.send_error(e) return if msg: channel = session.query(Channel).filter(Channel.channel_id == msg.channel.id).first() if channel and channel.poll_channel: await self.handle_poll(msg, channel)
async def verify(self, ctx: commands.Context, user: str): user = user.replace("u/", "").replace("/", "").strip().lower() discord_user = session.query(DiscordUser).filter( DiscordUser.user_id == ctx.author.id).first() if not discord_user: discord_user = DiscordUser(user_id=ctx.author.id) session.add(discord_user) session.commit() if session.query(RedditUser).filter( and_( func.lower(RedditUser.username) == user, RedditUser.discord_user != ctx.author.id)).first(): await ctx.message.channel.send( f"❗ /u/{user} is already linked to another Discord account! " + f"If you believe this is an error, please contact {MAINTAINER}." ) return if session.query(RedditUser).filter( and_( func.lower(RedditUser.username) == user, RedditUser.discord_user == ctx.author.id)).first(): await ctx.message.channel.send( f"❗ /u/{user} is already linked to your Discord account!") return if reddit_user := session.query(RedditUser).filter( and_(RedditUser.discord_user == ctx.author.id, RedditUser.verified == false())).first(): await ctx.message.channel.send( f"❗ /u/{reddit_user.username} is still awaiting verification on Reddit. " + f"If you would like to unlink it, use `!unverify {reddit_user.username}`." ) return
async def meloncats(self, ctx: commands.Context): guild = self.bot.get_db_guild(ctx.guild.id) enabled = [category.name for category in guild.categories] disabled_categories = session.query(Category).filter( ~Category.guilds.any(Guild.guild_id == ctx.guild.id)).all() disabled = [category.name for category in disabled_categories] await ctx.send( "These are the enabled/disabled Melon categories for this guild. " + "A server admin can enable a Melon category by typing `!enablecat [cat]`.\n\n" + f"**Enabled categories: **{', '.join(enabled)}\n" + f"**Disabled cateogires: **{', '.join(disabled)}")
async def addcat(self, ctx: commands.Context, *, cat: str): category = session.query(Category).filter( func.lower(Category.name) == cat.lower()).first() if category: await ctx.send( f"<{EMOJIS['XMARK']}> '{cat}' is already a category in the Melons!" ) else: category = Category(name=cat) session.add(category) session.commit() await ctx.send( f"<{EMOJIS['CHECK']}> Successfully added '{cat}' as a category in the Melons!" )
async def setchannelsub(self, ctx: commands.Context, sub: str): s = await self.bot.reddit.subreddit(sub) channel = session.query(Channel).filter( Channel.channel_id == ctx.channel.id).first() if not channel: channel = Channel(channel_id=ctx.channel.id) session.add(channel) channel.subreddit = sub session.commit() await ctx.send( f"<{EMOJIS['CHECK']}> Successfully set /r/{sub} as this channels's subreddit!" )
async def togglepoll(self, ctx: commands.Context): channel = session.query(Channel).filter(Channel.channel_id == ctx.channel.id).first() if not channel: channel = Channel(channel_id=ctx.channel.id, poll_channel=True) session.add(channel) else: channel.poll_channel = not channel.poll_channel session.commit() if channel.poll_channel: await ctx.send(f"<{EMOJIS['CHECK']}> Successfully registered channel as a poll channel!", delete_after=3) else: await ctx.send(f"<{EMOJIS['CHECK']}> Successfully removed polling feature from this channel!", delete_after=3) await ctx.message.delete()
async def rawmelon(self, ctx: commands.Context, *, key): guild = self.bot.get_db_guild(ctx.guild.id) melon = session.query(Melon).filter( and_( func.lower(Melon.key) == key.lower(), or_( Melon.guild_id == guild.guild_id, Melon.category_id.in_( c.category_id for c in guild.categories)))).first() if melon: value = melon.value.replace("```", r"\`\`\`") await ctx.send(f"```\n{value}\n```") else: await ctx.send("❗ No Melon or Tag found under that name!")
async def status(self, ctx: commands.Context): guild = session.query(Guild).filter( Guild.guild_id == ctx.guild.id).first() if not guild: guild = Guild(guild_id=ctx.guild.id) session.add(guild) session.commit() set_role = "on" if guild.set_role else "off" set_username = "******" if guild.set_username else "off" set_nickname = "on" if guild.custom_nick else "off" role_name = ctx.guild.get_role(guild.role).name if guild.role else None await ctx.channel.send( f"Adding '{role_name}' role: {set_role}\n" + f"Changing nickname to /u/username: {set_username}\n" + f"Allowing custom nicknames: {set_nickname}")
async def meloncat(self, ctx: commands.Context, *, cat): category = session.query(Category).join(Guild, Category.guilds).filter( and_(Guild.guild_id == ctx.guild.id, func.lower(Category.name) == cat.lower())).first() if category: popular, new, others = self.order_melons(category.melons) await ctx.send( f"Here are the available Melons in '{cat}'. " + "You can search for a Melon by typing `!melon [seach]`.\n\n" + f"**Popular Melons: **{', '.join(popular)}\n" + f"**New Melons: **{', '.join(new)}\n" + f"**Other Melons: **{', '.join(others)}") else: await ctx.send( f"<{EMOJIS['XMARK']}> This Melon category isn't available or has been disabled in this guild." )
async def setguildsub(self, ctx: commands.Context, sub: str): guild = session.query(Guild).filter( Guild.guild_id == ctx.guild.id).first() if not guild: guild = Guild(guild_id=ctx.guild.id) session.add(guild) session.commit() try: s = await self.bot.reddit.subreddit(sub) except BaseException: await ctx.send( "❗ /r/{} doesn't exist or isn't visible to me!".format(sub)) else: guild.subreddit = sub session.commit() await ctx.send( f"<{EMOJIS['CHECK']}> Successfully set /r/{sub} as this guild's subreddit!" )
async def adddefault(self, ctx: commands.Context, emoji: str): try: await ctx.message.add_reaction(emoji) except BaseException: await ctx.send(f"<{EMOJIS['XMARK']}> Uh-oh, looks like that emoji doesn't work!") return channel = session.query(Channel).filter(Channel.channel_id == ctx.channel.id).first() if not channel or not channel.poll_channel: msg = f"<{EMOJIS['XMARK']}> This channel isn't setup as a poll channel! Please use `!togglepoll` to enable the feature!" await ctx.send(msg) return channel_emoji = ChannelEmoji(channel_id=channel.channel_id, emoji=emoji, default_emoji=True) session.add(channel_emoji) session.commit() await ctx.send(f"<{EMOJIS['CHECK']}> Added {emoji} to the list of default emojis for this poll channel!", delete_after=3) await ctx.message.delete()
async def enablecat(self, ctx: commands.Context, *, cat: str): category = session.query(Category).filter( func.lower(Category.name) == cat.lower()).first() if not category: await ctx.send( f"<{EMOJIS['XMARK']}> Melon category '{cat}' doesn't exist!") return guild = self.bot.get_db_guild(ctx.guild.id) category_ids = [cat.category_id for cat in guild.categories] if category.category_id in category_ids: await ctx.send(f"❗ Melon category '{cat}' was already enabled!") else: guild.categories.append(category) session.commit() await ctx.send( f"<{EMOJIS['CHECK']}> Melon category '{cat}' successfully enabled!" )
async def remoji(self, ctx: commands.Context, emoji: str): member = ctx.guild.get_member(bot.user.id) try: await ctx.message.remove_reaction(emoji, member) except BaseException: await ctx.send(f"<{EMOJIS['XMARK']}> Uh-oh, looks like that emoji doesn't work!") return channel_emoji = session.query(ChannelEmoji).filter( and_( ChannelEmoji.channel_id == ctx.channel.id, ChannelEmoji.emoji == emoji)).first() if channel_emoji: channel_emoji.delete() session.commit() await ctx.send(f"<{EMOJIS['CHECK']}> Successfully removed {emoji} from the list of emojis to add in this channel!", delete_after=3) else: await ctx.send(f"<{EMOJIS['XMARK']}> Couldn't find {emoji} in the list of emojis to add in this channel!") async for msg in ctx.history(limit=None): await msg.remove_reaction(emoji, member)
async def disablecat(self, ctx, cat): category = session.query(Category).filter( func.lower(Category.name) == cat.lower()).first() if not category: await ctx.send( f"<{EMOJIS['XMARK']}> Melon category '{cat}' doesn't exist!") return guild = self.bot.get_db_guild(ctx.guild.id) category_ids = [cat.category_id for cat in guild.categories] if category.category_id in category_ids: guild.categories.remove(category) session.commit() await ctx.send( f"<{EMOJIS['CHECK']}> Melon category '{cat}' successfully disabled!" ) else: await ctx.send( f"<{EMOJIS['XMARK']}> Melon category '{cat}' was never enabled in this guild!" )
async def is_verified(ctx: commands.Context): if session.query(DiscordUser).join( RedditUser).filter(DiscordUser.user_id == ctx.author.id).first(): return True return False
def random(): m = session.query(Movie).order_by(func.random()).first() return 'You should watch "%s".\n' % (m.title)
class UserCog(commands.Cog): def __init__(self, bot: 'Reddify'): self.bot = bot @commands.command( help= "Sends a PM to the Reddit user to await confirmation of the account ownership." ) async def verify(self, ctx: commands.Context, user: str): user = user.replace("u/", "").replace("/", "").strip().lower() discord_user = session.query(DiscordUser).filter( DiscordUser.user_id == ctx.author.id).first() if not discord_user: discord_user = DiscordUser(user_id=ctx.author.id) session.add(discord_user) session.commit() if session.query(RedditUser).filter( and_( func.lower(RedditUser.username) == user, RedditUser.discord_user != ctx.author.id)).first(): await ctx.message.channel.send( f"❗ /u/{user} is already linked to another Discord account! " + f"If you believe this is an error, please contact {MAINTAINER}." ) return if session.query(RedditUser).filter( and_( func.lower(RedditUser.username) == user, RedditUser.discord_user == ctx.author.id)).first(): await ctx.message.channel.send( f"❗ /u/{user} is already linked to your Discord account!") return if reddit_user := session.query(RedditUser).filter( and_(RedditUser.discord_user == ctx.author.id, RedditUser.verified == false())).first(): await ctx.message.channel.send( f"❗ /u/{reddit_user.username} is still awaiting verification on Reddit. " + f"If you would like to unlink it, use `!unverify {reddit_user.username}`." ) return try: redditor = await self.bot.reddit.redditor(user) except Exception as e: await ctx.message.channel.send( f"❌ Redditor /u/{user} doesn't exist!\n\nError:{e}") else: await redditor.message( "Account Ownership Confirmation", f"Please reply to this message with 'verify' to confirm that you are Discord user '{ctx.message.author}'." ) msg = await ctx.message.channel.send( f"Check your Reddit PMs to verify you are the owner of /u/{redditor.name} by responding with `verify`!" ) reddit_user = RedditUser(user_id=redditor.fullname, discord_user=ctx.author.id, username=redditor.name) session.add(reddit_user) auth_user = await self.bot.reddit.user.me() async for message in auth_user.unread.stream(skip_existing=True): if not isinstance(message, apraw.models.Message): continue if not "Account Ownership Confirmation" in message.subject: continue try: author = await message.author() if author.name.lower() != reddit_user.username.lower(): continue except Exception as e: await self.bot.send_error(e) if "verify" in message.body.lower( ) and not "unverify" in message.body.lower(): reddit_user.verified = True user = self.bot.get_user(reddit_user.discord_user) if user: await msg.add_reaction(EMOJIS["CHECK"]) rep = await message.reply( f"Confirmation of {user} successful!") for guild in session.query(Guild).all(): await self.bot.update_guild_user( guild, discord_user) else: message.reply( "Discord user unlinked from your Reddit account. " + "Please run `!verify` again if this is an error and respond to the new PM." ) session.delete(reddit_user) break session.commit()