32 Commits

Author SHA1 Message Date
lars-winzer 01e3138f29 Change runner type from self-hosted to ubuntu-latest for overlooked file
Extends a41a483041
2025-12-08 14:45:51 +01:00
lars-winzer a41a483041 Change runner type from self-hosted to ubuntu-latest 2025-12-08 14:41:34 +01:00
lars-winzer 917e90b48b Remove renovate.json 2025-10-09 10:54:13 +02:00
lars-winzer 467aebc3af Add bandit task to run on push 2025-10-08 16:02:41 +02:00
lars-winzer 818f4dfdae Remove renovate workflow 2025-10-08 15:51:45 +02:00
lars-winzer 0db5d4d31c Add ruff task to run on push and pull_request
Source: https://docs.astral.sh/ruff/integrations/#github-actions
2025-10-08 15:17:04 +02:00
lars-winzer 6967fa5358 Add task to run and attach to docker container 2025-10-01 15:03:10 +02:00
lars-winzer 70363033c1 Add docker-build task and update .gitignore 2025-09-28 18:26:48 +02:00
lars-winzer baf774f4b0 Fix "No repositories found" error for renovate action 2025-09-21 17:35:50 +02:00
lars-winzer 64215ffda5 Ditch docker approach and run renovate directly 2025-09-21 17:30:57 +02:00
lars-winzer 13a9075376 Modify renovate workflow to use docker-mount 2025-09-21 17:26:08 +02:00
Cromatin 05e72bf0dc feat: Integrated Renovate Bot as gh action (#9) 2025-09-21 17:08:16 +02:00
lars-winzer 654274f632 Enable renovate workflow schedule 2025-09-21 17:04:47 +02:00
lars-winzer 56d7a8155c Add required files for renovate bot workflow
As outlined in: https://github.com/renovatebot/github-action
2025-09-21 16:57:24 +02:00
Cromatin b82d6fa518 Merge pull request #8 from official-Cromatin:refractor/remove-portal-class
Refractor: Remove portal class
2025-09-21 16:38:37 +02:00
lars-winzer c4cb477637 Move portal attributes to Bot class 2025-09-21 15:23:14 +02:00
lars-winzer 2f33d840f3 Move version to independent VERSION file 2025-09-21 14:59:12 +02:00
lars-winzer f01b942818 Merge 'bug/issue-6' into 'main' 2025-09-18 15:34:39 +02:00
lars-winzer 8e1b4269d1 Fix .github folder being included in the docker image 2025-09-18 15:23:23 +02:00
Cromatin e1455de14b Merge pull request #5 from official-Cromatin:official-Cromatin/issue4
Official-Cromatin/issue4
2025-09-17 14:43:07 +02:00
lars-winzer e6195d9a88 Add required permissions 2025-09-17 14:42:09 +02:00
lars-winzer 1221035369 Explicitly use bash shell 2025-09-17 14:41:46 +02:00
lars-winzer 796f43d629 Fix docker workspace error #4 2025-09-17 14:34:12 +02:00
Cromatin 0b031d20f9 Merge pull request #3 from official-Cromatin/release-v0.5
Merge branch 'release-v0.5' into 'main'
2025-09-16 18:49:29 +02:00
lars-winzer bfd497898e Update copyright year 2025-09-16 18:30:07 +02:00
lars-winzer 077f845b94 Merge commit '42bd226a6007780debe6ee1ca6357185f409f70e' into release-v0.5 2025-09-16 18:28:05 +02:00
lars-winzer 26e3a4bb8e Update LICENSE 2025-09-16 18:13:56 +02:00
lars-winzer f56636c1ff Create workflow to build and release docker image on ghcr 2025-09-16 17:30:08 +02:00
lars-winzer f427d06e61 Create Dockerfile and .dockerignore for upcoming official docker support
- Update .gitignore
2025-09-16 16:07:07 +02:00
lars-winzer 42bd226a60 Update LICENSE 2025-09-16 16:03:13 +02:00
lars-winzer 4dd578c9a8 Refine '/post'-command error handling when file size exceeds upload limit 2025-09-16 14:13:02 +02:00
lars-winzer 01d90e6625 Move imports to the top of the document
- Remove unnecessary prints
- Remove unnecessary f-strings
2025-09-16 14:10:04 +02:00
14 changed files with 235 additions and 73 deletions
+28
View File
@@ -0,0 +1,28 @@
**/__pycache__
**/.venv
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.github
**/.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
+15
View File
@@ -0,0 +1,15 @@
name: Code Analysis (Bandit)
on: push
jobs:
bandit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: mdegis/bandit-action@v1.0
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
path: "src/"
level: high
confidence: high
exit_zero: true
+44
View File
@@ -0,0 +1,44 @@
name: Release Multi-Arch Docker Image
on:
release:
types: [published]
permissions:
contents: read
packages: write
jobs:
build-and-push-docker:
runs-on: ubuntu-latest
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: Transform 'github.repository' to lowercase
id: lowercase_repo
shell: bash
run: echo "repository=${GITHUB_REPOSITORY,,}" >> $GITHUB_OUTPUT
- 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/${{ steps.lowercase_repo.outputs.repository }}:${{ github.event.release.tag_name }}
ghcr.io/${{ steps.lowercase_repo.outputs.repository }}:latest
+9
View File
@@ -0,0 +1,9 @@
name: Linting (Ruff)
on: [push, pull_request]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v3
+1 -2
View File
@@ -4,9 +4,8 @@ config/*.ini
pastebin.txt pastebin.txt
.DS_Store .DS_Store
test*.py test*.py
.dockerignore
docker-* docker-*
.vscode .vscode/settings.json
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
+36
View File
@@ -0,0 +1,36 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Docker: Build Image",
"type": "docker-build",
"platform": "python",
"dockerBuild": {
"context": "${workspaceFolder}",
"dockerfile": "${workspaceFolder}/Dockerfile",
"tag": "post-it:dev"
}
},
{
"label": "Run post-it (interactive, show logs)",
"type": "shell",
"dependsOn": ["Docker: Build Image"],
"command": "docker",
"args": [
"run",
"--rm",
"-it",
"--name",
"post-it-dev",
"-v",
"${workspaceFolder}/config:/app/config",
"post-it:dev"
],
"presentation": {
"reveal": "always",
"panel": "shared"
},
"problemMatcher": []
}
]
}
+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"]
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2024 Lars Winzer Copyright (c) 2024-2025 Lars Winzer
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+1
View File
@@ -0,0 +1 @@
0.5.2
+4 -6
View File
@@ -4,7 +4,6 @@ from discord.ext import commands
from cogs.base_cog import Base_Cog from cogs.base_cog import Base_Cog
import logging import logging
from utils.portal import Portal
from datetime import datetime from datetime import datetime
from utils.datetime_tools import get_elapsed_time_big from utils.datetime_tools import get_elapsed_time_big
from utils.logger.decorator import log_command_execution from utils.logger.decorator import log_command_execution
@@ -17,17 +16,16 @@ class Debug_Command(Base_Cog):
@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")
@log_command_execution @log_command_execution
async def debug(self, ctx:discord.Interaction): async def debug(self, ctx:discord.Interaction):
portal:Portal = Portal.instance()
embed = discord.Embed(title="Debug Information") embed = discord.Embed(title="Debug Information")
embed.add_field(name="Version", embed.add_field(name="Version",
value=f"`{portal.PROGRAM_VERSION}`", value=ctx.client.VERSION,
inline=True) inline=True)
embed.add_field(name="Uptime", embed.add_field(name="Uptime",
value=f"{get_elapsed_time_big(datetime.now().timestamp() - portal.STARTUP_TIMESTAMP)}", value=f"{get_elapsed_time_big(datetime.now().timestamp() - ctx.client.STARTUP_TIMESTAMP)}",
inline=True) inline=True)
embed.add_field(name="Bot Owner", embed.add_field(name="Bot Owner",
value=f"<@{portal.bot_config['DISCORD']['OWNER_ID']}>", value=f"<@{ctx.client.bot_config['DISCORD']['OWNER_ID']}>",
inline=True) inline=True)
embed.add_field(name="Latency to Gateway", embed.add_field(name="Latency to Gateway",
value=f"{round(self.__bot.latency * 1000, 2)}ms", value=f"{round(self.__bot.latency * 1000, 2)}ms",
@@ -38,7 +36,7 @@ class Debug_Command(Base_Cog):
embed.add_field(name="Number of Guilds", embed.add_field(name="Number of Guilds",
value=f"{len(self.__bot.guilds)}", value=f"{len(self.__bot.guilds)}",
inline=True) inline=True)
embed.add_field(name = "Number of executed commands", value=f"Total: {portal.no_executed_commands}\nSucceeded: {portal.no_succeeded_commands}\nFailed: {portal.no_failed_commands}") embed.add_field(name = "Number of executed commands", value=f"Total: {ctx.client.no_executed_commands}\nSucceeded: {ctx.client.no_succeeded_commands}\nFailed: {ctx.client.no_failed_commands}")
await ctx.response.send_message(embed=embed) await ctx.response.send_message(embed=embed)
+26 -7
View File
@@ -5,8 +5,7 @@ from cogs.base_cog import Base_Cog
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
import aiohttp import aiohttp
from PIL import Image from PIL import Image
from io import BytesIO from io import BytesIO
@@ -35,14 +34,12 @@ class Post_Command(Base_Cog):
async def post(self, ctx:discord.Interaction, url:str, custom_note:str = None, use_title:bool = True, quality:app_commands.Choice[int] = 95): async def post(self, ctx:discord.Interaction, url:str, custom_note:str = None, use_title:bool = True, quality:app_commands.Choice[int] = 95):
try: try:
domain_info = urlparse(url) domain_info = urlparse(url)
portal = Portal.instance()
toplevel_domain = '.'.join(domain_info.netloc.split('.')[-2:]) toplevel_domain = '.'.join(domain_info.netloc.split('.')[-2:])
begin_process = datetime.now().timestamp() begin_process = datetime.now().timestamp()
match toplevel_domain: match toplevel_domain:
case "reddit.com": case "reddit.com":
self._logger.debug(f"Recieved command by {ctx.user} ({ctx.user.id}) for reddit ({url})") self._logger.debug(f"Recieved command by {ctx.user} ({ctx.user.id}) for reddit ({url})")
subm:Submission = await ctx.client.reddit_adapter.fetch(url)
subm:Submission = await portal.reddit_adapter.fetch(url)
image_urls = [] image_urls = []
# Check if submission has a gallery # Check if submission has a gallery
if hasattr(subm, "media_metadata"): if hasattr(subm, "media_metadata"):
@@ -93,7 +90,7 @@ class Post_Command(Base_Cog):
author = subm.author.name if subm.author else "Author not found" author = subm.author.name if subm.author else "Author not found"
content = f":copyright: [{author}]({url})" content = f":copyright: [{author}]({url})"
if use_title: if use_title:
content += f"\n## {subm.title}" content += f"\n# {subm.title}"
if custom_note: if custom_note:
content += f"\n> {custom_note}" content += f"\n> {custom_note}"
@@ -134,6 +131,25 @@ class Post_Command(Base_Cog):
) )
embed.set_footer(text = "Supported image formats: jpg, jpeg, png, webp, heic, heif") 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: except Exception as error:
self._logger.error(f"Could not complete command by {ctx.user.name} ({ctx.user.id})") self._logger.error(f"Could not complete command by {ctx.user.name} ({ctx.user.id})")
self._logger.exception(error, stack_info = True) self._logger.exception(error, stack_info = True)
@@ -150,7 +166,10 @@ class Post_Command(Base_Cog):
description = f"While we processed your request, the following exception occured: `{error}`", description = f"While we processed your request, the following exception occured: `{error}`",
color = 0xED4337 color = 0xED4337
) )
await ctx.followup.send(embed = embed, ephemeral = True) if ctx.response.is_done():
await ctx.followup.send(embed = embed, ephemeral = True)
else:
await ctx.response.send_message(embed = embed)
async def setup(bot:commands.Bot): async def setup(bot:commands.Bot):
+8
View File
@@ -0,0 +1,8 @@
from pathlib import Path
# Read VERSION file
with open(Path(__file__).resolve().parent / "VERSION") as f:
VERSION = f.read().strip()
__all__ = VERSION
+40 -43
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
@@ -28,10 +9,27 @@ from pathlib import Path
import re import re
import sys import sys
import traceback import traceback
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 const import VERSION
print(" ____ ____ ___________ __________")
print(" / __ \/ __ \/ ___/_ __/ / _/_ __/")
print(" / /_/ / / / /\__ \ / /_____ / / / / ")
print(" / ____/ /_/ /___/ // /_____// / / / ")
print(f" /_/ \____//____//_/ /___/ /_/ v{VERSION}")
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]
@@ -43,11 +41,17 @@ intents.messages = True
class MyBot(commands.Bot): class MyBot(commands.Bot):
def __init__(self): def __init__(self):
super().__init__(command_prefix=None, help_command=None, intents=intents) super().__init__(command_prefix=None, help_command=None, intents=intents)
self.__portal:Portal
self.__first_on_ready = False self.__first_on_ready = False
def set_portal(self, portal:Portal): self.VERSION = VERSION
self.__portal = portal self.STARTUP_TIMESTAMP: float = None
self.platforms_config: Advanced_ConfigParser = None
self.bot_config: Advanced_ConfigParser = None
self.reddit_adapter: Reddit_Adapter = None
self.no_executed_commands:int = 0
self.no_succeeded_commands:int = 0
self.no_failed_commands:int = 0
async def setup_hook(self): async def setup_hook(self):
# Register cogs to handle commands # Register cogs to handle commands
@@ -57,15 +61,13 @@ class MyBot(commands.Bot):
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.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.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")
case discord.InteractionType.autocomplete.name: case discord.InteractionType.autocomplete.name:
@@ -78,21 +80,21 @@ 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:
startup_logger.info(f"Beginning startup routine ...") startup_logger.info("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 self.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"]) self.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)
@@ -105,22 +107,17 @@ class MyBot(commands.Bot):
app_logger.info(f"Successfully logged in (after {get_elapsed_time_smal(datetime.now().timestamp() - startup)}) as {self.user}") app_logger.info(f"Successfully logged in (after {get_elapsed_time_smal(datetime.now().timestamp() - startup)}) as {self.user}")
bot = MyBot() bot = MyBot()
bot_config = Advanced_ConfigParser(Path.joinpath(base_path, "config", "bot.ini")) bot.STARTUP_TIMESTAMP = startup
if re.match(r'[A-Za-z\d]{24}\.[\w-]{6}\.[\w-]{27}', bot_config["DISCORD"]["TOKEN"]): bot.bot_config = Advanced_ConfigParser(Path.joinpath(base_path, "config", "bot.ini"))
if re.match(r'[A-Za-z\d]{24}\.[\w-]{6}\.[\w-]{27}', bot.bot_config["DISCORD"]["TOKEN"]):
app_logger.critical("Bot (config/bot.ini) configuration invalid, please set a valid token") app_logger.critical("Bot (config/bot.ini) configuration invalid, please set a valid token")
quit(1) quit(1)
elif bot_config.compare_to_template() not in ("equal", "config_minus"): elif bot.bot_config.compare_to_template() not in ("equal", "config_minus"):
app_logger.critical("Bot (config/bot.ini) configuration is missing some parts. Make sure it at least has all the same keys as the template") app_logger.critical("Bot (config/bot.ini) configuration is missing some parts. Make sure it at least has all the same keys as the template")
quit(1) quit(1)
else: else:
app_logger.info("Bot configuration valid, continuing with startup") app_logger.info("Bot configuration valid, continuing with startup")
# Execute some housekeeping actions
portal = Portal.instance()
portal.bot_config = bot_config
portal.STARTUP_TIMESTAMP = startup
bot.set_portal(portal)
# Setup handlers to handle states of command execution # Setup handlers to handle states of command execution
@bot.tree.error @bot.tree.error
async def on_app_command_error(ctx:discord.Interaction, error): async def on_app_command_error(ctx:discord.Interaction, error):
@@ -128,10 +125,10 @@ async def on_app_command_error(ctx:discord.Interaction, error):
print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr) print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr)
traceback.print_exception(type(error), error, error.__traceback__) traceback.print_exception(type(error), error, error.__traceback__)
portal.no_failed_commands += 1 bot.no_failed_commands += 1
try: try:
bot.run(bot_config["DISCORD"]["TOKEN"], log_handler = None) bot.run(bot.bot_config["DISCORD"]["TOKEN"], log_handler = None)
except discord.errors.LoginFailure: except discord.errors.LoginFailure:
app_logger.critical("Improper token has been passed. Aborting startup") app_logger.critical("Improper token has been passed. Aborting startup")
quit(1) quit(1)
-14
View File
@@ -1,14 +0,0 @@
from utils.singleton import Singleton
from utils.adv_configparser import Advanced_ConfigParser
from platforms.reddit import Reddit_Adapter
@Singleton
class Portal:
PROGRAM_VERSION = "0.3"
bot_config:Advanced_ConfigParser = None
platforms_config:Advanced_ConfigParser = None
STARTUP_TIMESTAMP:float = None
no_executed_commands:int = 0
no_succeeded_commands:int = 0
no_failed_commands:int = 0
reddit_adapter: Reddit_Adapter = None