SDKs
lamma.js
Build Lamma bots in JavaScript with the patterns you know from discord.js.
lamma.js lets you build Lamma bots in JavaScript with the names and patterns you know from discord.js. It runs on Lamma's own REST API and gateway and never talks to Discord.
#Example
JavaScript
import { Client, Events, GatewayIntentBits, EmbedBuilder, SlashCommandBuilder, MessageFlags, Colors } from 'lamma.js';
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent]
});
const commands = [
new SlashCommandBuilder().setName('roll').setDescription('Roll a die')
.addIntegerOption(option => option.setName('sides').setDescription('How many sides').setRequired(true).setMinValue(2).setMaxValue(100))
];
client.once(Events.ClientReady, async ready => {
console.log(`Logged in as ${ready.user.username} in ${ready.guilds.cache.size} servers`);
await ready.application.commands.set(commands); // global commands show up everywhere at once
});
client.on(Events.MessageCreate, async message => {
if (message.author.bot || message.content !== '!ping') return;
const embed = new EmbedBuilder().setTitle('Pong!').setDescription(`Gateway ping: ${client.ws.ping} ms`).setColor(Colors.Green);
await message.reply({ embeds: [embed] });
});
client.on(Events.InteractionCreate, async interaction => {
if (!interaction.isChatInputCommand() || interaction.commandName !== 'roll') return;
const sides = interaction.options.getInteger('sides', true);
await interaction.reply({ content: `You rolled ${1 + Math.floor(Math.random() * sides)}`, flags: MessageFlags.Ephemeral });
});
client.login(); // reads LAMMA_TOKEN#Options
| Option | Default | Notes |
|---|---|---|
baseURL | LAMMA_URL | Another copy of Lamma to reach, such as a local one. Defaults to Lamma's own address |
intents | [] | Which events to receive |
gatewayURL | from GET /v1/gateway, or LAMMA_GATEWAY_URL | Set it when your bot reaches Lamma by another address |
rest.retries | 3 | How many times to retry after a rate limit |
allowedMentions | every mention allowed | The default for every message |
messageCacheLimit | 200 | Messages kept per channel, in memory only, for edit and delete events. 0 keeps none |
#Coming from discord.js
Most bots port by changing the import and the token.
| discord.js | lamma.js |
|---|---|
import { Client } from 'discord.js' | import { Client } from 'lamma.js' |
new Client({ intents }) | The same |
GatewayIntentBits (bits, arrays, flag names such as 'GuildMessages', IntentsBitField) | The same bits. Lamma sends Guilds, GuildMembers, GuildModeration, GuildExpressions, GuildMessages, GuildMessageReactions and MessageContent. Others are ignored with one warning |
client.login(token) | The same. Defaults to LAMMA_TOKEN |
Events.ClientReady ('clientReady'), MessageCreate, InteractionCreate, GuildCreate, Raw, … | The same names. The old 'ready' isn't emitted; a listener on it warns once |
messageReactionAdd and messageReactionRemove: (reaction, user, { type, burst }) | The same. reaction.emoji has id, name and identifier. A reaction on an uncached message comes as a partial, as with Partials.Message: check reaction.partial and call reaction.fetch() |
guildMemberAdd, guildMemberUpdate, guildMemberRemove, guildBanAdd, guildBanRemove, guildAuditLogEntryCreate | The same. Timeouts, bans and audit entries need moderation access |
roleCreate, channelCreate, emojiCreate and their update and delete events | The same. Caches stay up to date |
guild.fetchAuditLogs(), AuditLogEvent | The same. Entries Discord has no number for have action: null and entry.lammaAction |
message.reply(), edit(), delete(), react(), message.reactions.cache | The same. react() takes a Unicode emoji or a server emoji (GuildEmoji, '<:name:id>', 'name:id' or its id) |
ChannelType, channel.type, TextChannel, VoiceChannel, CategoryChannel, channel.parent, isTextBased(), isSendable() | The same. Voice channels have a text chat, as on Discord |
PermissionsBitField, PermissionFlagsBits, role.permissions, member.permissions, guild.members.me | The same |
interaction.appPermissions, interaction.memberPermissions | The same: what the bot, and the person who ran the command, may do in that channel |
channel.permissionsFor(), member.permissionsIn() | The same for the bot itself. For anyone else they return null and warn once, because Lamma doesn't send other members' channel permissions |
channel.send(), channel.messages.fetch() | The same |
guild.members.fetch() | The same. Needs members.read |
EmbedBuilder | The same. Images and thumbnails aren't shown yet |
SlashCommandBuilder | The same, for string, integer, number, boolean, user, channel and role options, with the same toJSON() as @discordjs/builders |
A command handler: { data, execute } files in client.commands, and a deploy-commands.js that calls rest.put(Routes.applicationGuildCommands(clientId, guildId), { body }) | The same, so a bot laid out like the discord.js guide ports as it is |
BitField, UserFlagsBitField, ChannelFlagsBitField, GuildMemberFlagsBitField, RoleFlagsBitField and the other bitfields | The same classes and values. Lamma has no account badges or channel, member and role flags, so those fields are empty |
client.application.commands.set(commands, guildId) | The same. Global commands appear at once |
interaction.reply(), deferReply(), editReply(), followUp() | The same, within 3 seconds and 15 minutes |
flags: MessageFlags.Ephemeral | The same. Other message flags are dropped with one warning |
InteractionType, ApplicationCommandType, InteractionContextType, Partials | The same values, so ported imports work |
interaction.options.getString(), getInteger(), getUser(), getChannel(), getRole() | The same |
Collection | The same helpers: find, filter, map, first, … |
REST and Routes | The same idea, with Lamma's paths |
#What's different
setDefaultMemberPermissionshas no effect yet and warns once, so checkinteraction.memberPermissionsin your command code.- Lamma's Mention roles permission has no Discord flag, so permission bitfields leave it out.
reply({ withResponse: true })givesundefinedwith one warning, andfetchReply()rejects.editReply()returns the message.- There are no buttons, select menus, modals, autocomplete or subcommands yet.
addSubcommandthrows so you notice. - Bots can't send DMs, show a status or show typing.
user.send()andcreateDM()reject with code50007, as Discord does for someone with DMs closed.sendTyping(),setPresence()andsetActivity()do nothing. Each warns once. - Bots can't kick, ban or time out members yet.
- When the connection drops, the client reconnects on its own. There is no resume, so read anything you missed with
channel.messages.fetch(). - An ephemeral reply is never stored, so
editReply()on one returns a plain object, not aMessage.
See Coming from Discord for everything that differs.