from redbot.core.utils.chat_formatting import * from redbot.core import Config, checks, commands, bank from redbot.core.data_manager import cog_data_path import discord import aiohttp import PIL import os class NitroEmoji(commands.Cog): """ Reward nitro boosters with a custom emoji. """ def __init__(self, bot): self.bot = bot self.config = Config.get_conf(self, identifier=123859659843, force_registration=True) default_guild = {"channel": None, "disabled": False} default_member = {"emojis": []} self.config.register_guild(**default_guild) self.config.register_member(**default_member) async def initialize(self): for member in self.bot.get_all_members(): async with self.config.member(member).emojis() as data: to_remove = [] for e in data: emoji = self.find_emoji(member.guild, e) # clean removed emojis if bot is down if not emoji: to_remove.append(e) for r in to_remove: data.remove(r) @staticmethod def get_boosts(member: discord.Member): # this will return number of boosts once api supports it return member.premium_since is not None def find_emoji(self, guild, name): emoji = self.bot.get_emoji(name) # find by name: if not emoji: emoji = discord.utils.get(guild.emojis, name=name) return emoji async def add_emoji(self, member, name, attachment_or_url, reason=None): path = str(cog_data_path(cog_instance=self)) path = os.path.join(path, str(member.id) + name) if isinstance(attachment_or_url, discord.Attachment): await attachment_or_url.save(path) elif isinstance(attachment_or_url, str): async with aiohttp.ClientSession(loop=self.bot.loop) as session: async with session.get(attachment_or_url) as r: if r.status == 200: with open(path, "wb") as f: f.write(await r.read()) # verify image im = PIL.Image.open(path) im.verify() # upload emoji with open(path, "rb") as f: emoji = await member.guild.create_custom_emoji(name=name, image=f.read()) os.remove(path) async with self.config.member(member).emojis() as e: e.append(emoji.id) channel = await self.config.guild(member.guild).channel() channel = member.guild.get_channel(channel) if not channel: return embed = discord.Embed(title="Custom Emoji Added", colour=member.colour) embed.set_footer(text="User ID:{}".format(member.id)) embed.set_author(name=str(member), url=emoji.url) embed.set_thumbnail(url=emoji.url) if reason: embed.add_field(name="Reason", value=reason) await channel.send(embed=embed) async def del_emoji(self, guild, member, emoji=None, reason=None): channel = await self.config.guild(member.guild).channel() channel = member.guild.get_channel(channel) if channel: embed = discord.Embed(title="Custom Emoji Removed", colour=member.colour) embed.set_footer(text="User ID:{}".format(member.id)) embed.set_author(name=str(member), url=emoji.url) embed.set_thumbnail(url=emoji.url) embed.set_author(name=str(member)) if reason: embed.add_field(name="Reason", value=reason) await channel.send(embed=embed) if emoji: await emoji.delete() async with self.config.member(member).emojis() as e: e.remove(emoji.id) @commands.group(name="nitroset") @commands.guild_only() @checks.admin() async def nitroset(self, ctx): """Manage nitro emoji settings.""" pass @nitroset.command(name="channel") async def nitroset_channel(self, ctx, channel: discord.TextChannel): """ Set the channel to log nitro events. Logs boosts, unboosts, and added emojis. """ await self.config.guild(ctx.guild).channel.set(channel.id) await ctx.tick() @nitroset.command(name="disable") async def nitroset_disable(self, ctx, *, on_off: bool = None): """ Disable users from adding more emojis. Users can still remove and list their own emojis. """ if on_off is None: curr = await self.config.guild(ctx.guild).disabled() msg = "enabled" if not curr else "disabled" await ctx.send(f"Nitro emojis is {msg}.") return await self.config.guild(ctx.guild).disabled.set(on_off) await ctx.tick() @commands.group(name="nitroemoji") @checks.bot_has_permissions(manage_emojis=True) async def nitroemoji(self, ctx): """ Manage your emojis if you boosted the server. """ pass @nitroemoji.command(name="add") async def nitroemoji_add(self, ctx, name: str, *, url: str = None): """ Add an emoji to the server, if you boosted. Can only add an emoji for every boost you have in the server. """ disabled = await self.config.guild(ctx.guild).disabled() if disabled: await ctx.send("Sorry, adding emojis is currently disabled right now.") return curr = await self.config.member(ctx.author).emojis() boosts = self.get_boosts(ctx.author) # TODO: add in checking for multiple emojis once supported if boosts and not curr: try: if url: emoji = await self.add_emoji(ctx.author, name, url) else: emoji = await self.add_emoji(ctx.author, name, ctx.message.attachments[0]) await ctx.tick() except discord.errors.HTTPException as e: await ctx.send(e.text) except PIL.UnidentifiedImageError: await ctx.send("That is not a valid picture! Pictures must be in PNG, JPEG, or GIF format.") except: await ctx.send( "Something went wrong, make sure to add a valid picture (PNG, JPG, or GIF) of the right size (256KB) and a valid name." ) return elif not boosts: await ctx.send("Sorry, you need to be a nitro booster to add an emoji!") elif curr: await ctx.send("You already have a custom emoji, please delete it first before adding another one.") @nitroemoji.command(name="rem") async def nitroemoji_rem(self, ctx, name: str): """ Remove an emoji to the server, if you boosted. """ curr = await self.config.member(ctx.author).emojis() emoji = self.find_emoji(ctx.guild, name) if emoji: if emoji.id in curr: await self.del_emoji(ctx.guild, ctx.author, emoji=emoji, reason="Removed by user.") await ctx.tick() else: await ctx.send("That isn't your custom emoji.") else: await ctx.send(warning("Emoji not found.")) @nitroemoji.command(name="list") async def nitroemoji_list(self, ctx): """ List your custom emojis in the server """ curr = await self.config.member(ctx.author).emojis() msg = "" if curr: msg += "Current emojis:\n" for emoji in curr: emoji = self.find_emoji(ctx.guild, emoji) if not emoji: continue msg += f"{emoji.url}\n" else: msg += "You have no custom emojis." for page in pagify(msg): await ctx.send(page) @commands.Cog.listener() async def on_member_update(self, before, after): # check if they stopped boosting if before.premium_since != after.premium_since and after.premium_since is None: emojis = await self.config.member(after).emojis() for emoji in emojis: emoji = self.find_emoji(after.guild, emoji) if not emoji: continue await self.del_emoji(after.guild, after, emoji=emoji, reason="Stopped boosting.") @commands.Cog.listener() async def on_guild_emojis_update(self, guild, before, after): b_e = set(before) a_e = set(after) diff = b_e - a_e if diff: for e in diff: for member in guild.premium_subscribers: curr = await self.config.member(member).emojis() if e.id in curr: curr.remove(e.id) await self.config.member(member).emojis.set(curr) await self.del_emoji(guild, member, emoji=e, reason="Manually deleted by admin.") break