BBotnexio

Buy now!

← All guides

Discord Welcome Message Bot: Build Your Own (discord.js 14)

Deutsche Version

A welcome bot posts a message when someone joins your server, optionally gives them a starter role, and says goodbye when they leave. Hosted bots can do this in a few clicks. If you want your own bot with your own texts and data on your own hosting, this guide shows the parts you need: the intent, the event, a template, auto-roles and the usual pitfalls.

Step 1: Turn on the Server Members intent

Join and leave events are behind a privileged intent. Discord sends GUILD_MEMBER_ADD only if your bot uses the Guild Members intent.

  1. Open the Discord Developer Portal, pick your app → Bot.
  2. Under Privileged Gateway Intents, turn on Server Members Intent.
  3. Request the same intent in your code (step 2). If the portal and the code don't match, the login fails with [DisallowedIntents].

Once a bot reaches 10,000 users, Discord asks for a review of privileged intents. For a bot on your own server, that won't apply.

Step 2: Listen for new members

import { Client, Events, GatewayIntentBits } from "discord.js";

const client = new Client({
  intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers],
});

const WELCOME_CHANNEL_ID = process.env.WELCOME_CHANNEL_ID!;
const TEMPLATE = "Welcome {user} to {server}! You are member #{count}. Start in <#RULES_CHANNEL_ID>.";

function render(template: string, vars: Record<string, string>) {
  return template.replace(/\{(\w+)\}/g, (m, key) => vars[key] ?? m);
}

client.on(Events.GuildMemberAdd, async (member) => {
  const channel = member.guild.channels.cache.get(WELCOME_CHANNEL_ID);
  if (!channel?.isTextBased()) return;
  await channel.send({
    content: render(TEMPLATE, {
      user: `<@${member.id}>`,
      server: member.guild.name,
      count: String(member.guild.memberCount),
    }),
    allowedMentions: { users: [member.id] }, // ping the new member only
  });
});

client.login(process.env.DISCORD_TOKEN);

allowedMentions stops a template from pinging @everyone by mistake. Keep the channel ID and the template in a config or database per server, not in code, if the bot runs on more than one server.

Step 3: Give a starter role (auto-role)

const AUTO_ROLE_ID = process.env.AUTO_ROLE_ID!;

client.on(Events.GuildMemberAdd, async (member) => {
  try {
    await member.roles.add(AUTO_ROLE_ID, "Auto-role on join");
  } catch (err) {
    console.error("Auto-role failed", err); // usually role hierarchy
  }
});

The bot needs Manage Roles, and its own role must sit above the role it hands out.

Step 4: A goodbye message

Events.GuildMemberRemove fires when someone leaves (same intent). The member may only be partially cached, so use the ID and the username, not roles.

Step 5: Template ideas that work

  • Short: one line, a mention, one link to the rules channel.
  • Point to self-roles ("pick your games in #roles") instead of listing all channels.
  • If your bot stores data (levels, opt-outs), add a short privacy sentence or a /privacy command.

Common problems

  • Nothing happens on join: the Server Members intent is off in the portal or missing in intents.
  • [DisallowedIntents] on login: the intent is in the code but not enabled in the portal.
  • "Missing Permissions" on auto-role: the bot's role is below the target role, or it lacks Manage Roles.
  • The welcome message pings everyone: set allowedMentions as above.

Aramac Community Bot + Dashboard Starter for Discord

If you'd rather start from a template, the Community Bot + Dashboard Starter is a self-hosted bot with a web dashboard and automated tests. Per the product page it includes welcome and goodbye messages with templates, auto-roles, self-roles via buttons or reactions, leveling with level roles, /rank and /leaderboard, /privacy, /mydata export|delete and /xp optout|optin, DE/EN messages per server, and a Next.js dashboard with Discord OAuth2 login that never needs the bot token (discord.js 14, Node.js 22.13 or newer, SQLite, Docker Compose). Requirements: Node.js 22.13 or newer (or Docker), your own Discord application and hosting. Contains no AI. Price: €29 incl. VAT, sold by Aramac GmbH. Licence: personal, non-commercial use on private and community servers only, not for shops, businesses or servers with paid memberships/roles (licence). Not affiliated with Discord Inc.

FAQ

Why doesn't my bot see new members?
Joins are only sent with the Guild Members intent. Enable "Server Members Intent" in the Developer Portal and add GatewayIntentBits.GuildMembers to your client.

Can I welcome people by DM instead of in a channel?
Yes, with member.send(), but many members have DMs from servers turned off. A channel message reaches more people.

How do I stop the welcome message from pinging everyone?
Pass allowedMentions: { users: [member.id] } so only the new member is mentioned.

Is the Community Bot Starter a subscription?
No. €29 incl. VAT, paid once, delivered as a ZIP. You host it yourself.

Can I use it on a business or shop server?
No. The licence covers private and community servers only, for personal, non-commercial use.

Related guides

→ Get the Community Bot + Dashboard Starter for €29

_Not affiliated with or endorsed by Discord Inc. "Discord" is a trademark of Discord Inc._