EDA.HTTP.Multipart (EDA v0.5.0-beta.2)

Copy Markdown View Source

Encodes multipart/form-data requests for Discord file uploads.

Produces iodata bodies with a payload_json part and indexed files[n] parts. Automatically injects the attachments array into the JSON payload.

An attachments array already present in the payload is kept and the uploaded files are appended to it. That is how a message edit retains the attachments it already has: Discord deletes any attachment missing from the array, so EDA.Attachment.keep/2 entries go in the payload and the new files are indexed after them.

Summary

Functions

Encodes a JSON payload and files into multipart/form-data.

Encodes a payload and named file fields into multipart/form-data.

Escapes a filename for use in Content-Disposition headers.

Returns the MIME type for a filename based on its extension.

Functions

encode(json_payload, files, opts \\ [])

@spec encode(map(), [EDA.File.t()], keyword()) :: {iodata(), String.t()}

Encodes a JSON payload and files into multipart/form-data.

Returns {body_iodata, content_type} where content_type includes the boundary.

Each uploaded file gets an entry with id matching the file index, filename, and optional description. Entries already in the payload's attachments array — the attachments an edit is retaining — are preserved and come first.

attachments_in: :data puts the array inside the payload's data instead of at its top level, which is where an interaction callback carries its message.

encode_named(json_payload, fields)

@spec encode_named(map(), [{String.t(), String.t(), binary()}]) ::
  {iodata(), String.t()}

Encodes a payload and named file fields into multipart/form-data.

encode/2 is for Discord's message attachments, which are always files[n] accompanied by an attachments array. A handful of endpoints instead want a file under a name of its own — target_users_file on invites — alongside the other params in payload_json.

fields is a list of {field_name, filename, data}. The payload is omitted when empty, since these endpoints accept a file on its own.

Returns {body_iodata, content_type}.

escape_filename(name)

@spec escape_filename(String.t()) :: String.t()

Escapes a filename for use in Content-Disposition headers.

Escapes backslashes, double quotes, and replaces newlines with underscores.

mime_type(filename)

@spec mime_type(String.t()) :: String.t()

Returns the MIME type for a filename based on its extension.

Falls back to application/octet-stream for unknown extensions.