twocents/open source

Reply to an X message and download its image

Open a personal X DM, heart a meme, reply, and download the image.

Suppose a friend sent a meme to your personal X account. The responses below are illustrative; replace the example IDs with IDs returned by your account.

Find the conversation

List the latest conversation:

dmbridge x conversations list --account personal --limit 1 --pretty
{
  "account": "personal",
  "command": "x conversations list",
  "ok": true,
  "protocolVersion": 1,
  "provider": "x",
  "requestID": "demo-request-1",
  "result": {
    "items": [
      {
        "archived": false,
        "conversationID": "x-conversation-123",
        "lastReadMessageID": "x-message-455",
        "latestMessageAt": "2026-10-14T19:42:11.000Z",
        "latestMessageID": "x-message-456",
        "muted": false,
        "participants": [
          {
            "displayName": "Jamie",
            "userID": "x-user-42",
            "username": "jamie"
          }
        ],
        "protocol": "xchat",
        "title": "Jamie",
        "type": "direct",
        "unread": true,
        "unreadCount": 1
      }
    ],
    "nextCursor": null
  }
}

Open it:

dmbridge x conversations get x-conversation-123 \
  --account personal --pretty
{
  "account": "personal",
  "command": "x conversations get x-conversation-123",
  "ok": true,
  "protocolVersion": 1,
  "provider": "x",
  "requestID": "demo-request-2",
  "result": {
    "archived": false,
    "conversationID": "x-conversation-123",
    "lastReadMessageID": "x-message-455",
    "latestMessageAt": "2026-10-14T19:42:11.000Z",
    "latestMessageID": "x-message-456",
    "muted": false,
    "participants": [
      {
        "displayName": "Jamie",
        "userID": "x-user-42",
        "username": "jamie"
      }
    ],
    "protocol": "xchat",
    "title": "Jamie",
    "type": "direct",
    "unread": true,
    "unreadCount": 1
  }
}

List its messages:

dmbridge x messages list x-conversation-123 \
  --account personal --limit 50 --pretty
{
  "account": "personal",
  "command": "x messages list x-conversation-123",
  "ok": true,
  "protocolVersion": 1,
  "provider": "x",
  "requestID": "demo-request-3",
  "result": {
    "items": [
      {
        "attachments": [
          {
            "attachmentID": "x-attachment-789",
            "filename": "meme.jpg",
            "mediaType": "image/jpeg",
            "sourceURL": null,
            "type": "image"
          }
        ],
        "conversationID": "x-conversation-123",
        "direction": "inbound",
        "messageID": "x-message-456",
        "providerType": "message",
        "read": false,
        "reactions": [],
        "replyToMessageID": null,
        "senderID": "x-user-42",
        "senderUsername": "jamie",
        "sentAt": "2026-10-14T19:42:11.000Z",
        "text": null,
        "type": "image"
      }
    ],
    "nextCursor": null
  }
}

The newest inbound message has messageID: "x-message-456" and an image with attachmentID: "x-attachment-789".

Heart the meme

Preview the mutation:

dmbridge x reactions add x-message-456 \
  --account personal --reaction '❤️' --dry-run --pretty
{
  "account": "personal",
  "command": "x reactions add x-message-456",
  "ok": true,
  "protocolVersion": 1,
  "provider": "x",
  "requestID": "demo-request-4",
  "result": {
    "account": "personal",
    "dryRun": true,
    "operation": "add",
    "provider": "x",
    "request": { "reaction": "❤️" },
    "target": "x-message-456"
  }
}

Then run the same action without --dry-run:

dmbridge x reactions add x-message-456 \
  --account personal --reaction '❤️' --pretty
{
  "account": "personal",
  "command": "x reactions add x-message-456",
  "ok": true,
  "protocolVersion": 1,
  "provider": "x",
  "requestID": "demo-request-5",
  "result": {
    "confirmed": true,
    "effect": "exact"
  }
}

Reply

Text replies do not need a JSON request body. Preview this one first:

dmbridge x messages reply x-message-456 "sick meme" \
  --account personal --dry-run --pretty
{
  "account": "personal",
  "command": "x messages reply x-message-456",
  "ok": true,
  "protocolVersion": 1,
  "provider": "x",
  "requestID": "demo-request-6",
  "result": {
    "account": "personal",
    "dryRun": true,
    "operation": "reply",
    "provider": "x",
    "request": { "text": "sick meme", "type": "text" },
    "target": "x-message-456"
  }
}

After inspecting the dry run:

dmbridge x messages reply x-message-456 "sick meme" \
  --account personal --pretty
{
  "account": "personal",
  "command": "x messages reply x-message-456",
  "ok": true,
  "protocolVersion": 1,
  "provider": "x",
  "requestID": "demo-request-7",
  "result": {
    "confirmed": true,
    "effect": "exact"
  }
}

Download the image

dmbridge x attachments download x-attachment-789 \
  --account personal \
  --destination-url "file://$HOME/Downloads/meme.jpg" --pretty
{
  "account": "personal",
  "command": "x attachments download x-attachment-789",
  "ok": true,
  "protocolVersion": 1,
  "provider": "x",
  "requestID": "demo-request-8",
  "result": {
    "bytes": 184320,
    "destinationURL": "file:///Users/you/Downloads/meme.jpg",
    "mediaType": "image/jpeg",
    "sha256": "d8e8fca2dc0f896fd7cb4cb0031ba2493a8bdf6d2230c53a8f18b3f4c2e3d7a1"
  }
}

The result includes the destination URL, media type, byte count, and SHA-256 digest of the saved file.

On this page