21 Commits

Author SHA1 Message Date
Cromatin a9cf3af487 Merge pull request #11 from 'feat-vscode-tasks' into 'main'
Change runner type from self-hosted to ubuntu-latest for overlooked file
2025-12-08 14:48:28 +01:00
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
Cromatin 46fc7d0d60 Merge pull request #10 from 'feat-vscode-tasks' into 'main'
Remove renovate workflow and integrate docker build
2025-12-08 14:44:05 +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
12 changed files with 98 additions and 46 deletions
+1
View File
@@ -4,6 +4,7 @@
**/.dockerignore
**/.env
**/.git
**/.github
**/.gitignore
**/.project
**/.settings
+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
+1 -1
View File
@@ -10,7 +10,7 @@ permissions:
jobs:
build-and-push-docker:
runs-on: self-hosted
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
+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 -1
View File
@@ -5,7 +5,7 @@ pastebin.txt
.DS_Store
test*.py
docker-*
.vscode
.vscode/settings.json
# 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": []
}
]
}
+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
import logging
from utils.portal import Portal
from datetime import datetime
from utils.datetime_tools import get_elapsed_time_big
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")
@log_command_execution
async def debug(self, ctx:discord.Interaction):
portal:Portal = Portal.instance()
embed = discord.Embed(title="Debug Information")
embed.add_field(name="Version",
value=f"`{portal.PROGRAM_VERSION}`",
value=ctx.client.VERSION,
inline=True)
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)
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)
embed.add_field(name="Latency to Gateway",
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",
value=f"{len(self.__bot.guilds)}",
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)
+1 -4
View File
@@ -6,7 +6,6 @@ from cogs.base_cog import Base_Cog
import logging
from urllib.parse import urlparse
from asyncpraw.models import Submission
from utils.portal import Portal
import aiohttp
from PIL import Image
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):
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})")
subm:Submission = await portal.reddit_adapter.fetch(url)
subm:Submission = await ctx.client.reddit_adapter.fetch(url)
image_urls = []
# Check if submission has a gallery
if hasattr(subm, "media_metadata"):
+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
+21 -20
View File
@@ -9,16 +9,16 @@ from pathlib import Path
import re
import sys
import traceback
from utils.portal import Portal
import asyncio
from typing import Union
from platforms.reddit import Reddit_Adapter
from const import VERSION
print(" ____ ____ ___________ __________")
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")
@@ -41,11 +41,17 @@ intents.messages = True
class MyBot(commands.Bot):
def __init__(self):
super().__init__(command_prefix=None, help_command=None, intents=intents)
self.__portal:Portal
self.__first_on_ready = False
def set_portal(self, portal:Portal):
self.__portal = portal
self.VERSION = VERSION
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):
# Register cogs to handle commands
@@ -55,13 +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]):
"""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
async def on_interaction(self, interaction: discord.Interaction):
"""Called when an interaction happened"""
match interaction.type.name:
case discord.InteractionType.application_command.name:
self.__portal.no_executed_commands += 1
self.no_executed_commands += 1
case discord.InteractionType.ping.name:
print("App got pinged by discord")
case discord.InteractionType.autocomplete.name:
@@ -82,13 +88,13 @@ class MyBot(commands.Bot):
task_start = datetime.now().timestamp()
startup_logger.debug("Loading platforms config ...")
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)}")
# Create platforms adapter
task_start = datetime.now().timestamp()
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)}")
await self.change_presence(status = discord.Status.online, activity = None)
@@ -101,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}")
bot = MyBot()
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_config["DISCORD"]["TOKEN"]):
bot.STARTUP_TIMESTAMP = startup
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")
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")
quit(1)
else:
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
@bot.tree.error
async def on_app_command_error(ctx:discord.Interaction, error):
@@ -124,10 +125,10 @@ async def on_app_command_error(ctx:discord.Interaction, error):
print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr)
traceback.print_exception(type(error), error, error.__traceback__)
portal.no_failed_commands += 1
bot.no_failed_commands += 1
try:
bot.run(bot_config["DISCORD"]["TOKEN"], log_handler = None)
bot.run(bot.bot_config["DISCORD"]["TOKEN"], log_handler = None)
except discord.errors.LoginFailure:
app_logger.critical("Improper token has been passed. Aborting startup")
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