Respuesta corta. Añade aiomysql a tu bot de discord.py, crea un único pool en setup_hook con autocommit=True y ejecuta todas las consultas con él, usando marcadores %s. Sin autocommit=True (o un commit() después de cada escritura), aiomysql no guarda tus INSERT. En PlayNHost, todos los planes de bot incluyen una base de datos MySQL: créala en la página Bases de datos, pega sus datos en el panel Entorno y el código de abajo funciona tal cual. La base de datos acepta conexiones de tus servidores de PlayNHost y de phpMyAdmin, no de tu propio ordenador. Probamos este código el 6 de noviembre de 2026 con discord.py 2.7.1, aiomysql 0.3.2 y Python 3.12, contra MariaDB 10.11 con una cuenta de base de datos configurada como la nuestra (20 conexiones, límite de consulta de 120 segundos): el arranque, la tabla y el comando /warn, pero no un inicio de sesión real en Discord.
Un plan de bot de PlayNHost con el tipo Python Bot. Todos los planes incluyen una base de datos.
1. Crea la base de datos
Abre tu servidor de bot en el panel y haz clic en Bases de datos.
Haz clic en Base de datos nueva. La Etiqueta es opcional; Se creará como muestra el nombre completo. Haz clic en Crear base de datos.
Se abre la página de la nueva base de datos (más adelante, haz clic en ella en Tus bases de datos). Su tarjeta Conexión muestra Host, Puerto, Base de datos, Nombre de usuario y Contraseña. En Config lista para usar, Config para incluye un fragmento Python (aiomysql) con los mismos valores ya rellenados.
2. Guarda los datos en Entorno
Abre la página Bot, el panel Entorno, y haz clic en Pegar un archivo .env.
Pega estas cinco líneas con los valores de la tarjeta Conexión:
import os
import aiomysql
import discord
from discord import app_commands
class Bot(discord.Client):
def __init__(self):
super().__init__(intents=discord.Intents.default())
self.tree = app_commands.CommandTree(self)
self.pool = None
async def setup_hook(self):
# One pool for the whole bot. The values come from the Environment pane.
self.pool = await aiomysql.create_pool(
host=os.environ["DB_HOST"],
port=int(os.environ["DB_PORT"]),
user=os.environ["DB_USER"],
password=os.environ["DB_PASSWORD"],
db=os.environ["DB_NAME"],
autocommit=True,
maxsize=5,
)
async with self.pool.acquire() as conn:
async with conn.cursor() as cur:
await cur.execute(
"CREATE TABLE IF NOT EXISTS warnings ("
"id INT AUTO_INCREMENT PRIMARY KEY, "
"guild_id BIGINT UNSIGNED NOT NULL, "
"user_id BIGINT UNSIGNED NOT NULL, "
"reason VARCHAR(200) NOT NULL, "
"created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
"KEY guild_user (guild_id, user_id))"
)
print("Database ready")
await self.tree.sync()
async def close(self):
if self.pool is not None:
self.pool.close()
await self.pool.wait_closed()
await super().close()
bot = Bot()
@bot.tree.command(description="Warn a member and count their warnings")
@app_commands.guild_only()
@app_commands.describe(user="Who to warn", reason="Why")
async def warn(interaction: discord.Interaction, user: discord.Member, reason: app_commands.Range[str, 1, 200]):
async with bot.pool.acquire() as conn:
async with conn.cursor() as cur:
await cur.execute(
"INSERT INTO warnings (guild_id, user_id, reason) VALUES (%s, %s, %s)",
(interaction.guild_id, user.id, reason),
)
await cur.execute(
"SELECT COUNT(*) FROM warnings WHERE guild_id = %s AND user_id = %s",
(interaction.guild_id, user.id),
)
(total,) = await cur.fetchone()
await interaction.response.send_message(f"{user.mention} has {total} warning(s).")
@bot.event
async def on_ready():
print(f"Logged in as {bot.user}")
bot.run(os.environ["DISCORD_TOKEN"])
Qué hace: discord.py ejecuta setup_hook una sola vez, durante el inicio de sesión y antes de que llegue cualquier evento, así que el pool y la tabla warnings existen antes de que llegue el primer comando. Después registra /warn en Discord. @app_commands.guild_only() deja el comando fuera de los mensajes directos, donde no hay ningún servidor al que asociar la advertencia. Los ID de Discord caben en BIGINT UNSIGNED. Los marcadores %s los rellena el controlador, así que un motivo como '); DROP TABLE warnings; -- se guarda como texto normal. Nunca construyas el SQL con una f-string.
4. Ejecútalo
Sube bot.py y requirements.txt en Archivos.
Pulsa Iniciar y abre Consola. Al arrancar se instalan los paquetes de requirements.txt y luego aparecen [PlayNHost] Starting your bot y Database ready.
En Discord, escribe /warn. Para ver las filas, abre la página de la base de datos y haz clic en Abrir en phpMyAdmin.
La trampa del autocommit
aiomysql abre las conexiones con autocommit desactivado. Un INSERT se queda entonces dentro de una transacción abierta. Cuando la conexión vuelve al pool con esa transacción todavía abierta, aiomysql la cierra y MariaDB descarta la fila sin confirmar. En nuestra prueba, una fila insertada en un pool sin autocommit=True fue vista por 0 conexiones más. Pasa autocommit=True a create_pool, como arriba, o llama a await conn.commit() después de tus escrituras.
Límites que importan para tu pool
20 conexiones a la vez por usuario de base de datos. Cuenta todo proceso que use el mismo acceso. La conexión 21 se rechaza con (1226, "User '...' has exceeded the 'max_user_connections' resource (current value: 20)"). maxsize=5 mantiene el bot muy por debajo.
Una consulta que dura más de 120 segundos se detiene. MariaDB la termina con el error 1969, Query execution was interrupted (max_statement_time exceeded). En nuestra prueba llegó a los 120.03 segundos. Las consultas de un bot suelen tardar milisegundos, así que esto solo afecta a una consulta descontrolada.
500 MB por base de datos. La página Bases de datos muestra cuánto se usa. Al llegar a 500 MB, se bloquean las escrituras hasta que borres filas.
Las copias de seguridad del servidor no incluyen la base de datos. Expórtala desde phpMyAdmin de vez en cuando.
Errores y qué significan
Estos son los errores exactos que dio aiomysql en nuestra prueba (vienen de PyMySQL, que aiomysql usa por debajo):
pymysql.err.OperationalError (1045, "Access denied for user ..."): DB_USER o DB_PASSWORD incorrectos. Tras Restablecer contraseña en la tarjeta Conexión, la contraseña antigua deja de funcionar al instante: cambia DB_PASSWORD y reinicia el bot.
pymysql.err.OperationalError (2003, "Can't connect to MySQL server on ..."): host o puerto incorrectos, o una conexión desde fuera de PlayNHost. La base de datos solo acepta conexiones de servidores de PlayNHost y de phpMyAdmin, así que esto es también lo que recibes en tu propio ordenador.
(1226, ... 'max_user_connections' ...): hay demasiadas conexiones abiertas, mira los límites de arriba.
Una variable que falte en Entorno detiene el bot antes de conectarse, con KeyError: 'DB_HOST' o el nombre de la que falta, porque el código lee cada valor con os.environ[...]. Añádela y reinicia el bot: las variables se aplican la próxima vez que arranca.
Instalar discord.py y aiomysql con pip desde cero alcanzó un máximo de 78 MB de memoria en nuestra prueba, en un contenedor limitado a 512 MB como nuestro plan de bot más pequeño. Lo que necesita el bot en marcha depende sobre todo de en cuántos servidores esté: ¿Cuánta RAM necesita un bot de Discord?
Usamos cookies para que el sitio funcione, entender cómo se utiliza y medir nuestro marketing. Puedes aceptar todo, aceptar solo las cookies esenciales o elegir por categoría.Usamos cookies para que el sitio funcione, entender cómo se utiliza y medir nuestro marketing. Aquí están activadas por defecto: puedes desactivar lo que no quieras.Leer nuestra política de privacidad