Skip to content

Commit

Permalink
[QRChecker] Cog model restructure
Browse files Browse the repository at this point in the history
- Restructure `QRChecker` according to SFUAnime#608.
  No functional changes.
- Move constants to `constants.py`.
- Remove redundant type hints (e.g., those fully inferrable from the
  return types of called functions).
  • Loading branch information
quachtridat committed Feb 4, 2023
1 parent c634c8c commit de6dcb6
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 152 deletions.
16 changes: 16 additions & 0 deletions cogs/qrchecker/commandHandlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from redbot.core import commands
from redbot.core.commands import Context

from .commandsCore import CommandsCore


class CommandHandlers(CommandsCore):
@commands.group(name="qrchecker")
@commands.guild_only()
@commands.admin_or_permissions(manage_guild=True)
async def _grpQrChecker(self, ctx: Context):
"""Configure QR code checker"""

@_grpQrChecker.command(name="toggle")
async def _cmdQrCheckerToggle(self, ctx: Context):
await self.cmdQrCheckerToggle(ctx=ctx)
21 changes: 21 additions & 0 deletions cogs/qrchecker/commandsCore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from redbot.core.commands import Context

from .constants import KEY_ENABLED
from .core import Core


class CommandsCore(Core):
async def cmdQrCheckerToggle(self, ctx: Context):
"""Toggle QR code checking"""
guild = ctx.guild
if not guild:
return
guildConfig = self.config.guild(guild)

enabled: bool = await guildConfig.get_attr(KEY_ENABLED)()
if enabled:
await guildConfig.get_attr(KEY_ENABLED).set(False)
await ctx.send("QR code checking is now **disabled** for this guild.")
else:
await guildConfig.get_attr(KEY_ENABLED).set(True)
await ctx.send("QR code checking is now **enabled** for this guild.")
3 changes: 3 additions & 0 deletions cogs/qrchecker/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
KEY_ENABLED = "enabled"

BASE_GUILD = {KEY_ENABLED: False}
14 changes: 14 additions & 0 deletions cogs/qrchecker/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from logging import getLogger

from redbot.core import Config
from redbot.core.bot import Red

from .constants import BASE_GUILD


class Core:
def __init__(self, bot: Red):
self.bot = bot
self.logger = getLogger("red.luicogs.QRChecker")
self.config = Config.get_conf(self, identifier=5842647, force_registration=True)
self.config.register_guild(**BASE_GUILD)
12 changes: 12 additions & 0 deletions cogs/qrchecker/eventHandlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from discord import Message

from redbot.core import commands

from .eventsCore import EventsCore


class EventHandlers(EventsCore):
@commands.Cog.listener("on_message")
async def _evtListener(self, message: Message):
"""Find QR code in message attachments"""
await self.evtListener(message=message)
121 changes: 121 additions & 0 deletions cogs/qrchecker/eventsCore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from io import BytesIO
from typing import List

from discord import AllowedMentions, Message
from PIL import Image
from pyzbar.pyzbar import Decoded, decode, ZBarSymbol


from redbot.core.commands import Context
from redbot.core.utils.chat_formatting import box, pagify

from .constants import KEY_ENABLED
from .core import Core


class EventsCore(Core):
async def evtListener(self, message: Message):
"""Find QR code in message attachments"""
if not message.guild:
return
# check if enabled
if not await self.config.guild(message.guild).get_attr(KEY_ENABLED)():
self.logger.debug(
"QR Checker disabled for %s (%s); return early",
message.guild.name,
message.guild.id,
)
return
if not message.attachments:
self.logger.debug("No attachments, return early")
return
for attachment in message.attachments:
contentType = attachment.content_type
if not contentType:
self.logger.debug("Unknown content type, continue")
continue
elif contentType and "image" not in contentType:
self.logger.debug("Not an image, continue")
continue

# At this point we decern that it's an image.
try:
fp = BytesIO(await attachment.read())
image = Image.open(fp)
codes: List[Decoded] = decode(image, symbols=[ZBarSymbol.QRCODE])
self.logger.debug("Found %s codes", len(codes))
except Exception:
self.logger.error("Couldn't check file.", exc_info=True)
return

if not codes:
self.logger.debug("No QR codes found.")
return

self.logger.info(
"%s#%s (%s) posted some QR code(s) in #%s (%s)",
message.author.name,
message.author.discriminator,
message.author.id,
message.channel.name,
message.channel.id,
)

numQrCodes = len(codes)
if numQrCodes == 1:
code = codes[0]
data: str = code.data.decode()
if len(data) == 0:
self.logger.debug("No data in QR code.")
return
if len(data) > 1900:
contents = f"{data[:1900]}..."
else:
contents = data
msg = (
f"Found a QR code from {message.author.mention}, "
f"the contents are: {box(contents)}"
)
await message.reply(
msg, mention_author=False, allowed_mentions=AllowedMentions.none()
)
else:
hasData: bool = False
pages: List[str] = []
pages.append(
f"Found several QR codes from {message.author.mention}, their contents are:"
)
for code in codes:
data: str = code.data.decode()
if len(data) == 0:
self.logger.debug("No data in QR code.")
continue
if len(data) > 1990:
contents = f"{box(data[:1990])}..."
else:
contents = f"{box(data)}"
pages.append(contents)
hasData |= True

if not hasData:
self.logger.debug("No data in %s QR codes.", numQrCodes)
return

firstMessage: bool = True
sentMessages: int = 0

ctx: Context = await self.bot.get_context(message)
for textToSend in pagify("\n".join(pages), escape_mass_mentions=True):
if firstMessage:
await message.reply(
textToSend,
mention_author=False,
allowed_mentions=AllowedMentions.none(),
)
firstMessage = False
elif sentMessages > 10:
self.logger.debug("Sent more than 10 messages, bail early")
break
else:
await ctx.send(textToSend, allowed_mentions=AllowedMentions.none())
sentMessages += 1
156 changes: 4 additions & 152 deletions cogs/qrchecker/qrchecker.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,160 +4,12 @@
- pyzbar from PyPI
- libzbar0 from your distro's package repo
"""
from io import BytesIO
from typing import Dict, List, Optional
from logging import Logger, getLogger

from discord import AllowedMentions, Guild, Message
from pyzbar.pyzbar import Decoded, decode, ZBarSymbol
from PIL import Image
from redbot.core import commands, Config
from redbot.core.bot import Red
from redbot.core.commands import Context
from redbot.core.config import Group
from redbot.core.utils.chat_formatting import box, pagify
from redbot.core import commands

KEY_ENABLED: str = "enabled"
from .commandHandlers import CommandHandlers
from .eventHandlers import EventHandlers

BASE_GUILD: Dict = {KEY_ENABLED: False}


class QRChecker(commands.Cog):
class QRChecker(commands.Cog, CommandHandlers, EventHandlers):
"""A QR code checker for attachments"""

def __init__(self, bot: Red):
self.bot: Red = bot
self.logger: Logger = getLogger("red.luicogs.QRChecker")
self.config: Config = Config.get_conf(self, identifier=5842647, force_registration=True)
self.config.register_guild(**BASE_GUILD)

@commands.Cog.listener("on_message")
async def listener(self, message: Message):
"""Find QR code in message attachments"""
if not message.guild:
return
# check if enabled
if not await self.config.guild(message.guild).get_attr(KEY_ENABLED)():
self.logger.debug(
"QR Checker disabled for %s (%s); return early",
message.guild.name,
message.guild.id,
)
return
if not message.attachments:
self.logger.debug("No attachments, return early")
return
for attachment in message.attachments:
contentType: Optional[str] = attachment.content_type
if not contentType:
self.logger.debug("Unknown content type, continue")
continue
elif contentType and "image" not in contentType:
self.logger.debug("Not an image, continue")
continue

# At this point we decern that it's an image.
try:
fp: BytesIO = BytesIO(await attachment.read())
image: Image = Image.open(fp)
codes: List[Decoded] = decode(image, symbols=[ZBarSymbol.QRCODE])
self.logger.debug("Found %s codes", len(codes))
except Exception:
self.logger.error("Couldn't check file.", exc_info=True)
return

if not codes:
self.logger.debug("No QR codes found.")
return

self.logger.info(
"%s#%s (%s) posted some QR code(s) in #%s (%s)",
message.author.name,
message.author.discriminator,
message.author.id,
message.channel.name,
message.channel.id,
)

numQrCodes = len(codes)
if numQrCodes == 1:
code: Decoded = codes[0]
data: str = code.data.decode()
if len(data) == 0:
self.logger.debug("No data in QR code.")
return
if len(data) > 1900:
contents: str = f"{data[:1900]}..."
else:
contents: str = data
msg = (
f"Found a QR code from {message.author.mention}, "
f"the contents are: {box(contents)}"
)
await message.reply(
msg, mention_author=False, allowed_mentions=AllowedMentions.none()
)
else:
hasData: bool = False
pages: List[str] = []
pages.append(
f"Found several QR codes from {message.author.mention}, their contents are:"
)
for code in codes:
data: str = code.data.decode()
if len(data) == 0:
self.logger.debug("No data in QR code.")
continue
if len(data) > 1990:
contents: str = f"{box(data[:1990])}..."
else:
contents: str = f"{box(data)}"
pages.append(contents)
hasData |= True

if not hasData:
self.logger.debug("No data in %s QR codes.", numQrCodes)
return

firstMessage: bool = True
sentMessages: int = 0

ctx: Context = await self.bot.get_context(message)
for textToSend in pagify("\n".join(pages), escape_mass_mentions=True):
if firstMessage:
await message.reply(
textToSend,
mention_author=False,
allowed_mentions=AllowedMentions.none(),
)
firstMessage = False
elif sentMessages > 10:
self.logger.debug("Sent more than 10 messages, bail early")
break
else:
await ctx.send(textToSend, allowed_mentions=AllowedMentions.none())
sentMessages += 1

@commands.group(name="qrchecker")
@commands.guild_only()
@commands.admin_or_permissions(manage_guild=True)
async def qrchecker(self, ctx: Context):
"""Configure QR code checker"""
pass

# toggle command
@qrchecker.command(name="toggle")
async def qrcheckerToggle(self, ctx: Context):
"""Toggle QR code checking"""
guild: Guild = ctx.guild
if not guild:
return
guildConfig: Group = self.config.guild(guild)

enabled: bool = await guildConfig.get_attr(KEY_ENABLED)()
if enabled:
await guildConfig.get_attr(KEY_ENABLED).set(False)
await ctx.send("QR code checking is now **disabled** for this guild.")
else:
await guildConfig.get_attr(KEY_ENABLED).set(True)
await ctx.send("QR code checking is now **enabled** for this guild.")

0 comments on commit de6dcb6

Please sign in to comment.