mirror of
https://github.com/Fluffy-Bean/Lynxie.git
synced 2025-05-14 08:02:17 +00:00
Add more overlay options
This commit is contained in:
parent
7e108350a5
commit
f0f3d3b1d3
6 changed files with 141 additions and 105 deletions
BIN
lynxie/assets/bandicam.png
Normal file
BIN
lynxie/assets/bandicam.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
BIN
lynxie/assets/jerm-a.png
Normal file
BIN
lynxie/assets/jerm-a.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
lynxie/assets/jerma.png
Normal file
BIN
lynxie/assets/jerma.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
|
@ -13,27 +13,29 @@ class Animals(commands.Cog):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def animal(self, ctx, animal):
|
async def animal(self, ctx, animal_choice: str = ""):
|
||||||
animal = animal.lower().strip() or "racc"
|
animal_choice = animal_choice.lower().strip() or None
|
||||||
|
|
||||||
if animal not in TINYFOX_ANIMALS:
|
if not animal_choice:
|
||||||
await ctx.reply(
|
error = f"You need to specify an animal! Try one of these: {', '.join(TINYFOX_ANIMALS)}"
|
||||||
embed=error_message(
|
await ctx.reply(embed=error_message(error))
|
||||||
f"That animal doesn't exist! Try one of these:\n"
|
return
|
||||||
f"`{', '.join(TINYFOX_ANIMALS)}`"
|
|
||||||
)
|
if animal_choice not in TINYFOX_ANIMALS:
|
||||||
)
|
error = f"That animal doesn't exist! Try one of these: {', '.join(TINYFOX_ANIMALS)}"
|
||||||
|
await ctx.reply(embed=error_message(error))
|
||||||
return
|
return
|
||||||
|
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
request = requests.get(f"https://api.tinyfox.dev/img?animal={animal}&json")
|
request = requests.get("https://api.tinyfox.dev/img?animal=" + animal_choice)
|
||||||
animal_image = BytesIO(request.content)
|
|
||||||
animal_image.seek(0)
|
|
||||||
animal_file = discord.File(animal_image, filename="image.png")
|
|
||||||
|
|
||||||
embed = discord.Embed(
|
with BytesIO(request.content) as response:
|
||||||
title=animal.capitalize(),
|
response.seek(0)
|
||||||
colour=discord.Colour.orange(),
|
animal_file = discord.File(response, filename="image.png")
|
||||||
).set_image(url="attachment://image.png")
|
|
||||||
|
|
||||||
await ctx.reply(embed=embed, file=animal_file, mention_author=False)
|
embed = discord.Embed(
|
||||||
|
title=animal_choice.capitalize(),
|
||||||
|
colour=discord.Colour.orange(),
|
||||||
|
).set_image(url="attachment://image.png")
|
||||||
|
|
||||||
|
await ctx.reply(embed=embed, file=animal_file, mention_author=False)
|
||||||
|
|
|
@ -1,31 +1,28 @@
|
||||||
import os
|
|
||||||
import datetime
|
import datetime
|
||||||
import requests
|
import requests
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image, ImageEnhance
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
from lynxie.config import IMAGE_EXTENSIONS, IMAGE_OVERLAYS, ASSETS_PATH
|
from lynxie.config import IMAGE_EXTENSIONS, IMAGE_OVERLAYS
|
||||||
from lynxie.utils import error_message
|
from lynxie.utils import error_message
|
||||||
|
|
||||||
|
|
||||||
class Img(commands.Cog):
|
class Img(commands.Cog):
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self._overlays = {
|
|
||||||
"bubble": Image.open(os.path.join(ASSETS_PATH, "bubble.png")),
|
|
||||||
"gang": Image.open(os.path.join(ASSETS_PATH, "gang.png")),
|
|
||||||
}
|
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def overlay(self, ctx, style: str = None):
|
async def overlay(self, ctx, overlay_choice: str = None, overlay_style: str = "default"):
|
||||||
start_time = datetime.datetime.now()
|
start_time = datetime.datetime.now()
|
||||||
style = style.lower().strip() if style else None
|
|
||||||
image_attachments = None
|
|
||||||
|
|
||||||
|
overlay_choice = overlay_choice.lower().strip() if overlay_choice else None
|
||||||
|
overlay_style = overlay_style.lower().strip() if overlay_style else "default"
|
||||||
|
|
||||||
|
image_attachments = None
|
||||||
if ctx.message.attachments:
|
if ctx.message.attachments:
|
||||||
image_attachments = ctx.message.attachments[0]
|
image_attachments = ctx.message.attachments[0]
|
||||||
elif ctx.message.reference and ctx.message.reference.resolved.attachments:
|
elif ctx.message.reference and ctx.message.reference.resolved.attachments:
|
||||||
|
@ -33,19 +30,22 @@ class Img(commands.Cog):
|
||||||
elif ctx.message.embeds and ctx.message.embeds[0].image:
|
elif ctx.message.embeds and ctx.message.embeds[0].image:
|
||||||
image_attachments = ctx.message.embeds[0].image
|
image_attachments = ctx.message.embeds[0].image
|
||||||
else:
|
else:
|
||||||
async for message in ctx.guild.get_channel(ctx.channel.id).history(
|
channel = ctx.guild.get_channel(ctx.channel.id)
|
||||||
limit=10
|
async for message in channel.history(limit=10):
|
||||||
):
|
|
||||||
if message.attachments:
|
if message.attachments:
|
||||||
image_attachments = message.attachments[0]
|
image_attachments = message.attachments[0]
|
||||||
break
|
break
|
||||||
elif message.embeds and message.embeds[0].image:
|
if message.embeds and message.embeds[0].image:
|
||||||
image_attachments = message.embeds[0].image
|
image_attachments = message.embeds[0].image
|
||||||
break
|
break
|
||||||
|
|
||||||
# Check if image should be processed
|
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
if not style or style not in IMAGE_OVERLAYS:
|
if not image_attachments:
|
||||||
|
error = "No image was found!"
|
||||||
|
await ctx.reply(embed=error_message(error))
|
||||||
|
return
|
||||||
|
|
||||||
|
if not overlay_choice or overlay_choice not in IMAGE_OVERLAYS:
|
||||||
error = (
|
error = (
|
||||||
"That is not a valid option! Valid options are:\n"
|
"That is not a valid option! Valid options are:\n"
|
||||||
f"`{', '.join(IMAGE_OVERLAYS)}`"
|
f"`{', '.join(IMAGE_OVERLAYS)}`"
|
||||||
|
@ -53,94 +53,105 @@ class Img(commands.Cog):
|
||||||
await ctx.reply(embed=error_message(error))
|
await ctx.reply(embed=error_message(error))
|
||||||
return
|
return
|
||||||
|
|
||||||
if not image_attachments:
|
if overlay_style not in IMAGE_OVERLAYS[overlay_choice]["options"]:
|
||||||
error = "You need to attach an image to use this command!"
|
|
||||||
await ctx.reply(embed=error_message(error))
|
|
||||||
return
|
|
||||||
|
|
||||||
# Extracts file extension from filename or url
|
|
||||||
if (
|
|
||||||
image_attachments.filename
|
|
||||||
and not image_attachments.filename.split(".")[-1].lower()
|
|
||||||
in IMAGE_EXTENSIONS
|
|
||||||
):
|
|
||||||
error = (
|
error = (
|
||||||
"Unsupported file type! Supported file types are:\n"
|
"That is not a valid option! Valid options are:\n"
|
||||||
f"`{', '.join(IMAGE_EXTENSIONS)}`"
|
f"`{', '.join(IMAGE_OVERLAYS[overlay_choice]['options'])}`"
|
||||||
)
|
)
|
||||||
await ctx.reply(embed=error_message(error))
|
await ctx.reply(embed=error_message(error))
|
||||||
return
|
return
|
||||||
elif (
|
|
||||||
image_attachments.url
|
# Defaults to gwa as I cant be asked to make a better error handler
|
||||||
and not image_attachments.url.split(".")[-1].lower() in IMAGE_EXTENSIONS
|
filename = image_attachments.filename or image_attachments.url or "image.gwa"
|
||||||
):
|
if not filename.split(".")[-1].lower() in IMAGE_EXTENSIONS:
|
||||||
error = (
|
error = (
|
||||||
"Unsupported file type! Supported file types are:\n"
|
"Unsupported file type! Supported file types are "
|
||||||
f"`{', '.join(IMAGE_EXTENSIONS)}`"
|
", ".join(IMAGE_EXTENSIONS)
|
||||||
)
|
)
|
||||||
await ctx.reply(embed=error_message(error))
|
await ctx.reply(embed=error_message(error))
|
||||||
return
|
return
|
||||||
|
|
||||||
if image_attachments.size and image_attachments.size > 8 * 1024 * 1024:
|
if image_attachments.size and image_attachments.size > 8 * 1024 * 1024:
|
||||||
error = (
|
error = (
|
||||||
"That image is too big! Please use an image that is less than 8MB."
|
"That image is too big! "
|
||||||
|
"Please use an image that is less than 8MB."
|
||||||
)
|
)
|
||||||
await ctx.reply(embed=error_message(error))
|
await ctx.reply(embed=error_message(error))
|
||||||
return
|
return
|
||||||
|
|
||||||
if (
|
if (
|
||||||
not 0 < image_attachments.width <= 3500
|
not 10 < image_attachments.width <= 3500
|
||||||
or not 0 < image_attachments.height <= 3500
|
or not 10 < image_attachments.height <= 3500
|
||||||
):
|
):
|
||||||
error = "Image must be at least 1x1 and under 3500x3500!"
|
error = "Image must be at least 10x10 and under 3500x3500!"
|
||||||
await ctx.reply(embed=error_message(error))
|
await ctx.reply(embed=error_message(error))
|
||||||
return
|
return
|
||||||
|
|
||||||
response = requests.get(image_attachments.url)
|
request = requests.get(image_attachments.url)
|
||||||
message_attachment = Image.open(BytesIO(response.content))
|
attachment = Image.open(BytesIO(request.content))
|
||||||
|
width, height = attachment.width, attachment.height
|
||||||
|
|
||||||
if message_attachment.width < message_attachment.height:
|
if width < height:
|
||||||
message_attachment.thumbnail((200, message_attachment.height))
|
attachment.thumbnail((200, height))
|
||||||
else:
|
else:
|
||||||
message_attachment.thumbnail((message_attachment.width, 200))
|
attachment.thumbnail((width, 200))
|
||||||
|
|
||||||
if style == "bubble":
|
width, height = attachment.width, attachment.height
|
||||||
# The bubble is resized twice as for some reason .copy() doesn't work
|
|
||||||
message_attachment.paste(
|
if overlay_choice == "bubble":
|
||||||
self._overlays["bubble"].resize(
|
overlay = Image.open(IMAGE_OVERLAYS[overlay_choice]["path"])
|
||||||
(message_attachment.width, self._overlays["bubble"].height)
|
overlay = overlay.resize((width, overlay.height))
|
||||||
),
|
|
||||||
(0, 0),
|
if overlay_style in ["default", "top"]:
|
||||||
self._overlays["bubble"].resize(
|
attachment.paste(overlay, (0, 0), overlay)
|
||||||
(message_attachment.width, self._overlays["bubble"].height)
|
elif overlay_style in ["bottom"]:
|
||||||
),
|
overlay = overlay.rotate(180)
|
||||||
)
|
attachment.paste(overlay, (0, height - overlay.height), overlay)
|
||||||
elif style == "gang":
|
elif overlay_style in ["mask", "mask-bottom"]:
|
||||||
message_attachment.paste(
|
# This is a lazy method of creating a mask
|
||||||
self._overlays["gang"],
|
# 1. Reduce brightness of overlay to 0 (black)
|
||||||
(
|
# 2. Create a white square the size of the image
|
||||||
(
|
# 3. Paste the overlay onto the white square
|
||||||
(message_attachment.width - self._overlays["gang"].width)
|
|
||||||
// 2
|
overlay = ImageEnhance.Brightness(overlay).enhance(0)
|
||||||
),
|
|
||||||
(message_attachment.height - self._overlays["gang"].height),
|
mask = Image.new("RGB", (width, height), (255, 255, 255))
|
||||||
),
|
mask.paste(overlay, (0, 0), overlay)
|
||||||
self._overlays["gang"],
|
|
||||||
|
if overlay_style == "mask-bottom":
|
||||||
|
mask = mask.rotate(180)
|
||||||
|
|
||||||
|
mask = mask.convert("L")
|
||||||
|
|
||||||
|
attachment.putalpha(mask)
|
||||||
|
elif overlay_choice == "gang":
|
||||||
|
overlay = Image.open(IMAGE_OVERLAYS[overlay_choice]["path"])
|
||||||
|
position = ((width - overlay.width) // 2, (height - overlay.height))
|
||||||
|
attachment.paste(overlay, position, overlay)
|
||||||
|
elif overlay_choice == "bandicam":
|
||||||
|
overlay = Image.open(IMAGE_OVERLAYS[overlay_choice]["path"])
|
||||||
|
overlay.thumbnail((width, overlay.height))
|
||||||
|
attachment.paste(overlay, ((width-overlay.width)//2, 0), overlay)
|
||||||
|
elif overlay_choice == "jerma":
|
||||||
|
overlay = Image.open(IMAGE_OVERLAYS[overlay_choice]["path"])
|
||||||
|
overlay.thumbnail((width, overlay.height))
|
||||||
|
attachment.paste(overlay, (width-overlay.width, height-overlay.height), overlay)
|
||||||
|
elif overlay_choice == "jerm-a":
|
||||||
|
overlay = Image.open(IMAGE_OVERLAYS[overlay_choice]["path"])
|
||||||
|
overlay.thumbnail((width, overlay.height))
|
||||||
|
attachment.paste(overlay, ((width-overlay.width)//2, height-overlay.height), overlay)
|
||||||
|
with BytesIO() as response:
|
||||||
|
attachment.save(response, format="PNG")
|
||||||
|
|
||||||
|
response.seek(0)
|
||||||
|
response = discord.File(response, filename="image.png")
|
||||||
|
|
||||||
|
time_taken = (datetime.datetime.now() - start_time).microseconds / 1000
|
||||||
|
|
||||||
|
embed = (
|
||||||
|
discord.Embed(title=overlay_choice.capitalize(), colour=discord.Colour.orange())
|
||||||
|
.set_image(url="attachment://image.png")
|
||||||
|
.set_footer(text=f"{width}x{height}, {time_taken}ms")
|
||||||
)
|
)
|
||||||
|
|
||||||
message_file = BytesIO()
|
await ctx.reply(embed=embed, file=response, mention_author=False)
|
||||||
message_attachment.save(message_file, format="PNG")
|
|
||||||
message_file.seek(0)
|
|
||||||
message_file = discord.File(message_file, filename="image.png")
|
|
||||||
|
|
||||||
time_taken = datetime.datetime.now() - start_time
|
|
||||||
embed = (
|
|
||||||
discord.Embed(title=style.capitalize(), colour=discord.Colour.orange())
|
|
||||||
.set_image(url="attachment://image.png")
|
|
||||||
.set_footer(
|
|
||||||
text=f"{message_attachment.width}x{message_attachment.height}, "
|
|
||||||
f"{time_taken.microseconds / 1000}ms"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
await ctx.reply(embed=embed, file=message_file, mention_author=False)
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from lynxie.utils import get_env_or_error
|
||||||
|
|
||||||
DISCORD_TOKEN = get_env_or_error("DISCORD_TOKEN")
|
DISCORD_TOKEN = get_env_or_error("DISCORD_TOKEN")
|
||||||
DISCORD_GUILD_ID = Object(id=1040757387033849976)
|
DISCORD_GUILD_ID = Object(id=1040757387033849976)
|
||||||
LYNXIE_PREFIX = "~"
|
LYNXIE_PREFIX = "?"
|
||||||
|
|
||||||
DATA_PATH = "data"
|
DATA_PATH = "data"
|
||||||
ASSETS_PATH = "assets"
|
ASSETS_PATH = "assets"
|
||||||
|
@ -49,7 +49,30 @@ TINYFOX_ANIMALS = [
|
||||||
|
|
||||||
IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "webp"]
|
IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "webp"]
|
||||||
|
|
||||||
IMAGE_OVERLAYS = [
|
IMAGE_OVERLAYS = {
|
||||||
"bubble",
|
"bubble": {
|
||||||
"gang",
|
"path": os.path.join(ASSETS_PATH, "bubble.png"),
|
||||||
]
|
"options": [
|
||||||
|
"default", # Positioned at top
|
||||||
|
"bottom", # Positioned at bottom
|
||||||
|
"mask", # Positioned at top, but transparent
|
||||||
|
"mask-bottom", # Positioned at bottom, but transparent
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"gang": {
|
||||||
|
"path": os.path.join(ASSETS_PATH, "gang.png"),
|
||||||
|
"options": ["default"],
|
||||||
|
},
|
||||||
|
"bandicam": {
|
||||||
|
"path": os.path.join(ASSETS_PATH, "bandicam.png"),
|
||||||
|
"options": ["default"],
|
||||||
|
},
|
||||||
|
"jerma": {
|
||||||
|
"path": os.path.join(ASSETS_PATH, "jerma.png"),
|
||||||
|
"options": ["default"],
|
||||||
|
},
|
||||||
|
"jerm-a": {
|
||||||
|
"path": os.path.join(ASSETS_PATH, "jerm-a.png"),
|
||||||
|
"options": ["default"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue