DislifyDislify

Command Palette

Search for a command to run...

Discord bot guide

Build a media bot that sends the heavy work to Dislify.

Accept media through a Discord slash command, compress it with the discord preset, show live progress, and return a secure result without building your own media-processing infrastructure.

Request lifecycle

One command from upload to delivery.

Keep the Discord interaction lightweight while Dislify handles authentication, upload orchestration, compression, status polling, and result delivery.

01

Receive /compress

Read the attachment from a Discord slash command and defer the initial response.

02

Submit the media

Fetch the attachment bytes and pass a Blob into Dislify with the discord preset.

03

Report progress

Throttle SDK progress callbacks and edit the original Discord response.

04

Return the result

Post the secure share URL, or download and attach output that fits the channel limit.

Bot quick starts

Build /compress in your language.

Choose TypeScript or JavaScript for the official SDK, or follow the complete REST lifecycle in Python, Go, or Ruby. Every example accepts a Discord attachment, applies the discord preset, waits for processing, and returns the result.

Keep both tokens on the server. Never place the Discord bot token or Dislify API key in browser code, a public repository, or a slash-command response.

Run locally with node --env-file=.env bot.mjs. Most deployment platforms can inject the same variables through their secrets settings.

Discord bot quick start

discord.js + @dislify/sdk

npm install discord.js @dislify/sdk

import {
  Client,
  Events,
  GatewayIntentBits,
} from "discord.js"
import { DislifyClient } from "@dislify/sdk"

const apiKey = process.env.DISLIFY_API_KEY
const botToken = process.env.DISCORD_BOT_TOKEN

if (!apiKey || !botToken) {
  throw new Error("Missing Discord or Dislify credentials")
}

const dislify = new DislifyClient({ apiKey })
const client = new Client({
  intents: [GatewayIntentBits.Guilds],
})

client.on(Events.InteractionCreate, async (interaction) => {
  if (
    !interaction.isChatInputCommand() ||
    interaction.commandName !== "compress"
  ) return

  const file = interaction.options.getAttachment("file", true)

  if (!file.contentType?.startsWith("image/")) {
    await interaction.reply({
      content: "This command accepts image attachments.",
      ephemeral: true,
    })
    return
  }

  await interaction.deferReply()

  const source = await fetch(file.url)
  if (!source.ok) throw new Error("Unable to read attachment")

  const image = new Blob([await source.arrayBuffer()], {
    type: file.contentType,
  })

  let lastUpdate = 0

  const { shareUrl } = await dislify.optimizeImage(image, {
    fileName: file.name,
    compression: "discord",
    onProgress(job) {
      const now = Date.now()
      if (now - lastUpdate < 1500) return

      lastUpdate = now
      void interaction
        .editReply(`Compressing: ${job.progress ?? 0}%`)
        .catch(() => undefined)
    },
  })

  await interaction.editReply(
    shareUrl
      ? `Compression complete: ${shareUrl}`
      : "Compression complete."
  )
})

client.login(botToken)

Uses the official JavaScript/TypeScript SDK.

TypeScript library

Media coverage

Use the right Dislify workflow for each attachment.

The image quick start is intentionally narrow and runnable. Video and audio belong in the asynchronous media-job path, especially when processing can outlive the Discord interaction.

Images

Use optimizeImage() for the shortest SDK path and live progress callbacks.

Video

Submit video through the media batch or REST job workflow and deliver asynchronously.

Audio

Process audio through the same job pipeline and return the completed share URL.

Production architecture

Use webhooks when the command should finish immediately.

For busy servers, acknowledge the slash command, submit the media job, store the Discord channel alongside the Dislify job ID, and let a signed job.completed webhook deliver the result later.

Free the interaction handler

Queue the work and avoid holding an application process open during long compression jobs.

Verify every callback

Validate Dislify's HMAC signature before trusting a completion or failure event.

Map jobs to channels

Store the job ID with the destination guild and channel, then post when processing completes.

Completion handler
webhooks/dislify.ts
// POST /webhooks/dislify
const event = verifyDislifyWebhook(request)

if (event.type === "job.completed") {
  const route = await findDiscordRoute(event.data.jobId)

  await discord.channels.send(route.channelId, {
    content: `Compression complete: ${event.data.shareUrl}`,
  })
}

Delivery rule

Attach when it fits. Link when it does not.

A bot upload is still subject to Discord's attachment rules. Use the discord preset to reduce the file, then attach the downloaded output only when it fits the destination channel. Otherwise, send the secure Dislify shareUrl returned by the completed job.

Ready when you are

Give your Discord community a real media workflow.

Create an API key, register the slash command, and let Dislify handle compression and delivery behind your bot.