NapNodes

← Guías de Bots de Discord

Bots de Discord · actualizada el 2 de noviembre de 2026

Bot de mensajes programados para Discord: avisos y recordatorios a su hora (Python y JavaScript)

El «¡buenos días!» de cada mañana, el aviso del evento de los viernes o el recordatorio de votar: los mensajes que se repiten son los primeros que se olvidan. Con este bot, quien gestiona el servidor los programa una vez desde Discord (canal, hora, texto y qué días) y el bot los publica solos, sin repetirlos aunque se reinicie. Código entero en discord.py y en discord.js, sin base de datos, sin librerías de fechas y sin intents privilegiados.

¿Se lo pasas a tu grupo?WhatsApp

Qué hace el bot

Tres comandos: /programar para crear un mensaje (canal, hora, texto y qué días), /programados para ver los que hay con su número y /quitar-programado para borrar uno. De serie solo los ve quien gestiona el servidor: nadie más puede llenar un canal de anuncios.

Sirve para todo lo que se repite: los buenos días, el aviso del evento semanal, el recordatorio de votar por el servidor, el «hay reinicio a las 6» de tu servidor de juego o la pregunta del día. Cada mensaje sale a su hora todos los días, de lunes a viernes o solo el fin de semana.

Apunta qué día ha mandado ya cada mensaje, así que un reinicio no lo repite. Si el bot estaba apagado a la hora, lo manda al volver, pero solo si no han pasado más de 2 horas: un «buenos días» a medianoche no le sirve a nadie. Todo va en programados.json, junto al bot, y un mismo bot lleva los mensajes de varios servidores a la vez (hasta 15 en cada uno).

¿Prefieres no montarlo tú? Hosting de bots de Discord desde 1,50 € al mes, sin permanencia y con devolución íntegra en 72 horas. O crea un bot de Discord gratis, sin tarjeta.

1. Crea el bot e invítalo

  1. Crea la aplicación y saca el token

    En discord.com/developers/applications, «New Application»; en la pestaña «Bot», «Reset Token» y copia el token. Es la contraseña del bot: no lo compartas ni lo subas a GitHub. Si es tu primer bot, el proceso con calma está en tu primer bot en Python o en JavaScript. Los «Privileged Gateway Intents» se quedan apagados.

  2. Invítalo con dos permisos

    En «OAuth2» → «URL Generator», marca «bot» y «applications.commands», y en los permisos «View Channels» y «Send Messages» (en total, el número 3072). Con la calculadora de permisos de bot los ves en castellano y te da el enlace.

2. El bot en Python (discord.py)

Tres ficheros en una carpeta. El token va en .env, sin comillas. Hace falta Python 3.10 o más nuevo. tzdata trae las zonas horarias por si el sistema donde corre el bot no las tiene. Las horas son las de ZONA; para otro país, cámbiala (por ejemplo, America/Mexico_City o Atlantic/Canary).

requirements.txt
discord.py
python-dotenv
tzdata
.env
DISCORD_TOKEN=pega-aquí-tu-token
app.py
import json
import os
import re
from datetime import datetime
from pathlib import Path
from typing import Optional
from zoneinfo import ZoneInfo

import discord
from discord import app_commands
from discord.ext import tasks
from dotenv import load_dotenv

load_dotenv()  # lee el token del fichero .env

# Los mensajes se guardan junto al bot: un reinicio no los pierde.
DATOS = Path("programados.json")
ZONA = ZoneInfo("Europe/Madrid")  # las horas son de esta zona, no la del ordenador
RETRASO_MAX = 120  # si estaba apagado a la hora, lo manda al volver, pero como mucho 2 horas tarde
MAXIMO = 15  # mensajes programados por servidor
DIAS = {"todos": "todos los días", "laborables": "de lunes a viernes", "finde": "sábados y domingos"}
TOCA = {"todos": range(7), "laborables": range(5), "finde": (5, 6)}  # 0 es lunes
HORA_VALIDA = re.compile(r"^([01]?\d|2[0-3]):([0-5]\d)$")
AVISOS = discord.AllowedMentions(everyone=False, users=True, roles=True)  # nunca @everyone


def leer() -> dict:
    return json.loads(DATOS.read_text("utf-8")) if DATOS.exists() else {}


def guardar(datos: dict) -> None:
    DATOS.write_text(json.dumps(datos, indent=2, ensure_ascii=False), "utf-8")


def minutos(hora: str) -> int:
    h, m = hora.split(":")
    return int(h) * 60 + int(m)


class Bot(discord.Client):
    def __init__(self):
        super().__init__(intents=discord.Intents.default())  # sin intents privilegiados
        self.tree = app_commands.CommandTree(self)

    async def setup_hook(self):
        await self.tree.sync()  # publica los comandos
        enviar.start()


bot = Bot()


@tasks.loop(minutes=1)  # mira cada minuto si a algún mensaje le toca
async def enviar():
    ahora = datetime.now(ZONA)
    hoy = ahora.date().isoformat()
    ya = ahora.hour * 60 + ahora.minute
    for guild_id, mensajes in leer().items():
        for m in mensajes:
            tarde = ya - minutos(m["hora"])
            if m["ultimo"] == hoy or not 0 <= tarde <= RETRASO_MAX or ahora.weekday() not in TOCA[m["dias"]]:
                continue
            canal = bot.get_channel(m["canal"])
            if canal is None:
                continue  # canal borrado, o el bot ya no está en ese servidor
            try:
                await canal.send(m["texto"], allowed_mentions=AVISOS)
            except discord.HTTPException as error:
                print(f"No he podido mandar el {m['id']} en {guild_id}: {error!r}")
                continue  # se vuelve a intentar en la siguiente vuelta
            fresco = leer()  # se relee por si alguien ha usado un comando mientras tanto
            for f in fresco.get(guild_id, []):
                if f["id"] == m["id"]:
                    f["ultimo"] = hoy
            guardar(fresco)


@enviar.before_loop
async def antes_de_enviar():
    await bot.wait_until_ready()


@bot.tree.command(name="programar", description="Programa un mensaje que el bot publica a una hora")
@app_commands.guild_only()
@app_commands.default_permissions(manage_guild=True)  # de serie, solo quien gestiona el servidor
@app_commands.describe(canal="Dónde se publica", hora="Hora de España, como 09:00 o 21:30",
                       texto="El mensaje; escribe \\n para saltar de línea", dias="Qué días (de serie, todos)")
@app_commands.choices(dias=[app_commands.Choice(name=nombre, value=clave) for clave, nombre in DIAS.items()])
async def programar(interaction: discord.Interaction, canal: discord.TextChannel, hora: str,
                    texto: app_commands.Range[str, 1, 1800], dias: Optional[app_commands.Choice[str]] = None):
    valida = HORA_VALIDA.match(hora.strip())
    if not valida:
        await interaction.response.send_message("Escribe la hora así: 09:00 o 21:30.", ephemeral=True)
        return
    hora = f"{int(valida[1]):02d}:{valida[2]}"
    if not canal.permissions_for(interaction.guild.me).send_messages:
        await interaction.response.send_message(f"No puedo escribir en {canal.mention}: dame «Ver canal» y «Enviar mensajes» allí.", ephemeral=True)
        return
    datos = leer()
    mensajes = datos.setdefault(str(interaction.guild_id), [])
    if len(mensajes) >= MAXIMO:
        await interaction.response.send_message(f"Ya hay {MAXIMO} mensajes programados: quita alguno con /quitar-programado.", ephemeral=True)
        return
    ahora = datetime.now(ZONA)
    # Si hoy ya ha pasado esa hora, empieza mañana (si no, saldría ahora mismo).
    pasada = ahora.hour * 60 + ahora.minute >= minutos(hora)
    nuevo = {"id": max((m["id"] for m in mensajes), default=0) + 1, "canal": canal.id, "hora": hora,
             "dias": dias.value if dias else "todos", "texto": texto.replace("\\n", "\n"),
             "ultimo": ahora.date().isoformat() if pasada else None}
    mensajes.append(nuevo)
    guardar(datos)
    await interaction.response.send_message(
        f"Programado (n.º {nuevo['id']}): {DIAS[nuevo['dias']]} a las {hora} en {canal.mention}.", ephemeral=True)


@bot.tree.command(name="programados", description="Los mensajes programados de este servidor")
@app_commands.guild_only()
@app_commands.default_permissions(manage_guild=True)
async def programados(interaction: discord.Interaction):
    mensajes = sorted(leer().get(str(interaction.guild_id), []), key=lambda m: m["hora"])
    if not mensajes:
        await interaction.response.send_message("No hay ninguno. Crea uno con /programar.", ephemeral=True)
        return
    lineas = []
    for m in mensajes:
        resumen = m["texto"].replace("\n", " ")
        if len(resumen) > 60:
            resumen = resumen[:60] + "…"
        lineas.append(f"**{m['id']}** · {m['hora']}, {DIAS[m['dias']]}, en <#{m['canal']}>: {resumen}")
    await interaction.response.send_message("\n".join(lineas), ephemeral=True,
                                            allowed_mentions=discord.AllowedMentions.none())


@bot.tree.command(name="quitar-programado", description="Quita un mensaje programado")
@app_commands.guild_only()
@app_commands.default_permissions(manage_guild=True)
@app_commands.describe(numero="El número que sale en /programados")
async def quitar_programado(interaction: discord.Interaction, numero: int):
    datos = leer()
    mensajes = datos.get(str(interaction.guild_id), [])
    quedan = [m for m in mensajes if m["id"] != numero]
    if len(quedan) == len(mensajes):
        await interaction.response.send_message(f"No hay ningún mensaje con el número {numero}. Míralos con /programados.", ephemeral=True)
        return
    datos[str(interaction.guild_id)] = quedan
    guardar(datos)
    await interaction.response.send_message(f"Quitado el {numero}: ya no se publicará.", ephemeral=True)


@bot.event
async def on_ready():
    print(f"Conectado como {bot.user}")


bot.run(os.environ["DISCORD_TOKEN"])

El mismo bot en JavaScript (discord.js)

package.json, .env e index.js. Hace falta Node.js 20 o más nuevo. La hora y el día de la semana en la zona elegida salen de Intl.DateTimeFormat, que ya viene con Node: no hace falta node-cron ni ninguna librería de fechas.

package.json
{
  "name": "bot-mensajes-programados",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "discord.js": "^14.16.0",
    "dotenv": "^16.4.0"
  }
}
.env
DISCORD_TOKEN=pega-aquí-tu-token
index.js
require("dotenv").config(); // lee el token del fichero .env
const fs = require("node:fs");
const path = require("node:path");
const { ChannelType, Client, Events, GatewayIntentBits, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder } = require("discord.js");

// Los mensajes se guardan junto al bot: un reinicio no los pierde.
const DATOS = path.join(__dirname, "programados.json");
const ZONA = "Europe/Madrid"; // las horas son de esta zona, no la del ordenador
const RETRASO_MAX = 120; // si estaba apagado a la hora, lo manda al volver, pero como mucho 2 horas tarde
const MAXIMO = 15; // mensajes programados por servidor
const DIAS = { todos: "todos los días", laborables: "de lunes a viernes", finde: "sábados y domingos" };
const TOCA = { todos: [0, 1, 2, 3, 4, 5, 6], laborables: [1, 2, 3, 4, 5], finde: [0, 6] }; // 0 es domingo
const HORA_VALIDA = /^([01]?\d|2[0-3]):([0-5]\d)$/;
const AVISOS = { parse: ["users", "roles"] }; // nunca @everyone

const leer = () => (fs.existsSync(DATOS) ? JSON.parse(fs.readFileSync(DATOS, "utf8")) : {});
const guardar = (datos) => fs.writeFileSync(DATOS, JSON.stringify(datos, null, 2));
const minutos = (hora) => Number(hora.slice(0, 2)) * 60 + Number(hora.slice(3));

// La fecha («AAAA-MM-DD»), los minutos desde medianoche y el día de la semana, en la zona elegida.
function ahora() {
  const partes = new Intl.DateTimeFormat("en-US", { timeZone: ZONA, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", weekday: "short", hourCycle: "h23" }).formatToParts(new Date());
  const p = Object.fromEntries(partes.map((x) => [x.type, x.value]));
  const semana = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].indexOf(p.weekday);
  return { hoy: `${p.year}-${p.month}-${p.day}`, ya: Number(p.hour) * 60 + Number(p.minute), semana };
}

const client = new Client({ intents: [GatewayIntentBits.Guilds] }); // sin intents privilegiados

async function enviar() {
  const { hoy, ya, semana } = ahora();
  for (const [guildId, mensajes] of Object.entries(leer())) {
    for (const m of mensajes) {
      const tarde = ya - minutos(m.hora);
      if (m.ultimo === hoy || tarde < 0 || tarde > RETRASO_MAX || !TOCA[m.dias].includes(semana)) continue;
      const canal = client.channels.cache.get(m.canal);
      if (!canal) continue; // canal borrado, o el bot ya no está en ese servidor
      try {
        await canal.send({ content: m.texto, allowedMentions: AVISOS });
      } catch (error) {
        console.error(`No he podido mandar el ${m.id} en ${guildId}:`, error);
        continue; // se vuelve a intentar en la siguiente vuelta
      }
      const fresco = leer(); // se relee por si alguien ha usado un comando mientras tanto
      for (const f of fresco[guildId] ?? []) if (f.id === m.id) f.ultimo = hoy;
      guardar(fresco);
    }
  }
}

const comandos = [
  new SlashCommandBuilder().setName("programar").setDescription("Programa un mensaje que el bot publica a una hora")
    .setContexts(InteractionContextType.Guild)
    .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild) // de serie, solo quien gestiona el servidor
    .addChannelOption((o) => o.setName("canal").setDescription("Dónde se publica").setRequired(true).addChannelTypes(ChannelType.GuildText))
    .addStringOption((o) => o.setName("hora").setDescription("Hora de España, como 09:00 o 21:30").setRequired(true))
    .addStringOption((o) => o.setName("texto").setDescription("El mensaje; escribe \\n para saltar de línea").setRequired(true).setMaxLength(1800))
    .addStringOption((o) => o.setName("dias").setDescription("Qué días (de serie, todos)").addChoices(...Object.entries(DIAS).map(([value, name]) => ({ name, value })))),
  new SlashCommandBuilder().setName("programados").setDescription("Los mensajes programados de este servidor")
    .setContexts(InteractionContextType.Guild).setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild),
  new SlashCommandBuilder().setName("quitar-programado").setDescription("Quita un mensaje programado")
    .setContexts(InteractionContextType.Guild).setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
    .addIntegerOption((o) => o.setName("numero").setDescription("El número que sale en /programados").setRequired(true)),
];

client.on(Events.InteractionCreate, async (i) => {
  if (!i.isChatInputCommand()) return;
  const privado = { flags: MessageFlags.Ephemeral };
  const datos = leer();
  const mensajes = (datos[i.guildId] ??= []);

  if (i.commandName === "programar") {
    const canal = i.options.getChannel("canal");
    const valida = HORA_VALIDA.exec(i.options.getString("hora").trim());
    if (!valida) {
      await i.reply({ content: "Escribe la hora así: 09:00 o 21:30.", ...privado });
      return;
    }
    const hora = `${valida[1].padStart(2, "0")}:${valida[2]}`;
    if (!canal.permissionsFor(i.guild.members.me).has(PermissionFlagsBits.SendMessages)) {
      await i.reply({ content: `No puedo escribir en ${canal}: dame «Ver canal» y «Enviar mensajes» allí.`, ...privado });
      return;
    }
    if (mensajes.length >= MAXIMO) {
      await i.reply({ content: `Ya hay ${MAXIMO} mensajes programados: quita alguno con /quitar-programado.`, ...privado });
      return;
    }
    const { hoy, ya } = ahora();
    const dias = i.options.getString("dias") ?? "todos";
    const nuevo = {
      id: Math.max(0, ...mensajes.map((m) => m.id)) + 1, canal: canal.id, hora, dias,
      texto: i.options.getString("texto").replaceAll("\\n", "\n"),
      ultimo: ya >= minutos(hora) ? hoy : null, // si hoy ya ha pasado esa hora, empieza mañana
    };
    mensajes.push(nuevo);
    guardar(datos);
    await i.reply({ content: `Programado (n.º ${nuevo.id}): ${DIAS[dias]} a las ${hora} en ${canal}.`, ...privado });
  } else if (i.commandName === "programados") {
    if (!mensajes.length) {
      await i.reply({ content: "No hay ninguno. Crea uno con /programar.", ...privado });
      return;
    }
    const lineas = [...mensajes].sort((a, b) => a.hora.localeCompare(b.hora)).map((m) => {
      const resumen = m.texto.replaceAll("\n", " ");
      return `**${m.id}** · ${m.hora}, ${DIAS[m.dias]}, en <#${m.canal}>: ${resumen.length > 60 ? resumen.slice(0, 60) + "…" : resumen}`;
    });
    await i.reply({ content: lineas.join("\n"), allowedMentions: { parse: [] }, ...privado });
  } else if (i.commandName === "quitar-programado") {
    const numero = i.options.getInteger("numero");
    const quedan = mensajes.filter((m) => m.id !== numero);
    if (quedan.length === mensajes.length) {
      await i.reply({ content: `No hay ningún mensaje con el número ${numero}. Míralos con /programados.`, ...privado });
      return;
    }
    datos[i.guildId] = quedan;
    guardar(datos);
    await i.reply({ content: `Quitado el ${numero}: ya no se publicará.`, ...privado });
  }
});

client.once(Events.ClientReady, async (c) => {
  await c.application.commands.set(comandos.map((x) => x.toJSON())); // publica los comandos
  console.log(`Conectado como ${c.user.tag}`);
  setInterval(() => enviar().catch(console.error), 60 * 1000); // mira cada minuto si a algún mensaje le toca
  enviar().catch(console.error);
});

client.login(process.env.DISCORD_TOKEN);

3. Pruébalo

Quién ve los tres comandos se cambia sin tocar el código en «Ajustes del servidor» → «Integraciones» → tu bot: por ejemplo, para que también los usen los moderadores.

  1. Arranca el bot

    En una terminal, en la carpeta: pip install -r requirements.txt y python app.py (o npm install y node index.js). Cuando diga «Conectado como…», ya está. Los comandos pueden tardar unos minutos en salir la primera vez.

  2. Programa uno para dentro de dos minutos

    Escribe /programar, elige el canal, pon una hora dos o tres minutos más tarde que la de ahora en tu zona y un texto como ¡Funciona! \n Segunda línea. En cuanto llegue esa hora, como mucho un minuto después, sale en el canal. Una hora que ya ha pasado hoy empieza mañana.

  3. Míralo y quítalo

    Con /programados ves la lista, ordenada por hora, con el número de cada uno; con /quitar-programado y ese número, lo borras.

La hora, los días y si el bot estaba apagado

El bot no usa la hora del ordenador donde corre, sino la de ZONA. Así da igual que esté en tu casa o en un servidor de otro país, y el cambio de hora de marzo y de octubre lo lleva solo.

Mira cada minuto. Si un mensaje es de las 9:00 y el bot estaba apagado, lo manda en cuanto vuelve, hasta las 11:00 (RETRASO_MAX, en minutos). Pasado eso, ese día se lo salta: mejor nada que un recordatorio de algo que ya ha pasado. Si no quieres que salga nunca tarde, baja RETRASO_MAX a 5.

«De lunes a viernes» y «sábados y domingos» cuentan con el día de la semana de ZONA. Para otra combinación (solo los viernes, por ejemplo), añade una línea a DIAS y a TOCA con sus números: en Python el 0 es el lunes; en JavaScript, el domingo.

¿Por qué no tasks.loop(hours=24) o un setInterval de 24 horas? Porque cuentan desde que arranca el bot: si lo reinicias a las 17:00, el mensaje pasa a salir a las 17:00. Para una hora fija hay que mirar la hora del día, como hace este bot.

Saltos de línea, menciones y horas para todos

Los comandos de barra no dejan escribir saltos de línea, así que el bot cambia \n por uno: Buenos días \n ¿Qué tal? sale en dos líneas. Negritas, enlaces y emojis se escriben como en cualquier mensaje.

Puede mencionar a personas y roles, pero nunca a @everyone ni a @here: es lo que más molesta y lo que antes hace que la gente silencie un servidor. Para un rol, escribe <@&ID> con el ID del rol (con el modo desarrollador de Discord activado, clic derecho en el rol → «Copiar ID del rol»). Un rol solo avisa si es mencionable (en «Ajustes del servidor» → «Roles») o si el bot tiene además «Mention Everyone».

Si tu comunidad está en varios países, pon la hora del evento con una marca de tiempo de Discord en formato de hora corta (<t:…:t>): cada uno la ve en su zona. Se saca con el generador de marcas de tiempo.

Si algo no va

No sale ningún mensaje: mira con /programados la hora y los días. Si lo programaste para una hora que ya había pasado, empieza mañana. Si al bot le han quitado el permiso de escribir en el canal, la consola dice «Missing Permissions» (el código 50013) y lo vuelve a intentar cada minuto mientras le quede margen.

Sale a la hora que no es: cambia ZONA a la de tu grupo. Los nombres válidos son los de la base de zonas horarias, como Europe/Madrid, Atlantic/Canary o America/Argentina/Buenos_Aires. En Python, si dice «No time zone found», falta tzdata en requirements.txt.

La mención al rol sale pero no avisa a nadie: el rol no es mencionable (mira el apartado de arriba).

No salen los comandos: qué mirar cuando los comandos no aparecen. Si el bot está en línea y no contesta, mi bot no responde.

Ideas para ampliarlo

Un aviso más vistoso, con color e imagen: cambia el texto por un embed (embeds en un bot de Discord).

Recordatorios de una sola vez («el sábado a las 18:00, torneo»): guarda la fecha además de la hora y borra el mensaje de programados.json al mandarlo.

Si ya tienes un bot de bienvenida o uno de cumpleaños, puedes juntar los comandos en el mismo bot: en el mismo plan gastan lo mismo que uno.

4. Déjalo encendido siempre

Un bot de mensajes programados solo funciona si está encendido a la hora: si vive en tu ordenador y está apagado, no sale nada. Hay que alojarlo en un servidor; opciones gratis y de pago, comparadas, en dónde alojar tu bot de Discord.

En NapNodes hay un bot gratis de 256 MB que no se apaga por estar vacío; se renueva cada 7 días con «Encender» (sus pegas, en esta guía): si se te olvida renovarlo, esos días no sale ningún aviso. Créalo, elige Node.js o Python en «Configuración» → «Lenguaje», sube tus ficheros en «Archivos» y pulsa «Encender»: las librerías de requirements.txt o package.json se instalan solas al arrancar, y programados.json se queda en la carpeta del bot entre reinicios. Para no tener que renovar, el plan Mini (512 MB) está encendido siempre (precios de los bots).

Preguntas frecuentes

¿Cómo hago que un bot de Discord mande un mensaje todos los días a la misma hora?

Con un bot que guarde el canal, la hora y el texto, y mire cada minuto si a algún mensaje le toca. El código completo en Python y en JavaScript, con comandos para programarlos desde Discord, está en esta guía.

¿Por qué mi mensaje diario sale cada vez a una hora distinta?

Porque un bucle de 24 horas cuenta desde que arrancó el bot: cada reinicio mueve la hora. Hay que comparar con la hora del día en una zona horaria fija, como hace el bot de esta guía.

¿Qué permisos necesita un bot de mensajes programados?

Ver canales y enviar mensajes (el número de permisos es 3072). Solo si quieres avisar a roles que no son mencionables necesita además «Mention Everyone».

¿El bot puede mencionar a @everyone?

Tal como está, no: puede mencionar a personas y roles, pero el código bloquea @everyone y @here para que un aviso diario no acabe con todo el servidor silenciándolo.

¿Dónde dejo el bot de mensajes programados encendido siempre?

En un servidor. En NapNodes hay un bot gratis de 256 MB que se renueva con un clic cada semana, o el plan Mini, encendido siempre.

¿Te ha servido esta guía?

¿Lo montamos?

Hosting de bots de Discord desde 1,50 € al mes, sin sobreventa: tu RAM es solo tuya. Y si quieres empezar sin pagar, tienes un bot de Discord gratis, sin tarjeta.

Herramienta gratis: Permisos de un bot de Discord. Marca los permisos de tu bot en castellano y sal con el número y el enlace de invitación; o pega un número y mira qué permisos da.

Herramienta gratis: Marcas de tiempo de Discord. Elige día, hora y zona y copia el <t:…> para que en Discord cada uno vea la hora en la suya, o cuánto falta.

Sigue leyendo

Todas las guías de Bots de Discord, ordenadas →