Merge pull request #3 from official-Cromatin/release-v0.5

Merge branch 'release-v0.5' into 'main'
This commit was merged in pull request #3.
This commit is contained in:
Cromatin
2025-09-16 18:49:29 +02:00
committed by GitHub
12 changed files with 268 additions and 300 deletions
+27
View File
@@ -0,0 +1,27 @@
**/__pycache__
**/.venv
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
+35
View File
@@ -0,0 +1,35 @@
name: Release Multi-Arch Docker Image
on:
release:
types: [published]
jobs:
build-and-push-docker:
runs-on: self-hosted
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push multi-arch image
uses: docker/build-push-action@v5
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: |
ghcr.io/${{ github.repository }}:${{ github.event.release.tag_name }}
ghcr.io/${{ github.repository }}:latest
-1
View File
@@ -4,7 +4,6 @@ config/*.ini
pastebin.txt pastebin.txt
.DS_Store .DS_Store
test*.py test*.py
.dockerignore
docker-* docker-*
.vscode .vscode
+22
View File
@@ -0,0 +1,22 @@
# Base image (multi-arch via buildx)
FROM python:3.10-slim
# Prevents pyc files + enables unbuffered logging
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
# Install dependencies
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy only necessary files (via .dockerignore)
COPY . .
# Create non-root user
RUN adduser --disabled-password --no-create-home --gecos "" appuser \
&& chown -R appuser /app
USER appuser
# Default command
ENTRYPOINT ["python", "src/main.py"]
+4 -2
View File
@@ -1,3 +1,5 @@
discord.py==2.4.0 discord.py==2.4.0
praw>=7.8.0 asyncpraw==7.8.1
colorama==0.4.6 colorama==0.4.6
aiohttp==3.11.11
pillow==11.1.0
-33
View File
@@ -1,33 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
from cogs.base_cog import Base_Cog
from cogs.maintenance import Maintenance_Command
import logging
from utils.portal import Portal
class About_Command(Base_Cog):
def __init__(self, bot:commands.Bot):
self.__bot = bot
super().__init__(logging.getLogger("cmds.about"))
@app_commands.command(name = "about", description = "Provides general information about the bot")
@app_commands.check(Maintenance_Command.handle_check)
async def about(self, ctx: discord.Interaction):
portal:Portal = Portal.instance()
embed = discord.Embed(title=f"POST IT - `{portal.PROGRAM_VERSION}`",
description="### Übersicht der Befehle\n- `/post` Veröffentlicht einen Post einer anderen Plattform, mit beliebigen Bildern als Anhang\n- `/help` Zeigt diese Übersicht an\n- `/settings` Persönliche Einstellungen für das verhalten des Bots\n- `/stats` Zeigt persönliche und globale Statistiken\n- `/debug` Erweiterte Informationen zum Betriebszustandes des Bots",
colour=0x5a5a5a)
embed.add_field(name="Status des Bots",
value="Fehlerfrei :green_circle:",
inline=True)
embed.add_field(name="Besitzer der Bot Instanz",
value=f"<@{portal.bot_config['DISCORD']['OWNER_ID']}>",
inline=True)
await ctx.response.send_message(embed = embed, ephemeral = True)
async def setup(bot:commands.Bot):
await bot.add_cog(About_Command(bot))
-2
View File
@@ -2,7 +2,6 @@ import discord
from discord import app_commands from discord import app_commands
from discord.ext import commands from discord.ext import commands
from cogs.base_cog import Base_Cog from cogs.base_cog import Base_Cog
from cogs.maintenance import Maintenance_Command
import logging import logging
from utils.portal import Portal from utils.portal import Portal
@@ -16,7 +15,6 @@ class Debug_Command(Base_Cog):
super().__init__(logging.getLogger("cmds.debug")) super().__init__(logging.getLogger("cmds.debug"))
@app_commands.command(name = "debug", description = "Provides debug informations about the bot, useful for troubleshooting") @app_commands.command(name = "debug", description = "Provides debug informations about the bot, useful for troubleshooting")
@app_commands.check(Maintenance_Command.handle_check)
@log_command_execution @log_command_execution
async def debug(self, ctx:discord.Interaction): async def debug(self, ctx:discord.Interaction):
portal:Portal = Portal.instance() portal:Portal = Portal.instance()
-61
View File
@@ -1,61 +0,0 @@
import discord
from discord.ext import commands
from discord import app_commands
from cogs.base_cog import Base_Cog
import logging
class Maintenance_Command(Base_Cog):
def __init__(self, bot):
self.__bot = bot
self.__maintenance_mode_enabled = False
super().__init__(logging.getLogger("cmds.maintenance"))
def enable_global_maintenance(self):
self.__maintenance_mode_enabled = True
def disable_gloabal_maintenance(self):
self.__maintenance_mode_enabled = False
@staticmethod
async def handle_check(ctx: discord.Integration):
"""Check function called for every command"""
instance = ctx.client.get_cog("Maintenance_Command")
if instance and instance.__maintenance_mode_enabled:
await instance.startup(ctx)
return False
return True
async def startup(self, ctx: discord.Interaction):
"""Returns an message, explaining that the bot is still starting up"""
embed = discord.Embed(
title = ":construction: Bot is still starting up! :construction:",
description = "While the bot is starting, all commands are locked and unable to be executed",
color = 0xED4337)
await ctx.response.send_message(embed = embed)
@commands.Cog.listener()
async def on_command_error(self, ctx, error):
if isinstance(error, app_commands.CheckFailure):
# Suppress the traceback and inform the user about maintenance mode
return # Already handled in the `startup` method
cmds_group = app_commands.Group(name="maintenance", description="Put the bot into maintenance mode and control its alternative behaviour")
@cmds_group.command(name = "enable", description = "Enables the maintenance mode globally")
async def maintenance_enable(self, ctx: discord.Interaction):
if self.__maintenance_mode_enabled:
await ctx.response.send_message("Maintenance mode was allready enabled", ephemeral = True)
else:
await ctx.response.send_message("Maintenance mode is now enabled", ephemeral = True)
self.__maintenance_mode_enabled = True
async def maintenance_disable(self, ctx: discord.Interaction):
if self.__maintenance_mode_enabled:
await ctx.response.send_message("Maintenance mode is now disabled", ephemeral = True)
else:
await ctx.response.send_message("Maintenance mode was allready disabled", ephemeral = True)
self.__maintenance_mode_enabled = False
async def setup(bot:commands.Bot):
await bot.add_cog(Maintenance_Command(bot))
+153 -48
View File
@@ -2,12 +2,19 @@ import discord
from discord import app_commands from discord import app_commands
from discord.ext import commands from discord.ext import commands
from cogs.base_cog import Base_Cog from cogs.base_cog import Base_Cog
from cogs.maintenance import Maintenance_Command
import logging import logging
from urllib.parse import urlparse from urllib.parse import urlparse
from praw.models import Submission, Subreddit from asyncpraw.models import Submission
from utils.portal import Portal from utils.portal import Portal
import aiohttp
from PIL import Image
from io import BytesIO
from datetime import datetime
from utils.datetime_tools import get_elapsed_time_milliseconds
class NoMediaFound(Exception):
pass
class Post_Command(Base_Cog): class Post_Command(Base_Cog):
def __init__(self, bot:commands.Bot): def __init__(self, bot:commands.Bot):
@@ -15,58 +22,156 @@ class Post_Command(Base_Cog):
super().__init__(logging.getLogger("cmds.post")) super().__init__(logging.getLogger("cmds.post"))
@app_commands.command(name = "post", description = "Post an embed in the Current Channel with a link to the content") @app_commands.command(name = "post", description = "Post an embed in the Current Channel with a link to the content")
@app_commands.check(Maintenance_Command.handle_check) @app_commands.describe(url = "URL to the post", custom_note = "Describe the post with your own note", use_title = "Display the title of the post", quality = "Specifies the quality of the converted image, closer to 100 is better")
async def post(self, ctx:discord.Interaction, url:str): @app_commands.choices(quality = [
domain_info = urlparse(url) app_commands.Choice(name = "Poor (60)", value = 60),
portal = Portal.instance() app_commands.Choice(name = "Fair (70)", value = 70),
toplevel_domain = '.'.join(domain_info.netloc.split('.')[-2:]) app_commands.Choice(name = "Good (80)", value = 80),
match toplevel_domain: app_commands.Choice(name = "Very Good (85)", value = 85),
case "reddit.com": app_commands.Choice(name = "Excellent (90)", value = 90),
subm:Submission = portal.reddit_adapter.fetch(url) app_commands.Choice(name = "Superior (95)", value = 95),
embeds = [] app_commands.Choice(name = "Perfect (100)", value = 100)
])
async def post(self, ctx:discord.Interaction, url:str, custom_note:str = None, use_title:bool = True, quality:app_commands.Choice[int] = 95):
try:
domain_info = urlparse(url)
portal = Portal.instance()
toplevel_domain = '.'.join(domain_info.netloc.split('.')[-2:])
begin_process = datetime.now().timestamp()
match toplevel_domain:
case "reddit.com":
self._logger.debug(f"Recieved command by {ctx.user} ({ctx.user.id}) for reddit ({url})")
image_urls = [] subm:Submission = await portal.reddit_adapter.fetch(url)
# Check if submission has a gallery image_urls = []
if hasattr(subm, "media_metadata"): # Check if submission has a gallery
for media_id, media in subm.media_metadata.items(): if hasattr(subm, "media_metadata"):
file_extension = media["m"].split("/")[1] for media_id, media in subm.media_metadata.items():
image_urls.append(f"https://i.redd.it/{media_id}.{file_extension}") file_extension = media["m"].split("/")[1]
else: if file_extension not in ("jpg", "jpeg", "png", "webp", "heic", "heif"):
image_urls.append(subm.url) continue
self._logger.debug(f"Found {len(image_urls)} image urls for the post") image_urls.append(f"https://i.redd.it/{media_id}.{file_extension}")
else:
image_urls.append(subm.url)
image_count = len(image_urls)
if image_count == 0:
raise NoMediaFound
self._logger.debug(f"Found {image_count} image urls for the post")
embed = discord.Embed(url = "https://discord.com/humans.txt", color = int(portal.platforms_config["REDDIT"]["embed_color"], 16)) progress_title = f"`{image_count}` images are going to be converted, it may take a while."
embed.set_author(name = subm.title) progress_temp = progress_title + f"\n`0` of `{image_count}` have already been loaded"
embeds.append(embed) await ctx.response.send_message(progress_temp, ephemeral = True)
embed.add_field( if isinstance(quality, app_commands.Choice):
name = "Author", quality_value = quality.value
value = f"[u/{subm.author}](https://www.reddit.com/user/{subm.author})", else:
inline = True) quality_value = quality
embed.add_field(
name = "Subreddit", # Download and convert each image
value = f"[r/{subm.subreddit.display_name}]({url})", image_files:list[discord.File] = []
inline = True) begin_conversion = datetime.now().timestamp()
if subm.selftext: async with aiohttp.ClientSession() as session:
embed.add_field( index = 0
name = "Discription", for image_url in image_urls:
value = subm.selftext, async with session.get(image_url) as response:
inline = False) response.raise_for_status()
image_data = await response.read()
original_image = Image.open(BytesIO(image_data))
webp_buffer = BytesIO()
original_image.save(webp_buffer, format = "WEBP", quality = quality_value)
webp_buffer.seek(0)
image_file = discord.File(webp_buffer, filename = f"image_{index}.webp")
image_files.append(image_file)
index += 1
progress_temp = progress_title + f"\n`{index}` of `{image_count}` have already been loaded"
await ctx.edit_original_response(content = progress_temp)
self._logger.debug(f"Downloaded and converted {len(image_files)} images in {get_elapsed_time_milliseconds(datetime.now().timestamp() - begin_conversion)}")
author = subm.author.name if subm.author else "Author not found"
content = f":copyright: [{author}]({url})"
if use_title:
content += f"\n# {subm.title}"
if custom_note:
content += f"\n> {custom_note}"
await ctx.delete_original_response()
message = await ctx.followup.send(
content = content,
suppress_embeds = True,
files = image_files
)
self._logger.info(f"Successfully processed the command executed by {ctx.user.name} ({ctx.user.id}) after {get_elapsed_time_milliseconds(datetime.now().timestamp() - begin_process)} (ID of message: {message.id})")
for image_url in image_urls: # No domain for seperation found
embed = discord.Embed(url = "https://discord.com/humans.txt") case _:
embed.set_image(url = image_url) if toplevel_domain == "":
embeds.append(embed) toplevel_domain = "not_found"
embed = discord.Embed(
title = "Domain not found",
description = f"The requested domain `{toplevel_domain}` is currently not supported\nOpen [an issue](https://github.com/official-Cromatin/Post-It/issues/new?assignees=&labels=feature-request&projects=&template=feature_request.yml) to request support for it.\n\nCurrently supported plattforms:\n- Reddit",
color = 0xED4337)
await ctx.response.send_message(embeds = embeds) await ctx.response.send_message(embed = embed, ephemeral = True)
# No domain for seperation found
case _:
embed = discord.Embed(
title = "Domain not found",
description = f"The requested domain `{toplevel_domain}` is currently not supported\nOpen [an issue](https://github.com/official-Cromatin/Post-It/issues/new?assignees=&labels=feature-request&projects=&template=feature_request.yml) to request support for it.\n\nSee an list of supported platforms with the `/platforms` command",
color = 0xED4337)
except NoMediaFound:
self._logger.error(f"Aborted issued command by {ctx.user.name} ({ctx.user.id}). Post had no media attatched")
# Delete the original response, if existing
try:
await ctx.delete_original_response()
except discord.NotFound:
pass
embed = discord.Embed(
title = "No media found",
description = "The post had no media attatched, or was in an unsupported format\nVideos are not supported! (yet)",
color = 0xED4337
)
embed.set_footer(text = "Supported image formats: jpg, jpeg, png, webp, heic, heif")
except discord.errors.HTTPException as error:
self._logger.error(f"Could not complete command by {ctx.user.name} ({ctx.user.id})")
self._logger.exception(error, stack_info = True)
match error.code:
case 40005:
self._logger.error("Failed to upload images, payload too large")
embed = discord.Embed(
title = "Error while processing",
description = "The attachments exceed the upload limit of Discord,\nchoose a different quality level via the argument `quality`",
color = 0xED4337
)
if ctx.response.is_done():
await ctx.followup.send(embed = embed, ephemeral = True)
else:
await ctx.response.send_message(embed = embed)
except Exception as error:
self._logger.error(f"Could not complete command by {ctx.user.name} ({ctx.user.id})")
self._logger.exception(error, stack_info = True)
# Delete the original response, if existing
try:
await ctx.delete_original_response()
except discord.NotFound:
pass
# Explain to the user what the error was
embed = discord.Embed(
title = "Error while processing",
description = f"While we processed your request, the following exception occured: `{error}`",
color = 0xED4337
)
if ctx.response.is_done():
await ctx.followup.send(embed = embed, ephemeral = True)
else:
await ctx.response.send_message(embed = embed) await ctx.response.send_message(embed = embed)
-116
View File
@@ -1,116 +0,0 @@
import discord
from discord import app_commands
from discord.ext import commands
from cogs.base_cog import Base_Cog
from cogs.maintenance import Maintenance_Command
import logging
from datetime import datetime
from utils.datetime_tools import get_elapsed_time_milliseconds
import traceback
from utils.truncate_str import truncate_message_with_notice
class Reload_Command(Base_Cog):
def __init__(self, bot:commands.Bot):
self.__bot = bot
self.__cached_cog_names:list[str] = None
self.__reload_running = False
super().__init__(logging.getLogger("cmds.reload"))
async def autocomplete_cog(self, ctx: discord.Interaction, cog_name_stw:str):
"""Helpermethod to recommend the right cog name to the user"""
# Cache the cog names if not cached yet
if not self.__cached_cog_names:
self.__cached_cog_names = list(self.__bot.extensions.keys())
if cog_name_stw == "":
# Return first 25 cog names, if provided name is empty
matching_cogs = sorted(self.__cached_cog_names)
else:
matching_cogs = []
for cog_name in self.__cached_cog_names:
if cog_name.startswith(cog_name_stw.lower()):
matching_cogs.append(cog_name)
choices = []
for matching_cog in matching_cogs[:25]:
choices.append(app_commands.Choice(name = matching_cog.removeprefix("cogs.").capitalize(), value = matching_cog))
return choices
@app_commands.command(name = "reload_all", description = "Reloads all cogs")
@app_commands.check(Maintenance_Command.handle_check)
async def reload_all(self, ctx: discord.Interaction):
if self.__reload_running:
await ctx.response.send_message("Reload is allready being executed", ephemeral = True)
else:
self.__reload_running = True
task_start = datetime.now().timestamp()
cog_names = list(self.__bot.extensions.keys())
self._logger.debug(f"Reloading all {len(cog_names)} cogs ...")
try:
embed_cog_stats = "Reloaded the following cogs:\n"
for extension_name in cog_names:
start_reload = datetime.now().timestamp()
await self.__bot.reload_extension(extension_name)
embed_cog_stats += f"- {extension_name.removeprefix('cogs.').capitalize()} (`{get_elapsed_time_milliseconds(datetime.now().timestamp() - start_reload)}`)\n"
except Exception as error:
embed_cog_stats += f"- {extension_name.removeprefix('cogs.').capitalize()} <--"
traceback_str = truncate_message_with_notice(traceback.format_exc(), 1800, "\nSee console for more details on the full traceback")
embed = discord.Embed(
title = "Reloading All Cogs",
description = f"{embed_cog_stats} \n\nDuring the reload the following exception occured:\n```{traceback_str}```",
color = 0xED4337)
raise error
else:
elapsed_time = get_elapsed_time_milliseconds(datetime.now().timestamp() - task_start)
embed = discord.Embed(
title = "Reloading All Cogs",
description = f"{embed_cog_stats}Total time spend: `{elapsed_time}`",
color = 0x4BB543)
self._logger.info(f"Cogs successfully reloaded after {elapsed_time}")
finally:
await ctx.response.send_message(embed = embed, ephemeral = True)
self.__reload_running = False
@app_commands.command(name = "reload", description = "Reload specific cog")
@app_commands.check(Maintenance_Command.handle_check)
@app_commands.autocomplete(cog_name = autocomplete_cog)
async def reload(self, ctx: discord.Interaction, cog_name:str):
if self.__reload_running:
await ctx.response.send_message("Reload is allready being executed", ephemeral = True)
else:
self.__reload_running = True
task_start = datetime.now().timestamp()
self._logger.debug(f"Reloading {cog_name} cog ...")
try:
await self.__bot.reload_extension(cog_name)
except Exception as error:
traceback_str = truncate_message_with_notice(traceback.format_exc(), 1800, "\nSee console for more details on the full traceback")
embed = discord.Embed(
title = f"Reloading `{cog_name.removeprefix('cogs.').capitalize()}` cog",
description = f"Reload failed with the following exception:\n```{traceback_str}```",
color = 0xED4337)
raise error
else:
embed = discord.Embed(
title = f"Reloading `{cog_name.removeprefix('cogs.').capitalize()}` cog",
description = f"Total time spend: `{get_elapsed_time_milliseconds(datetime.now().timestamp() - task_start)}`",
color = 0x4BB543)
self._logger.info(f"Cog {cog_name} successfully reloaded after {get_elapsed_time_milliseconds(datetime.now().timestamp() - task_start)}")
finally:
await ctx.response.send_message(embed = embed, ephemeral = True)
self.__reload_running = False
# Clear the cache
self.__cached_cog_names = None
async def setup(bot:commands.Bot):
await bot.add_cog(Reload_Command(bot))
+21 -30
View File
@@ -1,25 +1,6 @@
print(" ____ ____ ___________ __________")
print(" / __ \/ __ \/ ___/_ __/ / _/_ __/")
print(" / /_/ / / / /\__ \ / /_____ / / / / ")
print(" / ____/ /_/ /___/ // /_____// / / / ")
print(" /_/ \____//____//_/ /___/ /_/ ")
print(" Copyright (c) 2024 Lars Winzer")
print()
print(" Source: https://github.com/official-Cromatin/Post-It")
print(" Report an Issue: https://github.com/official-Cromatin/Post-It/issues/new?assignees=&labels=bug&projects=&template=issue_report.yml")
print("\n")
from datetime import datetime from datetime import datetime
startup = datetime.now().timestamp()
# Initialize the logger
from utils.logger.custom_logging import Custom_Logger from utils.logger.custom_logging import Custom_Logger
Custom_Logger.initialize()
import logging import logging
app_logger = logging.getLogger("app")
startup_logger = logging.getLogger("app.startup")
from utils.adv_configparser import Advanced_ConfigParser from utils.adv_configparser import Advanced_ConfigParser
from utils.datetime_tools import get_elapsed_time_smal, get_elapsed_time_big, get_elapsed_time_milliseconds from utils.datetime_tools import get_elapsed_time_smal, get_elapsed_time_big, get_elapsed_time_milliseconds
import discord import discord
@@ -32,7 +13,23 @@ from utils.portal import Portal
import asyncio import asyncio
from typing import Union from typing import Union
from platforms.reddit import Reddit_Adapter from platforms.reddit import Reddit_Adapter
from cogs.maintenance import Maintenance_Command
print(" ____ ____ ___________ __________")
print(" / __ \/ __ \/ ___/_ __/ / _/_ __/")
print(" / /_/ / / / /\__ \ / /_____ / / / / ")
print(" / ____/ /_/ /___/ // /_____// / / / ")
print(" /_/ \____//____//_/ /___/ /_/ ")
print(" Copyright (c) 2024-2025 Lars Winzer")
print()
print(" Source: https://github.com/official-Cromatin/Post-It")
print(" Report an Issue: https://github.com/official-Cromatin/Post-It/issues/new?assignees=&labels=bug&projects=&template=issue_report.yml")
print("\n")
startup = datetime.now().timestamp()
# Initialize the logger
Custom_Logger.initialize()
app_logger = logging.getLogger("app")
startup_logger = logging.getLogger("app.startup")
source_path = Path(__file__).resolve() source_path = Path(__file__).resolve()
base_path = source_path.parents[1] base_path = source_path.parents[1]
@@ -52,20 +49,18 @@ class MyBot(commands.Bot):
async def setup_hook(self): async def setup_hook(self):
# Register cogs to handle commands # Register cogs to handle commands
for cog_name in ["maintenance", "about", "debug", "reload", "post"]: for cog_name in ["debug", "post"]:
await self.load_extension(f"cogs.{cog_name}") await self.load_extension(f"cogs.{cog_name}")
await self.tree.sync() await self.tree.sync()
async def on_app_command_completion(self, interaction: discord.Interaction, command: Union[discord.app_commands.Command, discord.app_commands.ContextMenu]): async def on_app_command_completion(self, interaction: discord.Interaction, command: Union[discord.app_commands.Command, discord.app_commands.ContextMenu]):
"""Called when a `app_commands.Command` or `app_commands.ContextMenu` has successfully completed without error""" """Called when a `app_commands.Command` or `app_commands.ContextMenu` has successfully completed without error"""
self.__portal.no_succeeded_commands += 1 self.__portal.no_succeeded_commands += 1
print("Command succeeded")
async def on_interaction(self, interaction: discord.Interaction): async def on_interaction(self, interaction: discord.Interaction):
"""Called when an interaction happened""" """Called when an interaction happened"""
match interaction.type.name: match interaction.type.name:
case discord.InteractionType.application_command.name: case discord.InteractionType.application_command.name:
print("Interaction with bot", interaction.command.name)
self.__portal.no_executed_commands += 1 self.__portal.no_executed_commands += 1
case discord.InteractionType.ping.name: case discord.InteractionType.ping.name:
print("App got pinged by discord") print("App got pinged by discord")
@@ -79,29 +74,25 @@ class MyBot(commands.Bot):
async def on_connect(self): async def on_connect(self):
"""A coroutine to be called to setup the bot, after the bot is logged in but before it has connected to the Websocket""" """A coroutine to be called to setup the bot, after the bot is logged in but before it has connected to the Websocket"""
if not self.__first_on_ready: if not self.__first_on_ready:
instance: Maintenance_Command = self.get_cog("Maintenance_Command") startup_logger.info("Beginning startup routine ...")
instance.enable_global_maintenance()
startup_logger.info(f"Beginning startup routine ...")
routine_begin = datetime.now().timestamp() routine_begin = datetime.now().timestamp()
await self.change_presence(status = discord.Status.dnd, activity = discord.CustomActivity("Executing pre startup routine")) await self.change_presence(status = discord.Status.dnd, activity = discord.CustomActivity("Executing pre startup routine"))
# Create the adapters for the platforms # Create the adapters for the platforms
task_start = datetime.now().timestamp() task_start = datetime.now().timestamp()
startup_logger.debug(f"Loading platforms config ...") startup_logger.debug("Loading platforms config ...")
platforms_config = Advanced_ConfigParser(Path.joinpath(base_path, "config", "platforms.ini")) platforms_config = Advanced_ConfigParser(Path.joinpath(base_path, "config", "platforms.ini"))
portal.platforms_config = platforms_config portal.platforms_config = platforms_config
startup_logger.info(f"Loaded platforms config after {get_elapsed_time_milliseconds(datetime.now().timestamp() - task_start)}") startup_logger.info(f"Loaded platforms config after {get_elapsed_time_milliseconds(datetime.now().timestamp() - task_start)}")
# Create platforms adapter # Create platforms adapter
task_start = datetime.now().timestamp() task_start = datetime.now().timestamp()
startup_logger.debug(f"Creating reddit adapter ...") startup_logger.debug("Creating reddit adapter ...")
portal.reddit_adapter = Reddit_Adapter(platforms_config["REDDIT"]["CLIENT_ID"], platforms_config["REDDIT"]["CLIENT_SECRET"]) portal.reddit_adapter = Reddit_Adapter(platforms_config["REDDIT"]["CLIENT_ID"], platforms_config["REDDIT"]["CLIENT_SECRET"])
startup_logger.info(f"Created reddit adapter after {get_elapsed_time_milliseconds(datetime.now().timestamp() - task_start)}") startup_logger.info(f"Created reddit adapter after {get_elapsed_time_milliseconds(datetime.now().timestamp() - task_start)}")
await self.change_presence(status = discord.Status.online, activity = None) await self.change_presence(status = discord.Status.online, activity = None)
startup_logger.info(f"Startup routine finished after {get_elapsed_time_milliseconds(datetime.now().timestamp() - routine_begin)}") startup_logger.info(f"Startup routine finished after {get_elapsed_time_milliseconds(datetime.now().timestamp() - routine_begin)}")
instance.disable_gloabal_maintenance()
self.__first_on_ready = True self.__first_on_ready = True
else: else:
startup_logger.info("Startup routine allready executed, omitting this execution") startup_logger.info("Startup routine allready executed, omitting this execution")
+6 -7
View File
@@ -1,13 +1,12 @@
import praw import asyncpraw
import praw.models import asyncpraw.models
import logging import logging
from utils.event_counter import Event_Counter from utils.event_counter import Event_Counter
from utils.datetime_tools import get_elapsed_time_milliseconds from utils.datetime_tools import get_elapsed_time_milliseconds
from typing import Union
from datetime import datetime from datetime import datetime
class Reddit_Adapter(praw.Reddit): class Reddit_Adapter(asyncpraw.Reddit):
"""A class that extends and abstracts the functionality of the `praw.Reddit` class by adding """A class that extends and abstracts the functionality of the `asyncpraw.Reddit` class by adding
logging and request tracking capabilities.""" logging and request tracking capabilities."""
VERSION = "1.0" VERSION = "1.0"
number_of_instances = 0 number_of_instances = 0
@@ -25,11 +24,11 @@ class Reddit_Adapter(praw.Reddit):
self.__events = Event_Counter(1000) self.__events = Event_Counter(1000)
self.__logger = logging.getLogger(f"pltfm.reddit.{self.__instance_number}") self.__logger = logging.getLogger(f"pltfm.reddit.{self.__instance_number}")
def fetch(self, post_url:str) -> praw.models.Submission: async def fetch(self, post_url:str) -> asyncpraw.models.Submission:
"""Fetches specified submission (post) and returns it""" """Fetches specified submission (post) and returns it"""
start_time = datetime.now().timestamp() start_time = datetime.now().timestamp()
self.__events.increment() self.__events.increment()
subm = self.submission(url = post_url) subm = await self.submission(url = post_url)
self.__logger.debug(f"Submission for post (URL: {post_url}), successfully fetched after {get_elapsed_time_milliseconds(datetime.now().timestamp() - start_time)}") self.__logger.debug(f"Submission for post (URL: {post_url}), successfully fetched after {get_elapsed_time_milliseconds(datetime.now().timestamp() - start_time)}")
return subm return subm