LammaDevelopers Open Lamma

SDKs

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

OptionDefaultNotes
baseURLLAMMA_URLAnother copy of Lamma to reach, such as a local one. Defaults to Lamma's own address
intents[]Which events to receive
gatewayURLfrom GET /v1/gateway, or LAMMA_GATEWAY_URLSet it when your bot reaches Lamma by another address
rest.retries3How many times to retry after a rate limit
allowedMentionsevery mention allowedThe default for every message
messageCacheLimit200Messages 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.jslamma.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, guildAuditLogEntryCreateThe same. Timeouts, bans and audit entries need moderation access
roleCreate, channelCreate, emojiCreate and their update and delete eventsThe same. Caches stay up to date
guild.fetchAuditLogs(), AuditLogEventThe same. Entries Discord has no number for have action: null and entry.lammaAction
message.reply(), edit(), delete(), react(), message.reactions.cacheThe 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.meThe same
interaction.appPermissions, interaction.memberPermissionsThe 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
EmbedBuilderThe same. Images and thumbnails aren't shown yet
SlashCommandBuilderThe 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 bitfieldsThe 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.EphemeralThe same. Other message flags are dropped with one warning
InteractionType, ApplicationCommandType, InteractionContextType, PartialsThe same values, so ported imports work
interaction.options.getString(), getInteger(), getUser(), getChannel(), getRole()The same
CollectionThe same helpers: find, filter, map, first, …
REST and RoutesThe same idea, with Lamma's paths

#What's different

  • setDefaultMemberPermissions has no effect yet and warns once, so check interaction.memberPermissions in your command code.
  • Lamma's Mention roles permission has no Discord flag, so permission bitfields leave it out.
  • reply({ withResponse: true }) gives undefined with one warning, and fetchReply() rejects. editReply() returns the message.
  • There are no buttons, select menus, modals, autocomplete or subcommands yet. addSubcommand throws so you notice.
  • Bots can't send DMs, show a status or show typing. user.send() and createDM() reject with code 50007, as Discord does for someone with DMs closed. sendTyping(), setPresence() and setActivity() 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 a Message.

See Coming from Discord for everything that differs.