Skip to main content
Use media sending APIs to reply to the current conversation with images, voice, video, files, or mixed content. You can also push media proactively to a specific IM target.
New plugins should prefer Sender methods: replyImage, replyVoice, replyVideo, replyFile, and replyMixed. Legacy global functions such as sendImage, sendVoice, sendVideo, sendFile, and sendMixed still work for maintaining old reply scripts, but new examples use Sender.

Choose a sending method

Reply to the current chat

Use sender.replyImage(...) and related methods in a message-triggered plugin. The target comes from the current message.

Push proactively

Use pushImage(...), pushVoice(...), pushVideo(...), pushFile(...), or pushMixed(...) with imType, group, and user IDs.

Send combined content

Use replyMixed(...) to send text, images, voice, video, or files in one message sequence.

Save files first

Use fileDownload(...) to save a remote URL, base64 payload, or data URI, then pass the local path to a media method.

Quick example

//[author: your-name]
//[runtime: [email protected]]
//[rule: image]
const { Sender, getSenderID } = require('./middleware.js')

const sender = new Sender(getSenderID())
const receipt = await sender.replyImage('https://example.com/demo.png')

if (receipt.ok === false) {
  await sender.reply(`Image send failed: ${receipt.error || 'unknown error'}`)
}

Media sources

The source parameter of media methods supports these forms:
SourceExampleBest for
Remote URLhttps://example.com/a.pngFiles already hosted publicly or reachable by the adapter.
Local absolute path/opt/autclaw/plugin/tmp/a.pngFiles generated by the plugin.
Local relative path./tmp/a.pngFiles near the plugin runtime directory.
base64base64://...Sending generated binary content directly.
data URIdata:image/png;base64,...Base64 content with a MIME type.
Local paths are best for files generated by plugins, such as screenshots, reports, TTS audio, or downloaded temporary files. Remote URLs work well when the asset is already publicly reachable.

Reply with media

Create Sender first, then call the reply method. The return value is usually SendReceipt.
const { Sender, getSenderID } = require('./middleware.js')

const sender = new Sender(getSenderID())

await sender.replyImage('https://example.com/a.png')
await sender.replyVoice('./voice.wav')
await sender.replyVideo('./demo.mp4')
await sender.replyFile('./report.csv', { filename: 'report.csv' })

Method purpose

MethodPurposeCommon sources
replyImage(source, options?)Send an image.png, jpg, gif, image URL, base64.
replyVoice(source, options?)Send voice/audio.wav, mp3, ogg, opus, audio URL.
replyVideo(source, options?)Send video.mp4, mov, video URL.
replyFile(source, options?)Send a file.csv, txt, pdf, zip, or other files.
replyMixed(items, options?)Send a mixed message.Text + image/voice/video/file.

options

The second parameter of media reply methods is an optional object. Current regular plugin helpers use these fields:
FieldTypeDescription
filename / fileName / file_namestringDisplayed or saved filename.
mimeType / mime_typestringMIME type, such as image/png or video/mp4.
To specify a target, use pushImage(...), pushVoice(...), pushVideo(...), pushFile(...), or pushMixed(...). Regular replies send to the current conversation by default.

Send mixed messages

replyMixed(items, options?) combines text and media into one message sequence. Media items can use source, file, url, or path for the source.
await sender.replyMixed([
  { type: 'text', text: 'Done: ' },
  { type: 'image', source: './result.png', name: 'result.png' },
  { type: 'file', source: './report.csv', name: 'report.csv', mime_type: 'text/csv' },
])
Supported items:
itemRequired fieldDescription
{ type: 'text', text: '...' }textSend a plain text segment.
{ type: 'markdown', text: '...' }textSend a Markdown segment, depending on adapter support.
{ type: 'image', source: '...' }sourceSend an image.
{ type: 'voice', source: '...' }sourceSend voice/audio.
{ type: 'video', source: '...' }sourceSend video.
{ type: 'file', source: '...' }sourceSend a file.
Media items can also include name, file_name, filename, mime_type, or mimeType.

Push media proactively

Proactive push does not depend on the current message. Specify the target:
  • imType: IM type, such as qq or wx. Use the adapters enabled in your deployment.
  • groupCode: Group number or group ID. Usually an empty string for private chats.
  • userID: User ID. Usually an empty string for group pushes.
  • title: Push title or note. Some adapters ignore it.
const { pushImage, pushVideo, pushMixed } = require('./middleware.js')

await pushImage('qq', '123456', '', 'Daily report', './daily.png', { account_id: 'bot_qq_ops' })
await pushVideo('qq', '123456', '', 'Demo video', './demo.mp4', { account_id: 'bot_qq_ops' })
await pushMixed('qq', '123456', '', 'Result', [
  { type: 'text', text: 'Result:' },
  { type: 'image', source: './result.png' },
], { account_id: 'bot_qq_ops' })
MethodParametersReturns
pushImage(imType, groupCode, userID, title, imageUrl, options?, timeout?)Send count, usually 0 or 1
pushVoice(imType, groupCode, userID, title, voiceUrl, options?, timeout?)Send count
pushVideo(imType, groupCode, userID, title, videoUrl, options?, timeout?)Send count
pushFile(imType, groupCode, userID, title, fileUrl, options?, timeout?)Send count
pushMixed(imType, groupCode, userID, title, items, options?, timeout?)Send count
For proactive push account selection rules, see JavaScript proactive push or Python proactive push.

Save and send files

fileDownload(url, path?) saves http(s), base64://..., or data:*;base64,... sources as local files. Pass the returned path / file_path to media sending methods.
const { Sender, getSenderID, fileDownload } = require('./middleware.js')

const sender = new Sender(getSenderID())
const file = await fileDownload('data:image/png;base64,...', 'images')

await sender.replyImage(file.path, { filename: file.file_name, mimeType: file.mime_type })

fileDownload parameters

ParameterTypeRequiredDescription
urlstringYesSupports http(s), base64://..., and data:*;base64,....
pathstringNoSave directory. If omitted, saves under runtime file; relative paths save under file/<path>; absolute paths are used as-is.

FileDownloadResult

{
  "path": "/opt/autclaw/file/images/demo.png",
  "file_path": "/opt/autclaw/file/images/demo.png",
  "file_name": "demo.png",
  "mime_type": "image/png",
  "file_size": 12345
}
A single fileDownload(...) file is limited to about 20 MB by default. Download failures, unreachable sources, or oversized files throw exceptions.

Handle user-uploaded files

If a user uploads a file in a message, read media items first, then call downloadAdapterFile(...) to save it locally.
const { Sender, getSenderID, downloadAdapterFile } = require('./middleware.js')

const sender = new Sender(getSenderID())
const items = await sender.getMediaItems()
const fileItem = items.find((item) => item.type === 'file')

if (!fileItem) {
  await sender.reply('Please send a file')
} else {
  const saved = await downloadAdapterFile(fileItem, { path: 'imports' })
  await sender.reply(`Saved: ${saved.file_name}`)
}
downloadAdapterFile(...) returns the same fields as fileDownload(...) and may include extra fields:
{
  "ok": true,
  "url": "https://adapter.example/file?id=...",
  "action": "resolveFileURL"
}

SendReceipt

Text, media, and recall methods usually return a send receipt. Adapters may add extra fields.
{
  "ok": true,
  "message_id": "123456",
  "messageid": "123456",
  "message_ids": ["123456"],
  "raw": {},
  "action": "sendImage"
}
When checking results, treat ok === false / ok is False as failure. Do not rely on only one message ID field.
const receipt = await sender.replyVideo('./demo.mp4')

if (receipt.ok === false) {
  await sender.reply(`Video send failed: ${receipt.error || 'unknown error'}`)
} else {
  console.log('message id:', receipt.message_id || receipt.messageid)
}

Practical tips

  • Before sending a locally generated file, make sure it has been fully written.
  • Set filename / name for files and mixed media items so users can recognize downloads.
  • Prefer reachable URLs for large files. Save to disk with fileDownload(...) only when you need local processing.
  • Do not manually parse user-uploaded platform raw fields. Use sender.getMediaItems() + downloadAdapterFile(...).
  • When media sending fails, read error from the receipt and give users a concrete next step.

Legacy reply script entry points

When maintaining legacy plugin/replies/*.js scripts, you can still use global functions:
sendImage('https://example.com/a.png')
sendVoice('./voice.wav')
sendVideo('./demo.mp4')
sendFile('./report.csv')
sendMixed([
  { type: 'text', text: 'Result: ' },
  { type: 'image', source: './result.png' },
])
For new plugin/scripts/*.js or plugin/scripts/*.py files, prefer the Sender style for readability and type hints.
Last modified on June 3, 2026