From c3283dda811a60db32475ba8c411ac7dff5a4e43 Mon Sep 17 00:00:00 2001 From: zenfyr Date: Tue, 28 Apr 2026 03:26:06 +0700 Subject: [PATCH] feat: add `client_name` support --- atproto/models.py | 3 +++ bluesky/client.py | 4 ++++ bluesky/output.py | 6 ++++++ docs/services.md | 1 + 4 files changed, 14 insertions(+) diff --git a/atproto/models.py b/atproto/models.py index 3829002..aee8881 100644 --- a/atproto/models.py +++ b/atproto/models.py @@ -221,6 +221,7 @@ class PostRecord: reply: ReplyRef | None = None langs: list[str] | None = None labels: SelfLabels | None = None + via: str | None = None def to_dict(self) -> dict[str, Any]: data: dict[str, Any] = { @@ -238,6 +239,8 @@ class PostRecord: data["langs"] = self.langs if self.labels: data["labels"] = self.labels.to_dict() + if self.via: + data["via"] = self.via return data diff --git a/bluesky/client.py b/bluesky/client.py index 157b0ca..0385785 100644 --- a/bluesky/client.py +++ b/bluesky/client.py @@ -36,6 +36,7 @@ class BlueskyClient: identifier: str, http: httpx.Client | None = None, password: str | None = None, + client_name: str | None = None, ) -> None: self.pds_url: str = normalize_service_url(pds_url) self.store: AtprotoStore = store @@ -44,6 +45,8 @@ class BlueskyClient: self.did: str = identity.did self.xrpc: XRPCClient = XRPCClient(pds_url, store, http, self.did, password) + self.client_name = client_name + def _get_timestamp(self, time_iso: str | None = None) -> str: if time_iso: return time_iso @@ -70,6 +73,7 @@ class BlueskyClient: labels=labels, langs=langs, created_at=self._get_timestamp(time_iso), + via=self.client_name, ) response = self.xrpc.call( diff --git a/bluesky/output.py b/bluesky/output.py index c86bf27..43f7daf 100644 --- a/bluesky/output.py +++ b/bluesky/output.py @@ -3,6 +3,7 @@ import re from dataclasses import dataclass from typing import Any, override +import grapheme import httpx import misskey.mfm as mfm @@ -51,6 +52,7 @@ class BlueskyOutputOptions: quote_gate: bool = False thread_gate: list[str] | None = None encode_videos: bool = True + client_name: str | None = "XPost" @classmethod def from_dict(cls, data: dict[str, Any]) -> "BlueskyOutputOptions": @@ -75,6 +77,9 @@ class BlueskyOutputOptions: if "encode_videos" in data: data["encode_videos"] = bool(data["encode_videos"]) + if data.get("client_name") and grapheme.length(data["client_name"]) > 50: + raise ValueError(f"client_name: '{data['client_name']}' is above 50 chars!") + return BlueskyOutputOptions(**data) @@ -95,6 +100,7 @@ class BlueskyOutputService(BlueskyService, OutputService): self.did, http, self.options.password, + client_name=options.client_name ) self.options.password = "" self.log.info("Logged in as '%s'", self.did) diff --git a/docs/services.md b/docs/services.md index 273d2cc..2b0745d 100644 --- a/docs/services.md +++ b/docs/services.md @@ -127,6 +127,7 @@ Use Dev Tools 💔 | `password` | Account App Password. | | `quote_gate` | Disable ability for others to quote. | | `thread_gate` | Limit replies to the post. null - everybody, [] - nobody. accepts "mentioned", "following", "followers" | +| `client_name` | set the "via" field supported by some clients. defaults to "XPost", can be `null` | #### App Password -- 2.51.2