Moving from Discord
Migrate from discord.js
Move a discord.js v14 bot to lamma.js step by step, with every change your code needs.
This guide moves a bot written for current discord.js (14.27) to lamma.js one step at a time. Most bots keep their structure, events and command handlers. What changes is the import, the address, the token, a few names, and any feature Lamma doesn't offer bots.
#The short version
For most bots the whole change is the import and the token:
- import { Client, GatewayIntentBits, Events, SlashCommandBuilder } from 'discord.js';
+ import { Client, GatewayIntentBits, Events, SlashCommandBuilder } from 'lamma.js';LAMMA_TOKEN=lmb1_... node index.jsYour intents, events, command files and permission checks stay as they are. Lamma's address is built in, as Discord's is. login() with no token reads LAMMA_TOKEN, and client.login(process.env.DISCORD_TOKEN) works too if you put the Lamma token there. Once lamma.js is on npm, an install alias (npm install discord.js@npm:lamma.js) will leave even the import untouched.
The steps below are for bots that use something Lamma doesn't have yet, and for checking a port line by line.
#1. Check what your bot uses
Search your code for the features below first. Each needs a change, or is switched off on Lamma.
| If your bot uses | On Lamma |
|---|---|
user.send(), createDM(), DM commands | Bots can't DM people. The call fails with code 50007, like a person with DMs closed |
GuildPresences, member.presence, presenceUpdate | Never sent to bots. The intent reads as off, so code that checks it switches itself off |
sendTyping(), TypingStart | Does nothing, with one warning |
setPresence(), setActivity() | Does nothing for now, with one warning |
Buttons, select menus, modals (ButtonBuilder, ActionRowBuilder, ModalBuilder) | Not yet. Rewrite those flows as slash command options |
| Subcommands, autocomplete, context menus, attachment options | Not yet. addSubcommand throws so you notice. Use separate commands |
Components V2 (ContainerBuilder, TextDisplayBuilder, …) | Not yet. Use embeds |
files, AttachmentBuilder, embed images and thumbnails | Not yet. Images are dropped and files aren't sent |
ban(), kick(), timeout(), roles.add(), deleting other people's messages | Not in v1. Bots can still hear about bans, kicks and timeouts |
Voice (@discordjs/voice) | Not offered to bots |
| Threads, forums, stages, stickers | Lamma doesn't have them yet |
permissionsFor(), permissionsIn() for someone other than the bot | Returns null with one warning. See step 7 |
fetchReply(), reply({ withResponse: true }) | Not yet. Use the message editReply() returns |
Options, ShardingManager | Not exported. Remove them: Lamma needs no shards |
discord.com/channels/… links | Use message.url or Lamma's own links |
| Libraries that look people up on Discord by id | Remove them. Lamma ids aren't Discord ids, so they would find the wrong person |
#2. Make an app and get a token
Follow Build your first bot to create the app, copy its token and install it in a test server. Ask for the scopes and intents your bot really uses.
Discord ids saved in your database won't match anything on Lamma. Plan to set up each server's settings again.
#3. Swap the package
npm uninstall discord.js
npm install lamma.js#4. Change the import and the client
// Before
import { Client, GatewayIntentBits, Partials } from 'discord.js';
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent],
partials: [Partials.Message, Partials.Reaction]
});
client.login(process.env.DISCORD_TOKEN);
// After
import { Client, GatewayIntentBits, Partials } from 'lamma.js';
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent],
partials: [Partials.Message, Partials.Reaction]
});
client.login(process.env.LAMMA_TOKEN);login() with no token reads LAMMA_TOKEN. To reach another copy of Lamma, such as a local one, set LAMMA_URL or baseURL. partials can stay: lamma.js always delivers events about uncached messages and users as partials.
#5. Keep your intents
Keep GatewayIntentBits as it is. Bits, arrays, flag names such as 'GuildMessages' and IntentsBitField all work.
| discord.js intent | On Lamma |
|---|---|
Guilds, GuildMessages, GuildMessageReactions | Sent |
MessageContent, GuildMembers | Sent. Privileged |
GuildModeration (or GuildBans) | Sent. Also needs View audit log |
GuildExpressions (or GuildEmojisAndStickers) | Sent, for emoji |
GuildPresences, GuildMessageTyping, DirectMessages, GuildVoiceStates, GuildInvites, GuildWebhooks, GuildScheduledEvents, AutoModeration* | Ignored with one warning, and read as off |
Privileged intents work in up to five servers you own while you develop, and everywhere once the app is reviewed. In the app's settings, intents are listed by what they send, such as New, edited and deleted messages for GuildMessages.
#6. Keep your events and handlers
These work as they are: Events.ClientReady ('clientReady', as in current discord.js), MessageCreate, MessageUpdate, MessageDelete, MessageBulkDelete, MessageReactionAdd, MessageReactionRemove, InteractionCreate, GuildCreate, GuildDelete, GuildUpdate, GuildMemberAdd, GuildMemberUpdate, GuildMemberRemove, GuildBanAdd, GuildBanRemove, GuildAuditLogEntryCreate, ChannelCreate, ChannelUpdate, ChannelDelete, GuildRoleCreate, GuildRoleUpdate, GuildRoleDelete, GuildEmojiCreate, GuildEmojiUpdate, GuildEmojiDelete, Error, Warn and Raw.
Reaction events give (reaction, user, { type, burst }), as in discord.js. A reaction on a message the bot hasn't cached comes as a partial: check reaction.partial and call reaction.fetch().
The message cache keeps 200 messages per channel in memory, so messageUpdate and messageDelete carry the old message as they do in discord.js. Follow the message cache rule.
When the connection drops, the client reconnects by itself. There is no resume, so ClientReady fires again; fetch anything you missed with channel.messages.fetch({ after }).
#7. Keep your permission checks
PermissionsBitField and PermissionFlagsBits work as in discord.js:
if (!interaction.memberPermissions.has(PermissionFlagsBits.ManageMessages)) return;
if (!interaction.appPermissions.has(PermissionFlagsBits.EmbedLinks)) return interaction.reply('I need Embed links here.');
const canKick = message.member.permissions.has(PermissionFlagsBits.KickMembers);interaction.memberPermissionsandinteraction.appPermissionsare exact for the command's channel, including channel overrides.member.permissionsandrole.permissionsare server-wide, as in discord.js.guild.members.me,channel.permissionsFor(client.user)andme.permissionsIn(channel)give the bot's own permissions.- Lamma doesn't send other members' channel overrides, so
channel.permissionsFor()for anyone but the bot returnsnulland warns once. In a command, useinteraction.memberPermissionsinstead.
Lamma's permissions have Discord's flags. A few have different names in Lamma's settings: Manage server is ManageGuild, Create invite is CreateInstantInvite, Create polls is SendPolls and Manage expressions is ManageGuildExpressions. Lamma's Mention roles has no Discord flag, so it's left out. Discord flags for features Lamma doesn't have, such as ManageThreads, read as off unless the member is an administrator.
#8. Update slash commands
SlashCommandBuilder works as it is for string, integer, number, boolean, user, channel and role options, and toJSON() gives the same JSON as @discordjs/builders. A bot laid out like the discord.js guide keeps its layout: one { data, execute } file per command in commands/, one file per event in events/, a client.commands Collection, and a deploy-commands.js that registers them with REST and Routes.
// commands/utility/ping.js
import { SlashCommandBuilder } from 'lamma.js';
export default {
data: new SlashCommandBuilder().setName('ping').setDescription('Check the bot'),
async execute(interaction) { await interaction.reply('Pong!'); }
};
// deploy-commands.js
import { REST, Routes } from 'lamma.js';
const rest = new REST().setToken(process.env.LAMMA_TOKEN);
await rest.put(Routes.applicationGuildCommands(clientId, guildId), { body: commands.map(command => command.data.toJSON()) });You can also register them from the client:
client.once(Events.ClientReady, async ready => {
await ready.application.commands.set(commands); // every server, at once
// await ready.application.commands.set(commands, guildId); // one server
});- Global commands appear at once, so you don't need test-server tricks.
setDefaultMemberPermissions()isn't enforced yet and warns once. Keep aninteraction.memberPermissionscheck in each sensitive command (step 7).setContexts()andsetIntegrationTypes()are accepted. Commands are used in servers only.- Mentionable and attachment options, autocomplete and subcommands throw when you add them, so you notice.
- Split subcommands into separate commands, for example
/config-showand/config-setinstead of/config showand/config set.
interaction.reply(), deferReply(), editReply(), followUp(), flags: MessageFlags.Ephemeral and the interaction.options getters work as before, within 3 seconds and 15 minutes. reply({ withResponse: true }) gives undefined with one warning and fetchReply() rejects, so use the message editReply() returns.
#9. Small changes in messages and channels
channel.typeis aChannelType:GuildText,GuildVoiceorGuildCategory. Voice channels have a text chat, as on Discord, soisTextBased()is true for them.- Ids are strings in the same format, but they are Lamma ids.
- Embeds keep their fields and limits. Images, thumbnails and video are dropped for now.
- Lamma doesn't notify on replies, so
allowedMentions.repliedUserchanges nothing. Everything else inallowedMentionsworks. - An ephemeral reply is never stored, so
editReply()on one returns a plain object, not aMessage. - On a rate limit,
retry_afteris in milliseconds. lamma.js waits and retries for you.
#10. Test it
- Run the bot against a test server you own.
- Watch for
warnevents: lamma.js warns once for each intent, call or option Lamma doesn't support, so nothing fails silently. - Try every command and the events your bot relies on.
- When it works, share the install link with the servers that use it.
Anything missing that your bot needs? Email lamma.help@gmail.com and say what it is.