236 lines
9.0 KiB
Python
Executable File
236 lines
9.0 KiB
Python
Executable File
"""
|
|
utils/embeds.py — cyberpunk / hacker-aesthetic embed builders for RepoRadar.
|
|
|
|
Every embed is built here so styling stays consistent: dark backdrops, neon
|
|
accent colours, monospace code blocks, and tidy mobile + desktop formatting.
|
|
Functions accept the normalised `Repo` dataclass from utils.git_api and return
|
|
ready-to-send nextcord.Embed objects.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
import nextcord
|
|
|
|
from config import settings
|
|
from utils.git_api import Repo
|
|
from utils.helpers import discord_timestamp, truncate
|
|
|
|
BRAND = "RepoRadar // Project Intelligence"
|
|
|
|
|
|
def _footer(embed: nextcord.Embed) -> nextcord.Embed:
|
|
embed.set_footer(text=f"⛓ {BRAND}")
|
|
embed.timestamp = datetime.now(timezone.utc)
|
|
return embed
|
|
|
|
|
|
# ──────────────────────────────────────────────────────────────
|
|
# Error / utility embeds
|
|
# ──────────────────────────────────────────────────────────────
|
|
def build_error_embed(title: str, message: str) -> nextcord.Embed:
|
|
embed = nextcord.Embed(
|
|
title=f"⛔ {title}",
|
|
description=message,
|
|
color=settings.color_error,
|
|
)
|
|
return _footer(embed)
|
|
|
|
|
|
def build_info_embed(title: str, message: str) -> nextcord.Embed:
|
|
embed = nextcord.Embed(
|
|
title=f"✅ {title}",
|
|
description=message,
|
|
color=settings.color_success,
|
|
)
|
|
return _footer(embed)
|
|
|
|
|
|
# ──────────────────────────────────────────────────────────────
|
|
# Project-drop embeds
|
|
# ──────────────────────────────────────────────────────────────
|
|
def _lang_emoji(language: str) -> str:
|
|
"""A glanceable marker for the repo's primary language."""
|
|
return {
|
|
"python": "🐍", "go": "🐹", "rust": "🦀", "c": "🔧", "c++": "🔧",
|
|
"java": "☕", "javascript": "🟨", "typescript": "🔷", "shell": "🐚",
|
|
"ruby": "💎", "php": "🐘", "html": "🌐", "powershell": "💠",
|
|
}.get((language or "").lower(), "📦")
|
|
|
|
|
|
def build_repo_drop_embed(
|
|
repo: Repo,
|
|
*,
|
|
votes: dict[str, int] | None = None,
|
|
watch_label: str | None = None,
|
|
highlight: bool = False,
|
|
admin_note: str | None = None,
|
|
) -> nextcord.Embed:
|
|
"""The card posted when a brand-new project is discovered on the git server."""
|
|
votes = votes or {"up": 0, "down": 0, "score": 0}
|
|
title_prefix = "🌟 FEATURED DROP" if highlight else "🆕 NEW PROJECT"
|
|
embed = nextcord.Embed(
|
|
title=f"{_lang_emoji(repo.language)} {truncate(repo.full_name or repo.name, 240)}",
|
|
url=repo.url or None,
|
|
description=truncate(repo.description or "*No description provided.*", 1024),
|
|
color=settings.color_highlight if highlight else settings.color_drop,
|
|
)
|
|
embed.set_author(name=f"⛓ {title_prefix}")
|
|
|
|
embed.add_field(name="◈ Owner", value=f"`{repo.owner or 'unknown'}`", inline=True)
|
|
if repo.language:
|
|
embed.add_field(name="◈ Language", value=f"`{repo.language}`", inline=True)
|
|
embed.add_field(
|
|
name="◈ Created",
|
|
value=discord_timestamp(repo.created_at, "R"),
|
|
inline=True,
|
|
)
|
|
embed.add_field(name="◈ ⭐ Stars", value=f"`{repo.stars}`", inline=True)
|
|
embed.add_field(name="◈ 🍴 Forks", value=f"`{repo.forks}`", inline=True)
|
|
embed.add_field(
|
|
name="◈ 🗳 Community",
|
|
value=f"👍 `{votes['up']}` 👎 `{votes['down']}` • net `{votes['score']:+d}`",
|
|
inline=True,
|
|
)
|
|
|
|
if repo.topics:
|
|
embed.add_field(
|
|
name="◈ Topics",
|
|
value=" ".join(f"`{t}`" for t in repo.topics[:10]),
|
|
inline=False,
|
|
)
|
|
|
|
if admin_note:
|
|
embed.add_field(name="◈ Curator Note", value=truncate(admin_note, 1024), inline=False)
|
|
|
|
if watch_label:
|
|
embed.add_field(name="◈ Source", value=watch_label, inline=False)
|
|
|
|
if repo.url:
|
|
embed.add_field(name="◈ Repository", value=f"[🔗 Open project]({repo.url})", inline=False)
|
|
|
|
return _footer(embed)
|
|
|
|
|
|
def build_repo_list_embed(
|
|
repos: list[Repo],
|
|
*,
|
|
title: str = "🛰 LATEST PROJECTS",
|
|
limit: int = 10,
|
|
) -> nextcord.Embed:
|
|
"""Compact chronological list of the newest repositories (newest first)."""
|
|
embed = nextcord.Embed(
|
|
title=f"☣ {title}",
|
|
description=(
|
|
"```ansi\n\u001b[1;35m> Freshest projects from the git server\u001b[0m\n```"
|
|
f"Showing the **{min(limit, len(repos))}** most recent."
|
|
),
|
|
color=settings.color_drop,
|
|
)
|
|
for repo in repos[:limit]:
|
|
created = discord_timestamp(repo.created_at, "R")
|
|
meta = f"⭐ {repo.stars} · 🍴 {repo.forks}"
|
|
if repo.language:
|
|
meta = f"`{repo.language}` · " + meta
|
|
desc = truncate(repo.description or "*No description.*", 130)
|
|
link = f"[open]({repo.url})" if repo.url else ""
|
|
embed.add_field(
|
|
name=f"{_lang_emoji(repo.language)} {truncate(repo.full_name or repo.name, 60)}",
|
|
value=f"{desc}\n{meta} · {created} · {link}",
|
|
inline=False,
|
|
)
|
|
if not repos:
|
|
embed.description = "No projects could be retrieved right now."
|
|
return _footer(embed)
|
|
|
|
|
|
def build_git_leaderboard_embed(rows: list[dict], *, limit: int = 10) -> nextcord.Embed:
|
|
"""Top community-voted projects."""
|
|
embed = nextcord.Embed(
|
|
title="🏆 COMMUNITY TOP PROJECTS",
|
|
description="```ansi\n\u001b[1;32m> Ranked by community vote score\u001b[0m\n```",
|
|
color=settings.color_success,
|
|
)
|
|
medals = {0: "🥇", 1: "🥈", 2: "🥉"}
|
|
for i, row in enumerate(rows[:limit]):
|
|
name = row.get("full_name") or row.get("repo_key", "unknown")
|
|
url = row.get("url") or ""
|
|
score = int(row.get("score") or 0)
|
|
up = int(row.get("up") or 0)
|
|
down = int(row.get("down") or 0)
|
|
marker = medals.get(i, f"`#{i + 1}`")
|
|
link = f"[open]({url})" if url else ""
|
|
embed.add_field(
|
|
name=f"{marker} {truncate(name, 60)}",
|
|
value=f"net `{score:+d}` • 👍 `{up}` 👎 `{down}` {link}",
|
|
inline=False,
|
|
)
|
|
if not rows:
|
|
embed.description = "No votes have been cast yet. Be the first to vote on a drop!"
|
|
return _footer(embed)
|
|
|
|
|
|
def build_git_status_embed(
|
|
*,
|
|
provider: str,
|
|
base_url: str,
|
|
configured: bool,
|
|
drop_channel_id: int | None,
|
|
highlight_channel_id: int | None,
|
|
highlight_role_id: int | None,
|
|
min_stars: int,
|
|
keywords: str,
|
|
enabled: bool,
|
|
watches: list[dict],
|
|
poll_interval: int,
|
|
) -> nextcord.Embed:
|
|
"""Render the current monitor configuration for a guild."""
|
|
embed = nextcord.Embed(
|
|
title="⚙ REPORADAR — CONFIGURATION",
|
|
color=settings.color_drop if configured else settings.color_medium,
|
|
)
|
|
status = "🟢 enabled" if enabled else "🔴 disabled"
|
|
embed.add_field(name="◈ Provider", value=f"`{provider or 'unconfigured'}`", inline=True)
|
|
embed.add_field(name="◈ Server", value=f"`{base_url or 'api.github.com'}`", inline=True)
|
|
embed.add_field(name="◈ Monitor", value=status, inline=True)
|
|
|
|
embed.add_field(
|
|
name="◈ Drop channel",
|
|
value=f"<#{drop_channel_id}>" if drop_channel_id else "`not set`",
|
|
inline=True,
|
|
)
|
|
embed.add_field(
|
|
name="◈ Highlight channel",
|
|
value=f"<#{highlight_channel_id}>" if highlight_channel_id else "`not set`",
|
|
inline=True,
|
|
)
|
|
embed.add_field(
|
|
name="◈ Highlight role",
|
|
value=f"<@&{highlight_role_id}>" if highlight_role_id else "`none`",
|
|
inline=True,
|
|
)
|
|
embed.add_field(name="◈ Min ⭐ to highlight", value=f"`{min_stars}`", inline=True)
|
|
embed.add_field(name="◈ Poll interval", value=f"`{poll_interval}s`", inline=True)
|
|
embed.add_field(
|
|
name="◈ Highlight keywords",
|
|
value=f"`{keywords}`" if keywords else "`none`",
|
|
inline=False,
|
|
)
|
|
|
|
if watches:
|
|
lines = []
|
|
for w in watches:
|
|
scope = w["scope_type"]
|
|
val = w["scope_value"] or "*entire server*"
|
|
ch = f" → <#{w['channel_id']}>" if w.get("channel_id") else ""
|
|
lines.append(f"• `{scope}` **{val}**{ch}")
|
|
embed.add_field(name="◈ Active watches", value=truncate("\n".join(lines), 1024), inline=False)
|
|
else:
|
|
embed.add_field(
|
|
name="◈ Active watches",
|
|
value="None yet — add one with `/radar_watch`.",
|
|
inline=False,
|
|
)
|
|
return _footer(embed)
|