LammaDevelopers Open Lamma

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:

Text
- import { Client, GatewayIntentBits, Events, SlashCommandBuilder } from 'discord.js';
+ import { Client, GatewayIntentBits, Events, SlashCommandBuilder } from 'lamma.js';
Terminal
LAMMA_TOKEN=lmb1_... node index.js

Your 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 usesOn Lamma
user.send(), createDM(), DM commandsBots can't DM people. The call fails with code 50007, like a person with DMs closed
GuildPresences, member.presence, presenceUpdateNever sent to bots. The intent reads as off, so code that checks it switches itself off
sendTyping(), TypingStartDoes 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 optionsNot yet. addSubcommand throws so you notice. Use separate commands
Components V2 (ContainerBuilder, TextDisplayBuilder, …)Not yet. Use embeds
files, AttachmentBuilder, embed images and thumbnailsNot yet. Images are dropped and files aren't sent
ban(), kick(), timeout(), roles.add(), deleting other people's messagesNot in v1. Bots can still hear about bans, kicks and timeouts
Voice (@discordjs/voice)Not offered to bots
Threads, forums, stages, stickersLamma doesn't have them yet
permissionsFor(), permissionsIn() for someone other than the botReturns null with one warning. See step 7
fetchReply(), reply({ withResponse: true })Not yet. Use the message editReply() returns
Options, ShardingManagerNot exported. Remove them: Lamma needs no shards
discord.com/channels/… linksUse message.url or Lamma's own links
Libraries that look people up on Discord by idRemove 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

Terminal
npm uninstall discord.js
npm install lamma.js

#4. Change the import and the client

JavaScript
// 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 intentOn Lamma
Guilds, GuildMessages, GuildMessageReactionsSent
MessageContent, GuildMembersSent. 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:

JavaScript
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.memberPermissions and interaction.appPermissions are exact for the command's channel, including channel overrides.
  • member.permissions and role.permissions are server-wide, as in discord.js.
  • guild.members.me, channel.permissionsFor(client.user) and me.permissionsIn(channel) give the bot's own permissions.
  • Lamma doesn't send other members' channel overrides, so channel.permissionsFor() for anyone but the bot returns null and warns once. In a command, use interaction.memberPermissions instead.

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.

JavaScript
// 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:

JavaScript
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 an interaction.memberPermissions check in each sensitive command (step 7).
  • setContexts() and setIntegrationTypes() 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-show and /config-set instead of /config show and /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.type is a ChannelType: GuildText, GuildVoice or GuildCategory. Voice channels have a text chat, as on Discord, so isTextBased() 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.repliedUser changes nothing. Everything else in allowedMentions works.
  • An ephemeral reply is never stored, so editReply() on one returns a plain object, not a Message.
  • On a rate limit, retry_after is in milliseconds. lamma.js waits and retries for you.

#10. Test it

  1. Run the bot against a test server you own.
  2. Watch for warn events: lamma.js warns once for each intent, call or option Lamma doesn't support, so nothing fails silently.
  3. Try every command and the events your bot relies on.
  4. 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.