Put custom emoji in messages, add reactions, send community stickers and create emoji
Unicode emoji such as ๐ work directly in message content and reactions. Custom emoji belong to a community and have a name and an ID. Fluxer displays them from markup such as <:name:id> for a static image or <a:name:id> for an animated one
| Task | Input |
|---|---|
| Display an emoji in a message | Unicode or custom markup in content |
| React to a message | Unicode, custom markup, parsed emoji fields or a Guild |
| Send a sticker | Its ID in sticker |
| Manage community emoji or stickers | The client or client namespace |
The SDK does not turn shortcodes such as :party: into emoji IDs. Look up the name in application code, or configure the ID when several communities have an emoji with the same name
Find an Emoji and Use It Twice
Fetch a community's emoji and find the configured ID. The helper uses that emoji in a message and as a reaction to it. Add it to a bot that already has a client
import { format } from "@neontechspace/fluxerly"
export async function celebrateWithEmoji(client, guildId, channelId, emojiId) {
const listed = await client.emojis.fetchAll(guildId)
if (listed.isErr())
return listed
const emoji = listed.value.find((item) => item.id === emojiId)
if (!emoji)
return
const sent = await client.messages.send(channelId, `Build passed ${format.customEmoji(emoji)}`)
if (sent.isErr())
return sent
return await client.messages.addReaction(sent.value, emoji)
}
import { format, type Client } from "@neontechspace/fluxerly"
export async function celebrateWithEmoji(
client: Client,
guildId: string,
channelId: string,
emojiId: string,
) {
const listed = await client.emojis.fetchAll(guildId)
if (listed.isErr()) return listed
const emoji = listed.value.find((item) => item.id === emojiId)
if (!emoji) return
const sent = await client.messages.send(channelId, `Build passed ${format.customEmoji(emoji)}`)
if (sent.isErr()) return sent
return await client.messages.addReaction(sent.value, emoji)
}
The helper returns undefined when the configured emoji is not in the community. If adding the reaction fails, the sent message stays posted. Fluxer checks that the emoji is available and that the bot has permission for each request
Custom markup can also be passed directly, for example client, with the example ID replaced by an available emoji. The SDK converts it to the form Fluxer's reaction endpoint expects
Reuse a Received or Parsed Emoji
Reaction events already contain everything needed to add the same reaction. This handler, registered with client, mirrors each person's reaction with the bot's own. Supply the connected bot's ID so the bot ignores its own reactions
import { describeError } from "@neontechspace/fluxerly"
export function mirrorReactions(client, botId) {
return client.on("messageReactionAdd", async (reaction) => {
if (reaction.userId === botId)
return
const added = await client.messages.addReaction(reaction, reaction.emoji)
if (added.isErr())
console.warn(describeError(added.error))
})
}
import { describeError, type Client } from "@neontechspace/fluxerly"
export function mirrorReactions(client: Client, botId: string) {
return client.on("messageReactionAdd", async (reaction) => {
if (reaction.userId === botId) return
const added = await client.messages.addReaction(reaction, reaction.emoji)
if (added.isErr()) console.warn(describeError(added.error))
})
}
The client method returns a subscription. Keep it and call its close() method when mirroring should end. The same emoji inputs work for removing reactions, listing the people who reacted and choosing a collector's emoji. To read custom emoji markup typed by a user, use format, which returns a Result instead of throwing on invalid text, and pass the parsed value on
Grouped Reactions and Emoji Matching
This example handles one added reaction at a time, which is how reactions in a community always arrive. Fluxer groups additions into a message event only in direct messages, and only when gateway is set. For a time or item limit that handles both forms, use a reaction collector.
Collectors match custom emoji by ID, even after a rename. Unicode emoji match the exact text, including skin tone and variation selectors
Send a Community Sticker
Fetch the community's stickers and select one by configured ID. A message can contain up to three sticker IDs, with optional text, embeds and attachments
export async function sendGuildSticker(client, guildId, channelId, stickerId) {
const listed = await client.stickers.fetchAll(guildId)
if (listed.isErr())
return listed
const sticker = listed.value.find((item) => item.id === stickerId)
if (!sticker)
return
return await client.messages.send(channelId, {
content: "Ready for the next round",
stickerIds: [sticker.id],
})
}
import type { Client } from "@neontechspace/fluxerly"
export async function sendGuildSticker(
client: Client,
guildId: string,
channelId: string,
stickerId: string,
) {
const listed = await client.stickers.fetchAll(guildId)
if (listed.isErr()) return listed
const sticker = listed.value.find((item) => item.id === stickerId)
if (!sticker) return
return await client.messages.send(channelId, {
content: "Ready for the next round",
stickerIds: [sticker.id],
})
}
Received messages list their stickers in message. Image URLs come from the asset helpers, and image bytes are downloaded only on request
Create a Reusable Community Emoji
Supply image bytes from a trusted source. Community emoji uploads accept at most 512 KiB of image data. Fluxer checks the image format, dimensions, the community's remaining emoji slots and the bot's permission to manage emoji
export function createBuildEmoji(client, guildId, image) {
return client.emojis.create(guildId, {
name: "build_passed",
image: Buffer.from(image).toString("base64"),
})
}
import type { Client } from "@neontechspace/fluxerly"
export function createBuildEmoji(client: Client, guildId: string, image: Uint8Array) {
return client.emojis.create(guildId, {
name: "build_passed",
image: Buffer.from(image).toString("base64"),
})
}
The returned Guild can be formatted into a message or used as a reaction. Stickers are created with client and a name, image and optional description and tags. Both namespaces can also fetch, rename, copy and delete. To find which community owns a custom emoji or sticker, use emojis or stickers, which Fluxer answers when that community is discoverable or the bot is a member of it
Creating Several Emoji at Once
Batch creation with create returns separate success and failed lists, so inspect both. If the whole request fails with an unknown outcome, some emoji may still have been created, so fetch the community's emoji before retrying. The reliability guide explains these uncertain writes
Send and React with Effect
The Effect API accepts the same reaction inputs. This function sends a reply, parses a custom emoji, then reacts to the reply. Run it inside the Effect bot's existing runtime
import { Effect } from "effect"
import { format, type Client, type Message } from "@neontechspace/fluxerly/effect"
export function celebrateWithEffect(client: Client, message: Message, emojiMarkup: string) {
return Effect.gen(function* () {
const emoji = yield* format.tryParseCustomEmoji(emojiMarkup)
const sent = yield* client.messages.reply(message, "Build passed")
yield* client.messages.addReaction(sent, emoji)
return sent
})
}
Each step adds its possible failures to the function's Effect type. Interrupting the surrounding task stops the remaining steps. A completed reply stays posted if a later step fails
Wire It Up
In a run bot, a command can reply and then react to its own reply with the client from its context. This bot answers !celebrate and adds ๐ to the answer
import { runBot } from "@neontechspace/fluxerly"
await runBot({
token: process.env.FLUXER_BOT_TOKEN,
commands: {
prefix: "!",
commands: {
celebrate: {
description: "Celebrate with a reaction",
execute: async ({ client, reply, signal }) => {
const sent = await reply("Build passed")
if (sent.isErr())
return sent
return client.messages.addReaction(sent.value, "๐", { signal })
},
},
},
},
})
import { runBot } from "@neontechspace/fluxerly"
await runBot({
token: process.env.FLUXER_BOT_TOKEN,
commands: {
prefix: "!",
commands: {
celebrate: {
description: "Celebrate with a reaction",
execute: async ({ client, reply, signal }) => {
const sent = await reply("Build passed")
if (sent.isErr()) return sent
return client.messages.addReaction(sent.value, "๐", { signal })
},
},
},
},
})
Returning the reaction's Result lets the router report a failed reaction with the command name. To mirror reactions instead, add a message handler to the events option. Its event is the same reaction that mirror above receives, and its context also provides the client