import os import hashlib import requests from dotenv import load_dotenv load_dotenv() def _env_list(key): value = os.getenv(key, "") return [item.strip() for item in value.split(",") if item.strip()] REPOSITORY = "Juzlus/jRandomSkills" DISCORD_USER_IDS = _env_list("DISCORD_USER_IDS") EXTRA_USERS_GITHUB = _env_list("EXTRA_USERS_GITHUB") CONTRIBUTORS_URL = "https://github.com/Juzlus/jRandomSkills/graphs/contributors" AVATARS_DIR = "./avatars" AVATARS_RAW_URL = "https://raw.githubusercontent.com/Juzlus/jRandomSkills/main/.github/avatars" MARKER_GITHUB = "[CONTRIBUTORS]" MARKER_DISCORD = "[THANKS]" def build_table(cells, max_per_row=6): rows = [] for i in range(0, len(cells), max_per_row): row_cells = "".join(cells[i:i + max_per_row]) rows.append(f" {row_cells}") return "\n" + "\n".join(rows) + "\n
" def make_cell(avatar_url, name, profile_url=None): if profile_url: return ( f'' f'' f'{name}' f'
{name}' f'
' f'' ) return ( f'' f'{name}' f'
{name}' f'' ) def save_avatar(source_url, filename): os.makedirs(AVATARS_DIR, exist_ok=True) filepath = os.path.join(AVATARS_DIR, filename) response = requests.get(source_url, timeout=10) if response.status_code != 200: print(f"Failed to download avatar: {source_url}") return None with open(filepath, "wb") as f: f.write(response.content) return f"{AVATARS_RAW_URL}/{filename}" def fetch_github_user(login): response = requests.get(f"https://api.github.com/users/{login}", timeout=10) if response.status_code != 200: print(f"GitHub API Error for user '{login}': Status {response.status_code}") return None return response.json() def get_github_contributors(repo, extra_logins): try: url = f"https://api.github.com/repos/{repo}/contributors" response = requests.get(url, timeout=10) if response.status_code != 200: print(f"GitHub API Error: Status {response.status_code}") return "" seen = set() cells = [] for user in response.json(): name = user.get("login") if not name or name == "web-flow": continue seen.add(name.lower()) cells.append(make_cell( avatar_url=f"{user['avatar_url']}&s=75", name=name, profile_url=user["html_url"], )) for login in extra_logins: if login.lower() in seen: print(f"Skipping duplicate extra user: {login}") continue user = fetch_github_user(login) if not user: continue seen.add(login.lower()) cells.append(make_cell( avatar_url=f"{user['avatar_url']}&s=75", name=user["login"], profile_url=user["html_url"], )) return build_table(cells) if cells else "" except Exception as e: print(f"GitHub Fetch Error: {e}") return "" def get_discord_users(user_ids): cells = [] for uid in user_ids: try: response = requests.get( f"https://pfpfinder.com/api/discord/user/{uid}", timeout=10 ) if response.status_code != 200: print(f"Discord API: Status {response.status_code} for {uid}") continue data = response.json() global_name = data.get("global_name") username = data.get("username") if global_name: name = global_name else: print(f"Warning: {uid} has no global_name, falling back to username '{username}' (can be used for invites)") name = username or f"User {uid}" source_url = (data.get("avatar") or "").replace(".gif", ".png") if not source_url: default_index = (int(uid) >> 22) % 6 source_url = f"https://cdn.discordapp.com/embed/avatars/{default_index}.png" uid_hash = hashlib.sha1(uid.encode()).hexdigest()[:12] filename = f"discord_{uid_hash}.png" avatar_url = save_avatar(source_url, filename) if not avatar_url: continue cells.append(make_cell(avatar_url=avatar_url, name=name)) except Exception as e: print(f"Discord API Error for {uid}: {e}") return build_table(cells) if cells else "" def process_file(filename, github_elements, discord_elements, is_pl): input_path = f"./{filename}" output_path = f"../{filename}" if not os.path.exists(input_path): print(f"Error: File {input_path} does not exist") return with open(input_path, "r", encoding="utf-8") as f: content = f.read() if is_pl: info_github = ( f'Jeśli jesteś współtwórcą i chcesz, ' f'aby Twój profil został usunięty z tej listy, skontaktuj się ze mną.' ) info_discord = ( "Jeśli jesteś na tej liście i chcesz usunąć swój profil, skontaktuj się ze mną." ) else: info_github = ( f'If you are a contributor and want your profile ' f'removed from this list, please contact me.' ) info_discord = ( "If you are on this list and want your profile removed, please contact me." ) if MARKER_GITHUB in content and github_elements: content = content.replace(MARKER_GITHUB, ( f'
\n\n' f'{github_elements}\n\n' f'{info_github}\n\n' f'
' )) elif MARKER_GITHUB in content: print(f"Warning: No GitHub data to replace marker in {input_path}") if MARKER_DISCORD in content and discord_elements: content = content.replace(MARKER_DISCORD, ( f'
\n\n' f'{discord_elements}\n\n' f'{info_discord}\n\n' f'
' )) elif MARKER_DISCORD in content: print(f"Warning: No Discord data to replace marker in {input_path}") with open(output_path, "w", encoding="utf-8") as f: f.write(content) print(f"Success: Created {output_path}") def main(): print("Fetching GitHub contributors...") github_elements = get_github_contributors(REPOSITORY, EXTRA_USERS_GITHUB) print("Fetching Discord users...") discord_elements = get_discord_users(DISCORD_USER_IDS) files = [("README.md", False), ("README-PL.md", True)] for filename, is_pl in files: process_file(filename, github_elements, discord_elements, is_pl) if __name__ == "__main__": main()