Implement Modrinth project tracking with commands to add, remove, and list projects. Introduce background task for update checking and enhance error handling. Update API usage for project and version retrieval.
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run

This commit is contained in:
Valerie 2025-05-23 05:01:02 -04:00
parent 4495e8cd63
commit e64a1e2536

View file

@ -1,70 +1,158 @@
import discord import discord
import aiohttp import aiohttp
from datetime import timedelta from datetime import datetime, timedelta
import asyncio
from redbot.core import commands, Config, checks from redbot.core import commands, Config, checks
BASE_URL = "https://api.modrinth.com/v2/project/" BASE_URL = "https://api.modrinth.com/v2"
class ModrinthTracker(commands.Cog): class ModrinthTracker(commands.Cog):
def __init__(self, bot): def __init__(self, bot):
self.bot = bot self.bot = bot
self.config = Config.get_conf(self, identifier=1234567890, force_registration=True) self.config = Config.get_conf(self, identifier=1234567890, force_registration=True)
self.config.register_guild(tracked_projects={}) self.config.register_guild(tracked_projects={})
self.session = None
self.bg_task = None
async def cog_load(self):
self.session = aiohttp.ClientSession()
self.bg_task = self.bot.loop.create_task(self.update_checker())
async def cog_unload(self):
if self.session:
await self.session.close()
if self.bg_task:
self.bg_task.cancel()
@commands.group() @commands.group()
@checks.admin() @checks.admin()
async def modrinth(self, ctx): async def modrinth(self, ctx):
pass """Commands for tracking Modrinth projects"""
if ctx.invoked_subcommand is None:
await ctx.send_help()
@modrinth.command() @modrinth.command()
async def add(self, ctx, project_id: str, channel: discord.TextChannel): async def add(self, ctx, project_id: str, channel: discord.TextChannel):
"""Add a Modrinth project to track
Arguments:
project_id: The Modrinth project ID or slug
channel: The channel to send updates to
"""
try:
# Verify the project exists and get its info
async with self.session.get(f"{BASE_URL}/project/{project_id}") as response:
if response.status != 200:
await ctx.send(f"Error: Project `{project_id}` not found on Modrinth.")
return
project_data = await response.json()
# Get the latest version
async with self.session.get(f"{BASE_URL}/project/{project_id}/version") as response:
if response.status != 200:
await ctx.send("Error: Could not fetch version information.")
return
versions = await response.json()
latest_version = versions[0] if versions else None
tracked_projects = await self.config.guild(ctx.guild).tracked_projects() tracked_projects = await self.config.guild(ctx.guild).tracked_projects()
if project_id in tracked_projects: if project_id in tracked_projects:
await ctx.send("This project is already being tracked.") await ctx.send("This project is already being tracked.")
return return
tracked_projects[project_id] = {"channel": channel.id, "latest_version": None} tracked_projects[project_id] = {
"channel": channel.id,
"latest_version": latest_version["id"] if latest_version else None,
"name": project_data["title"]
}
await self.config.guild(ctx.guild).tracked_projects.set(tracked_projects) await self.config.guild(ctx.guild).tracked_projects.set(tracked_projects)
await ctx.send(f"Tracking project `{project_id}` in {channel.mention}.") await ctx.send(f"Now tracking {project_data['title']} (`{project_id}`) in {channel.mention}.")
except Exception as e:
await ctx.send(f"An error occurred while adding the project: {str(e)}")
@modrinth.command() @modrinth.command()
async def remove(self, ctx, project_id: str): async def remove(self, ctx, project_id: str):
"""Remove a tracked Modrinth project
Arguments:
project_id: The Modrinth project ID or slug to stop tracking
"""
tracked_projects = await self.config.guild(ctx.guild).tracked_projects() tracked_projects = await self.config.guild(ctx.guild).tracked_projects()
if project_id not in tracked_projects: if project_id not in tracked_projects:
await ctx.send("This project is not being tracked.") await ctx.send("This project is not being tracked.")
return return
project_name = tracked_projects[project_id].get("name", project_id)
del tracked_projects[project_id] del tracked_projects[project_id]
await self.config.guild(ctx.guild).tracked_projects.set(tracked_projects) await self.config.guild(ctx.guild).tracked_projects.set(tracked_projects)
await ctx.send(f"Stopped tracking project `{project_id}`.") await ctx.send(f"Stopped tracking {project_name} (`{project_id}`).")
async def check_updates(self): @modrinth.command()
async with aiohttp.ClientSession() as session: async def list(self, ctx):
"""List all tracked Modrinth projects"""
tracked_projects = await self.config.guild(ctx.guild).tracked_projects() tracked_projects = await self.config.guild(ctx.guild).tracked_projects()
if not tracked_projects:
await ctx.send("No projects are currently being tracked.")
return
embed = discord.Embed(title="Tracked Modrinth Projects", color=discord.Color.blue())
for project_id, data in tracked_projects.items(): for project_id, data in tracked_projects.items():
url = BASE_URL + project_id channel = self.bot.get_channel(data["channel"])
async with session.get(url) as response: channel_mention = channel.mention if channel else "Unknown channel"
embed.add_field(
name=data.get("name", project_id),
value=f"ID: `{project_id}`\nChannel: {channel_mention}",
inline=False
)
await ctx.send(embed=embed)
async def update_checker(self):
await self.bot.wait_until_ready()
while True:
try:
all_guilds = await self.config.all_guilds()
for guild_id, guild_data in all_guilds.items():
guild = self.bot.get_guild(guild_id)
if not guild:
continue
tracked_projects = guild_data.get("tracked_projects", {})
for project_id, data in tracked_projects.items():
try:
async with self.session.get(f"{BASE_URL}/project/{project_id}/version") as response:
if response.status != 200: if response.status != 200:
continue continue
project_data = await response.json() versions = await response.json()
latest_version = project_data.get("latest_version") if not versions:
if not latest_version or latest_version == data.get("latest_version"): continue
latest_version = versions[0]
if latest_version["id"] == data.get("latest_version"):
continue continue
channel = self.bot.get_channel(data["channel"]) channel = self.bot.get_channel(data["channel"])
if channel: if channel:
await channel.send(f"New update for `{project_data['title']}`: `{latest_version}`\n{project_data['id']}") embed = discord.Embed(
title=f"New Update for {data.get('name', project_id)}!",
description=f"Version: {latest_version.get('version_number', 'Unknown')}\n\n{latest_version.get('changelog', 'No changelog provided')}",
url=f"https://modrinth.com/project/{project_id}",
color=discord.Color.green(),
timestamp=datetime.now()
)
await channel.send(embed=embed)
tracked_projects[project_id]["latest_version"] = latest_version tracked_projects[project_id]["latest_version"] = latest_version["id"]
await self.config.guild(guild).tracked_projects.set(tracked_projects)
await self.config.guild(ctx.guild).tracked_projects.set(tracked_projects) except Exception as e:
continue
@commands.Cog.listener() except Exception as e:
async def on_ready(self): pass
while True:
await self.check_updates() await asyncio.sleep(300) # Check every 5 minutes
await discord.utils.sleep_until(discord.utils.utcnow().replace(second=0, microsecond=0) + timedelta(minutes=5))
async def setup(bot): async def setup(bot):
await bot.add_cog(ModrinthTracker(bot)) await bot.add_cog(ModrinthTracker(bot))