async def main(): bot = SkyComicBot(command_prefix=commands.when_mentioned_or("$", "!"), help_command=None, case_insensitive=True, intents=discord.Intents.all()) now = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') utils.ensure_dir(utils.abs_join("logs")) logging.setLoggerClass(utils.DBLogger) log_handlers = [ logging.FileHandler(utils.abs_join("logs", f"{now}.log")), logging.StreamHandler() ] if "logging" in bot.config and "socket_handlers" in bot.config["logging"]: for num, handler in enumerate(bot.config["logging"]["socket_handlers"], start=1): buffer = utils.abs_join("logs", f"log_buffer_{num}.bin") socket_handler = BufferingSocketHandler(handler["host"], handler["port"], buffer) socket_handler.closeOnError = True log_handlers.append(socket_handler) # noinspection PyArgumentList logging.basicConfig( level=logging.DEBUG, format="[%(asctime)s] [%(levelname)-9.9s]-[%(name)-15.15s]: %(message)s", handlers=log_handlers ) logging.getLogger("tortoise").setLevel(logging.INFO) logging.getLogger("db_client").setLevel(logging.INFO) logging.getLogger("aiomysql").setLevel(logging.INFO) logging.getLogger("discord.client").setLevel(logging.CRITICAL) logging.getLogger("discord.gateway").setLevel(logging.ERROR) logging.getLogger("discord.http").setLevel(logging.ERROR) logging.getLogger("PIL").setLevel(logging.ERROR) logging.getLogger("cogs.automod").setLevel(logging.INFO) initial_extensions = ["cogs.errors", "cogs.permissions", "cogs.service", "cogs.channels", "cogs.greetings", "cogs.automod", "cogs.timezones", "cogs.reactions", "cogs.emotes", "cogs.roles", "cogs.games"] bot.load_initial_extensions(initial_extensions) models = ["cogs.permissions", "cogs.roles", "cogs.timezones", "cogs.channels", "cogs.automod", "cogs.greetings", ] # "cogs.comics", current_models = [model for model in bot.extensions if model in models] try: await Tortoise.init(db_url=bot.config["auth"]["db_url"], modules={"models": current_models}) await Tortoise.generate_schemas() await bot.start() finally: await bot.logout() await Tortoise.close_connections()
def load_config(self, path): with open(utils.abs_join(path), "r") as f: self.config = json.load(f) self.owner_ids = set(self.config["discord"]["owner_ids"]) utils.guild_ids = self.config["discord"]["guild_ids"] self.token = self.config["auth"]["discord_token"]
async def load_emotes(self): files = multi_glob(*(abs_join("emotes", f"*{ext}") for ext in image_exts)) self.emotes = { os.path.splitext(os.path.split(filename)[1])[0].replace( "_", " ").strip().lower(): filename for filename in files } self.emote_pick.options[0]["choices"] = [ create_choice(name=key, value=key) for key in self.emotes.keys() ][:25] if self.emotes: self.generate_thumbnail_image() self.has_thumbnail = True else: if os.path.exists(self.emotes_thumbnail): os.remove(self.emotes_thumbnail) self.has_thumbnail = False logger.debug(f"Loaded emotes: {self.emotes}") if not self._first_ready: await self.bot.slash.sync_all_commands()
def __init__(self, bot): utils.StartupCog.__init__(self) utils.AutoLogCog.__init__(self, logger) self.bot = bot self.activity_file_path = utils.abs_join("last_activity") self._last_greeted_member = None self._started_at = None self._last_active_at = None
def __init__(self, bot): utils.AutoLogCog.__init__(self, logger) utils.StartupCog.__init__(self) self.bot = bot self.emotes = dict() self.emotes_thumbnail = abs_join("emotes", "tmp", "thumbnail.png") self.has_thumbnail = False utils.ensure_path_dirs(self.emotes_thumbnail)
async def emote_add(self, ctx: SlashContext, name: str, attachment_link: str): """Adds new emote. Will replace old emote with same name.""" await ctx.defer(hidden=False) logger.important( f"{self.format_caller(ctx)} trying to add emote '{name}' (image link - {attachment_link})" ) ext = Path(urlparse(attachment_link).path).suffix if ext not in image_exts: logger.error(f"Unsupported image extension '{ext}'") raise commands.BadArgument( f"File extension ({ext}) should be one of ({', '.join(image_exts)})" ) if not re.fullmatch("[A-z\\s]+", name): logger.error(f"Unsupported image name '{name}'") raise commands.BadArgument( "Emote name should contain only english letters, whitespaces and underscores!" ) filename = f"{name.strip().replace(' ', '_')}{ext}" # Create directory for emotes, if it not exists, attachment.save won't do it utils.ensure_dir(os.path.abspath("emotes")) async with aiohttp.ClientSession() as session: async with session.get(attachment_link) as response: if response.ok: attachment = await response.read() with open(abs_join("emotes", filename), 'wb') as f: f.write(attachment) logger.important(f"Saved emote '{name}' as '{filename}'") await self.load_emotes() await ctx.send( f"Successfully added emote **{fuzzy_search(filename, self.emotes.keys())}**." )
def generate_thumbnail_image(self): logger.debug("Constructing thumbnail mosaic image...") frame_width = 1920 images_per_row = min(6, len(self.emotes)) padding = 15 v_padding = 100 max_width = (frame_width - (images_per_row - 1) * padding) / images_per_row images = {name: Image.open(path) for name, path in self.emotes.items()} images = { k: v for k, v in sorted(images.items(), key=lambda x: (x[1].width / x[1].height, x[0])) } image_rows = [ dict(row) for row in grouper(images_per_row, images.items()) ] row_heights = [] for row_num, row in enumerate(image_rows): max_height = 0 for col_num, item in enumerate(row.items()): name, image = item scale = image.width / max_width new_width = ceil(image.width / scale) new_height = ceil(image.height / scale) image = image.resize((new_width, new_height), Image.ANTIALIAS) row[name] = image max_height = max(max_height, image.height) row_heights.append(max_height) total_height = sum(row_heights) + (padding + v_padding) * len(image_rows) canvas = Image.new('RGBA', (frame_width, total_height)) draw = ImageDraw.Draw(canvas) font = ImageFont.truetype(utils.abs_join("v_ComicGeek_v1.0.ttf"), size=48) y = 0 for row_num, row in enumerate(image_rows): for col_num, item in enumerate(row.items()): name, image = item x = (padding + max_width) * col_num width_diff = (max_width - image.width) / 2 height_diff = (row_heights[row_num] - image.height) / 2 x_p = ceil(x + width_diff) y_p = ceil(y + height_diff) canvas.paste(image, (x_p, y_p)) # draw.rectangle([(x_p, y_p), (x_p+image.width, y_p+image.height)], outline=(255, 0, 0, 255), width=5) # draw.rectangle([(x, y), (x+max_width, y+row_heights[row_num])], outline=(0, 255, 0, 255), width=5) if Path(self.emotes[name]).suffix == ".gif": name += " [GIF]" text = "\n".join(text_to_lines(name, max_width, draw, font)) draw.text((ceil(x_p + max_width / 2), y + row_heights[row_num] + padding), text, anchor="ma", align="center", font=font) y += row_heights[row_num] + padding + v_padding logger.info("Constructed thumbnail mosaic image") with open(self.emotes_thumbnail, "wb") as image_file: canvas.save(image_file, "PNG") logger.debug("Saved thumbnail mosaic image")
async def timezones(self, message): await send_file(message.channel, abs_join("reactions", "timezones.gif"), "timezones.gif")
async def wrong_layer(self, message): await send_file(message.channel, abs_join("reactions", "wrong_layer.gif"), "wronglayersong.gif")
async def telling(self, message): await send_file(message.channel, abs_join("reactions", "telling.gif"), "thatwouldbetelling.gif")