Mastodon API から「未収載」でトゥートするサンプル（bash と PHP）

「[未収載 Mastodon Toot API](https://www.google.co.jp/search?q=%E6%9C%AA%E5%8F%8E%E8%BC%89+Mastodon+Toot+API)」で検索してもなかなか出てこなかったので記事にしました。

# TL;DR

> `visibility` を `unlisted` にして `POST` します。

```shell:bashで１行で「Hello␣Qiitadon!」を「未収載」でトゥートするサンプル
curl -X POST \
    -d 'status=Hello%20Qiitadon%21' \
    -d 'visibility=unlisted' \
    --header 'Authorization: Bearer xxxxx...xxxxx' \
    -sS https://qiitadon.com/api/v1/statuses
```

「[未収載](#%E6%9C%AA%E5%8F%8E%E8%BC%89%E3%83%88%E3%82%A5%E3%83%BC%E3%83%88%E3%81%A8%E3%81%AF)」でトゥートするには、自身のマストドン・インスタンス（サーバー）の API のエンドポイントに以下のデータを `POST` します。

- エンドポイント
    - `/api/v1/statuses`<br>（例：`https://qiitadon.com/api/v1/statuses`）
- ヘッダー
    - `Content-Type: application/x-www-form-urlencoded`
    - `Authorization: Bearer <アクセストークン>`
- ボディー（`POST` するデータ）
    - `visibility=unlisted`<br>（公開トゥートにする場合は `visibility=public`）
    - `status=<メッセージ内容>`<br>（URLエンコードされていること）
    - `spoiler_text=<メッセージ内容>` がセットされていると、その内容が表示され、`status` の内容は「もっと見る」に隠し表示される


- API 情報
    - [Statuses](https://docs.joinmastodon.org/methods/statuses/) | API METHODS | Mastodon 公式ドキュメント

```bash:LTLの取得はアクセス・トークンは不要
server="https://qiitadon.com"
endpoint="/api/v1/timelines/public"
numLimit="50" #デフォルトは 20 件
query="?local=something&limit=${numLimit}"

curl -X GET -sS "${server}${endpoint}${query}" | jq .
```

# TS;DR

## 「未収載」トゥートとは

トゥートの種類のうち「未収載」（"Unlisted toot"）とは、**ローカルタイムラインに掲載されない「限定公開的なトゥート」のこと**で、以下の特徴があります。

- **検索の対象から外されます**。ハッシュタグ一覧にも掲載されません。
- フォロワー／リモートフォロワーの「ホーム」に掲載されます。
- 直接、トゥートの URL を開けば誰でも見ることができます。

ローカルや連合タイムラインを汚したくない場合で、かつフォロワー以外にも**誰でも見れるようにしたいトゥート**をするときに使います（例えばニュース BOT 系のトゥートなど）。

ちなみに、**フォロワー<ruby>のみ<rt>・・</rt></ruby>にトゥートを見せたい**場合は `visibility` を `private`（「非公開」）に設定します。

## PHP で外部コマンドの `cURL` を使ったサンプル

上記の `bash` と同じことを PHP から外部コマンドとして実行するサンプルです。

```php:sample1.php
<?php
/* Settings */
$schema       = 'https';
$host         = 'qiitadon.com';
$access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$method       = 'POST';
$endpoint     = '/api/v1/statuses';
$url          = "${schema}://${host}${endpoint}";
$visibility   = 'unlisted'; //投稿のプライバシー設定→「未収載」
$toot_msg     = 'Hello Qiitadon!';
$toot_msg     = rawurlencode($toot_msg); //メッセージをcURL用にエスケープ

/* Build request */
$query  = "curl -X ${method}";
$query .= " -d 'status=${toot_msg}'";
$query .= " -d 'visibility=${visibility}'";
$query .= " --header 'Authorization:";
$query .= " Bearer ${access_token}'";
$query .= " -sS ${url}";

/* Request */
$result = `$query`; //バッククォートに注意

/* Show result */
print_r(json_decode($result, JSON_OBJECT_AS_ARRAY));
```

- [オンラインでクエリを確認する](https://paiza.io/projects/iYrT-NDzcIiAv2CVkMc2iA) @ paiza.IO

## PHP の `file_get_contents` を使ったサンプル

Pure PHP でトゥートするサンプルです。

```php:sample2.php
<?php
/* Settings */
$schema       = 'https';
$host         = 'qiitadon.com';
$access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$method       = 'POST';
$endpoint     = '/api/v1/statuses';
$url          = "${schema}://${host}${endpoint}";
$visibility   = 'unlisted'; //投稿のプライバシー設定→「未収載」
$toot_msg     = 'Hello Qiitadon!';

/* Build request */
$data_post = [
    'status'     => $toot_msg,
    'visibility' => $visibility,
];
$data_post = http_build_query($data_post, "", "&");

$header = [
    'Content-Type: application/x-www-form-urlencoded',
    "Authorization: Bearer ${access_token}",
];
$header = implode("\r\n", $header);

$context = [
    'http' => [
        'method'  => $method,
        'header'  => $header,
        'content' => $data_post,
    ],
];

/* Request */
$result = file_get_contents($url, false, stream_context_create($context));

/* Show result */
print_r(json_decode($result, JSON_OBJECT_AS_ARRAY));
```

## 併せて読みたい

- [Mastodon API から「返信」をトゥートする PHP サンプル](http://qiita.com/KEINOS/items/68ae99c7f98fe309f9bf)
- [Mastodon API で「You are being redirected」とアクセストークンの取得](http://qiita.com/KEINOS/items/78445aad59e5818bf87e)

# 投稿のプライバシー（公開範囲）について

トゥートの公開範囲がドキュメントによって違うのですが、以下は公式のドキュメントに記載されている内容です。（2021/04/15現在）

https://docs.joinmastodon.org/user/posting/#privacy

Level | Public timelines | Permalink | Profile view | Home feeds
----- | ---------------- | --------- | ------------ | ----------
Public | Yes| Yes | Yes | Yes
Unlisted | No | Yes | Yes | Yes
Followers-only | No | Logged in on the same site | In-app or logged in | Yes
Direct | No | Logged in and mentioned | In-app or logged in | No


公開レベル | 公開・連合タイムラインに流れるか | パーマリンクで表示されるか | プロフィールで表示されるか | 自分のホームに表示されるか
----- | ---------------- | --------- | ------------ | ----------
公開 | Yes| Yes | Yes | Yes
未収載  | No | Yes | Yes | Yes
非公開 | No | 同じサイトにログインしていた場合のみ | アプリ（API）やログインしていた場合のみ | Yes
ダイレクト | No | ログインしておりメンション（`@yourname`）が付いていた場合のみ | アプリ（API）やログインしていた場合のみ | No

# 参考文献
- Publishing levels | Posting toots | Docs @ joinmastodon.org<br>https://docs.joinmastodon.org/user/posting/#privacy
- mastodonをpythonからさわる API解説
 @ Qiita<br>http://qiita.com/code_monkey/items/e4929ef13e2a2032d467