Skip to content

Call Baxter integration

Plugin configuration

plugins:
  - module: cb_social_plugin.bluesky
    config:
      identifier: "publisher.example"
      app_password: "${BLUESKY_APP_PASSWORD}"
      state_db_path: "/srv/data/social-publications.db"
      media_group_wait_seconds: 2

  - module: cb_social_plugin.mastodon
    config:
      base_url: "https://social.example"
      access_token: "${MASTODON_ACCESS_TOKEN}"
      state_db_path: "/srv/data/social-publications.db"
      visibility: "unlisted"

  - module: cb_social_plugin.x_plugin
    config:
      access_token: "${X_ACCESS_TOKEN}"
      state_db_path: "/srv/data/social-publications.db"
      edit_policy: "ignore"

Secrets above are placeholders; keep actual credentials in the normal Call Baxter secret/env boundary rather than committing them to configuration files.

Mirror the same Telegram event to several providers

id: mirror-telegram-social
enabled: true
match:
  source: telegram
  kind: telegram.message.new
actions:
  - kind: bluesky.mirror_telegram_message
    args:
      update: "{{ event.attrs.raw_update }}"
      required_hashtag: "#social"
  - kind: mastodon.mirror_telegram_message
    args:
      update: "{{ event.attrs.raw_update }}"
      required_hashtag: "#social"
  - kind: x.mirror_telegram_message
    args:
      update: "{{ event.attrs.raw_update }}"
      required_hashtag: "#social"

Add equivalent telegram.message.edited rules when edits should propagate. Bluesky and Mastodon update their existing mapped post. X keeps its historical edit_policy: ignore default; choose edit_policy: native explicitly to propagate Telegram edits through X's edit chain. Existing edit_policy: replace rules remain accepted with their delete-then-repost semantics.

X durability and required read scope

Use persistent state_db_path for production X mirroring. The same database holds the normal source mapping and a short-lived in-flight mutation intent used to harden the non-idempotent X create boundary. Stateless X mirroring remains available for compatibility, but it cannot perform this recovery and retains the remote-success/local-commit duplicate window.

X credentials used for mirroring should be able to read the authenticated user and their recent posts as well as create/edit posts. The read path is only used when a previous mutation ended in an ambiguous state (for example, a timeout after X may already have accepted the post). In that case Call Baxter attempts a narrow timeline reconciliation. If it cannot prove exactly one matching remote post, it raises XMirrorRecoveryRequired and does not send another create request.

This is intentionally different from an ordinary retry loop. The unresolved intent must remain in the state database so later deliveries continue reconciling rather than duplicating. Investigate X directly before manually removing an unresolved intent.

Media groups

When media_group_wait_seconds is greater than zero, every mirror action stages Telegram messages with media_group_id. Schedule each configured provider's flush action against the same state DB:

actions:
  - kind: bluesky.flush_due_media_groups
  - kind: mastodon.flush_due_media_groups
  - kind: x.flush_due_media_groups

Do not add a second broad album-staging mirror rule. As with the WordPress plugin, the ordinary mirror action itself is the staging path.