mirror of
https://github.com/allthingslinux/tux.git
synced 2024-10-03 00:53:12 +00:00
Merge pull request #52 from allthingslinux/dev
[Feature] Implement Ruff Linting for Enhanced Code Quality
This commit is contained in:
commit
70757e9f45
20 changed files with 176 additions and 262 deletions
31
.github/workflows/linting.yml
vendored
31
.github/workflows/linting.yml
vendored
|
@ -1,5 +1,5 @@
|
|||
name: 'Linting'
|
||||
on: [push, pull_request]
|
||||
on: [ push, pull_request ]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
@ -8,7 +8,7 @@ permissions:
|
|||
|
||||
jobs:
|
||||
Linting:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v4
|
||||
|
@ -19,26 +19,13 @@ jobs:
|
|||
with:
|
||||
python-version: 3.11
|
||||
|
||||
# Install Poetry
|
||||
- name: Poetry Install
|
||||
uses: snok/install-poetry@v1.3.4
|
||||
|
||||
# Cache dependencies
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pypoetry
|
||||
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-poetry-
|
||||
|
||||
# Use Lint Action for linting and formatting
|
||||
- name: Run linters
|
||||
uses: wearerequired/lint-action@v2.3.0
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
auto_fix: true
|
||||
# Install Ruff
|
||||
- name: Install Ruff
|
||||
run: sudo snap install ruff
|
||||
|
||||
# Run Ruff linter
|
||||
- name: Run Ruff format
|
||||
run: ruff format && ruff check . --fix
|
||||
- uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: 'style fixes by linter'
|
||||
commit_message: 'style fixes by ruff'
|
||||
|
|
103
pyproject.toml
103
pyproject.toml
|
@ -44,32 +44,32 @@ include = '\.pyi?$'
|
|||
[tool.ruff]
|
||||
# Exclude a variety of commonly ignored directories.
|
||||
exclude = [
|
||||
".bzr",
|
||||
".direnv",
|
||||
".eggs",
|
||||
".git",
|
||||
".git-rewrite",
|
||||
".hg",
|
||||
".ipynb_checkpoints",
|
||||
".mypy_cache",
|
||||
".nox",
|
||||
".pants.d",
|
||||
".pyenv",
|
||||
".pytest_cache",
|
||||
".pytype",
|
||||
".ruff_cache",
|
||||
".svn",
|
||||
".tox",
|
||||
".venv",
|
||||
".vscode",
|
||||
"__pypackages__",
|
||||
"_build",
|
||||
"buck-out",
|
||||
"build",
|
||||
"dist",
|
||||
"node_modules",
|
||||
"site-packages",
|
||||
"venv",
|
||||
".bzr",
|
||||
".direnv",
|
||||
".eggs",
|
||||
".git",
|
||||
".git-rewrite",
|
||||
".hg",
|
||||
".ipynb_checkpoints",
|
||||
".mypy_cache",
|
||||
".nox",
|
||||
".pants.d",
|
||||
".pyenv",
|
||||
".pytest_cache",
|
||||
".pytype",
|
||||
".ruff_cache",
|
||||
".svn",
|
||||
".tox",
|
||||
".venv",
|
||||
".vscode",
|
||||
"__pypackages__",
|
||||
"_build",
|
||||
"buck-out",
|
||||
"build",
|
||||
"dist",
|
||||
"node_modules",
|
||||
"site-packages",
|
||||
"venv",
|
||||
]
|
||||
|
||||
# Same as Black.
|
||||
|
@ -87,7 +87,6 @@ target-version = "py311"
|
|||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
||||
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
|
||||
# McCabe complexity (`C901`) by default.
|
||||
ignore = []
|
||||
select = [
|
||||
# pycodestyle
|
||||
"E",
|
||||
|
@ -102,6 +101,8 @@ select = [
|
|||
# isort
|
||||
"I",
|
||||
]
|
||||
ignore = ["E501"]
|
||||
|
||||
|
||||
# Allow fix for all enabled rules (when `--fix`) is provided.
|
||||
fixable = ["ALL"]
|
||||
|
@ -147,32 +148,32 @@ docstring-code-line-length = "dynamic"
|
|||
################################################################
|
||||
|
||||
[tool.pyright]
|
||||
defineConstant = {DEBUG = true}
|
||||
defineConstant = { DEBUG = true }
|
||||
|
||||
exclude = [
|
||||
".direnv",
|
||||
".eggs",
|
||||
".git",
|
||||
".hg",
|
||||
".ipynb_checkpoints",
|
||||
".mypy_cache",
|
||||
".nox",
|
||||
".pants.d",
|
||||
".pyenv",
|
||||
".pytest_cache",
|
||||
".pytype",
|
||||
".svn",
|
||||
".tox",
|
||||
".venv",
|
||||
".vscode",
|
||||
"__pypackages__",
|
||||
"_build",
|
||||
"buck-out",
|
||||
"build",
|
||||
"dist",
|
||||
"node_modules",
|
||||
"site-packages",
|
||||
"venv",
|
||||
".direnv",
|
||||
".eggs",
|
||||
".git",
|
||||
".hg",
|
||||
".ipynb_checkpoints",
|
||||
".mypy_cache",
|
||||
".nox",
|
||||
".pants.d",
|
||||
".pyenv",
|
||||
".pytest_cache",
|
||||
".pytype",
|
||||
".svn",
|
||||
".tox",
|
||||
".venv",
|
||||
".vscode",
|
||||
"__pypackages__",
|
||||
"_build",
|
||||
"buck-out",
|
||||
"build",
|
||||
"dist",
|
||||
"node_modules",
|
||||
"site-packages",
|
||||
"venv",
|
||||
]
|
||||
|
||||
include = ["."]
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_bulk_message_delete.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_guild_channel_create.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_guild_channel_delete.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
# on_guild_channel_pins_update.py
|
||||
from datetime import datetime
|
||||
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_guild_channel_update.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_guild_role_create.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_guild_role_delete.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_guild_role_update.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_member_ban.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_member_join.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_member_remove.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_member_unban.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,43 +1,84 @@
|
|||
# Import the necessary modules
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
# Initialize the logger
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
||||
# Define the OnMemberUpdate class, which is a subclass of commands.Cog
|
||||
class OnMemberUpdate(commands.Cog):
|
||||
# Initialize the class with the bot as an argument
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
# Define a listener for the 'on_member_update' event
|
||||
@commands.Cog.listener("on_member_update")
|
||||
async def on_member_update(self, before: discord.Member, after: discord.Member):
|
||||
"""
|
||||
Handles the event when a Discord member updates their profile.
|
||||
This function is triggered when a member's profile is updated. It
|
||||
compares the before and after states of the member, logs any changes,
|
||||
creates an embed for the changes, and sends the embed to a specified
|
||||
channel.
|
||||
"""
|
||||
try:
|
||||
# Compare the before and after states of the member
|
||||
changes = self.compare_member_changes(before, after)
|
||||
if changes:
|
||||
# Log the changes
|
||||
self.log_member_changes(changes)
|
||||
# Create an embed for the changes
|
||||
embed = self.create_embed_for_changes(changes)
|
||||
# Send the embed to a specified channel
|
||||
await self.send_embed(embed)
|
||||
except Exception as e:
|
||||
# Log any errors that occur
|
||||
logger.error(f"Error handling member update: {e}")
|
||||
|
||||
This function is triggered when a member's nickname, roles, pending status, timeout status, guild avatar, or flags are modified. It does not react when a member's timeout expires due to a restriction in Discord.
|
||||
def compare_member_changes(self, before, after):
|
||||
"""
|
||||
This function compares the before and after states of a member.
|
||||
It currently only checks for changes in nickname, but more comparisons can be added as needed.
|
||||
"""
|
||||
changes = {}
|
||||
if before.nick != after.nick:
|
||||
changes["nickname"] = {"before": before.nick, "after": after.nick}
|
||||
return changes
|
||||
|
||||
Args:
|
||||
before (discord.Member): The previous state of the member before the update.
|
||||
after (discord.Member): The updated state of the member after the update.
|
||||
def log_member_changes(self, changes):
|
||||
"""
|
||||
This function logs any changes that have occurred.
|
||||
"""
|
||||
for change, values in changes.items():
|
||||
logger.info(
|
||||
f"{change} changed from {values['before']} to {values['after']}"
|
||||
)
|
||||
|
||||
Note:
|
||||
This function requires the `Intents.members` to be enabled.
|
||||
def create_embed_for_changes(self, changes):
|
||||
"""
|
||||
This function creates an embed for the changes that have occurred.
|
||||
"""
|
||||
embed = discord.Embed(
|
||||
title="Member Update", description="A member has updated their profile."
|
||||
)
|
||||
for change, values in changes.items():
|
||||
embed.add_field(name=f"Old {change}", value=values["before"], inline=False)
|
||||
embed.add_field(name=f"New {change}", value=values["after"], inline=False)
|
||||
return embed
|
||||
|
||||
https://discordpy.readthedocs.io/en/stable/api.html#discord.on_member_update
|
||||
""" # noqa E501
|
||||
|
||||
# TODO: On member update logic goes here.
|
||||
|
||||
# You can use the before and after objects to compare the changes.
|
||||
# We should probably abstract this into multiple functions:
|
||||
|
||||
# 1. Comparing changes
|
||||
# 2. Logging changes
|
||||
# 3. Creating the embed
|
||||
# 4. Sending the embed
|
||||
|
||||
logger.debug(f"Member {before} updated to {after}.")
|
||||
async def send_embed(self, embed):
|
||||
"""
|
||||
This function sends the embed to a specified channel.
|
||||
The channel ID needs to be replaced with the actual ID of the channel you want to send the embed to.
|
||||
"""
|
||||
some_channel = self.bot.get_channel(
|
||||
"1191472088695980083"
|
||||
) # Replace 'channel_id' with the actual ID
|
||||
await some_channel.send(embed=embed)
|
||||
|
||||
|
||||
# Define an asynchronous setup function that adds the OnMemberUpdate cog to the bot
|
||||
async def setup(bot):
|
||||
await bot.add_cog(OnMemberUpdate(bot))
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_message.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_message_delete.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# on_message_edit.py
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from utils._tux_logger import TuxLogger
|
||||
|
||||
from tux.utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
|
16
tux/main.py
16
tux/main.py
|
@ -5,14 +5,12 @@ import discord
|
|||
from cog_loader import CogLoader
|
||||
from discord.ext import commands
|
||||
from dotenv import load_dotenv
|
||||
from utils.error_handler import ErrorHandler
|
||||
from utils.tux_logger import TuxLogger
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
load_dotenv()
|
||||
|
||||
from discord.ext import commands
|
||||
from utils.error_handler import ErrorHandler
|
||||
|
||||
|
||||
async def setup(bot: commands.Bot, debug: bool = False):
|
||||
"""
|
||||
|
@ -34,7 +32,7 @@ async def main():
|
|||
@commands.has_permissions(administrator=True)
|
||||
async def sync(ctx: commands.Context):
|
||||
"""Syncs the slash command tree. This command is only available to administrators.
|
||||
|
||||
|
||||
Args:
|
||||
ctx (commands.Context): The invocation context sent by the Discord API which contains information
|
||||
about the command and from where it was called.
|
||||
|
@ -49,7 +47,7 @@ async def main():
|
|||
@commands.has_permissions(administrator=True)
|
||||
async def clear(ctx: commands.Context):
|
||||
"""Clears the slash command tree. This command is only available to administrators.
|
||||
|
||||
|
||||
Args:
|
||||
ctx (commands.Context): The invocation context sent by the Discord API which contains information
|
||||
about the command and from where it was called.
|
||||
|
@ -64,11 +62,11 @@ async def main():
|
|||
@bot.event
|
||||
async def on_command_error(ctx: commands.Context, error):
|
||||
"""Handles the event when a command has been invoked but an error has occurred.
|
||||
|
||||
|
||||
Args:
|
||||
ctx (commands.Context): The invocation context sent by the Discord API which contains information
|
||||
about the command and from where it was called.
|
||||
|
||||
|
||||
error (Exception): The error that occurred.
|
||||
""" # noqa E501
|
||||
if isinstance(error, commands.CommandNotFound):
|
||||
|
@ -83,7 +81,7 @@ async def main():
|
|||
@bot.event
|
||||
async def on_command_completion(ctx: commands.Context):
|
||||
"""Handles the event when a command has been completed its invocation. This event is called only if the command succeeded, i.e. all checks have passed and the user input it correctly.
|
||||
|
||||
|
||||
Args:
|
||||
ctx (commands.Context): The invocation context sent by the Discord API which contains information
|
||||
about the command and from where it was called.
|
||||
|
@ -100,7 +98,7 @@ async def main():
|
|||
logger.info(f"{bot.user} has connected to Discord!", __name__)
|
||||
|
||||
await bot.start(os.getenv("TOKEN") or "", reconnect=True)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
logger.error("An error occurred:", exc_info=True)
|
||||
|
||||
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
import os
|
||||
import logging
|
||||
import colorlog
|
||||
from discord.ext import commands
|
||||
|
||||
# ==================
|
||||
# Usage Instructions
|
||||
# ==================
|
||||
# Hey contributor, Ty here! To use the logger in your cog files, please follow these steps:
|
||||
#
|
||||
# 1. Import the logger by adding the following line at the top of your main bot file:
|
||||
#
|
||||
# from utils._tux_logger import TuxLogger
|
||||
#
|
||||
# logger = TuxLogger(__name__)
|
||||
#
|
||||
# 2. Once imported, you can use the logger to log messages in your code. For example:
|
||||
# logger.info("This is an information message.")
|
||||
# logger.warning("This is a warning message.")
|
||||
# logger.error("This is an error message.")
|
||||
# logger.debug("This is a debug message.")
|
||||
#
|
||||
# I love you all and thank you for contributing <3
|
||||
# =========================
|
||||
# End of Usage Instructions
|
||||
# =========================
|
||||
|
||||
|
||||
class TuxLogger(logging.Logger):
|
||||
def __init__(self,
|
||||
name,
|
||||
project_logging_level=logging.INFO):
|
||||
"""
|
||||
Constructor for the custom logger class.
|
||||
|
||||
Parameters:
|
||||
- name: The name of the logger.
|
||||
- project_logging_level: The logging level for the project (default is INFO).
|
||||
"""
|
||||
super().__init__(name, level=project_logging_level)
|
||||
self._setup_logging()
|
||||
|
||||
def _setup_logging(self):
|
||||
"""
|
||||
Set up the logging configuration for the custom logger.
|
||||
"""
|
||||
log_format = '%(asctime)s [%(log_color)s%(levelname)s%(reset)s] [%(name)s]: %(message)s'
|
||||
log_dir = 'logs'
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
|
||||
handler = colorlog.StreamHandler()
|
||||
handler.setFormatter(colorlog.ColoredFormatter(log_format))
|
||||
self.addHandler(handler)
|
||||
|
||||
file_handler = logging.FileHandler(
|
||||
os.path.join(log_dir, 'bot.log'),
|
||||
mode='a'
|
||||
)
|
||||
file_handler.setFormatter(logging.Formatter(
|
||||
'%(asctime)s [%(levelname)s] [%(name)s]: %(message)s'))
|
||||
self.addHandler(file_handler)
|
||||
|
||||
def _log_to_file(self, level, message, caller_module):
|
||||
"""
|
||||
Log a message to a specific file.
|
||||
|
||||
Parameters:
|
||||
- level: The logging level (e.g., logging.DEBUG, logging.INFO, etc.).
|
||||
- message: The log message.
|
||||
- filename: The name of the file to log to.
|
||||
"""
|
||||
|
||||
file_handler = logging.FileHandler(
|
||||
os.path.join('logs', f"{caller_module}.log"),
|
||||
mode='a'
|
||||
)
|
||||
file_handler.setFormatter(
|
||||
logging.Formatter(
|
||||
f'%(asctime)s [%(levelname)s] [{caller_module}]: %(message)s'
|
||||
)
|
||||
)
|
||||
self.addHandler(file_handler)
|
||||
self.log(level, message)
|
||||
self.removeHandler(file_handler)
|
||||
|
||||
def exception(self, message, filename="unknown"):
|
||||
self._log_to_file(logging.ERROR, message, filename, exc_info=True)
|
||||
|
||||
def debug(self, message, filename="unknown"):
|
||||
self._log_to_file(logging.DEBUG, message, filename)
|
||||
|
||||
def info(self, message, filename="unknown"):
|
||||
self._log_to_file(logging.INFO, message, filename)
|
||||
|
||||
def warning(self, message, filename="unknown"):
|
||||
self._log_to_file(logging.WARNING, message, filename)
|
||||
|
||||
def error(self, message, filename="unknown"):
|
||||
self._log_to_file(logging.ERROR, message, filename)
|
||||
|
||||
def critical(self, message, filename="unknown"):
|
||||
self._log_to_file(logging.CRITICAL, message, filename)
|
||||
|
||||
|
||||
class LoggingCog(commands.Cog):
|
||||
def __init__(self,
|
||||
bot,
|
||||
discord_logging_level=logging.WARNING):
|
||||
"""
|
||||
Constructor for the LoggingCog class.
|
||||
|
||||
Parameters:
|
||||
- bot: The Discord bot instance.
|
||||
- discord_logging_level: The logging level for the Discord library (default is WARNING).
|
||||
"""
|
||||
self.bot = bot
|
||||
self.discord_logging_level = discord_logging_level
|
||||
|
||||
discord_logger = logging.getLogger('discord')
|
||||
discord_logger.setLevel(self.discord_logging_level)
|
||||
|
||||
|
||||
logger = TuxLogger(__name__)
|
||||
|
||||
|
||||
async def setup(bot,
|
||||
project_logging_level=logging.DEBUG,
|
||||
discord_logging_level=logging.WARNING):
|
||||
"""
|
||||
Asynchronous function to set up the LoggingCog and add it to the Discord bot.
|
||||
|
||||
Parameters:
|
||||
- bot: The Discord bot instance.
|
||||
- project_logging_level: The logging level for the project (default is DEBUG).
|
||||
- discord_logging_level: The logging level for the Discord library (default is WARNING).
|
||||
"""
|
||||
global logger
|
||||
log_cog = LoggingCog(
|
||||
bot,
|
||||
discord_logging_level
|
||||
)
|
||||
logger.setLevel(project_logging_level)
|
||||
await bot.add_cog(log_cog)
|
Loading…
Reference in a new issue