bentosmau commited on
Commit ·
4426eaf
1
Parent(s): 878dd3d
Add Roblox API integration to search for players and games
Browse filesIntegrates Roblox API endpoints into the chatbot to allow searching for player data (username, display name, ID, description, creation date, avatar URL, ban status) and game data (name, creator, current players, universe ID).
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: e3ff2484-bbd8-4aba-bea0-1940769b874a
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 3c675f32-c915-4c67-8d22-a6216abc09e1
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1739408b-93a5-479b-a658-30f2493b0467/e3ff2484-bbd8-4aba-bea0-1940769b874a/h035Gwm
Replit-Helium-Checkpoint-Created: true
- chat-app/app.py +67 -5
- chat-app/roblox_api.py +98 -0
chat-app/app.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
| 1 |
import os
|
|
|
|
| 2 |
import json
|
| 3 |
import time
|
| 4 |
import gradio as gr
|
| 5 |
from openai import OpenAI
|
|
|
|
| 6 |
|
| 7 |
client = OpenAI(
|
| 8 |
base_url=os.environ.get("AI_INTEGRATIONS_OPENAI_BASE_URL"),
|
|
@@ -32,14 +34,74 @@ def buscar_respuesta_personalizada(mensaje):
|
|
| 32 |
return entrada.get("respuesta")
|
| 33 |
return None
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
def responder(mensaje, historial):
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
|
|
|
| 38 |
if respuesta_personalizada:
|
| 39 |
historial = historial + [[mensaje, ""]]
|
| 40 |
for caracter in respuesta_personalizada:
|
| 41 |
historial[-1][1] += caracter
|
| 42 |
-
time.sleep(0.
|
| 43 |
yield historial, ""
|
| 44 |
return
|
| 45 |
|
|
@@ -82,7 +144,7 @@ with gr.Blocks(title="mdfjbots-neo-1") as demo:
|
|
| 82 |
|
| 83 |
with gr.Row():
|
| 84 |
entrada = gr.Textbox(
|
| 85 |
-
placeholder="Escribe tu mensaje aquí...",
|
| 86 |
show_label=False,
|
| 87 |
scale=9,
|
| 88 |
container=False,
|
|
@@ -96,9 +158,9 @@ with gr.Blocks(title="mdfjbots-neo-1") as demo:
|
|
| 96 |
gr.Examples(
|
| 97 |
examples=[
|
| 98 |
"Hola, ¿quién eres?",
|
| 99 |
-
"
|
|
|
|
| 100 |
"¿Cuál es la diferencia entre machine learning e inteligencia artificial?",
|
| 101 |
-
"Explícame cómo funciona una red neuronal de forma sencilla",
|
| 102 |
],
|
| 103 |
inputs=entrada,
|
| 104 |
label="Ejemplos de preguntas",
|
|
|
|
| 1 |
import os
|
| 2 |
+
import re
|
| 3 |
import json
|
| 4 |
import time
|
| 5 |
import gradio as gr
|
| 6 |
from openai import OpenAI
|
| 7 |
+
from roblox_api import buscar_jugador, buscar_juego, formatear_jugador, formatear_juego
|
| 8 |
|
| 9 |
client = OpenAI(
|
| 10 |
base_url=os.environ.get("AI_INTEGRATIONS_OPENAI_BASE_URL"),
|
|
|
|
| 34 |
return entrada.get("respuesta")
|
| 35 |
return None
|
| 36 |
|
| 37 |
+
PATRONES_JUGADOR = [
|
| 38 |
+
r"buscar\s+jugador\s+(.+)",
|
| 39 |
+
r"busca\s+jugador\s+(.+)",
|
| 40 |
+
r"jugador\s+de\s+roblox\s+(.+)",
|
| 41 |
+
r"usuario\s+de\s+roblox\s+(.+)",
|
| 42 |
+
r"perfil\s+de\s+roblox\s+(.+)",
|
| 43 |
+
r"buscar\s+usuario\s+(.+)",
|
| 44 |
+
r"busca\s+usuario\s+(.+)",
|
| 45 |
+
r"quien\s+es\s+(.+)\s+en\s+roblox",
|
| 46 |
+
r"quién\s+es\s+(.+)\s+en\s+roblox",
|
| 47 |
+
r"info\s+de\s+(.+)\s+roblox",
|
| 48 |
+
]
|
| 49 |
+
|
| 50 |
+
PATRONES_JUEGO = [
|
| 51 |
+
r"buscar\s+juego\s+(.+)",
|
| 52 |
+
r"busca\s+juego\s+(.+)",
|
| 53 |
+
r"juego\s+de\s+roblox\s+(.+)",
|
| 54 |
+
r"buscar\s+(.+)\s+en\s+roblox",
|
| 55 |
+
r"busca\s+(.+)\s+en\s+roblox",
|
| 56 |
+
r"información\s+del\s+juego\s+(.+)",
|
| 57 |
+
r"informacion\s+del\s+juego\s+(.+)",
|
| 58 |
+
]
|
| 59 |
+
|
| 60 |
+
def detectar_roblox(mensaje):
|
| 61 |
+
texto = mensaje.lower().strip()
|
| 62 |
+
for patron in PATRONES_JUGADOR:
|
| 63 |
+
m = re.search(patron, texto)
|
| 64 |
+
if m:
|
| 65 |
+
return "jugador", m.group(1).strip()
|
| 66 |
+
for patron in PATRONES_JUEGO:
|
| 67 |
+
m = re.search(patron, texto)
|
| 68 |
+
if m:
|
| 69 |
+
return "juego", m.group(1).strip()
|
| 70 |
+
return None, None
|
| 71 |
+
|
| 72 |
+
def emitir_respuesta(texto, historial, mensaje):
|
| 73 |
+
historial = historial + [[mensaje, ""]]
|
| 74 |
+
for caracter in texto:
|
| 75 |
+
historial[-1][1] += caracter
|
| 76 |
+
time.sleep(0.008)
|
| 77 |
+
yield historial, ""
|
| 78 |
+
|
| 79 |
def responder(mensaje, historial):
|
| 80 |
+
tipo_roblox, nombre_roblox = detectar_roblox(mensaje)
|
| 81 |
+
if tipo_roblox == "jugador":
|
| 82 |
+
historial = historial + [[mensaje, "🔍 Buscando jugador en Roblox..."]]
|
| 83 |
+
yield historial, ""
|
| 84 |
+
datos = buscar_jugador(nombre_roblox)
|
| 85 |
+
resultado = formatear_jugador(datos)
|
| 86 |
+
historial[-1][1] = resultado
|
| 87 |
+
yield historial, ""
|
| 88 |
+
return
|
| 89 |
+
|
| 90 |
+
if tipo_roblox == "juego":
|
| 91 |
+
historial = historial + [[mensaje, "🔍 Buscando juego en Roblox..."]]
|
| 92 |
+
yield historial, ""
|
| 93 |
+
datos = buscar_juego(nombre_roblox)
|
| 94 |
+
resultado = formatear_juego(datos)
|
| 95 |
+
historial[-1][1] = resultado
|
| 96 |
+
yield historial, ""
|
| 97 |
+
return
|
| 98 |
|
| 99 |
+
respuesta_personalizada = buscar_respuesta_personalizada(mensaje)
|
| 100 |
if respuesta_personalizada:
|
| 101 |
historial = historial + [[mensaje, ""]]
|
| 102 |
for caracter in respuesta_personalizada:
|
| 103 |
historial[-1][1] += caracter
|
| 104 |
+
time.sleep(0.008)
|
| 105 |
yield historial, ""
|
| 106 |
return
|
| 107 |
|
|
|
|
| 144 |
|
| 145 |
with gr.Row():
|
| 146 |
entrada = gr.Textbox(
|
| 147 |
+
placeholder="Escribe tu mensaje aquí... (ej: 'buscar jugador Builderman')",
|
| 148 |
show_label=False,
|
| 149 |
scale=9,
|
| 150 |
container=False,
|
|
|
|
| 158 |
gr.Examples(
|
| 159 |
examples=[
|
| 160 |
"Hola, ¿quién eres?",
|
| 161 |
+
"buscar jugador Builderman",
|
| 162 |
+
"buscar juego Adopt Me",
|
| 163 |
"¿Cuál es la diferencia entre machine learning e inteligencia artificial?",
|
|
|
|
| 164 |
],
|
| 165 |
inputs=entrada,
|
| 166 |
label="Ejemplos de preguntas",
|
chat-app/roblox_api.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
|
| 3 |
+
HEADERS = {
|
| 4 |
+
"User-Agent": "mdfjbots-neo-1/1.0",
|
| 5 |
+
"Accept": "application/json",
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
def buscar_jugador(nombre):
|
| 9 |
+
try:
|
| 10 |
+
url = "https://users.roblox.com/v1/usernames/users"
|
| 11 |
+
resp = requests.post(url, json={"usernames": [nombre], "excludeBannedUsers": False}, headers=HEADERS, timeout=8)
|
| 12 |
+
datos = resp.json()
|
| 13 |
+
usuarios = datos.get("data", [])
|
| 14 |
+
if not usuarios:
|
| 15 |
+
return None
|
| 16 |
+
usuario = usuarios[0]
|
| 17 |
+
user_id = usuario["id"]
|
| 18 |
+
perfil = obtener_perfil(user_id)
|
| 19 |
+
avatar_url = obtener_avatar(user_id)
|
| 20 |
+
return {
|
| 21 |
+
"id": user_id,
|
| 22 |
+
"nombre": usuario.get("name"),
|
| 23 |
+
"nombre_display": usuario.get("displayName"),
|
| 24 |
+
"descripcion": perfil.get("description", "Sin descripción."),
|
| 25 |
+
"creado": perfil.get("created", "Desconocido"),
|
| 26 |
+
"is_banned": perfil.get("isBanned", False),
|
| 27 |
+
"avatar": avatar_url,
|
| 28 |
+
}
|
| 29 |
+
except Exception as e:
|
| 30 |
+
return {"error": str(e)}
|
| 31 |
+
|
| 32 |
+
def obtener_perfil(user_id):
|
| 33 |
+
try:
|
| 34 |
+
url = f"https://users.roblox.com/v1/users/{user_id}"
|
| 35 |
+
resp = requests.get(url, headers=HEADERS, timeout=8)
|
| 36 |
+
return resp.json()
|
| 37 |
+
except Exception:
|
| 38 |
+
return {}
|
| 39 |
+
|
| 40 |
+
def obtener_avatar(user_id):
|
| 41 |
+
try:
|
| 42 |
+
url = f"https://thumbnails.roblox.com/v1/users/avatar-headshot?userIds={user_id}&size=420x420&format=Png&isCircular=false"
|
| 43 |
+
resp = requests.get(url, headers=HEADERS, timeout=8)
|
| 44 |
+
datos = resp.json()
|
| 45 |
+
return datos.get("data", [{}])[0].get("imageUrl", None)
|
| 46 |
+
except Exception:
|
| 47 |
+
return None
|
| 48 |
+
|
| 49 |
+
def buscar_juego(nombre):
|
| 50 |
+
try:
|
| 51 |
+
url = f"https://games.roblox.com/v1/games/list?keyword={nombre}&maxRows=5&startRows=0"
|
| 52 |
+
resp = requests.get(url, headers=HEADERS, timeout=8)
|
| 53 |
+
datos = resp.json()
|
| 54 |
+
juegos = datos.get("games", [])
|
| 55 |
+
if not juegos:
|
| 56 |
+
return None
|
| 57 |
+
juego = juegos[0]
|
| 58 |
+
return {
|
| 59 |
+
"nombre": juego.get("name"),
|
| 60 |
+
"creador": juego.get("creatorName"),
|
| 61 |
+
"jugando": juego.get("playerCount", 0),
|
| 62 |
+
"visitas": juego.get("totalUpVotes", 0),
|
| 63 |
+
"id": juego.get("universeId"),
|
| 64 |
+
}
|
| 65 |
+
except Exception as e:
|
| 66 |
+
return {"error": str(e)}
|
| 67 |
+
|
| 68 |
+
def formatear_jugador(datos):
|
| 69 |
+
if not datos:
|
| 70 |
+
return "No encontré ese jugador en Roblox. Verifica que el nombre de usuario sea correcto."
|
| 71 |
+
if "error" in datos:
|
| 72 |
+
return f"Hubo un error al buscar el jugador: {datos['error']}"
|
| 73 |
+
baneado = "⚠️ Este usuario está baneado." if datos.get("is_banned") else ""
|
| 74 |
+
descripcion = datos.get("descripcion") or "Sin descripción."
|
| 75 |
+
avatar = f"\n🖼️ **Avatar:** {datos['avatar']}" if datos.get("avatar") else ""
|
| 76 |
+
return (
|
| 77 |
+
f"🎮 **Jugador encontrado en Roblox:**\n\n"
|
| 78 |
+
f"👤 **Usuario:** {datos['nombre']}\n"
|
| 79 |
+
f"✏️ **Nombre display:** {datos['nombre_display']}\n"
|
| 80 |
+
f"🆔 **ID:** {datos['id']}\n"
|
| 81 |
+
f"📝 **Descripción:** {descripcion}\n"
|
| 82 |
+
f"📅 **Cuenta creada:** {datos['creado'][:10] if datos['creado'] != 'Desconocido' else 'Desconocido'}"
|
| 83 |
+
f"{avatar}\n"
|
| 84 |
+
f"{baneado}"
|
| 85 |
+
).strip()
|
| 86 |
+
|
| 87 |
+
def formatear_juego(datos):
|
| 88 |
+
if not datos:
|
| 89 |
+
return "No encontré ese juego en Roblox. Intenta con otro nombre."
|
| 90 |
+
if "error" in datos:
|
| 91 |
+
return f"Hubo un error al buscar el juego: {datos['error']}"
|
| 92 |
+
return (
|
| 93 |
+
f"🕹️ **Juego encontrado en Roblox:**\n\n"
|
| 94 |
+
f"🎮 **Nombre:** {datos['nombre']}\n"
|
| 95 |
+
f"👤 **Creador:** {datos['creador']}\n"
|
| 96 |
+
f"👥 **Jugando ahora:** {datos['jugando']:,}\n"
|
| 97 |
+
f"🆔 **Universe ID:** {datos['id']}"
|
| 98 |
+
)
|