That little line under a bot's name — Playing with slash commands, Watching 5 servers, Listening to /help — is its presence, and setting it is a nice, quick win. This guide sets a presence the moment your bot is ready, walks the five activity types, and rotates through several on a timer. The main code is discord.js; the discord.py equivalent is at the end.
| At a glance | |
|---|---|
| You need | a working bot (see the discord.js or discord.py guide) |
| Plan | Free or premium — but see the honest note below about when the status shows |
| Time | about fifteen minutes |
⚠️ Heads up: A presence only shows while your bot is actually running and connected. It isn't stored on Discord's side — the moment the process stops, the status goes with it. On the free plan, when your session timer runs out and the server stops, the bot goes offline and the status disappears until you start it again. Premium's 24/7 is what keeps a status up around the clock.
The five activity types
The type decides the verb Discord shows before your text:
| Type | Shows as | Notes |
|---|---|---|
ActivityType.Playing |
Playing your text | The default, most-used one |
ActivityType.Watching |
Watching your text | Great for "Watching 5 servers" |
ActivityType.Listening |
Listening to your text | "Listening to /help" |
ActivityType.Streaming |
Streaming your text | Needs a Twitch/YouTube url to turn the name purple |
ActivityType.Competing |
Competing in your text | Less common, but there |
There's also ActivityType.Custom for a free-form status line — handy, but the four above are what most bots use.
Set it when the bot is ready
Presence is set on client.user, which only exists after login — so the ClientReady handler is exactly the right place. Import ActivityType alongside your other names and set an initial presence:
const { Client, GatewayIntentBits, ActivityType, Events } = require('discord.js');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.once(Events.ClientReady, (c) => {
c.user.setPresence({
activities: [{ name: '/help', type: ActivityType.Watching }],
status: 'online',
});
console.log(`Listening as ${c.user.tag}`);
});
client.login(process.env.DISCORD_TOKEN);
setPresence takes an activities array (Discord shows the first) and a status. The four statuses are online, idle (yellow moon), dnd (red, "Do Not Disturb"), and invisible (appears offline but still works). For just the activity, c.user.setActivity('/help', { type: ActivityType.Watching }) is the shorthand.
🎯 Good to know: Set presence in
ClientReady, not at the top of the file. Before login,client.userisnull— try to set a status there and you'll crash on startup.
Rotate through several statuses
A rotating status keeps things lively and is a natural place to show live numbers — like how many servers the bot is in. Add a setInterval inside ClientReady:
client.once(Events.ClientReady, (c) => {
const statuses = [
{ name: 'with slash commands', type: ActivityType.Playing },
() => ({ name: `${c.guilds.cache.size} servers`, type: ActivityType.Watching }),
{ name: '/help', type: ActivityType.Listening },
];
let i = 0;
const rotate = () => {
const entry = statuses[i % statuses.length];
const activity = typeof entry === 'function' ? entry() : entry;
c.user.setActivity(activity.name, { type: activity.type });
i++;
};
rotate(); // set the first one immediately
setInterval(rotate, 30_000); // then change every 30 seconds
console.log(`Listening as ${c.user.tag}`);
});
Each entry is either a fixed { name, type } or a function that's called fresh each rotation — that's how ${c.guilds.cache.size} servers stays current as the bot joins new servers.
💡 Tip: Don't rotate faster than every ~15 seconds. Presence updates are rate-limited, and a status flickering every second just burns your budget with Discord — see Discord rate limits. Thirty seconds looks lively and stays well clear of the limit.
The discord.py equivalent
Same idea: build an Activity and set it once the bot is ready. The activity classes map to the same types — discord.Game(...) is Playing, and discord.Activity(type=..., name=...) covers Watching, Listening, and the rest:
@client.event
async def on_ready():
await client.change_presence(
status=discord.Status.online,
activity=discord.Activity(type=discord.ActivityType.watching, name="/help"),
)
print(f"Listening as {client.user}", flush=True)
To rotate, use a discord.ext.tasks loop (the Pythonic timer):
from discord.ext import tasks
@tasks.loop(seconds=30)
async def rotate_status():
activity = discord.Activity(type=discord.ActivityType.watching,
name=f"{len(client.guilds)} servers")
await client.change_presence(activity=activity)
Start it from on_ready with rotate_status.start(). discord.Status.idle, .dnd, and .invisible give you the same status options as discord.js.
Verify it works
Start the bot and look at it in your member list. Under its name you should see your status — Watching /help, then (if you added rotation) the text changing every 30 seconds. If it's blank, the bot either isn't fully connected yet (give it a moment after the Listening as … line) or the presence code is running in the wrong place.
Troubleshooting
Cannot read properties of null (reading 'setPresence')— you set presence before login. Move it inside theClientReadyhandler, whereclient.userexists.- The status never appears — confirm the bot is actually online (green dot) and that
setPresence/change_presenceruns afterClientReady/on_ready. Nothing shows while the bot is offline — that's the platform limit above, not a bug. - Streaming type doesn't turn purple — the
Streamingtype needs a valid Twitch or YouTubeurlin the activity; without it, Discord falls back to plain text. - Rotation stops or stutters — you're likely updating too fast and hitting the presence rate limit. Slow the interval to 15–30 seconds.
- Custom status text won't set — bots use the activity
namefor Playing/Watching/etc.; the free-form custom status behaves differently and isn't the usual path. Stick to the typed activities above.
Next steps
- Structure a discord.js bot past one file
- One bot, many servers — show a live server count in your status
- Keep your bot online — because a status only shows while it runs
- The full presence API is at discord.js.org