Files
Syntrel/utils/contributors.py

107 lines
2.3 KiB
Python
Raw Normal View History

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