feat(botinfo): contributors!!!

This commit is contained in:
neoarz
2025-10-17 01:44:26 -04:00
parent 61848f6bc8
commit b18355e749
3 changed files with 136 additions and 8 deletions

View File

@@ -6,6 +6,7 @@ from discord.ext.commands import Context
from datetime import datetime
import time
import pytz
from utils.contributors import generate_contributors_image
class FeedbackForm(discord.ui.Modal, title="Feedback"):
@@ -125,6 +126,9 @@ class BotInfo(commands.Cog, name="botinfo"):
@app_commands.allowed_contexts(guilds=True, dms=True, private_channels=True)
@app_commands.allowed_installs(guilds=True, users=True)
async def botinfo(self, context: Context) -> None:
if context.interaction:
await context.interaction.response.defer(ephemeral=False)
ny_tz = pytz.timezone('America/New_York')
current_time = datetime.now(ny_tz).strftime("%m/%d/%y, %I:%M %p")
@@ -137,21 +141,38 @@ class BotInfo(commands.Cog, name="botinfo"):
f"**Prefix:** / (Slash Commands) or {self.bot.bot_prefix} for normal commands"
)
embed = discord.Embed(
embed1 = discord.Embed(
title="Syntrel Discord Bot",
description=description_text,
color=0x7289DA,
)
embed.set_author(name="Syntrel", icon_url="https://github.com/neoarz/Syntrel/blob/main/assets/icon.png?raw=true")
embed.set_image(url="https://github.com/neoarz/Syntrel/raw/main/assets/bannerdark.png")
embed.set_footer(text=f"neoarz • {current_time}", icon_url="https://yes.nighty.works/raw/P1Us35.webp")
embed1.set_author(name="Syntrel", icon_url="https://github.com/neoarz/Syntrel/blob/main/assets/icon.png?raw=true")
embed1.set_image(url="https://github.com/neoarz/Syntrel/raw/main/assets/bannerdark.png")
embed1.set_footer(text=f"neoarz • {current_time}", icon_url="https://yes.nighty.works/raw/P1Us35.webp")
embed2 = discord.Embed(
title="Contributors",
description="Giving credit where it's due! <a:pandasquish:1428617277317709915>",
color=0x7289DA,
)
contributors_image = generate_contributors_image()
view = BotInfoView(self.bot)
if contributors_image:
file = discord.File(contributors_image, filename="contributors.png")
embed2.set_image(url="attachment://contributors.png")
if context.interaction:
await context.interaction.response.send_message(embed=embed, view=view, ephemeral=False)
await context.interaction.followup.send(embeds=[embed1, embed2], file=file, view=view)
else:
await context.send(embed=embed, view=view)
await context.send(embeds=[embed1, embed2], file=file, view=view)
else:
if context.interaction:
await context.interaction.followup.send(embeds=[embed1, embed2], view=view)
else:
await context.send(embeds=[embed1, embed2], view=view)
async def setup(bot) -> None:

View File

@@ -2,8 +2,10 @@ from .ascii_art import ascii, ascii_plain, gradient_text, gradient_text_selectiv
from .logging import LoggingFormatter, setup_logger
from .time import get_uptime
from .signal import setup_signal_handlers
from .contributors import generate_contributors_image
__all__ = [
'ascii', 'ascii_plain', 'gradient_text', 'gradient_text_selective',
'LoggingFormatter', 'setup_logger', 'get_uptime', 'setup_signal_handlers'
'LoggingFormatter', 'setup_logger', 'get_uptime', 'setup_signal_handlers',
'generate_contributors_image'
]

105
utils/contributors.py Normal file
View File

@@ -0,0 +1,105 @@
from io import BytesIO
from math import ceil
import requests
from PIL import Image
def fetch_contributors(owner, repo):
contributors = []
page = 1
while True:
url = f"https://api.github.com/repos/{owner}/{repo}/contributors"
params = {"page": page, "per_page": 100}
headers = {
"User-Agent": "github-contributors-graph",
"Accept": "application/vnd.github.v3+json"
}
response = requests.get(url, headers=headers, params=params)
if not response.ok:
return []
data = response.json()
if not data:
break
contributors.extend(data)
page += 1
if response.headers.get('X-RateLimit-Remaining') == '0':
break
return contributors
def download_avatar(avatar_url, size):
try:
if "avatars.githubusercontent.com" in avatar_url:
avatar_url = f"{avatar_url}?s={size}"
response = requests.get(avatar_url, timeout=10)
if not response.ok:
return None
img = Image.open(BytesIO(response.content))
if img.size != (size, size):
img = img.resize((size, size), Image.Resampling.LANCZOS)
return img
except Exception:
return None
def generate_contributors_image(owner="neoarz", repo="syntrel", size=64, images_per_row=20):
contributors = fetch_contributors(owner, repo)
if not contributors:
return None
images = []
for contributor in contributors:
avatar_url = contributor.get("avatar_url")
if not avatar_url:
continue
img = download_avatar(avatar_url, size)
if img is None:
continue
images.append(img)
if not images:
return None
actual_images_per_row = min(len(images), images_per_row)
width = size * actual_images_per_row
row_count = ceil(len(images) / images_per_row)
height = size * row_count
canvas = Image.new("RGB", (width, height), color="black")
for idx, img in enumerate(images):
col = idx % images_per_row
row = idx // images_per_row
x = col * size
y = row * size
canvas.paste(img, (x, y))
buffer = BytesIO()
canvas.save(buffer, "PNG")
buffer.seek(0)
return buffer