Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
| import os | |
| import re | |
| import csv | |
| import sys | |
| import json | |
| import time | |
| import random | |
| import asyncio | |
| import discord | |
| import logging | |
| import os.path | |
| import secrets | |
| import gspread | |
| import datetime | |
| import tempfile | |
| import requests | |
| import threading | |
| import traceback | |
| import gradio_client | |
| import numpy as np | |
| import pandas as pd | |
| import gradio as gr | |
| import plotly.graph_objects as go | |
| from tabulate import tabulate | |
| from requests import HTTPError | |
| from gradio_client import Client | |
| from discord import Color, Embed | |
| from huggingface_hub import HfApi | |
| from discord.ui import Button, View | |
| from discord.ext import commands, tasks | |
| from datetime import datetime, timedelta | |
| from urllib.parse import urlparse, parse_qs | |
| # starting to migrate to HF datasets and away from google sheets | |
| from huggingface_hub import HfApi | |
| from apscheduler.executors.pool import ThreadPoolExecutor | |
| from apscheduler.executors.asyncio import AsyncIOExecutor | |
| from apscheduler.schedulers.asyncio import AsyncIOScheduler | |
| from gspread_formatting.dataframe import format_with_dataframe | |
| discord.utils.setup_logging(level=logging.DEBUG) # discord.py built-in | |
| logging.getLogger("discord.http").setLevel(logging.DEBUG) # HTTP layer | |
| logging.basicConfig( | |
| level=logging.DEBUG, | |
| format="%(asctime)s %(levelname)s %(name)s: %(message)s", | |
| stream=sys.stdout, | |
| ) | |
| DISCORD_TOKEN = os.environ.get("DISCORD_TOKEN", None) | |
| intents = discord.Intents.all() | |
| bot = commands.Bot(command_prefix='!', intents=intents) | |
| GRADIO_APP_URL = "https://huggingface.co/spaces/discord-community/LevelBot" | |
| """""" | |
| bot_ids = [1136614989411655780, 1166392942387265536, 1158038249835610123, 1130774761031610388, 1155489509518098565, 1155169841276260546, 1152238037355474964, 1154395078735953930] | |
| """""" | |
| hf_token = os.environ.get("HF_TOKEN") | |
| discord_loop = None # global variable | |
| # new | |
| async def on_interaction(interaction: discord.Interaction): | |
| # Fires for every interaction—great catch-all probe. | |
| cid = getattr(interaction.data, "get", lambda k, d=None: None)("custom_id") | |
| logging.debug(f"on_interaction custom_id={cid!r} responded={interaction.response.is_done()}") | |
| class DMButton(Button): | |
| def __init__(self): | |
| super().__init__( | |
| label="Verify Discord Account", | |
| style=discord.ButtonStyle.primary, | |
| custom_id="verify_discord_account_button" # <- stable | |
| ) | |
| async def callback(self, interaction: discord.Interaction): | |
| # await interaction.user.send(self.message) # this is for DMs, but users may have DMs disabled | |
| await interaction.response.defer(ephemeral=True) # to fix interaction issue | |
| user_id = interaction.user.id | |
| guild = interaction.guild | |
| # safer than cache: | |
| try: | |
| member = await guild.fetch_member(user_id) | |
| except discord.NotFound: | |
| await interaction.followup.send("Could not find your member record.", ephemeral=True) | |
| return | |
| pending_role = guild.get_role(1380908157475360899) | |
| if pending_role: | |
| try: | |
| await member.add_roles(pending_role, reason="Clicked verification button") | |
| logging.info("Assigned Pending to %s (%s)", member, member.id) | |
| except discord.Forbidden: | |
| logging.error("Missing permissions or role hierarchy below Pending.") | |
| except Exception: | |
| logging.exception("Failed to add Pending role") | |
| unique_link = f"<{GRADIO_APP_URL}?user_id={user_id}>" | |
| msg = ( | |
| "Login link generated! To complete verification:\n" | |
| "- 1 Visit this link,\n- 2 click '🤗 Sign in with Hugging Face'\n\n" | |
| f"{unique_link}" | |
| ) | |
| # since we deferred: | |
| await interaction.followup.send(msg, ephemeral=True) | |
| # new | |
| class PersistentVerifyView(discord.ui.View): | |
| def __init__(self): | |
| super().__init__(timeout=None) # <- persistent | |
| self.add_item(DMButton()) | |
| async def on_error(self, error: Exception, item, interaction: discord.Interaction): | |
| logging.exception("Component error") | |
| try: | |
| if interaction.response.is_done(): | |
| await interaction.followup.send("Something broke while handling that click.", ephemeral=True) | |
| else: | |
| await interaction.response.send_message("Something broke while handling that click.", ephemeral=True) | |
| except Exception: | |
| logging.exception("Failed to notify user after component error.") | |
| async def on_ready(): | |
| logging.info("Logged in as %s (%s)", bot.user, bot.user.id) | |
| view = PersistentVerifyView() | |
| bot.add_view(view) # binds handler for the custom_id | |
| channel = bot.get_channel(900125909984624713) | |
| msg = await channel.fetch_message(1271145797433557023) | |
| # Step A: clear existing components | |
| await msg.edit(view=None) | |
| # Step B: attach the new persistent view (with stable custom_id) | |
| await msg.edit(view=view) | |
| # fetch and print the custom_ids currently on the message | |
| msg2 = await channel.fetch_message(msg.id) | |
| try: | |
| current_ids = [] | |
| for action_row in msg2.components: | |
| for comp in action_row.children: | |
| current_ids.append(getattr(comp, "custom_id", None)) | |
| logging.info("Message components now: %s", current_ids) | |
| except Exception: | |
| logging.exception("Could not introspect message components") | |
| logging.info("------------------------------------------------------------------------") | |
| DISCORD_TOKEN = os.environ.get("DISCORD_TOKEN", None) | |
| def run_bot(): | |
| global discord_loop | |
| discord_loop = asyncio.new_event_loop() | |
| asyncio.set_event_loop(discord_loop) | |
| discord_loop.create_task(bot.start(DISCORD_TOKEN)) | |
| discord_loop.run_forever() | |
| threading.Thread(target=run_bot).start() | |
| def verify_button(profile: gr.OAuthProfile | None, request: gr.Request) -> str: | |
| query_params = parse_qs(urlparse(str(request.url)).query) | |
| user_id = query_params.get('user_id', [None])[0] | |
| if not user_id: | |
| return "# ❌ Missing Discord user ID." | |
| if profile is None: | |
| return "# ❌ You're not logged in with Hugging Face." | |
| async def upgrade_role(): | |
| await bot.wait_until_ready() | |
| guild = bot.get_guild(879548962464493619) | |
| if not guild: | |
| logging.error("Guild not ready") | |
| return | |
| try: | |
| member = await guild.fetch_member(int(user_id)) | |
| except discord.NotFound: | |
| logging.error("User %s not found in guild", user_id) | |
| return | |
| pending_role = guild.get_role(1380908157475360899) | |
| verified_role = guild.get_role(900063512829755413) | |
| if not verified_role: | |
| logging.error("Verified role missing") | |
| return | |
| # remove pending if present | |
| if pending_role and pending_role in member.roles: | |
| try: | |
| await member.remove_roles(pending_role, reason="Verification complete") | |
| except Exception: | |
| logging.exception("Remove pending failed") | |
| # always try to add verified | |
| if verified_role not in member.roles: | |
| try: | |
| await member.add_roles(verified_role, reason=f"HF verified {profile.username}") | |
| logging.info("Added Verified to %s", member) | |
| except discord.Forbidden: | |
| logging.error("Forbidden adding Verified (perm/hierarchy)") | |
| except Exception: | |
| logging.exception("Add Verified failed") | |
| if discord_loop: | |
| asyncio.run_coroutine_threadsafe(upgrade_role(), discord_loop) | |
| return f"# ✅ Verification successful! Welcome, {profile.username} 🎉" | |
| demo = gr.Blocks() | |
| with demo: | |
| try: | |
| TITLE = """<h1 align="center" id="space-title">🤗 Hugging Face Discord Verification</h1>""" | |
| gr.HTML(TITLE) | |
| with gr.Tabs(elem_classes="tab-buttons") as tabs: | |
| #------------------------------------------------------------------------------ | |
| with gr.TabItem("✅ Discord Verification", elem_id="verify-tab", id=2): | |
| login_button = gr.LoginButton() | |
| m1 = gr.Markdown() | |
| demo.load(verify_button, inputs=None, outputs=m1) | |
| def check_login_status(): | |
| try: | |
| return login_button.get_session().get("oauth_info", None) | |
| except AttributeError: | |
| return None | |
| def check_login_wrapper(): | |
| session = check_login_status() | |
| if session is None: | |
| return "Not logged in." | |
| else: | |
| return f"Logged in as {session.get('username', 'Unknown')}" | |
| login_button.click(check_login_wrapper, inputs=None, outputs=m1) | |
| #------------------------------------------------------------------------------ | |
| #with gr.TabItem("📈 Hub-only leaderboard", elem_id="hub-table", id=2): | |
| except Exception as e: | |
| print(f"gradio demo Error: {e}") | |
| demo.queue().launch() |