Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added examples/command_example_bridge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/command_example_ping.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/command_example_rank.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from src.nerdearla import nerdearlacharlasfunc
from src.shithappens import ShitHappens
from src.f1 import f1func
from src.mundial import mundialfun

#IMPORT DE FUNCIONES PARA SISTEMA DE JOBS
from src.jobsearch import jobsearchfunc
Expand Down Expand Up @@ -62,6 +63,7 @@
from src.ctxcommands.ctxnerdearla import nerdearlafunctx
from src.ctxcommands.ctxjobsearch import jobsearchfunctx
from src.ctxcommands.ctxf1 import formula1ctxfunc
from src.ctxcommands.ctxmundial import mundialfunctx


# BOFH - Discord community bot for Sysarmy
Expand Down Expand Up @@ -633,6 +635,11 @@ async def on_message(message):
async def help(ctx, texto):
await helpfunctx(ctx, texto)

# COMANDO MUNDIAL
@bot.command()
async def mundial(ctx):
await mundialfunctx(ctx)

# COMANDO CLIMA
@bot.command()
async def clima(ctx, ciudad):
Expand Down Expand Up @@ -785,6 +792,15 @@ async def tasacaucho(interaction: Interaction):
print(f"Limite de API calls excedido. Ultimo call hecho por {interaction.user}")
await interaction.response.send_message("Comando /caucho: Limite de API calls excedido. Sori el CTO no nos dio budget.")

# COMANDO MUNDIAL (SLASH)
@bot.tree.command(name="mundial", description="Partidos del dia de la Copa del Mundo FIFA 2026")
async def mundial_slash(interaction: discord.Interaction):
try:
await interaction.response.send_message(embed=await mundialfun(interaction))
except Exception as e:
print(f"Error en /mundial: {e}")
await interaction.response.send_message("Comando /mundial: Error al consultar los partidos. Intenta mas tarde.")

# COMANDO DOLAR
@bot.tree.command(name="preciodolar", description="Cotizacion del dolar")
async def preciodolar(interaction: Interaction):
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ platformdirs==4.3.8
portend==3.2.0
proto-plus==1.26.0
protobuf==5.29.6
pyasn1==0.6.2
pyasn1==0.6.3
pyasn1_modules==0.4.1
pyngrok==7.2.3
pyparsing==3.2.1
Expand Down
5 changes: 4 additions & 1 deletion src/ctxcommands/ctxhelp.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ async def helpfunctx(ctx, texto):
if texto is None:

mensajeayuda_general = """Informacion general sobre los comandos del bot de Sysarmy
!dolar !cripto !euro !fulbo !pesos !clima !subte !underground !feriadoar !feriadocl !feriadoes !feriadomx !feriadouy !q !qsearch !qadd !rank !kgivers !kgiven !karma !birras !flip !shrug !nerdearla !jobs !f1
!dolar !cripto !euro !fulbo !pesos !clima !subte !underground !feriadoar !feriadocl !feriadoes !feriadomx !feriadouy !q !qsearch !qadd !rank !kgivers !kgiven !karma !birras !flip !shrug !nerdearla !jobs !f1 !mundial
Mas detalles en el canal #help-bot-commands de Discord, dentro de la seccion de Welcome! - o ejecutando /help desde Discord"""
await ctx.send(mensajeayuda_general)

Expand Down Expand Up @@ -71,6 +71,9 @@ async def helpfunctx(ctx, texto):
elif texto == "f1":
await ctx.send("'!f1 carrera' devuelve resultados del ultimo GP || '!f1 temporada' las posiciones generales")

elif texto == "mundial":
await ctx.send("Pone !mundial para consultar los partidos del dia de la Copa del Mundo FIFA 2026. No requiere argumentos")

# Log
print(FechaActual)
print (f'Se ha ejecutado el comando !help para {texto}')
100 changes: 100 additions & 0 deletions src/ctxcommands/ctxmundial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import datetime
import os
import json
import http.client

from dotenv import load_dotenv


load_dotenv()
MUNDIAL_COMPETITION_CODE = "WC"


async def mundialfunctx(ctx):
"""Comando !mundial (ctx): responde en texto plano para IRC/bridge."""
try:
fulbo_token = os.getenv("FULBO_token")
if not fulbo_token:
await ctx.send("Error: No se encontro FULBO_token en el .env")
return

hoy = datetime.date.today()
hoy_string = hoy.strftime("%Y-%m-%d")

connection = http.client.HTTPSConnection("api.football-data.org")
headers = {"X-Auth-Token": f"{fulbo_token}"}
connection.request(
"GET",
f"/v4/competitions/{MUNDIAL_COMPETITION_CODE}/matches?dateFrom={hoy_string}&dateTo={hoy_string}",
None,
headers,
)
api_response = connection.getresponse()
raw_body = api_response.read().decode("utf-8", errors="replace")

if api_response.status != 200:
await ctx.send(f"Error API football-data ({api_response.status}). Intenta mas tarde.")
return

if not raw_body.strip():
await ctx.send("La API devolvio una respuesta vacia. Intenta mas tarde.")
return

try:
data = json.loads(raw_body)
except json.JSONDecodeError:
await ctx.send("La API devolvio una respuesta invalida. Intenta mas tarde.")
return
partidos = data.get("matches", [])
competition_name = data.get("competition", {}).get("name", "Copa del Mundo FIFA")

print(f"{datetime.datetime.now()} - Se ejecuto el comando !mundial")

if not partidos:
await ctx.send(f"🏆 {competition_name} - {hoy_string}: no hay partidos programados para hoy.")
return

mensaje = f"🏆 {competition_name} - Partidos del {hoy_string}\n"
estados_en_juego = {"IN_PLAY", "PAUSED", "EXTRA_TIME", "PENALTY_SHOOTOUT"}
estados_finalizado = {"FINISHED", "AWARDED"}
estado_suspendido = {"POSTPONED", "SUSPENDED", "CANCELLED"}

for partido in partidos:
equipo_local = partido["homeTeam"]["name"]
equipo_visitante = partido["awayTeam"]["name"]
estado = partido.get("status", "")
fecha_iso = partido.get("utcDate", "")
ronda = partido.get("stage", "")
matchday = partido.get("matchday")

try:
dt = datetime.datetime.fromisoformat(fecha_iso.replace("Z", "+00:00"))
hora = dt.strftime("%H:%M UTC")
except Exception:
hora = "??:??"

goles_local = partido.get("score", {}).get("fullTime", {}).get("home")
goles_visitante = partido.get("score", {}).get("fullTime", {}).get("away")
goles_local_txt = "-" if goles_local is None else goles_local
goles_visitante_txt = "-" if goles_visitante is None else goles_visitante

if estado in estados_finalizado:
resultado = f"{goles_local_txt}-{goles_visitante_txt} Final"
elif estado in estados_en_juego:
resultado = f"{goles_local_txt}-{goles_visitante_txt} En juego"
elif estado == "SCHEDULED":
resultado = f"{hora} hs"
elif estado in estado_suspendido:
resultado = f"Suspendido/Postergado ({estado})"
else:
resultado = hora

ronda_txt = f" [{ronda}]" if ronda else ""
fecha_txt = f" [Fecha {matchday}]" if matchday is not None else ""
mensaje += f"{equipo_local} vs {equipo_visitante}: {resultado}{ronda_txt}{fecha_txt}\n"

await ctx.send(mensaje.strip())

except Exception as e:
print(f"Error en !mundial: {e}")
await ctx.send("Error al consultar los partidos del Mundial. Intenta mas tarde.")
138 changes: 138 additions & 0 deletions src/mundial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import discord
from discord import Embed
import json
import http.client
import datetime
import os
from dotenv import load_dotenv

load_dotenv()

# Codigo de competencia para FIFA World Cup en football-data.org
MUNDIAL_COMPETITION_CODE = "WC"

async def mundialfun(interaction):
try:
fulbo_token = os.getenv("FULBO_token")
if not fulbo_token:
embed = Embed(
title="🏆 Copa del Mundo FIFA 2026",
description="Error: No se encontro la API key. Configura FULBO_token en el .env",
color=discord.Color.red()
)
return embed

hoy = datetime.date.today()
hoy_string = hoy.strftime("%Y-%m-%d")

connection = http.client.HTTPSConnection("api.football-data.org")
headers = {"X-Auth-Token": f"{fulbo_token}"}
connection.request(
"GET",
f"/v4/competitions/{MUNDIAL_COMPETITION_CODE}/matches?dateFrom={hoy_string}&dateTo={hoy_string}",
None,
headers,
)
api_response = connection.getresponse()
raw_body = api_response.read().decode("utf-8", errors="replace")

if api_response.status != 200:
error_embed = Embed(
title="🏆 Copa del Mundo FIFA 2026",
description=f"Error API football-data ({api_response.status}). Intenta mas tarde.",
color=discord.Color.red(),
)
return error_embed

if not raw_body.strip():
error_embed = Embed(
title="🏆 Copa del Mundo FIFA 2026",
description="La API devolvio una respuesta vacia. Intenta mas tarde.",
color=discord.Color.red(),
)
return error_embed

try:
data = json.loads(raw_body)
except json.JSONDecodeError:
error_embed = Embed(
title="🏆 Copa del Mundo FIFA 2026",
description="La API devolvio una respuesta invalida. Intenta mas tarde.",
color=discord.Color.red(),
)
return error_embed

# Log
print(f"{datetime.datetime.now()} - Se ejecuto el comando /mundial")

partidos = data.get("matches", [])
competition_name = data.get("competition", {}).get("name", "Copa del Mundo FIFA")

embed = Embed(
title=f"🏆 {competition_name}",
description=f"Partidos del {hoy_string}",
color=discord.Color.gold()
)

if not partidos:
embed.add_field(
name="Sin partidos",
value="No hay partidos programados para hoy.",
inline=False
)
return embed

for partido in partidos:
equipo_local = partido["homeTeam"]["name"]
equipo_visitante = partido["awayTeam"]["name"]
estado = partido.get("status", "")
fecha_iso = partido.get("utcDate", "")
ronda = partido.get("stage", "")
matchday = partido.get("matchday")

# Parseamos la hora del partido
try:
dt = datetime.datetime.fromisoformat(fecha_iso.replace("Z", "+00:00"))
hora = dt.strftime("%H:%M UTC")
except Exception:
hora = "??:??"

# Mostramos el marcador segun el estado del partido
goles_local = partido.get("score", {}).get("fullTime", {}).get("home")
goles_visitante = partido.get("score", {}).get("fullTime", {}).get("away")
goles_local_txt = "-" if goles_local is None else goles_local
goles_visitante_txt = "-" if goles_visitante is None else goles_visitante

estados_en_juego = {"IN_PLAY", "PAUSED", "EXTRA_TIME", "PENALTY_SHOOTOUT"}
estados_finalizado = {"FINISHED", "AWARDED"}
estado_suspendido = {"POSTPONED", "SUSPENDED", "CANCELLED"}

if estado in estados_finalizado:
resultado = f"{goles_local_txt} - {goles_visitante_txt} (Final)"
elif estado in estados_en_juego:
resultado = f"{goles_local_txt} - {goles_visitante_txt} (En juego)"
elif estado == "SCHEDULED":
resultado = f"{hora} hs"
elif estado in estado_suspendido:
resultado = f"Suspendido/Postergado ({estado})"
else:
resultado = hora

detalle_fecha = f" - Fecha {matchday}" if matchday is not None else ""

embed.add_field(
name=f"⚽ {equipo_local} vs {equipo_visitante}",
value=f"{resultado}\n_{ronda}{detalle_fecha}_",
inline=False
)

return embed

except Exception as e:
print(f"Error en mundialfun: {e}")
embed = Embed(
title="🏆 Copa del Mundo FIFA 2026",
description="Error al consultar los partidos. Intenta mas tarde.",
color=discord.Color.red()
)
return embed