Skip to content

Commit

Permalink
[Triggered] Cog model restructure
Browse files Browse the repository at this point in the history
- Restructure `Triggered` according to SFUAnime/Ren#608. No functional
  changes.
- Remove redundant imports.
- Remove redundant constant `AVATAR_URL`.
- Sort imports in this order: standard library imports, third-party
  imports, upstream (`redbot`) imports, and lastly, our code.
- Rework enum `Modes` by making it an `Enum`-derived class.
  • Loading branch information
quachtridat committed Jan 29, 2023
1 parent 917fb70 commit d7cbb4b
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 114 deletions.
20 changes: 20 additions & 0 deletions triggered/commandHandlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import discord

from redbot.core import commands
from redbot.core.commands import Context

from .commandsCore import CommandsCore


class CommandHandlers(CommandsCore):
@commands.hybrid_command(name="triggered")
async def _cmdTriggered(self, ctx: Context, user: discord.Member = None):
await self.cmdTriggered(ctx=ctx, user=user)

@commands.hybrid_command(name="reallytriggered")
async def _cmdHypertriggered(self, ctx: Context, user: discord.Member = None):
await self.cmdHypertriggered(ctx=ctx, user=user)

@commands.hybrid_command(name="hypertriggered")
async def _cmdDeepfry(self, ctx: Context, user: discord.Member = None):
await self.cmdDeepfry(ctx=ctx, user=user)
40 changes: 40 additions & 0 deletions triggered/commandsCore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import discord

from redbot.core.commands import Context

from .core import AVATAR_FILE_NAME, Core, Modes


class CommandsCore(Core):
async def cmdTriggered(self, ctx: Context, user: discord.Member = None):
"""Are you triggered? Say no more."""
await ctx.defer()
if not user:
user = ctx.message.author
data = await self._createTrigger(user, mode=Modes.TRIGGERED)
if not data:
await ctx.send("Something went wrong, try again.")
return
await ctx.send(file=discord.File(data, filename=AVATAR_FILE_NAME.format(user)))

async def cmdHypertriggered(self, ctx: Context, user: discord.Member = None):
"""Are you in an elevated state of triggered? Say no more."""
await ctx.defer()
if not user:
user = ctx.message.author
data = await self._createTrigger(user, mode=Modes.REALLY_TRIGGERED)
if not data:
await ctx.send("Something went wrong, try again.")
return
await ctx.send(file=discord.File(data, filename=AVATAR_FILE_NAME.format(user)))

async def cmdDeepfry(self, ctx: Context, user: discord.Member = None):
"""Are you incredibly triggered? Say no more."""
await ctx.defer()
if not user:
user = ctx.message.author
data = await self._createTrigger(user, mode=Modes.HYPER_TRIGGERED)
if not data:
await ctx.send("Something went wrong, try again.")
return
await ctx.send(file=discord.File(data, filename=AVATAR_FILE_NAME.format(user)))
84 changes: 84 additions & 0 deletions triggered/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import enum
from enum import Enum
import io
import logging

import discord
from PIL import Image, ImageChops, ImageOps, ImageEnhance

from redbot.core import data_manager
from redbot.core.bot import Red

AVATAR_FILE_NAME = "{0.id}-triggered.gif"


class Modes(Enum):
TRIGGERED = enum.auto()
REALLY_TRIGGERED = enum.auto()
HYPER_TRIGGERED = enum.auto()


class Core:
# Class constructor
def __init__(self, bot: Red):
self.bot = bot
self.logger = logging.getLogger("red.luicogs.Triggered")
self.saveFolder = data_manager.cog_data_path(cog_instance=self)
# We need a custom header or else we get a HTTP 403 Unauthorized
self.headers = {"User-agent": "Mozilla/5.0"}

async def _createTrigger(self, user: discord.User, mode=Modes.TRIGGERED):
"""Fetches the user's avatar, and creates a triggered GIF, applies additional PIL image transformations based on specified mode
Parameters:
-----------
user: discord.User
mode: Modes
Returns:
--------
An io.BytesIO object containing the data for the generated trigger image
"""
avatarData: bytes

avatar = user.display_avatar.with_size(512)
avatarData = await avatar.read()

if not avatarData:
self.logger.error("No avatar data received!")
return

with Image.open(io.BytesIO(avatarData)) as avatar:

if not avatar:
return

offsets = [(15, 15), (5, 10), (-15, -15), (10, -10), (10, 0), (-15, 10), (10, -5)]
images = []

# if hyper mode is set
if mode == Modes.REALLY_TRIGGERED:
red_overlay = Image.new(mode="RGBA", size=avatar.size, color=(255, 0, 0, 255))
mask = Image.new(mode="RGBA", size=avatar.size, color=(255, 255, 255, 127))
avatar = Image.composite(avatar, red_overlay, mask)

elif mode == Modes.HYPER_TRIGGERED:
avatar = ImageEnhance.Color(avatar).enhance(5)
avatar = ImageEnhance.Sharpness(avatar).enhance(24)
avatar = ImageEnhance.Contrast(avatar).enhance(4)

for xcoord, ycoord in offsets:
image = ImageChops.offset(avatar, xcoord, ycoord)
image = ImageOps.crop(image, 15)
images.append(image)
avatar = ImageOps.crop(avatar, 15)

result = io.BytesIO()
avatar.save(
result, format="GIF", append_images=images, save_all=True, duration=25, loop=0
)

# IMPORTANT: rewind to beginning of the stream before returning
result.seek(0)

return result
117 changes: 3 additions & 114 deletions triggered/triggered.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,121 +2,10 @@
`triggered from spoopy.
"""

import logging
import io
import aiohttp
import discord
from redbot.core import commands, data_manager
from redbot.core.bot import Red
from redbot.core.commands import Context
from PIL import Image, ImageChops, ImageOps, ImageFilter, ImageEnhance
from enum import Enum
from redbot.core import commands

Modes = Enum("Modes", "triggered reallytriggered hypertriggered")
from .commandHandlers import CommandHandlers

AVATAR_URL = "https://cdn.discordapp.com/avatars/{0.id}/{0.avatar}.png?size=512"
AVATAR_FILE_NAME = "{0.id}-triggered.gif"


class Triggered(commands.Cog):
class Triggered(commands.Cog, CommandHandlers):
"""We triggered, fam."""

# Class constructor
def __init__(self, bot: Red):
self.bot = bot
self.logger = logging.getLogger("red.luicogs.Triggered")
self.saveFolder = data_manager.cog_data_path(cog_instance=self)
# We need a custom header or else we get a HTTP 403 Unauthorized
self.headers = {"User-agent": "Mozilla/5.0"}

@commands.hybrid_command(name="triggered")
async def triggered(self, ctx: Context, user: discord.Member = None):
"""Are you triggered? Say no more."""
await ctx.defer()
if not user:
user = ctx.message.author
data = await self._createTrigger(user, mode=Modes.triggered)
if not data:
await ctx.send("Something went wrong, try again.")
return
await ctx.send(file=discord.File(data, filename=AVATAR_FILE_NAME.format(user)))

@commands.hybrid_command(name="reallytriggered")
async def hypertriggered(self, ctx: Context, user: discord.Member = None):
"""Are you in an elevated state of triggered? Say no more."""
await ctx.defer()
if not user:
user = ctx.message.author
data = await self._createTrigger(user, mode=Modes.reallytriggered)
if not data:
await ctx.send("Something went wrong, try again.")
return
await ctx.send(file=discord.File(data, filename=AVATAR_FILE_NAME.format(user)))

@commands.hybrid_command(name="hypertriggered")
async def deepfry(self, ctx: Context, user: discord.Member = None):
"""Are you incredibly triggered? Say no more."""
await ctx.defer()
if not user:
user = ctx.message.author
data = await self._createTrigger(user, mode=Modes.hypertriggered)
if not data:
await ctx.send("Something went wrong, try again.")
return
await ctx.send(file=discord.File(data, filename=AVATAR_FILE_NAME.format(user)))

async def _createTrigger(self, user: discord.User, mode=Modes.triggered):
"""Fetches the user's avatar, and creates a triggered GIF, applies additional PIL image transformations based on specified mode
Parameters:
-----------
user: discord.User
mode: Modes
Returns:
--------
An io.BytesIO object containing the data for the generated trigger image
"""
avatarData: bytes

avatar = user.display_avatar.with_size(512)
avatarData = await avatar.read()

if not avatarData:
self.logger.error("No avatar data received!")
return

with Image.open(io.BytesIO(avatarData)) as avatar:

if not avatar:
return

offsets = [(15, 15), (5, 10), (-15, -15), (10, -10), (10, 0), (-15, 10), (10, -5)]
images = []

# if hyper mode is set
if mode == Modes.reallytriggered:
red_overlay = Image.new(mode="RGBA", size=avatar.size, color=(255, 0, 0, 255))
mask = Image.new(mode="RGBA", size=avatar.size, color=(255, 255, 255, 127))
avatar = Image.composite(avatar, red_overlay, mask)

elif mode == Modes.hypertriggered:
avatar = ImageEnhance.Color(avatar).enhance(5)
avatar = ImageEnhance.Sharpness(avatar).enhance(24)
avatar = ImageEnhance.Contrast(avatar).enhance(4)

for xcoord, ycoord in offsets:
image = ImageChops.offset(avatar, xcoord, ycoord)
image = ImageOps.crop(image, 15)
images.append(image)
avatar = ImageOps.crop(avatar, 15)

result = io.BytesIO()
avatar.save(
result, format="GIF", append_images=images, save_all=True, duration=25, loop=0
)

# IMPORTANT: rewind to beginning of the stream before returning
result.seek(0)

return result

0 comments on commit d7cbb4b

Please sign in to comment.