Exemplo n.º 1
0
async def on_command_error(ctx, error):
    if isinstance(error, commands.CommandOnCooldown):
        await ctx.send(getlang(ctx.guild.id, "bot.error.command_on_cooldown").format(error.retry_after))
        return
    if isinstance(error, commands.CommandNotFound):
        # await ctx.send(getlang(ctx.guild.id, "bot.error.command_not_found").format(get_prefix(bot, ctx.message)))
        return
    if isinstance(error, commands.MissingRequiredArgument):
        pages = bot.formatter.format_help_for(ctx, ctx.command)
        print(pages)
        for p in pages:
            await ctx.send(p)
        return
    if isinstance(error, commands.BadArgument):
        pages = bot.formatter.format_help_for(ctx, ctx.command)
        for p in pages:
            await ctx.send(p)
        return
    if isinstance(error, NoPermission):
        await ctx.send(getlang(ctx.guild.id, "bot.error.no_permission"))
        return
    if isinstance(error, commands.NoPrivateMessage):
        await ctx.send(getlang(ctx.guild.id, "bot.error.no_private_message"))
        return
    if isinstance(error, commands.CommandInvokeError) and isinstance(error.original, discord.HTTPException) \
            and error.original.code == 50013:
        return
    cname = ctx.command.qualified_name if ctx.command is not None else "None"
    await channel_logger.log_to_channel("An error occurred while executing command `{0}` in server **{1.name}** "
                                        "(ID: `{1.id}`):".format(cname, ctx.guild))
    await channel_logger.log_to_channel("```{}```".format(error))
    log.error("An error occurred while executing command {}: {}\n{}"
              .format(cname, error, ''.join(traceback.format_exception(None, error, error.__traceback__))))
    await ctx.send(getlang(ctx.guild.id, "bot.error.unhandled_command_error"))
Exemplo n.º 2
0
 async def parse_diff(ctx, coords):
     if len(ctx.message.attachments) < 1:
         await ctx.send(
             getlang(ctx.guild.id, "bot.error.missing_attachment"))
         return
     att = ctx.message.attachments[0]
     if att.filename[-4:].lower() != ".png":
         if att.filename[-4:].lower() == ".jpg" or att.filename[-5:].lower(
         ) == ".jpeg":
             await ctx.send(getlang(ctx.guild.id, "bot.error.jpeg"))
             return
         await ctx.send(getlang(ctx.guild.id, "bot.error.no_png"))
         return
     if att.width is None or att.height is None:
         await ctx.send(
             getlang(ctx.guild.id, "bot.error.bad_png").format(
                 sql.get_guild_prefix(ctx.guild.id),
                 getlang(ctx.guild.id, "command.quantize")))
         return
     m = re.search('\(?(-?\d+), ?(-?\d+)\)?\s?#?(\d+)?', coords)
     if m is not None:
         x = int(m.group(1))
         y = int(m.group(2))
         zoom = int(m.group(3)) if m.group(3) is not None else 1
         zoom = max(1, min(zoom, 400 // att.width, 400 // att.height))
         return ctx, x, y, att, zoom
Exemplo n.º 3
0
async def diff(ctx, x, y, att, zoom, fetch, palette):
    async with ctx.typing():
        with io.BytesIO() as bio:
            await att.save(bio)
            template = Image.open(bio).convert('RGBA')

        log.debug("X:{0} | Y:{1} | Dim: {2}x{3} | Zoom: {4}".format(
            x, y, template.width, template.height, zoom))

        if template.width * template.height > 600000:
            await ctx.send(getlang(ctx.guild.id, "render.large_template"))

        diff_img = await fetch(x, y, template.width, template.height)

        tot = 0  # Total non-transparent pixels in template
        err = 0  # Number of errors
        bad = 0  # Number of pixels in the template that are not in the color palette

        for py in range(template.height):
            for px in range(template.width):
                tp = template.getpixel((px, py))
                dp = diff_img.getpixel((px, py))
                if tp[3] is not 0:
                    # All non-transparent pixels count to the total
                    tot += 1
                if 0 < tp[3] < 255 or (tp[3] is 255 and tp[:3] not in palette):
                    # All non-opaque and non-transparent pixels, and opaque pixels of bad colors, are bad
                    pixel = (0, 0, 255, 255)
                    err += 1
                    bad += 1
                elif tp[3] is 255 and (tp[0] is not dp[0] or tp[1] is not dp[1]
                                       or tp[2] is not dp[2]):
                    # All pixels that are valid and opaque but do not match the canvas are wrong
                    pixel = (255, 0, 0, 255)
                    err += 1
                else:
                    # Render all correct/irrelevant pixels in greyscale
                    avg = round(dp[0] * 0.3 + dp[1] * 0.52 + dp[2] * 0.18)
                    pixel = (avg, avg, avg, 255)

                diff_img.putpixel((px, py), pixel)

        if zoom > 1:
            diff_img = diff_img.resize(tuple(zoom * x for x in diff_img.size),
                                       Image.NEAREST)

        if bad > 0:
            content = getlang(ctx.guild.id, "render.diff_bad_color")\
                .format(tot - err, tot, err, bad, 100 * (tot - err) / tot)
        else:
            content = getlang(ctx.guild.id,
                              "render.diff").format(tot - err, tot, err,
                                                    100 * (tot - err) / tot)

        with io.BytesIO() as bio:
            diff_img.save(bio, format="PNG")
            bio.seek(0)
            f = discord.File(bio, "diff.png")
            await ctx.send(content=content, file=f)
Exemplo n.º 4
0
 async def register(self, ctx):
     if not sql.is_user_animote_user(ctx.author.id):
         sql.add_animote_user(ctx.author.id)
         message = getlang(ctx.guild.id, "animotes.member_opt_in")
     else:
         sql.delete_animote_user(ctx.author.id)
         message = getlang(ctx.guild.id, "animotes.member_opt_out")
     await ctx.message.author.send(content=message)
Exemplo n.º 5
0
 async def autoscan(self, ctx):
     if not ctx.author.permissions_in(ctx.channel).administrator:
         raise NoPermission
     if sql.select_guild_by_id(ctx.guild.id)['autoscan'] == 0:
         sql.update_guild(ctx.guild.id, autoscan=1)
         await ctx.send(
             getlang(ctx.guild.id, "configuration.autoscan_enabled"))
     else:
         sql.update_guild(ctx.guild.id, autoscan=0)
         await ctx.send(
             getlang(ctx.guild.id, "configuration.autoscan_disabled"))
Exemplo n.º 6
0
 async def check_attachment(ctx):
     if len(ctx.message.attachments) < 1:
         await ctx.send(
             getlang(ctx.guild.id, "bot.error.missing_attachment"))
         return False
     filename = ctx.message.attachments[0].filename
     if filename[-4:].lower() != ".png":
         if filename[-4:].lower() == ".jpg" or filename[-5:].lower(
         ) == ".jpeg":
             await ctx.send(getlang(ctx.guild.id, "bot.error.jpeg"))
             return False
         await ctx.send(getlang(ctx.guild.id, "bot.error.no_png"))
         return False
     return True
Exemplo n.º 7
0
 async def setdefaultcanvas_pxlsspace(self, ctx):
     if not ctx.author.permissions_in(ctx.channel).administrator:
         raise NoPermission
     sql.update_guild(ctx.guild.id, default_canvas="pxlsspace")
     await ctx.send(
         getlang(ctx.guild.id,
                 "configuration.default_canvas_set").format("Pxls.space"))
Exemplo n.º 8
0
 async def alertchannel_set(self, ctx, channel: TextChannel):
     if not ctx.author.permissions_in(ctx.channel).administrator:
         raise NoPermission
     sql.update_guild(ctx.guild.id, alert_channel=channel.id)
     await ctx.send(
         getlang(ctx.guild.id,
                 "configuration.alert_channel_set").format(channel.mention))
Exemplo n.º 9
0
 async def registerguild(self, ctx):
     if not ctx.author.permissions_in(ctx.channel).manage_emojis:
         raise NoPermission
     if not sql.is_server_emojishare_server(ctx.guild.id):
         sql.update_guild(ctx.guild.id, emojishare=1)
         self.log.info("Guild {0.name} (ID: {0.id}) has opted in to emoji sharing.".format(ctx.guild))
         await self.channel_logger.log_to_channel("Guild **{0.name}** (ID: `{0.id}`) has opted in to emoji sharing."
                                             .format(ctx.guild))
         message = getlang(ctx.guild.id, "animotes.guild_opt_in")
     else:
         sql.update_guild(ctx.guild.id, emojishare=0)
         self.log.info("Guild {0.name} (ID: {0.id}) has opted out of emoji sharing.".format(ctx.guild))
         await self.channel_logger.log_to_channel("Guild **{0.name}** (ID: `{0.id}`) has opted out of emoji sharing."
                                             .format(ctx.guild))
         message = getlang(ctx.guild.id, "animotes.guild_opt_out")
     await ctx.send(message)
Exemplo n.º 10
0
 async def setprefix(self, ctx, prefix):
     if not ctx.author.permissions_in(ctx.channel).administrator:
         raise NoPermission
     if len(prefix) > 10:
         raise commands.BadArgument
     sql.update_guild(ctx.guild.id, prefix=prefix)
     await ctx.send(
         getlang(ctx.guild.id, "configuration.prefix_set").format(prefix))
Exemplo n.º 11
0
    def add_localized_subcommands_to_page(self, max_width, commands):
        gid = self.context.guild.id
        for name, command in commands:
            if name in command.aliases:
                # skip aliases
                continue

            short_doc = getlang(
                gid, "brief." + command.qualified_name.replace(' ', '.'))
            entry = '  {0:<{width}} - {1}'.format(name,
                                                  short_doc,
                                                  width=max_width)
            shortened = self.shorten(entry)
            self._paginator.add_line(shortened)
Exemplo n.º 12
0
async def on_ready():
    log.info("Performing guild check...")
    if sql.get_version() is None:
        sql.init_version(VERSION)
        new_ver_alert = False
    else:
        new_ver_alert = sql.get_version() != VERSION and sql.get_version() is not None
        if new_ver_alert:
            sql.update_version(VERSION)
    for g in bot.guilds:
        log.info("Servicing guild '{0.name}' (ID: {0.id})".format(g))
        row = sql.select_guild_by_id(g.id)
        if row is not None:
            prefix = row['prefix'] if row['prefix'] is not None else cfg.prefix
            if g.name != row['name']:
                await channel_logger.log_to_channel("Guild ID `{0.id}` changed name from **{1}** to **{0.name}** since "
                                                    "last bot start".format(g, row['name']))
                sql.update_guild(g.id, name=g.name)
            alert_channel_id = row['alert_channel'] if row['alert_channel'] is not None else 0
            if new_ver_alert and alert_channel_id is not 0:
                alert_channel = next((x for x in g.channels if x.id == alert_channel_id), None)
                if alert_channel is not None:
                    await alert_channel.send(getlang(g.id, "bot.alert_update").format(VERSION, prefix))
                    log.info("Sent update message to guild {0.name} (ID: {0.id})")
                else:
                    log.info("Could not send update message to guild {0.name} (ID: {0.id}): "
                             "Alert channel could not be found.")
        else:
            await channel_logger.log_to_channel("Joined guild **{0.name}** (ID: `{0.id}`) between sessions at `{1}`"
                                                .format(g, g.me.joined_at.isoformat(' ')))
            sql.add_guild(g.id, g.name, int(g.me.joined_at.timestamp()))
            await print_welcome_message(g)

    db_guilds = sql.get_all_guilds()
    if len(bot.guilds) != len(db_guilds):
        for g in db_guilds:
            if not any(x for x in bot.guilds if x.id == g['id']):
                log.info("Kicked from guild '{0}' (ID: {1}) between sessions".format(g['name'], g['id']))
                await channel_logger.log_to_channel("Kicked from guild **{0}** (ID: `{1}`)".format(g['name'], g['id']))
                sql.delete_guild(g['id'])

    for extension in extensions:
        try:
            bot.load_extension(extension)
        except Exception as e:
            log.error("Failed to load extension {}\n{}: {}".format(extension, type(e).__name__, e))

    print("I am ready!")
    log.info('I am ready!')
    await channel_logger.log_to_channel("I am ready!")
Exemplo n.º 13
0
async def quantize(ctx, att, palette):
    with io.BytesIO() as bio:
        await att.save(bio)
        template = Image.open(bio).convert('RGBA')

    log.debug("Dim: {0}x{1}".format(template.width, template.height))

    bad_pixels = template.height * template.width
    for py in range(template.height):
        for px in range(template.width):
            pix = template.getpixel((px, py))

            if pix[3] == 0:  # Ignore fully transparent pixels
                bad_pixels -= 1
                continue
            if pix[3] < 30:  # Make barely visible pixels transparent
                template.putpixel((px, py), (0, 0, 0, 0))
                continue

            dist = 450
            best_fit = (0, 0, 0)
            for c in palette:
                if pix[:3] == c:  # If pixel matches exactly, break
                    best_fit = c
                    if pix[3] == 255:  # Pixel is only not bad if it's fully opaque
                        bad_pixels -= 1
                    break
                tmp = sqrt(
                    pow(pix[0] - c[0], 2) + pow(pix[1] - c[1], 2) +
                    pow(pix[2] - c[2], 2))
                if tmp < dist:
                    dist = tmp
                    best_fit = c
            template.putpixel((px, py), best_fit + (255, ))

    with io.BytesIO() as bio:
        template.save(bio, format="PNG")
        bio.seek(0)
        f = discord.File(bio, "template.png")
        await ctx.send(getlang(ctx.guild.id,
                               "render.quantize").format(bad_pixels),
                       file=f)
Exemplo n.º 14
0
async def ping(ctx):
    ping_start = time()
    ping_msg = await ctx.send(getlang(ctx.guild.id, "bot.ping"))
    ping_time = time() - ping_start
    await ping_msg.edit(content=getlang(ctx.guild.id, "bot.pong").format(ping_time))
Exemplo n.º 15
0
 async def setdefaultcanvas(self, ctx):
     await ctx.send(getlang(ctx.guild.id, "bot.error.no_subcommand"))
Exemplo n.º 16
0
async def version(ctx):
    await ctx.send(getlang(ctx.guild.id, "bot.version").format(VERSION))
Exemplo n.º 17
0
 async def alertchannel_clear(self, ctx):
     if not ctx.author.permissions_in(ctx.channel).administrator:
         raise NoPermission
     sql.update_guild(ctx.guild.id, alert_channel=0)
     await ctx.send(
         getlang(ctx.guild.id, "configuration.alert_channel_cleared"))
Exemplo n.º 18
0
async def suggest(ctx, *, suggestion: str):
    await channel_logger.log_to_channel("New suggestion from **{0.name}#{0.discriminator}** (ID: `{0.id}`) in guild "
                                        "**{1.name}** (ID: `{1.id}`):".format(ctx.author, ctx.guild))
    await channel_logger.log_to_channel("> `{}`".format(suggestion))
    await ctx.send(getlang(ctx.guild.id, "bot.suggest"))
Exemplo n.º 19
0
 async def alertchannel(self, ctx):
     await ctx.send(getlang(ctx.guild.id, "bot.error.no_subcommand"))
Exemplo n.º 20
0
async def ditherchart(ctx):
    await ctx.send(getlang(ctx.guild.id, "bot.error.no_subcommand"))
Exemplo n.º 21
0
 def get_localized_ending_note(self):
     gid = self.context.guild.id
     command_name = self.context.invoked_with
     return getlang(gid,
                    "bot.help_ending_note").format(self.clean_prefix,
                                                   command_name)
Exemplo n.º 22
0
 async def diff(self, ctx):
     await ctx.send(getlang(ctx.guild.id, "bot.error.no_subcommand"))
Exemplo n.º 23
0
    def format(self):
        self._paginator = Paginator()
        gid = self.context.guild.id

        if self.is_bot():
            self._paginator.add_line(inspect.cleandoc(
                getlang(gid, "bot.description").format(cfg.name)),
                                     empty=True)
        elif self.is_cog():
            # self._paginator.add_line(getlang(gid, ))
            pass  # TODO: HELP!!
        elif isinstance(self.command, Command):
            self._paginator.add_line(getlang(
                gid, "brief." + self.command.qualified_name.replace(' ', '.')),
                                     empty=True)

            # TODO: Translate signatures
            # <signature portion>
            signature = self.get_command_signature()
            self._paginator.add_line(signature, empty=True)

            # <long doc> section
            long_doc = getlang(
                gid, "help." + self.command.qualified_name.replace(' ', '.'))
            if long_doc:
                self._paginator.add_line(inspect.cleandoc(long_doc),
                                         empty=True)

            # end it here if it's just a regular command
            if not self.has_subcommands():
                self._paginator.close_page()
                return self._paginator.pages

        max_width = self.max_name_size

        def category(tup):
            cog = tup[1].cog_name
            # we insert the zero width space there to give it approximate
            # last place sorting position.
            return cog + ':' if cog is not None else '\u200bNo Category:'

        filtered = yield from self.filter_command_list()
        if self.is_bot():
            data = sorted(filtered, key=category)
            for category, commands in itertools.groupby(data, key=category):
                # there simply is no prettier way of doing this.
                commands = sorted(commands)
                if len(commands) > 0:
                    self._paginator.add_line(category)

                self.add_localized_subcommands_to_page(max_width, commands)
        else:
            filtered = sorted(filtered)
            if filtered:
                self._paginator.add_line('Commands:')
                self.add_localized_subcommands_to_page(max_width, filtered)

        # add the ending note
        self._paginator.add_line()
        ending_note = self.get_localized_ending_note()
        self._paginator.add_line(ending_note)

        return self._paginator.pages
Exemplo n.º 24
0
    async def repeat(self, ctx):
        async for msg in ctx.history(limit=50, before=ctx.message):
            log_msg = "repeated by {0.name}#{0.discriminator} (ID: {0.id}) in {1.name} (ID: {1.id})" \
                .format(ctx.author, ctx.guild)
            regex = ctx.prefix \
                + '(diff|preview) ((?:pixel(?:canvas|zio|zone))|pxlsspace)(?: (-?\d+), ?(-?\d+)/?\s?#?(\d+)?)'
            match = re.match(regex, msg.content)

            if match:
                cmd = match.group(1)
                sub_cmd = match.group(2)
                x = int(match.group(3))
                y = int(match.group(4))
                zoom = int(match.group(5)) if match.group(5) is not None else 1
                if cmd == "diff" and len(
                        msg.attachments
                ) > 0 and msg.attachments[0].filename[-4:].lower() == ".png":
                    att = msg.attachments[0]
                    if att.width is None or att.height is None:
                        await ctx.send(
                            getlang(ctx.guild.id, "bot.error.bad_png").format(
                                sql.get_guild_prefix(ctx.guild.id),
                                getlang(ctx.guild.id, "command.quantize")))
                        return
                    zoom = max(1, min(zoom, 400 // att.width,
                                      400 // att.height))
                    if sub_cmd == "pixelcanvas":
                        log.debug("Pixelcanvas diff " + log_msg)
                        await render.diff(ctx, x, y, att, zoom,
                                          render.fetch_pixelcanvas,
                                          colors.pixelcanvas)
                        return
                    elif sub_cmd == "pixelzio":
                        log.debug("Pixelzio diff " + log_msg)
                        await render.diff(ctx, x, y, att, zoom,
                                          render.fetch_pixelzio,
                                          colors.pixelzio)
                        return
                    elif sub_cmd == "pixelzone":
                        log.debug("Pixelzone diff " + log_msg)
                        await render.diff(ctx, x, y, att, zoom,
                                          render.fetch_pixelzone,
                                          colors.pixelzone)
                        return
                    elif sub_cmd == "pxlsspace":
                        log.debug("Pxlsspace diff " + log_msg)
                        await render.diff(ctx, x, y, att, zoom,
                                          render.fetch_pxlsspace,
                                          colors.pxlsspace)
                        return
                if cmd == "preview":
                    zoom = max(1, min(16, zoom))
                    if sub_cmd == "pixelcanvas":
                        log.debug("Pixelcanvas preview " + log_msg)
                        await render.preview(ctx, x, y, zoom,
                                             render.fetch_pixelcanvas)
                        return
                    elif sub_cmd == "pixelzio":
                        log.debug("Pixelzio preview " + log_msg)
                        await render.preview(ctx, x, y, zoom,
                                             render.fetch_pixelzio)
                        return
                    elif sub_cmd == "pixelzone":
                        log.debug("Pixelzone preview " + log_msg)
                        await render.preview(ctx, x, y, zoom,
                                             render.fetch_pixelzone)
                        return
                    elif sub_cmd == "pxlsspace":
                        log.debug("Pxlsspace preview " + log_msg)
                        await render.preview(ctx, x, y, zoom,
                                             render.fetch_pxlsspace)
                        return

            default_canvas = sql.select_guild_by_id(
                ctx.guild.id)['default_canvas']
            pc_match = re.search(
                '(?:pixelcanvas\.io/)@(-?\d+),(-?\d+)/?(?:\s?#?(\d+))?',
                msg.content)
            pzio_match = re.search(
                '(?:pixelz\.io/)@(-?\d+),(-?\d+)(?:\s?#?(\d+))?', msg.content)
            pzone_match = re.search(
                '(?:pixelzone\.io/)\?p=(-?\d+),(-?\d+)(?:,(\d+))?(?:\s?#?(\d+))?',
                msg.content)
            pxlsp_match = re.search(
                'pxls\.space/#x=(\d+)&y=(\d+)(?:&scale=(\d+))?\s?#?(\d+)?',
                msg.content)
            prev_match = re.search('@\(?(-?\d+), ?(-?\d+)\)?(?: ?#(\d+))?',
                                   msg.content)
            diff_match = re.search('\(?(-?\d+), ?(-?\d+)\)?(?: ?#(\d+))?',
                                   msg.content)

            if pc_match is not None:
                x = int(pc_match.group(1))
                y = int(pc_match.group(2))
                zoom = int(
                    pc_match.group(3)) if pc_match.group(3) is not None else 1
                zoom = max(min(zoom, 16), 1)
                log.debug("Pixelcanvas preview " + log_msg)
                await render.preview(ctx, x, y, zoom, render.fetch_pixelcanvas)
                return

            if pzio_match is not None:
                x = int(pzio_match.group(1))
                y = int(pzio_match.group(2))
                zoom = int(pzio_match.group(3)) if pzio_match.group(
                    3) is not None else 1
                zoom = max(min(zoom, 16), 1)
                log.debug("Pixelzio preview " + log_msg)
                await render.preview(ctx, x, y, zoom, render.fetch_pixelzio)
                return

            if pzone_match is not None:
                x = int(pzone_match.group(1))
                y = int(pzone_match.group(2))
                if pzone_match.group(4) is not None:
                    zoom = int(pzone_match.group(4))
                elif pzone_match.group(3) is not None:
                    zoom = int(pzio_match.group(3))
                else:
                    zoom = 1
                zoom = max(min(zoom, 16), 1)
                log.debug("Pixelzone preview " + log_msg)
                await render.preview(ctx, x, y, zoom, render.fetch_pixelzone)
                return

            if pxlsp_match is not None:
                x = int(pxlsp_match.group(1))
                y = int(pxlsp_match.group(2))
                if pxlsp_match.group(4) is not None:
                    zoom = int(pxlsp_match.group(4))
                elif pxlsp_match.group(3) is not None:
                    zoom = int(pxlsp_match.group(3))
                else:
                    zoom = 1
                zoom = max(min(zoom, 16), 1)
                log.debug("Pxlsspace preview " + log_msg)
                await render.preview(ctx, x, y, zoom, render.fetch_pxlsspace)
                return

            if prev_match is not None:
                x = int(prev_match.group(1))
                y = int(prev_match.group(2))
                zoom = int(prev_match.group(3)) if prev_match.group(
                    3) is not None else 1
                zoom = max(min(zoom, 16), 1)
                if default_canvas == "pixelcanvas":
                    log.debug("Pixelcanvas preview " + log_msg)
                    await render.preview(ctx, x, y, zoom,
                                         render.fetch_pixelcanvas)
                elif default_canvas == "pixelz":
                    log.debug("Pixelzio preview " + log_msg)
                    await render.preview(ctx, x, y, zoom,
                                         render.fetch_pixelzio)
                elif default_canvas == "pixelzone":
                    log.debug("Pixelzone preview " + log_msg)
                    await render.preview(ctx, x, y, zoom,
                                         render.fetch_pixelzone)
                elif default_canvas == "pxlsspace":
                    log.debug("Pxlsspace preview " + log_msg)
                    await render.preview(ctx, x, y, zoom,
                                         render.fetch_pxlsspace)
                return

            if diff_match is not None and len(msg.attachments) > 0 \
                    and msg.attachments[0].filename[-4:].lower() == ".png":
                att = msg.attachments[0]
                if att.width is None or att.height is None:
                    await ctx.send(
                        getlang(ctx.guild.id, "bot.error.bad_png").format(
                            sql.get_guild_prefix(ctx.guild.id),
                            getlang(ctx.guild.id, "command.quantize")))
                    return
                x = int(diff_match.group(1))
                y = int(diff_match.group(2))
                zoom = int(diff_match.group(3)) if diff_match.group(
                    3) is not None else 1
                zoom = max(1, min(zoom, 400 // att.width, 400 // att.height))
                if default_canvas == "pixelcanvas":
                    log.debug("Pixelcanvas diff " + log_msg)
                    await render.diff(ctx, x, y, att, zoom,
                                      render.fetch_pixelcanvas,
                                      colors.pixelcanvas)
                elif default_canvas == "pixelz":
                    log.debug("Pixelzio diff " + log_msg)
                    await render.diff(ctx, x, y, att, zoom,
                                      render.fetch_pixelzio, colors.pixelzio)
                elif default_canvas == "pixelzone":
                    log.debug("Pixelzone diff " + log_msg)
                    await render.diff(ctx, x, y, att, zoom,
                                      render.fetch_pixelzone, colors.pixelzone)
                elif default_canvas == "pxlsspace":
                    log.debug("Pxlsspace diff " + log_msg)
                    await render.diff(ctx, x, y, att, zoom,
                                      render.fetch_pxlsspace, colors.pxlsspace)
                return

            ctx.send(getlang(ctx.guild.id, "render.repeat_not_found"))