2023-09-21 19:25:01 +01:00
|
|
|
# You have been warned - this file is very EXTREME!
|
|
|
|
import asyncio
|
2023-09-21 20:07:22 +01:00
|
|
|
import io
|
2023-09-21 19:25:01 +01:00
|
|
|
from functools import partial
|
2023-11-30 10:35:16 +00:00
|
|
|
from urllib.parse import parse_qs, urlparse
|
2023-11-04 17:18:39 +00:00
|
|
|
|
|
|
|
import blend_modes
|
|
|
|
import discord
|
|
|
|
import numpy
|
2023-09-21 19:25:01 +01:00
|
|
|
import PIL.Image
|
2023-11-30 10:35:16 +00:00
|
|
|
import PIL.ImageSequence
|
2023-11-04 17:18:39 +00:00
|
|
|
from discord.ext import commands
|
2023-11-30 10:35:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
def resize_gif(img: PIL.Image.Image, width: int, height: int) -> PIL.Image.Image:
|
|
|
|
"""Resizes a gif properly"""
|
|
|
|
new_frames = []
|
|
|
|
for frame in PIL.ImageSequence.Iterator(img):
|
|
|
|
frame: PIL.Image.Image = frame.copy()
|
|
|
|
frame = frame.resize((width, height))
|
|
|
|
new_frames.append(frame)
|
|
|
|
_bio = io.BytesIO()
|
|
|
|
new_frames[0].save(_bio, "GIF", save_all=True, append_images=new_frames[1:])
|
|
|
|
return PIL.Image.open(_bio).convert("RGBA")
|
2023-09-21 20:07:22 +01:00
|
|
|
|
|
|
|
|
|
|
|
def _overlay_images(
|
2023-11-04 17:18:39 +00:00
|
|
|
background: PIL.Image.Image, foreground: PIL.Image.Image, mode=blend_modes.overlay, opacity: float = 1.0
|
2023-09-21 20:07:22 +01:00
|
|
|
) -> PIL.Image.Image:
|
|
|
|
background.load()
|
|
|
|
foreground.load()
|
2024-02-05 23:39:46 +00:00
|
|
|
background = background.convert("RGBA")
|
|
|
|
foreground = foreground.convert("RGBA")
|
2023-09-21 20:07:22 +01:00
|
|
|
background_img = numpy.array(background)
|
|
|
|
background_img_float = background_img.astype(float)
|
|
|
|
foreground_img = numpy.array(foreground)
|
|
|
|
foreground_img_float = foreground_img.astype(float)
|
|
|
|
|
|
|
|
blended_img_float = mode(background_img_float, foreground_img_float, opacity)
|
|
|
|
|
|
|
|
blended_img = numpy.uint8(blended_img_float)
|
|
|
|
return PIL.Image.fromarray(blended_img)
|
2023-09-21 19:25:01 +01:00
|
|
|
|
|
|
|
|
2023-11-30 10:35:16 +00:00
|
|
|
def _overlay_gif(background: PIL.Image.Image, foreground: PIL.Image.Image) -> PIL.Image.Image:
|
|
|
|
"""Overlays a GIF onto a static background"""
|
|
|
|
background = background.convert("RGBA")
|
|
|
|
frames = []
|
|
|
|
for frame in PIL.ImageSequence.Iterator(foreground):
|
|
|
|
bg = background.copy()
|
|
|
|
bg.paste(frame, mask=frame)
|
|
|
|
frames.append(bg)
|
|
|
|
|
|
|
|
# Save it as a GIF and return it as a PIL image
|
|
|
|
_io = io.BytesIO()
|
|
|
|
frames[0].save(_io, format="gif", save_all=True, append_images=frames[1:])
|
|
|
|
_io.seek(0)
|
|
|
|
return PIL.Image.open(_io)
|
|
|
|
|
|
|
|
|
2023-09-21 19:25:01 +01:00
|
|
|
def overlay_logo(img: PIL.Image.Image) -> PIL.Image.Image:
|
|
|
|
"""Overlay the logo on an image."""
|
2023-09-21 20:07:22 +01:00
|
|
|
# clone the image
|
|
|
|
img = img.copy()
|
|
|
|
logo = PIL.Image.open("assets/extreme.png")
|
|
|
|
logo.convert("RGBA")
|
|
|
|
logo.load()
|
2023-09-21 19:25:01 +01:00
|
|
|
logo = logo.resize((1024, 1024))
|
|
|
|
img = img.resize((1024, 1024))
|
2023-10-30 18:40:44 +00:00
|
|
|
|
2023-09-21 20:24:58 +01:00
|
|
|
img = _overlay_images(img, logo, blend_modes.lighten_only, 1)
|
2023-09-21 20:07:22 +01:00
|
|
|
return img
|
2023-09-21 19:25:01 +01:00
|
|
|
|
|
|
|
|
|
|
|
def overlay_purple(img: PIL.Image.Image) -> PIL.Image.Image:
|
|
|
|
"""Overlay the purple on an image."""
|
|
|
|
# purple_overlay_rgb = 0x440099
|
2023-09-21 20:07:22 +01:00
|
|
|
purple_overlay_rgba = (68, 0, 153)
|
2023-09-21 19:25:01 +01:00
|
|
|
# Create the overlay image
|
|
|
|
overlay = PIL.Image.new("RGBA", img.size, purple_overlay_rgba)
|
|
|
|
|
|
|
|
# resize to 1024x1024
|
2023-09-21 20:07:22 +01:00
|
|
|
img = img.copy().resize((1024, 1024))
|
2023-09-21 19:25:01 +01:00
|
|
|
overlay = overlay.resize((1024, 1024))
|
|
|
|
|
2023-09-21 20:07:22 +01:00
|
|
|
img = _overlay_images(img, overlay)
|
|
|
|
return img
|
2023-09-21 19:25:01 +01:00
|
|
|
|
|
|
|
|
2023-11-30 10:35:16 +00:00
|
|
|
def make_circle(img: PIL.Image.Image) -> PIL.Image.Image:
|
|
|
|
"""Makes an image a circle"""
|
|
|
|
# clone the image
|
|
|
|
img = img.copy()
|
|
|
|
# Create a mask
|
|
|
|
mask = PIL.Image.new("L", img.size, 0)
|
|
|
|
draw = PIL.ImageDraw.Draw(mask)
|
|
|
|
draw.ellipse((0, 0) + img.size, fill=255)
|
|
|
|
# Apply the mask
|
|
|
|
img.putalpha(mask)
|
|
|
|
return img
|
|
|
|
|
|
|
|
|
2023-09-21 19:25:01 +01:00
|
|
|
def extremify(img: PIL.Image.Image) -> PIL.Image.Image:
|
|
|
|
"""Apply the EXTREME effect to an image."""
|
|
|
|
img = overlay_purple(img)
|
2023-09-21 20:07:22 +01:00
|
|
|
img = overlay_logo(img)
|
2023-09-21 19:25:01 +01:00
|
|
|
return img
|
|
|
|
|
|
|
|
|
|
|
|
class Extremism(commands.Cog):
|
|
|
|
def __init__(self, bot):
|
|
|
|
self.bot = bot
|
2023-11-04 17:18:39 +00:00
|
|
|
|
2023-09-21 19:25:01 +01:00
|
|
|
@commands.slash_command(name="radicalise")
|
2023-09-21 20:08:27 +01:00
|
|
|
async def radicalise(self, ctx, image: discord.Attachment = None, user: discord.User = None):
|
2023-09-21 19:25:01 +01:00
|
|
|
"""Makes an image extremely radical."""
|
2023-09-21 20:08:27 +01:00
|
|
|
if image is None:
|
|
|
|
if user is None:
|
|
|
|
user = ctx.author
|
|
|
|
image = user.avatar.with_format("png")
|
|
|
|
else:
|
|
|
|
if not image.content_type.startswith("image/"):
|
|
|
|
await ctx.send("That's not an image!")
|
|
|
|
return
|
2023-09-21 19:25:01 +01:00
|
|
|
await ctx.defer()
|
|
|
|
# Download the image
|
|
|
|
_img_bytes = await image.read()
|
|
|
|
# Open the image
|
2023-09-21 20:07:22 +01:00
|
|
|
img = PIL.Image.open(io.BytesIO(_img_bytes))
|
2023-09-21 19:25:01 +01:00
|
|
|
# Apply the EXTREME effect
|
|
|
|
img = await asyncio.to_thread(extremify, img)
|
|
|
|
# Save the image
|
|
|
|
img_bytes = io.BytesIO()
|
|
|
|
img.save(img_bytes, format="PNG")
|
|
|
|
img_bytes.seek(0)
|
|
|
|
# Send the image
|
|
|
|
await ctx.respond(file=discord.File(img_bytes, filename="extreme.png"))
|
|
|
|
|
2023-11-30 10:35:16 +00:00
|
|
|
@commands.slash_command(name="decorate")
|
|
|
|
async def decorate(
|
2023-12-05 21:12:33 +00:00
|
|
|
self,
|
|
|
|
ctx: discord.ApplicationContext,
|
|
|
|
decoration_url: str,
|
|
|
|
user: discord.User = None,
|
|
|
|
# animated: bool = True
|
2023-11-30 10:35:16 +00:00
|
|
|
):
|
|
|
|
"""Decorates an avatar with a decoration."""
|
|
|
|
if user is None:
|
|
|
|
user = ctx.user
|
|
|
|
|
|
|
|
# Download the image
|
|
|
|
await ctx.defer()
|
|
|
|
_img_bytes = await user.display_avatar.with_format("png").read()
|
|
|
|
img_bio = io.BytesIO(_img_bytes)
|
|
|
|
img = PIL.Image.open(img_bio)
|
|
|
|
|
|
|
|
# Parse the URL and get the highest resolution possible
|
|
|
|
query = parse_qs(urlparse(decoration_url).query)
|
|
|
|
if "size" in query:
|
|
|
|
size = int(query["size"][0])
|
|
|
|
else:
|
|
|
|
size = 640
|
|
|
|
size = min(640, max(160, size))
|
|
|
|
|
2023-12-05 21:12:33 +00:00
|
|
|
decoration_url = urlparse(decoration_url)._replace(query="?size={!s}&passthrough=true".format(size)).geturl()
|
2023-11-30 10:35:16 +00:00
|
|
|
|
|
|
|
# Download the decoration
|
|
|
|
try:
|
|
|
|
_decoration_bytes = await self.bot.http.get_from_cdn(decoration_url)
|
|
|
|
decoration_bio = io.BytesIO(_decoration_bytes)
|
|
|
|
decoration = PIL.Image.open(decoration_bio)
|
|
|
|
except discord.Forbidden:
|
|
|
|
return await ctx.respond("Failed to download the decoration (403).")
|
|
|
|
except discord.NotFound:
|
|
|
|
return await ctx.respond("Failed to download the decoration (404).")
|
|
|
|
|
|
|
|
# Resize the decoration to the avatar size
|
|
|
|
# decoration = await asyncio.to_thread(
|
|
|
|
# partial(resize_gif, decoration, img.width, img.height)
|
|
|
|
# )
|
|
|
|
decoration = decoration.resize((img.width, img.height))
|
|
|
|
|
|
|
|
# Apply the decoration
|
|
|
|
new = await asyncio.to_thread(partial(_overlay_gif, img, decoration))
|
|
|
|
|
|
|
|
# Save the image
|
|
|
|
img_bytes = io.BytesIO()
|
|
|
|
ext = "png"
|
|
|
|
new.save(img_bytes, format=ext)
|
|
|
|
img_bytes.seek(0)
|
|
|
|
|
|
|
|
# Send the image
|
|
|
|
img_bio.seek(0)
|
|
|
|
decoration_bio.seek(0)
|
|
|
|
files = [
|
|
|
|
discord.File(img_bytes, filename="decorated." + ext)
|
|
|
|
]
|
2023-12-05 21:12:33 +00:00
|
|
|
await ctx.respond(files=files)
|
2023-11-30 10:35:16 +00:00
|
|
|
|
2023-09-21 19:25:01 +01:00
|
|
|
|
|
|
|
def setup(bot):
|
|
|
|
bot.add_cog(Extremism(bot))
|