Skip to content

feat: enhance service cogs with improved functionality #959

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion tux/cogs/services/gif_limiter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import discord
from discord.ext import commands, tasks
from loguru import logger

Check warning on line 7 in tux/cogs/services/gif_limiter.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/gif_limiter.py#L7

Added line #L7 was not covered by tests

from tux.bot import Tux
from tux.utils.config import CONFIG
Expand Down Expand Up @@ -120,7 +121,7 @@
if await self._should_process_message(message):
await self._handle_gif_message(message)

@tasks.loop(seconds=20)
@tasks.loop(seconds=20, name="old_gif_remover")

Check warning on line 124 in tux/cogs/services/gif_limiter.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/gif_limiter.py#L124

Added line #L124 was not covered by tests
async def old_gif_remover(self) -> None:
"""
Regularly cleans old GIF timestamps
Expand All @@ -143,6 +144,23 @@
else:
del self.recent_gifs_by_user[user_id]

@old_gif_remover.before_loop
async def before_old_gif_remover(self) -> None:

Check warning on line 148 in tux/cogs/services/gif_limiter.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/gif_limiter.py#L147-L148

Added lines #L147 - L148 were not covered by tests
"""Wait until the bot is ready."""
await self.bot.wait_until_ready()

Check warning on line 150 in tux/cogs/services/gif_limiter.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/gif_limiter.py#L150

Added line #L150 was not covered by tests

@old_gif_remover.error
async def on_old_gif_remover_error(self, error: BaseException) -> None:

Check warning on line 153 in tux/cogs/services/gif_limiter.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/gif_limiter.py#L152-L153

Added lines #L152 - L153 were not covered by tests
"""Handles errors in the old_gif_remover loop."""
logger.error(f"Error in old_gif_remover loop: {error}")

Check warning on line 155 in tux/cogs/services/gif_limiter.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/gif_limiter.py#L155

Added line #L155 was not covered by tests

if isinstance(error, Exception):
self.bot.sentry_manager.capture_exception(error)

Check failure on line 158 in tux/cogs/services/gif_limiter.py

View workflow job for this annotation

GitHub Actions / Python Type Checking

Cannot access attribute "sentry_manager" for class "Tux"   Attribute "sentry_manager" is unknown (reportAttributeAccessIssue)

Check failure on line 158 in tux/cogs/services/gif_limiter.py

View workflow job for this annotation

GitHub Actions / Python Type Checking

Type of "capture_exception" is unknown (reportUnknownMemberType)

Check failure on line 158 in tux/cogs/services/gif_limiter.py

View workflow job for this annotation

GitHub Actions / Python Type Checking

Type of "sentry_manager" is unknown (reportUnknownMemberType)

Check warning on line 158 in tux/cogs/services/gif_limiter.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/gif_limiter.py#L158

Added line #L158 was not covered by tests
else:
# For BaseExceptions that are not Exceptions (like KeyboardInterrupt),
# it's often better to let them propagate.
raise error

Check warning on line 162 in tux/cogs/services/gif_limiter.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/gif_limiter.py#L162

Added line #L162 was not covered by tests

async def cog_unload(self) -> None:
"""Cancel the background task when the cog is unloaded."""
self.old_gif_remover.cancel()
Expand Down
109 changes: 82 additions & 27 deletions tux/cogs/services/influxdblogger.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Any

import discord

Check warning on line 3 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L3

Added line #L3 was not covered by tests
from discord.ext import commands, tasks
from influxdb_client.client.influxdb_client import InfluxDBClient
from influxdb_client.client.write.point import Point
Expand All @@ -12,13 +13,14 @@


class InfluxLogger(commands.Cog):
def __init__(self, bot: Tux):
def __init__(self, bot: Tux) -> None:

Check warning on line 16 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L16

Added line #L16 was not covered by tests
self.bot = bot
self.db = DatabaseController()
self.influx_write_api: Any | None = None
self.influx_org: str = ""

if self.init_influx():
self._log_guild_stats.start()

Check warning on line 23 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L23

Added line #L23 was not covered by tests
self.logger.start()
else:
logger.warning("InfluxDB logger failed to init. Check .env configuration if you want to use it.")
Expand All @@ -42,7 +44,46 @@
return True
return False

@tasks.loop(seconds=60)
@tasks.loop(seconds=60, name="influx_guild_stats")
async def _log_guild_stats(self) -> None:

Check warning on line 48 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L47-L48

Added lines #L47 - L48 were not covered by tests
"""Logs guild statistics to InfluxDB."""
if not self.bot.is_ready() or not self.influx_write_api:
logger.debug("Bot not ready or InfluxDB writer not initialized, skipping InfluxDB logging.")
return

Check warning on line 52 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L51-L52

Added lines #L51 - L52 were not covered by tests

for guild in self.bot.guilds:
online_members = sum(m.status != discord.Status.offline for m in guild.members)

Check warning on line 55 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L55

Added line #L55 was not covered by tests

tags = {"guild": guild.name}
fields = {

Check warning on line 58 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L57-L58

Added lines #L57 - L58 were not covered by tests
"members": guild.member_count,
"online": online_members,
}

point = {"measurement": "guild_stats", "tags": tags, "fields": fields}

Check warning on line 63 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L63

Added line #L63 was not covered by tests

self.influx_write_api.write(bucket="tux_stats", org=self.influx_org, record=point)

Check warning on line 65 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L65

Added line #L65 was not covered by tests

@_log_guild_stats.before_loop
async def before_log_guild_stats(self) -> None:

Check warning on line 68 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L67-L68

Added lines #L67 - L68 were not covered by tests
"""Wait until the bot is ready."""
await self.bot.wait_until_ready()

Check warning on line 70 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L70

Added line #L70 was not covered by tests

@_log_guild_stats.error
async def on_log_guild_stats_error(self, error: BaseException) -> None:

Check warning on line 73 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L72-L73

Added lines #L72 - L73 were not covered by tests
"""Handles errors in the guild stats logging loop."""
logger.error(f"Error in InfluxDB guild stats logger loop: {error}")

Check warning on line 75 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L75

Added line #L75 was not covered by tests
if isinstance(error, Exception):
self.bot.sentry_manager.capture_exception(error)

Check failure on line 77 in tux/cogs/services/influxdblogger.py

View workflow job for this annotation

GitHub Actions / Python Type Checking

Cannot access attribute "sentry_manager" for class "Tux"   Attribute "sentry_manager" is unknown (reportAttributeAccessIssue)

Check failure on line 77 in tux/cogs/services/influxdblogger.py

View workflow job for this annotation

GitHub Actions / Python Type Checking

Type of "capture_exception" is unknown (reportUnknownMemberType)

Check failure on line 77 in tux/cogs/services/influxdblogger.py

View workflow job for this annotation

GitHub Actions / Python Type Checking

Type of "sentry_manager" is unknown (reportUnknownMemberType)

Check warning on line 77 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L77

Added line #L77 was not covered by tests
else:
raise error

Check warning on line 79 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L79

Added line #L79 was not covered by tests

async def cog_unload(self) -> None:

Check warning on line 81 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L81

Added line #L81 was not covered by tests
if self.influx_write_api:
self._log_guild_stats.cancel()
self.logger.cancel()

Check warning on line 84 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L83-L84

Added lines #L83 - L84 were not covered by tests

@tasks.loop(seconds=60, name="influx_db_logger")

Check warning on line 86 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L86

Added line #L86 was not covered by tests
async def logger(self) -> None:
"""Log statistics to InfluxDB at regular intervals.

Expand All @@ -55,40 +96,54 @@
influx_bucket = "tux stats"

# Collect the guild list from the database
try:
guild_list = await self.db.guild.find_many(where={})
guild_list = await self.db.guild.find_many(where={})

Check warning on line 99 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L99

Added line #L99 was not covered by tests

# Iterate through each guild and collect metrics
for guild in guild_list:
if not guild.guild_id:
continue

Check warning on line 104 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L104

Added line #L104 was not covered by tests

# Iterate through each guild and collect metrics
for guild in guild_list:
if not guild.guild_id:
continue
guild_id = int(guild.guild_id)

Check warning on line 106 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L106

Added line #L106 was not covered by tests

guild_id = int(guild.guild_id)
# Collect data by querying controllers
starboard_stats = await self.db.starboard_message.find_many(where={"message_guild_id": guild_id})

Check warning on line 109 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L109

Added line #L109 was not covered by tests

# Collect data by querying controllers
starboard_stats = await self.db.starboard_message.find_many(where={"message_guild_id": guild_id})
snippet_stats = await self.db.snippet.find_many(where={"guild_id": guild_id})

Check warning on line 111 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L111

Added line #L111 was not covered by tests

snippet_stats = await self.db.snippet.find_many(where={"guild_id": guild_id})
afk_stats = await self.db.afk.find_many(where={"guild_id": guild_id})

Check warning on line 113 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L113

Added line #L113 was not covered by tests

afk_stats = await self.db.afk.find_many(where={"guild_id": guild_id})
case_stats = await self.db.case.find_many(where={"guild_id": guild_id})

Check warning on line 115 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L115

Added line #L115 was not covered by tests

case_stats = await self.db.case.find_many(where={"guild_id": guild_id})
# Create data points with type ignores for InfluxDB methods
# The InfluxDB client's type hints are incomplete
points: list[Point] = [

Check warning on line 119 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L119

Added line #L119 was not covered by tests
Point("guild stats").tag("guild", guild_id).field("starboard count", len(starboard_stats)), # type: ignore
Point("guild stats").tag("guild", guild_id).field("snippet count", len(snippet_stats)), # type: ignore
Point("guild stats").tag("guild", guild_id).field("afk count", len(afk_stats)), # type: ignore
Point("guild stats").tag("guild", guild_id).field("case count", len(case_stats)), # type: ignore
]

# Create data points with type ignores for InfluxDB methods
# The InfluxDB client's type hints are incomplete
points: list[Point] = [
Point("guild stats").tag("guild", guild_id).field("starboard count", len(starboard_stats)), # type: ignore
Point("guild stats").tag("guild", guild_id).field("snippet count", len(snippet_stats)), # type: ignore
Point("guild stats").tag("guild", guild_id).field("afk count", len(afk_stats)), # type: ignore
Point("guild stats").tag("guild", guild_id).field("case count", len(case_stats)), # type: ignore
]
# Write to InfluxDB
self.influx_write_api.write(bucket=influx_bucket, org=self.influx_org, record=points)

Check warning on line 127 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L127

Added line #L127 was not covered by tests

# Write to InfluxDB
self.influx_write_api.write(bucket=influx_bucket, org=self.influx_org, record=points)
@logger.before_loop
async def before_logger(self) -> None:

Check warning on line 130 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L129-L130

Added lines #L129 - L130 were not covered by tests
"""Wait until the bot is ready."""
await self.bot.wait_until_ready()

Check warning on line 132 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L132

Added line #L132 was not covered by tests

except Exception as e:
logger.error(f"Error collecting metrics for InfluxDB: {e}")
@logger.error
async def on_logger_error(self, error: BaseException) -> None:

Check warning on line 135 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L134-L135

Added lines #L134 - L135 were not covered by tests
"""Handles errors in the logger loop."""
logger.error(f"Error in InfluxDB logger loop: {error}")

Check warning on line 137 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L137

Added line #L137 was not covered by tests
if isinstance(error, Exception):
self.bot.sentry_manager.capture_exception(error)

Check failure on line 139 in tux/cogs/services/influxdblogger.py

View workflow job for this annotation

GitHub Actions / Python Type Checking

Cannot access attribute "sentry_manager" for class "Tux"   Attribute "sentry_manager" is unknown (reportAttributeAccessIssue)

Check failure on line 139 in tux/cogs/services/influxdblogger.py

View workflow job for this annotation

GitHub Actions / Python Type Checking

Type of "capture_exception" is unknown (reportUnknownMemberType)

Check failure on line 139 in tux/cogs/services/influxdblogger.py

View workflow job for this annotation

GitHub Actions / Python Type Checking

Type of "sentry_manager" is unknown (reportUnknownMemberType)

Check warning on line 139 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L139

Added line #L139 was not covered by tests
else:
raise error

Check warning on line 141 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L141

Added line #L141 was not covered by tests


async def setup(bot: Tux) -> None:
await bot.add_cog(InfluxLogger(bot))
# Only load the cog if InfluxDB configuration is available
if all([CONFIG.INFLUXDB_TOKEN, CONFIG.INFLUXDB_URL, CONFIG.INFLUXDB_ORG]):
await bot.add_cog(InfluxLogger(bot))

Check warning on line 147 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L147

Added line #L147 was not covered by tests
else:
logger.warning("InfluxDB configuration incomplete, skipping InfluxLogger cog")

Check warning on line 149 in tux/cogs/services/influxdblogger.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/influxdblogger.py#L149

Added line #L149 was not covered by tests
48 changes: 31 additions & 17 deletions tux/cogs/services/levels.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from discord.ext import commands
from loguru import logger

from tux.app import get_prefix
from prisma.models import Levels

Check warning on line 8 in tux/cogs/services/levels.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/levels.py#L8

Added line #L8 was not covered by tests
from tux.bot import Tux
from tux.database.controllers import DatabaseController
from tux.ui.embeds import EmbedCreator
Expand Down Expand Up @@ -33,20 +33,40 @@
message : discord.Message
The message object.
"""
if message.author.bot or message.guild is None or message.channel.id in CONFIG.XP_BLACKLIST_CHANNELS:
if message.author.bot or not message.guild or message.channel.id in CONFIG.XP_BLACKLIST_CHANNELS:
return

prefixes = await get_prefix(self.bot, message)
if any(message.content.startswith(prefix) for prefix in prefixes):
# Ignore messages that are commands
ctx = await self.bot.get_context(message)

Check warning on line 40 in tux/cogs/services/levels.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/levels.py#L40

Added line #L40 was not covered by tests
if ctx.valid:
return

# Fetch member object
member = message.guild.get_member(message.author.id)
if member is None:
if not member:
return

await self.process_xp_gain(member, message.guild)
# Fetch all user level data in one query to minimize DB calls
user_level_data = await self.db.levels.get_user_level_data(member.id, message.guild.id)

Check warning on line 50 in tux/cogs/services/levels.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/levels.py#L50

Added line #L50 was not covered by tests

async def process_xp_gain(self, member: discord.Member, guild: discord.Guild) -> None:
# Check if the user is blacklisted
if user_level_data and user_level_data.blacklisted:
return

Check warning on line 54 in tux/cogs/services/levels.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/levels.py#L54

Added line #L54 was not covered by tests

# Check if the user is on cooldown
last_message_time = user_level_data.last_message if user_level_data else None

Check warning on line 57 in tux/cogs/services/levels.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/levels.py#L57

Added line #L57 was not covered by tests
if last_message_time and self.is_on_cooldown(last_message_time):
return

Check warning on line 59 in tux/cogs/services/levels.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/levels.py#L59

Added line #L59 was not covered by tests

# Process XP gain with the already fetched data
await self.process_xp_gain(member, message.guild, user_level_data)

Check warning on line 62 in tux/cogs/services/levels.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/levels.py#L62

Added line #L62 was not covered by tests

async def process_xp_gain(

Check warning on line 64 in tux/cogs/services/levels.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/levels.py#L64

Added line #L64 was not covered by tests
self,
member: discord.Member,
guild: discord.Guild,
user_level_data: Levels | None,
) -> None:
"""
Processes XP gain for a member.

Expand All @@ -56,17 +76,11 @@
The member gaining XP.
guild : discord.Guild
The guild where the member is gaining XP.
user_level_data : Levels | None
The existing level data for the user.
"""
# Get blacklist status
is_blacklisted = await self.db.levels.is_blacklisted(member.id, guild.id)
if is_blacklisted:
return

last_message_time = await self.db.levels.get_last_message_time(member.id, guild.id)
if last_message_time and self.is_on_cooldown(last_message_time):
return

current_xp, current_level = await self.db.levels.get_xp_and_level(member.id, guild.id)
current_xp = user_level_data.xp if user_level_data else 0.0
current_level = user_level_data.level if user_level_data else 0

Check warning on line 83 in tux/cogs/services/levels.py

View check run for this annotation

Codecov / codecov/patch

tux/cogs/services/levels.py#L82-L83

Added lines #L82 - L83 were not covered by tests

xp_increment = self.calculate_xp_increment(member)
new_xp = current_xp + xp_increment
Expand Down
Loading