import os
import json
from typing import Union
import discord
from PIL import Image, ImageDraw, ImageFont
from discord.ext import commands
import aiohttp
import io
import emoji
with open("config.json", "r") as f:
config = json.load(f)
ALLOWED_CHANNELS = config.get("allowed_channels", [])
COLORS = {
(0, 0, 0): "⬛",
(0, 0, 255): "🟦",
(255, 0, 0): "🟥",
(255, 255, 0): "🟨",
(255, 165, 0): "🟧",
(255, 255, 255): "⬜",
(0, 255, 0): "🟩",
}
def euclidean_distance(c1, c2):
r1, g1, b1 = c1
r2, g2, b2 = c2
return ((r2 - r1) ** 2 + (g2 - g1) ** 2 + (b2 - b1) ** 2) ** 0.5
def find_closest_emoji(color):
c = sorted(list(COLORS), key=lambda k: euclidean_distance(color, k))
return COLORS[c[0]]
def emojify_image(img, size=14):
WIDTH, HEIGHT = (size, size)
small_img = img.resize((WIDTH, HEIGHT), Image.NEAREST)
emoji = ""
for y in range(HEIGHT):
for x in range(WIDTH):
emoji += find_closest_emoji(small_img.getpixel((x, y)))
emoji += "\n"
return emoji
async def emoji_to_image(emoji_string, size=(280, 280)):
img = Image.new('RGB', size, (255, 255, 255))
draw = ImageDraw.Draw(img)
font = ImageFont.truetype("fonts/NotoEmoji-Regular.ttf", 16)
emoji_string = emoji.emojize(emoji_string)
bbox = draw.textbbox((0, 0), emoji_string, font=font)
text_width, text_height = bbox[2] - bbox[0], bbox[3] - bbox[1]
position = ((size[0] - text_width) // 2, (size[1] - text_height) // 2)
draw.text(position, emoji_string, fill=(0, 0, 0), font=font)
byte_io = io.BytesIO()
img.save(byte_io, 'PNG')
byte_io.seek(0)
return byte_io
class Emojify(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.command()
@commands.cooldown(1, 20, commands.BucketType.user)
async def emojify(self, ctx, url: Union[discord.Member, str], size: int = 14):
if ctx.channel.id not in ALLOWED_CHANNELS:
await ctx.send("❌ This command can only be used in specific channels.")
return
if size > 30:
await ctx.send("❌ Max size allowed is 30.")
return
if isinstance(url, discord.Member):
url = url.display_avatar.url
if not isinstance(url, str) or not url.startswith("http"):
await ctx.send("❌ Invalid URL or Member mention. Please provide a valid
URL or mention a valid member.")
return
try:
async with aiohttp.ClientSession() as session:
async with session.get(url) as r:
if r.status == 200:
image_data = await r.read()
image = Image.open(io.BytesIO(image_data)).convert("RGB")
emoji_string = emojify_image(image, size)
if size > 14:
emoji_string = f"```{emoji_string}```"
embed = discord.Embed(description="**Image description:**
Emojified image below!")
embed.set_thumbnail(url="attachment://emoji_image.png")
message = await ctx.send(embed=embed)
await message.reply(emoji_string)
emoji_image_file = await emoji_to_image(emoji_string,
size=(512, 512))
await ctx.send(file=discord.File(emoji_image_file,
filename="emoji_image.png"))
else:
await ctx.send(f"❌ Failed to fetch the image. Received
status code: {r.status}")
except aiohttp.ClientError as e:
await ctx.send(f"❌ Network error: Unable to fetch image. Error:
{str(e)}")
except Exception as e:
await ctx.send(f"❌ An unexpected error occurred: {str(e)}")
@emojify.error
async def emojify_error(self, ctx, error):
if isinstance(error, commands.CommandOnCooldown):
await ctx.send(f"❌ Please wait {error.retry_after:.2f} seconds before
using this command again.")
async def setup(bot):
await bot.add_cog(Emojify(bot))