+++ date = "2024-04-02" tags = ["bluesky", "atproto"] title = "blueskyのコメントシステムを作ってみた" slug = "bluesky" +++ この前、blueskyで特定の投稿に返信することで反映されるコメントシステムを作ってみました。 どういうふうに実現しているのかというと、結構複雑ですが、簡単に説明すると、私は以前からbotを動かしていて、そのついでに返信くらいは取得することができるので、新しく`openapi`を追加してそこにコメント情報を登録し、このapiから取得する情報でwebページを生成します。 ![](/img/bluesky_comment_system_0001.png) - https://manga.syui.ai 簡単に概要を見ていくとこんな感じ。 ```rust:src/main.rs if uri_root == &manga_uri { println!("manga_uri:{}", manga_uri); let output = Command::new(data_scpt(&"ai")) .arg(&"atproto").arg(&"manga") .arg(&handle) .arg(&did) .arg(&cid) .arg(&uri) .arg(&cid_root) .arg(&uri_root) .arg(&host) .arg(&avatar) .arg(&prompt_chat) .output() .expect("zsh"); let d = String::from_utf8_lossy(&output.stdout); let d = d.to_string(); let text_limit = c_char(d); let str_rep = reply::post_request( text_limit.to_string(), cid.to_string(), uri.to_string(), cid_root.to_string(), uri_root.to_string(), ) .await; println!("{}", str_rep); w_cid(cid.to_string(), log_file(&"n1"), true); } ``` ```sh:src/manga.zsh function manga_text() { repo=$did collection=app.bsky.feed.post url="https://$host/xrpc/com.atproto.repo.getRecord?repo=$repo&collection=$collection&rkey=$rkey&cid=$cid" export text=`curl -sL $url|jq -r .value.text` } function manga_add() { aid=2 api=https://api.syui.ai avatar=$com_option text=$com_option_sub_all export rkey=`echo $uri|cut -d / -f 5` bsky_url="https://bsky.app/profile/$did/post/$rkey" if [ "$host" = "syu.is" ];then bsky_url="https://web.syu.is/profile/$did/post/$rkey" fi manga_text j="{\"owner\":$aid, \"password\":\"$pass\"}" export mid=`curl -X POST -H "Content-Type: application/json" -d $j -sL $api/mas|jq -r .id` j="{\"updated_at\":\"$date_iso\", \"token\":\"$token\", \"did\":\"$did\", \"cid\":\"$cid\", \"uri\":\"$uri\", \"rkey\":\"$rkey\", \"bsky_url\":\"$bsky_url\", \"avatar\":\"$avatar\", \"handle\":\"$handle\", \"text\": \"$text\"}" tmp=`curl -X PATCH -H "Content-Type: application/json" -d $j -sL $api/mas/$mid` echo thx } ``` ```json:openapi.json "/mas": { "get": { "tags": [ "Ma" ], "summary": "List Mas", "description": "List Mas.", "operationId": "listMa", "parameters": [ { "name": "page", "in": "query", "description": "what page to render", "schema": { "type": "integer", "minimum": 1 } }, { "name": "itemsPerPage", "in": "query", "description": "item count to render per page", "schema": { "type": "integer", "maximum": 5000, "minimum": 1 } } ], "responses": { "200": { "description": "result Ma list", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/MaList" } } } } }, "400": { "$ref": "#/components/responses/400" }, "404": { "$ref": "#/components/responses/404" }, "409": { "$ref": "#/components/responses/409" }, "500": { "$ref": "#/components/responses/500" } } }, "post": { "tags": [ "Ma" ], "summary": "Create a new Ma", "description": "Creates a new Ma and persists it to storage.", "operationId": "createMa", "requestBody": { "description": "Ma to create", "content": { "application/json": { "schema": { "type": "object", "properties": { "password": { "type": "string" }, "token": { "type": "string" }, "limit": { "type": "boolean" }, "count": { "type": "integer" }, "handle": { "type": "string" }, "text": { "type": "string" }, "did": { "type": "string" }, "avatar": { "type": "string" }, "cid": { "type": "string" }, "uri": { "type": "string" }, "rkey": { "type": "string" }, "bsky_url": { "type": "string" }, "updated_at": { "type": "string", "format": "date-time" }, "created_at": { "type": "string", "format": "date-time" }, "owner": { "type": "integer" } }, "required": [ "password", "owner" ] } } }, "required": true }, "responses": { "200": { "description": "Ma created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MaCreate" } } } }, "400": { "$ref": "#/components/responses/400" }, "409": { "$ref": "#/components/responses/409" }, "500": { "$ref": "#/components/responses/500" } } } } ``` ```html:src/App.vue
{{ axios_check('/icon/' + i.did.replace('did:plc:', '') + '.jpg') }} {{ moment(i.updated_at) }} @{{ i.handle }} {{ i.text }}
{{ moment(comment_first.updated_at) }} @{{ comment_first.handle }} {{ comment_first.text }}
post