Introduction
Pulsate Specification は Pulsate プロジェクトに関するAPIエンドポイントや設計などの仕様を記述したドキュメントです.
プロジェクトの概要については こちら をご覧ください.
note
この仕様書は現在開発中の Pulsate v0.1 となります. 今後のバージョンアップにより仕様が変更される可能性があります.
プロジェクト概要
プロジェクトの目標
高速で安全な ActivityPub 実装
-
パフォーマンス重視
-
セキュリティ重視
-
ActivityPub 仕様の重視
実装方針
パフォーマンス・セキュリティの強化
- Misskey や Mastodon よりも高速に動作する次世代の ActivityPub 実装.
- 各APIリクエストに対するレスポンスを始めとするパフォーマンスの向上(タイムラインの読み込み, メディアのレスポンスなど).
- SQL Injection や XSS などのセキュリティに関わる脆弱性の発生可能性を減少させる.
見つかったとしてもすぐに修正できる仕組みづくり.
- パスワードレスログイン, 2FA など.
ActivityPub 実装の重視
- 無駄な機能は実装せずあくまで ActivityPub の実装として忠実に従う.
- MFM など特定のソフトウェア固有の実装に従わない.
- ただし, Mastodon や Misskey などの主要な実装が互いに実装している機能は実装する. (引用など)
クライアント, App 周りのサポートの強化
- ターゲットはモバイルユーザーが大半だと考え, Pulsate ではネイティブアプリとして
iOS App, Android App をリリースする(構想段階).
- PWAはChromium系ブラウザは概ね対応しているが, Firefox や Safari は対応していない.
- また、大部分のユーザーはアプリストアで検索すると考えられるため、マーケティング的な観点からもネイティブアプリの実装は必要.
- コミュニティが開発するサードパーティクライアントも尊重し, API リファレンスなどのリソースを多く用意しコミュニティの開発を支援する.
開発手法の厳格化 / 改善
- semver (セマンティックバージョニング) に従い, 破壊的変更をなるべく少なくする. 破壊的変更が行われる場合は予めコミュニティに通告する.
コミュニティの声を聞く
- 簡単に言えば Discord や GitHub Discussions の採用.
- バグ報告は基本的に インスタンス管理者 , ユーザー から受けるのが多いと考えるため, Discord での報告も受け付ける. (GitHub Issues には Discord Bot を使ってうまく Import する)
プロジェクトコードネーム
Pulsate
- 意味: 脈打つ, 鼓動する; 〈光・音などが〉振動する; ドキドキする; など.
- 由来: indigo la End のアルバム "PULSATE" から.
バージョンコードネーム
v0, v1 - Antenna
v0 (開発途上バージョン), v1 (最初の安定版) のコードネーム.
由来は Mrs. GREEN APPLE の ANTENNA から.
モデルリファレンス
ID
Twitter の Snowflake をベースに, 次のようなビットフィールド (図はビッグエンディアン) からなる 64 ビット整数です.
先頭に時刻を含んでいるため, そのまま時刻順にソートが可能です.
111111111111111111111111111111111111111111 1111111111 111111111111
64 22 12 0
timestamp worker id incremental
各ビットフィールドの意味は以下のとおりです.
フィールド | ビット範囲 | 意味 |
---|---|---|
timestamp | [64, 22) | Pulsate エポックからのミリ秒数. エポックは 2022 年 1 月 1 日 0 時 0 分 0.000 秒. UNIX 時間のミリ秒に 1640995200000 を足すことでこれに変換可能. |
worker id | [22, 12) | この ID を生成したワーカーの識別子 |
incremental | [12, 0) | ワーカーにて同一時刻で ID を生成する度に増える値 |
TypeScript 上ではこのような型として表現します. 型引数によりエンティティ間で ID を取り違えるようなミスを防ぎます.
declare const snowflakeNominal: unique symbol;
export type ID<T> = string & { [snowflakeNominal]: T };
生成と利用の方法
モデル側での定義
モデルごとにこのような形でモデル固有のIDの型を定義します
export type AccountID = ID<Account>;
ID生成方法
IDはid
パッケージのSnowflakeIDGenerator
クラスを利用して生成します。
important
このとき、SnowflakeIDGeneratorは必ずコンストラクタで受け取るようにしてください。
export class RegisterService {
/** 省略 **/
private readonly snowflakeIDGenerator: SnowflakeIDGenerator;
constructor(arg: {
/** 省略 **/
idGenerator: SnowflakeIDGenerator;
}) {
/** 省略 **/
this.snowflakeIDGenerator = arg.idGenerator;
}
IDを生成するにはSnowflakeIDGenerator.generate<T>()
メソッドを呼び出します。
この時の型引数 T は、生成したいIDの型を入れてください
const idRes = this.snowflakeIDGenerator.generate<AccountID>();
ID生成時にエラーが発生する場合があるので、必ずエラーハンドリングが必要です。
IDの利用方法
テストなどで利用する際にIDを静的に定義したい場合や、APIなどでユーザーからstring形式で受け取ったIDはアサーションすることでIDに変換できます。
const accountID = "31415926535" as AccountID;
認証トークン
JSON Web Token の一種で, ペイロードには 更新トークン など以下の情報を含みます.
更新トークンも JWT なので, JWT がネストしています.
sub
: 発行対象のアカウント名iat
: 発行時刻 (UNIX エポックの秒数)
{
"sub": "hogehoge-user",
"iat": 1640995201
}
このトークンの有効期限は発行時刻から 15 分 (900 秒) です.
生成や検証の自作は実装ミスによる脆弱性を誘発しますのでライブラリを利用します.
更新トークン
JSON Web Token の一種で, ペイロードには以下の情報を含みます.
{
"sub": "3e1644833000002",
"iat": 1640995201
}
このトークンの有効期限は発行時刻から 30 日 (2,592,000 秒) です. 特に再発行はされず, 手動でのログイン時にのみ発行されます.
なおこのトークンは長命なので, 利用後に無効化しておかないと奪取されて再利用されるリスクがあります. 危険度は高いですがネットワーク上に流れることがほとんどないため, このリスクには対処せず保有することにします.
アカウント
自然人, 団体, ボットなど, このシステム上でオブジェクトを発行する主体となるものです. これは次の制約を持つ属性を備えます.
name
: アカウント名- 1 文字以上の URL 安全な ASCII 文字からなる
nickname
: 表示名, アカウントの表示に用いる自然言語の名称- 初期値は空文字列
- 空文字列の場合はアカウント名にフォールバックする
- RTL 制御文字を除く任意の
UTF-8
シーケンス
created_at
: このアカウントが作成された時刻mail
: メールアドレス- 有効なメールアドレスであることが検証済み
passphrase_hash
: パスフレーズのハッシュ- 元となったパスフレーズは以下の性質を満たします
- 文字種は正規化された
UTF-8
シーケンス - 連続する空白タイプの文字 (スペース, タブ, 全角スペース, 改行文字など) は 1 つの半角スペースへと置き換えられる
- 長さは Unicode スカラー値で 8 つぶん以上
- 文字種は正規化された
- 元となったパスフレーズは以下の性質を満たします
salt
: パスフレーズのハッシュに使ったソルト- パスフレーズの平文にこれを連結したもののハッシュは,
passphrase_hash
に等しい
- パスフレーズの平文にこれを連結したもののハッシュは,
なお, 基本情報はアカウント ID に関連付けられるようにし, このモデル自体は基本情報を保持しません.
アカウント状態
Mermaid code
stateDiagram-v2
NOT_ACTIVATED --> ACTIVE
ACTIVE --> FROZEN
ACTIVE --> SILENCED
SILENCED --> FROZEN
FROZEN --> SILENCED
SILENCED --> ACTIVE
FROZEN --> ACTIVE
登録中アカウント
まだ実際にアカウントが発行されておらず, メールアドレスを検証するスキームに入れられているアカウントです. アカウントに加えて次の属性を持っており, このアカウントは通常のアカウントとは別の領域に永続化されます.
state
: メールアドレスの検証のためにmail
へと送信した暗号学的乱数
なお, 登録中アカウントが追加されてから 168 時間(7days) が経過したものは無効とみなします.
アカウント関係
アカウントが他のアカウントに対してどのような関係性を持っているかを表すステートマシンです. あるアカウントから他のアカウントに対しては次のような関係が存在します.
NONE
: なしREQUESTING_FOLLOW
: フォローリクエスト中FOLLOWING
: フォロー中BLOCKING
: ブロック中
そしてこれらの関係は次の遷移図のように遷移できます.
Mermaid code
stateDiagram-v2
NONE --> REQUESTING_FOLLOW
REQUESTING_FOLLOW --> NONE
REQUESTING_FOLLOW --> FOLLOWING
FOLLOWING --> NONE
FOLLOWING --> BLOCKING
NONE --> BLOCKING
BLOCKING --> NONE
APIについて
main
ブランチの最新 API リファレンスは
api.pulsate.dev/reference で閲覧可能.
APIのバージョニング
APIの規定パス(Base Path)は /api/v0
である.
APIのバージョンは以下の通り.
バージョン | ステータス |
---|---|
v0 | 策定途中 |
資格情報が必要なエンドポイントの実行方法
Pulsate v0 APIでは殆どのエンドポイントで資格情報が必要である.
資格情報が必要なエンドポイントへアクセスするためには, 以下の方法でエンドポイントにアクセスする必要がある.
エンドポイントへアクセスするときのAuthorization
HTTPヘッダーに
Bearer <token>
の形式で 認証トークン をつけて送信する必要がある.
例:
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
資格情報が必要ないエンドポイントについて Pulsate v0
APIでは、資格情報が必要なエンドポイントであっても、資格情報がない場合にアクセス可能なエンドポイントが存在する。
例えば、あるアカウントのタイムラインを取得するエンドポイントの場合:
- 有効な資格情報がある場合
- フォローしている場合は FOLLOWER 公開範囲の投稿も返す
- フォローしていない場合は資格情報がない場合と変わらない
- 資格情報が無効な場合
- 認証トークンが期限切れや改ざんされている場合
- 認証エラーを返す
- 資格情報がない場合
Authorization
ヘッダーが空の場合- PUBLIC/HOME 公開範囲の投稿を返す
“文字数”の扱い
特に明記しない限り、文字数は 以下のコードを実行して計算できる文字数 とする.
const count = (s: string) => {
const segmenter = new Intl.Segmenter("ja-JP", { granularity: "grapheme" });
return [...segmenter.segment(s)].length;
};
このAPIを実装する実行環境のリストはMDNを参照せよ.
Intl.Segmenter - JavaScript | MDN
“時刻/時間”の扱い
特に明記しない限り、システム上の時刻の扱いは以下の通りである.
- 日時は例外なくタイムゾーンをUTCとして扱う
- 時刻のフォーマットは ISO 8601 とする.
"yyyy-MM-DDTHH:mm:ss.SSSZ"
形式- タイムゾーン指定されていた場合は指定したタイムゾーンの日時をUTCに変換する.
- 例:
2023-11-21T09:00:00.000+09:00
: 日本時間(JST, UTC+9) 2023年11月21日09:00:00.000 →2023-11-21T00:00:00.000Z
: 世界協定時(UTC) 2023年11月21日00:00:00.000
- 例:
- 以上の形式に合致しない形で日時の表現がされた場合はエラー終了するものとする.
絵文字の扱い
絵文字とは, Unicodeによって定義される絵文字とカスタム絵文字のことである.
その他の記号や環境依存文字は絵文字ではないとする.
Unicode絵文字
Unicode絵文字とは,Unicodeによって定義される絵文字である.
APIとやり取りするテキストデータでは特殊な記法を用いずUnicodeで表現する.
例:
{
"content": "🎉"
}
カスタム絵文字
カスタム絵文字を文字列内に埋め込むための形式は以下の通りである.
以下の形式に沿わない場合は単なる文字列であると解釈する.
- 全体を
<>
で囲む- カスタム絵文字のエイリアスは
::
で囲む - カスタム絵文字に関する詳細な定義はここでは行わない. (ToDo)
- カスタム絵文字のエイリアスは
- エイリアスのあとに(絵文字があるインスタンスではなく,APIを提供するインスタンスでの)カスタム絵文字のIDを表記する.
例:
<:alias:2949583895994> <:ii_hanashi:284745363>
特殊なテキスト埋め込みオブジェクト
投稿本文, CW注釈, アカウントbio のテキスト内にカスタム絵文字やメンションなど, Unicodeで規定された文字以外のデータを表現するときの記法
カスタム絵文字は カスタム絵文字 を参照せよ.
メンション
表記法は以下の通り
- メンションは
<>
で囲む - 囲んだ中にアカウント名を入れる
- RFC 7565で規定される形式
のスキームである
acct:
を取り除き,@
に置き換えたもの - 例: アカウント名が
@[email protected]
である場合
- RFC 7565で規定される形式
のスキームである
<@[email protected]>
- 宛先のホスト名(FQDN)は 省略不可
<@[email protected]> <@[email protected]> <@[email protected]>
基本エラーオブジェクト
APIにおいて何らかのエラーが発生した際には以下のようなエラーオブジェクトを返却する.
{
"error": "TEST_ERROR_CODE"
}
errorの中にはエラーコード
が入る.
エラーコードはエラーを返却するAPIのエンドポイントでどのようなコードを返すか指定するものとする.
エラーオブジェクトはエラーコードのみをメンバーとして含むオブジェクトである.
エラーコード以外の情報をエラーオブジェクトとして返却することは許容されない.
NSFWフラグ
言葉の意味:
NSFWとは、英語圏で用いられるインターネットスラングで、「職場では見ない方がいい」(職場での閲覧は危険)という意味の略語である。日本語の「閲覧注意」におおむね相当する。
NSFWは画像がメインの掲示板や、掲示板中の画像へのリンクなどに付けて用いられやすい。たいていは卑猥(エロ画像)・残酷(グロ画像)といった要素の強い、不用意な閲覧ははばかられる類のコンテンツが掲載されている。
- NSFWフラグとは,画像/音声/動画の内容がNSFWであることを示すためのフラグである.
- 大抵の場合画像/音声/動画にぼかし(ブラー)がかけられたり,閲覧注意の文言が(システムのUI上で)表示される.
ContentsWarning/CWフラグ
- CWフラグとは,投稿本文に何らかのコンテンツ警告があることを示すフラグである.
- 大抵の場合,投稿の注釈(投稿本文より短い,本文の内容を端的に表した文)のみが表示され,本文は[続きを読む]といったボタンを押さないと見ることができない
アカウントAPI
POST /accounts
✓
新規アカウントを作成する 正確には登録中の状態のアカウントを作成し,アカウント登録スキームを開始するものである.
入力
body: application/json
項目名 | 型 | 制約/説明 | 文字数制約 | 例 |
---|---|---|---|---|
name | string | 文字は A-Z a-z 0-9 - . _ である必要がある. 先頭,及び最後の文字は A-Z a-z 0-9 のみとする. | 1 ≤ N ≤ 64 [文字] | john ※登録される情報は@[email protected] |
string | メールアドレスとして正しい形式 (メールアドレスを受信可能であるかは問わない) | 7≤N≤319[文字] | [email protected] | |
passphrase | string | スペース,タブ,全角スペース,改行,ヌルを除くUTF-8文字列 | 8≤N≤512[文字] | じゃすた・いぐざんぽぅ , just~@_examp1e! |
captcha_token | string | Cloudflare Turnstile などの手動操作検証のトークン |
入力例
{
"name": "example",
"email": "[email protected]",
"passphrase": "じゃすた・いぐざんぽぅ",
"captcha_token": "hogehogehgoe"
}
出力
200 OK
{
"id": "38477395",
"name": "example",
"email": "[email protected]"
}
body: application/json
項目名 | 型 | 説明 | 文字数 | 例 |
---|---|---|---|---|
id | snowflake | アカウントのID | - | 30848577730000 |
name | string | ユーザー名 | 8≤N≤512[文字] | @[email protected] |
string | メールアドレスとして正しい形式 (メールアドレスを受信可能であるかは問わない) | 7≤N≤319[文字] | [email protected] |
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
INVALID_ACCOUNT_NAME
: 使用できない文字が含まれている,文字の使用制限に違反しているTOO_LONG_ACCOUNT_NAME
: アカウント名が長すぎるEMAIL_IN_USE
: メールアドレスが既に使用されているYOU_ARE_BOT
: captcha_token の検証に失敗した
409 Conflict
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NAME_IN_USE
: アカウント名が既に使用されているEMAIL_IN_USE
: メールアドレスが既に使用されている
PATCH /accounts/{account_name}
✓
アカウント情報を指定のパラメータで編集するエンドポイント.
入力の body から次の情報のいずれか 1 つ以上を受け付け、それらを同時に適用できるかを検証し,反映する.
競合を防ぐために「更新前のニックネームと更新前のメールアドレスを結合した文字列」のハッシュを ETag (Entity Tag) として用いる.
これをMD5でハッシュするものとする.
メールアドレスを更新した場合はそのメールアドレスの確認スキームが開始される.
そしてそのメールアドレスが確認されるまで,メールアドレスの更新処理は遅延される.
ニックネームも同時に更新した場合,ニックネームの更新は先に反映される.
warning
ETag が一致しないときは 412 Precondition Failed のエラーとなる. 結合方法は
更新前のニックネーム:更新前のメールアドレス
である
入力
- body:
application/json
項目名 | 型 | 制約/説明 | 文字数制約 | 例 |
---|---|---|---|---|
name | string | 文字は A-Z a-z 0-9 - . _ である必要がある. 先頭,及び最後の文字は A-Z a-z 0-9 のみとする. | 1 ≤ N ≤ 64 [文字] | john ※登録される情報は@[email protected] |
string | メールアドレスとして正しい形式 (メールアドレスを受信可能であるかは問わない) | 7≤N≤319[文字] | [email protected] | |
passphrase | string | スペース,タブ,全角スペース,改行,ヌルを除くUTF-8文字列 | 8≤N≤512[文字] | じゃすた・いぐざんぽぅ , just~@_examp1e! |
captcha_token | string | Cloudflare Turnstile などの手動操作検証のトークン | ||
bio | string | 自己紹介文. 0文字である場合はnull やundefined ではなく空の文字列"" である必要がある. | 0≤N≤1024 | "" (空文字列), いい感じの自己紹介🆓 , This is bio hello^~ <:javascript:358409384> |
入力例
{
"nickname": "example",
"email": "[email protected]",
"passphrase": "じゃすた・いぐざんぽぅ",
"bio": "テストユーザーなのぜ"
}
出力
200 OK
編集に成功しました.
{
"id": "38477395",
"name": "@[email protected]",
"nickname": "John Doe",
"bio": "テストユーザーなのぜ",
"email": "[email protected]"
}
項目名 | 型 | 説明 | 文字数 | 例 |
---|---|---|---|---|
id | snowflake | アカウントのID | - | 30848577730000 |
name | string | ユーザー名 | 8≤N≤512[文字] | @[email protected] |
string | メールアドレスとして正しい形式 (メールアドレスを受信可能であるかは問わない) | 7≤N≤319[文字] | [email protected] | |
nickname | string | 表示名,アカウントの表示に用いる短い文字列 | 1≤N≤256 | JohnDoe<:json:299384730049> |
ジョン・ドゥ🚉 |
202 Accepted
メールアドレスが更新された場合
{
"id": "38477395",
"name": "@[email protected]",
"nickname": "John Doe",
"bio": "テストユーザーなのぜ",
"email": "[email protected]"
}
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
INVALID_SEQUENCE
: パラメータ内に使用不可能な文字種を含んでいるVULNERABLE_PASSPHRASE
: 新しいパスフレーズがパスフレーズとしての要件を満たしていない
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NOT_FOUND
: 名前がaccount_name
のアカウントが見つからない
412 Precondition Failed
{
"error": "TEST_ERROR_CODE"
}
INVALID_ETAG
: ETagが不正である
PUT /accounts/{account_name}/freeze
✓
アカウントを凍結するエンドポイント.
モデレータ以上の権限を持つアカウント認証情報が必要である.
凍結されると以下のような挙動になる.
- ログインできなくなる.
- ログインしようとするとエラー終了する.
- 投稿できなくなる.
- (公開APIを除く)いかなるAPIへの操作も受け付けなくなる.
入力
- パスパラメータ
account_name
:string
- アカウント名
- body:
application/json
- 空のオブジェクトを送信せよ.
- 空でない場合のメンバーは無視される.
- 空のオブジェクトを送信せよ.
入力例
{}
出力
204 No Content
凍結が完了した.
※レスポンスボディは空になる.
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
ALREADY_FROZEN
: すでに凍結済みである.
403 Forbidden
{
"error": "TEST_ERROR_CODE"
}
NO_PERMISSION
: アカウントを凍結できる権限がない.
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NOT_FOUND
: アカウントが見つからない.
DELETE /accounts/{account_name}/freeze
✓
アカウントを凍結解除する(解凍と表記することもある).
モデレータ以上の権限の持つアカウント認証情報が必要.
入力
- パスパラメータ
account_name
:string
- アカウント名
- body:
application/json
- 空のオブジェクトを送信せよ.
- 空でない場合のメンバは無視される.
- 空のオブジェクトを送信せよ.
入力例
{}
出力
204 No Content
凍結解除した.
※レスポンスボディは空になる.
403 Forbidden
{
"error": "TEST_ERROR_CODE"
}
NO_PERMISSION
: アカウントを凍結解除できる権限がない.
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NOT_FOUND
: アカウントが見つからない.
POST /accounts/{account_name}/resend_verify_email
✓
メールアドレスの検証コードを再送
入力
- パスパラメータ
account_name
:string
- アカウント名
body: application/json
項目名 | 型 | 制約/説明 | 文字数 | 例 |
---|---|---|---|---|
captcha_token | string | Cloudflare Trunstileなどの検証トークン |
入力例
{
"captcha_token": "hogehogehgoe"
}
出力
204 No Content
再送した
※レスポンスボディは空になる.
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_ALREADY_VERIFIED
: アカウントのメールアドレスはすでに検証されている.
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NOT_FOUND
: アカウントが見つからない
POST /accounts/{account_name}/verify_email
✓
メールアドレス認証が終了していないアカウントに対して,
メールアドレスに送信された認証トークンを検証することでメールアドレス認証を行う.
処理が完了すると,検証済みアカウントになりログインなどの各種操作が行えるようになる(ToDo 行うことのできる操作の一覧へのリンク
入力
- パスパラメータ
account_name
:string
- アカウント名
- body:
application/json
項目名 | 型 | 制約/説明 | 文字数 | 例 |
---|---|---|---|---|
token | string | 認証トークン: モデル を参照 |
入力例
{
"token": "vq34rvyanho10q9hbc98ydbvaervna43r0varhj"
}
出力
204 No Content
検証に成功
※ レスポンスボディは空になります
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
INVALID_TOKEN
: トークンの検証に失敗.
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NOT_FOUND
: 指定した名前のアカウントは存在しない.
POST /login
✓
ログインして認証/更新トークンを作成します.
warning
認証トークンの有効期限は15分(900秒), 更新トークンの有効期限は30日(2,592,000秒)である. どちらのトークンも,APIサーバーが再起動されると無効になり,その場合は再度ログインする必要がある.
入力
- body:
application/json
項目名 | 型 | 制約/説明 | 文字数 | 例 |
---|---|---|---|---|
name | string | アカウント名 | 8≤N≤512[文字] | @[email protected] |
passphrase | string | パスフレーズ | 8≤N≤512 | じゃすた・いぐざんぽぅ , just~@_examp1e! |
captcha_token | string | Cloudflare Trunstileなどの検証トークン |
入力例
{
"name": "@[email protected]",
"passphrase": "じゃすた・いぐざんぽぅ",
"captcha_token": "hogehogehoge"
}
出力
200 OK
ログインしました
{
"authorization_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIzZTE2NDQ4MzMwMDAwMDIiLCJpYXQiOjE2NDA5OTUyMDEsInJlZnJlc2hfdG9rZW4iOiJleUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKemRXSWlPaUl6WlRFMk5EUTR Nek13TURBd01ESWlMQ0pwWVhRaU9qRTJOREE1T1RVeU1ERjkud2Q4cWJVcWowWGtCU1hud0FxM0lRYU1nQS1RTFd2MHVKU1NLX3BIVTZCYyJ9.mRUfLIYOGlLuC9D72zBriVvrHYrQgVHW7ntQ-bp5SHs",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIzZTE2NDQ4MzMwMDAwMDIiLCJpYXQiOjE2NDA5OTUyMDEsInJlZnJlc2hfdG9rZW4iOiJleUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKemRXSWlPaUl6WlRFMk5EUTR Nek13TURBd01ESWlMQ0pwWVhRaU9qRTJOREE1T1RVeU1ERjkud2Q4cWJVcWowWGtCU1hud0FxM0lRYU1nQS1RTFd2MHVKU1NLX3BIVTZCYyJ9.mRUfLIYOGlLuC9D72zBriVvrHYrQgVHW7ntQ-bp5SHs",
"expires_in": 1672498800
}
項目名 | 型 | 制約/説明 | 文字数 | 例 |
---|---|---|---|---|
authorization_token | string | 認証トークン | ||
refresh_token | string | 更新トークン | ||
expires_in | number | 有効期限. Pulsate Epochからの秒数: モデル を参照. |
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
FAILED_TO_LOGIN
: パスフレーズかアカウント名が間違っているYOU_ARE_BOT
: captchaトークンの検証に失敗
403 Forbidden
{
"error": "TEST_ERROR_CODE"
}
YOU_ARE_FROZEN
: ログインしようとしたアカウントは凍結されている
POST /refresh
✓
更新トークンで認証トークンを再発行
入力
- body:
application/json
refresh_token
:string
- 更新トークン
項目名 | 型 | 制約/説明 | 文字数 | 例 |
---|---|---|---|---|
refresh_token | string | 更新トークン 参照 |
入力例
{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzZTE2NDQ4MzMwMDAwMDIiLCJpYXQiOjE2NDA5OTUyMDEsInJlZnJlc2hfdG9rZW4iOiJleUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKemRXSWlPaUl6WlRFMk5EUTRNek13TURBd01ESWlMQ0pwWVhRaU9qRTJOREE1T1RVeU1ERjkud2Q4cWJVcWowWGtCU1hud0FxM0lRYU1nQS1RTFd2MHVKU1NLX3BIVTZCYyJ9.mRUfLIYOGlLuC9D72zBriVvrHYrQgVHW7ntQ-bp5SHs"
}
出力
200 OK
ログインに成功した
{
"authorization_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzZTE2NDQ4MzMwMDAwMDIiLCJpYXQiOjE2NDA5OTUyMDEsInJlZnJlc2hfdG9rZW4iOiJleUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKemRXSWlPaUl6WlRFMk5EUTRNek13TURBd01ESWlMQ0pwWVhRaU9qRTJOREE1T1RVeU1ERjkud2Q4cWJVcWowWGtCU1hud0FxM0lRYU1nQS1RTFd2MHVKU1NLX3BIVTZCYyJ9.mRUfLIYOGlLuC9D72zBriVvrHYrQgVHW7ntQ-bp5SHs"
}
項目名 | 型 | 制約/説明 | 文字数 | 例 |
---|---|---|---|---|
autorization_token | string | 認証トークン | ||
共通 を参照 |
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
INVALID_TOKEN
: 更新トークンが不正EXPIRED_TOKEN
: トークンの有効期限切れ
GET /accounts/{account_name}
✓
アカウント情報を取得
入力
- パスパラメータ
account_name
:string
- アカウント名
出力
200 OK
取得に成功
{
"id": "2874987398",
"name": "@[email protected]",
"nickname": "John Doe",
"bio": "I am Test User.",
"avatar": "https://example.com/images/avatar.png",
"header": "https://example.com/images/header.png",
"followed_count": 200,
"following_count": 10,
"note_count": 20000
}
項目名 | 型 | 制約/説明 | 文字数 | 例 |
---|---|---|---|---|
id | snowflake | アカウントID | 2934002842 | |
name | string | アカウント名 | 8≤N≤512[文字] | @[email protected] |
nickname | string | 表示名,アカウントの表示に用いる短い文字列 | 1≤N≤256 | JohnDoe<:json:299384730049> , ジョン・ドゥ🚉 |
bio | string | 自己紹介文. 0文字である場合はnullやundefinedではなくからの文字列である必要がある. | 0≤N≤1024 | "" (空文字列), いい感じの自己紹介🆓 ,This is bio hello^~ <:javascript:358409384> |
avatar | string | アカウントのアイコン画像のURL | ||
header | string | アカウントのヘッダー画像のURL | ||
followed_count | number | そのアカウントをフォローしている人数 | ||
following_count | number | そのアカウントがフォローしている人数 | ||
note_count | number | そのアカウントの投稿数 |
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NOT_FOUND
: 指定した名前のアカウントは存在しない
PUT /accounts/{account_name}/silence
✓
アカウントをサイレンスする.モデレータ以上の権限を持つアカウント認証情報が必要.
サイレンスされると以下のような挙動になる.
- 公開投稿(投稿範囲が
public
になる投稿)ができなくなる
入力
- パスパラメータ
account_name
:string
- アカウント名
- body:
application/json
- ボディは空オブジェクトである必要がある.
- 空でない場合,メンバは無視される.
入力例
{}
出力
204 No Content
凍結に成功
※レスポンスボディは空になる.
403 Forbidden
{
"error": "TEST_ERROR_CODE"
}
NO_PERMISSION
: アカウントをサイレンスできる権限がない.
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NOT_FOUND
: 指定した名前のアカウントは存在しない.
DELETE /accounts/{account_name}/silence
✓
アカウントをサイレンス解除
モデレータ以上の権限の持つアカウント認証情報が必要.
入力
- パスパラメータ
account_name
:string
- アカウント名
- body:
application/json
- オブジェクトは空である必要がある.
- 空でない場合,メンバは無視される.
入力例
{}
出力
204 No Content
サイレンスを解除
※レスポンスボディは空になる.
403 Forbidden
{
"error": "TEST_ERROR_CODE"
}
NO_PERMISSION
: アカウントをサイレンス解除できる権限がない
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NOT_FOUND
: 指定した名前のアカウントは存在しない
POST /accounts/{account_name}/follow
✓
指定したアカウントをフォロー
入力
- パスパラメータ
account_name
:string
- フォローしたいアカウント名
- body:
application/json
- 空オブジェクトを送信せよ
- 空でない場合のメンバは無視される
- 空オブジェクトを送信せよ
入力例
{}
出力
201 Accepted
フォローを受け付けた
{
"pending": true
}
- フォローする相手がフォローを手動承認制にしている場合はpendingがtrueになる.
- フォローする相手がフォローを手動承認制にしていない場合もある.
- この場合はpendingは必ず
false
になる.
- この場合はpendingは必ず
- フォローする相手がフォローを手動承認制にしていない場合もある.
- v2以降の予定: フォローする相手が同じインスタンスに所属していない場合はすべての場合でpendingがtrueになる.
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
ALREADY_FOLLOWING
: アカウントをすでにフォローしているYOU_ARE_BLOCKED
: 相手にブロックされている
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NOT_FOUND
: 指定した名前のアカウントは存在しない.
DELETE /accounts/{account_name}/follow
✓
フォローを解除します
入力
- パスパラメータ
account_name
:string
- フォロー解除したいアカウント名
- body:
application/json
- リクエストボディは空オブジェクトを送信せよ.
- 空でない場合のメンバは無視される.
- リクエストボディは空オブジェクトを送信せよ.
入力例
{}
出力
204 No Content
フォローを解除した.
※レスポンスボディは空になる.
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
YOU_
ARE_NOT_FOLLOW_ACCOUNT
: 指定したアカウントをフォローしていない
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NOT_FOUND
: 指定した名前のアカウントは存在しない
ドライブAPI
GET /drive
自分がアップロードした画像のリストを取得します
入力
なし
出力
200 OK
[
{
"id": "2938492384",
"name": "image.jpeg",
"author_id": "384880009940302",
"hash": "nf9:e;g711*c@drgj55",
"mime": "image/jpeg",
"nsfw": false,
"url": "https://images.example.com/image.jpeg",
"thumbnail": "https://images.example.com/thumb-image.jpeg"
}
]
項目名 | 型 | 説明 | 制約 | 例 |
---|---|---|---|---|
id | snowflake | ファイルのID | ||
name | string | ファイル名 | 1≤N≤256[文字] | |
author_id | snowflake | アップロードしたアカウントのID | ||
hash | string | 画像のblurhash | ||
mime | string | mimeタイプ | ||
nsfw | boolean | NSFWフラグ | ||
url | string | 画像へのリンク | ||
thumbnail | string , undefined | (利用可能な場合のみ) 縮小版のサムネイル画像 |
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
FILE_NOT_FOUND
: ファイルが存在しません
POST /drive/
添付ファイルをアップロードします
入力
- body:
multipart/form-data
項目名 | 型 | 説明 | 制約 | 例 |
---|---|---|---|---|
name | string | ファイル名拡張子が必須 | 1≤N≤256[文字] | neko.png , 箱根駅伝.mp3 |
file | (ファイルの実体) | アップロードするファイル. アップロード可能なファイル種類は後述 | 1≤100 [MB] | |
nsfw | string ( "true", "false" ) | NSFWフラグ |
アップロード可能なファイル種類(mimeタイプ) mimeタイプについて詳しくは [https://www.iana.org/assignments/media-types/media-types.xhtml](https://www.iana.org/assignments/media-types/media-types.xhtml) を参照
画像:
image/apng
image/avif
image/gif
image/jpeg
image/png
image/webp
音声、動画:
audio/wave
,audio/wav
audio/webm
audio/mpeg
video/mpeg
video/webm
audio/ogg
出力
200 OK
アップロードが完了しました.
{
"id": "2938492384",
"name": "image.jpeg",
"author_id": "493094050",
"hash": "nf9:e;g711*c@drgj55",
"mime": "image/jpeg",
"nsfw": false,
"url": "https://images.example.com/image.jpeg",
"thumbnail": "https://images.example.com/thumb-image.jpeg"
}
項目名 | 型 | 説明 | 制約 | 例 |
---|---|---|---|---|
id | snowflake | ファイルのID | ||
name | string | ファイル名 | 1≤N≤256[文字] | |
author_id | snowflake | アップロードしたアカウントのID | ||
hash | string | 画像のblurhash | ||
mime | string | mimeタイプ | ||
nsfw | boolean | NSFWフラグ | ||
url | string | 画像へのリンク | ||
thumbnail | string , undefined | (利用可能な場合のみ) 縮小版のサムネイル画像 |
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
FILE_SIZE_TOO_BIG
: ファイルサイズが大きすぎますFILE_NAME_TOO_LONG
: ファイル名が長すぎます
DELETE /drive/{file_id}
指定したファイルを削除します
warning
ファイルを削除すると,紐付けられているすべてのノートに影響します.
入力
- パスパラメータ
file_id
:snowflake
- ファイルのID
出力
204 No Content
削除しました.
※レスポンスボディは空になります.
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
FILE_NOT_FOUND
: ファイルが存在しません
GET /drive/{file_id}
ファイルのメタ情報を取得します
入力
- パスパラメータ
file_id
:snowflake
- ファイルのID
出力
200 OK
{
"id": "2938492384",
"name": "image.jpeg",
"author_id": "384880009940302",
"hash": "nf9:e;g711*c@drgj55",
"mime": "image/jpeg",
"nsfw": false,
"url": "https://images.example.com/image.jpeg",
"thumbnail": "https://images.example.com/thumb-image.jpeg"
}
項目名 | 型 | 説明 | 制約 | 例 |
---|---|---|---|---|
id | snowflake | ファイルのID | ||
name | string | ファイル名 | 1≤N≤256[文字] | |
author_id | snowflake | アップロードしたアカウントのID | ||
hash | string | 画像のblurhash | ||
mime | string | mimeタイプ | ||
nsfw | boolean | NSFWフラグ | ||
url | string | 画像へのリンク | ||
thumbnail | string , undefined | (利用可能な場合のみ) 縮小版のサムネイル画像 |
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
FILE_NOT_FOUND
: ファイルが存在しません
PUT /drive/{file_id}
ファイルの情報を変更します
warning
情報を変更すると、そのファイルが紐付けられているすべてのノートに影響します
入力
-
パスパラメータ
file_id
:snowflake
- ファイルのID
-
body:
application/json
項目名 | 型 | 説明 | 制約 | 例 |
---|---|---|---|---|
nsfw | boolean | NSFWフラグ |
入力例
{
"nsfw": false
}
出力
200 OK
{
"id": "2938492384",
"name": "image.jpeg",
"author_id": "384880009940302",
"hash": "nf9:e;g711*c@drgj55",
"mime": "image/jpeg",
"nsfw": false,
"url": "https://images.example.com/image.jpeg",
"thumbnail": "https://images.example.com/thumb-image.jpeg"
}
項目名 | 型 | 説明 | 制約 | 例 |
---|---|---|---|---|
id | snowflake | ファイルのID | ||
name | string | ファイル名 | 1≤N≤256[文字] | |
author_id | snowflake | アップロードしたアカウントのID | ||
hash | string | 画像のblurhash | ||
mime | string | mimeタイプ | ||
nsfw | boolean | NSFWフラグ | ||
url | string | 画像へのリンク | ||
thumbnail | string , undefined | (利用可能な場合のみ) 縮小版のサムネイル画像 |
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
FILE_NOT_FOUND
: ファイルが存在しません
項目名 | 型 | 説明 | 制約 | 例 |
---|---|---|---|---|
id | snowflake | ファイルのID | ||
name | string | ファイル名 | 1≤N≤256[文字] | |
author_id | snowflake | アップロードしたアカウントのID | ||
hash | string | 画像のblurhash | ||
mime | string | mimeタイプ | ||
nsfw | boolean | NSFWフラグ | ||
url | string | 画像へのリンク | ||
thumbnail | string , undefined | (利用可能な場合のみ) 縮小版のサムネイル画像 |
リストAPI
POST /lists/
リストを作成します
入力
- body:
application/json
項目名 | 型 | 説明 | 制約 | 例 |
---|---|---|---|---|
title | string | リストのタイトル | 1≤N≤100[文字] | |
public | boolean | デフォルト: false (非公開) 公開: リストにアサインされたアカウントには通知が飛びます. 非公開: 通知は飛ばず、自分以外のアカウントからは見えません |
入力例
{
"title": "Pulsate Developers",
"public": false
}
出力
200 OK
{
"id": "18342938400393",
"title": "Pulsate Developers",
"public": false
}
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
TITLE_TOO_LONG
: タイトルが長すぎます
PATCH /lists/{list_id}
リスト情報を編集します
入力
-
パスパラメータ
list_id
:string
- 編集したいリストのID
-
body:
application/json
項目名 | 型 | 説明 | 制約 |
---|---|---|---|
title | string | リストのタイトル | 1≤N≤100[文字] |
public | boolean | 公開・非公開のフラグ |
入力例
{
"title": "Edited Title",
"public": true
}
出力
200 OK
{
"id": "18342938400393",
"title": "Edited Title",
"public": true,
"assignees": [
{
"id": "1838933554",
"name": "@[email protected]"
}
]
}
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
LIST_NOTFOUND
: リストが見つかりません
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
TITLE_TOO_LONG
: タイトルが長すぎます
GET /lists/{list_id}
リスト情報を取得します
入力
- パスパラメータ
list_id
:string
- 取得したいリストのID
出力
200 OK
{
"id": "18342938400393",
"title": "Pulsate Developers",
"public": false,
"assignees": [
{
"id": "1838933554",
"name": "@[email protected]"
}
]
}
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
LIST_NOTFOUND
: リストが見つかりません
GET /lists/accounts/{account_id}
アカウントが持つリストを取得します
入力
- パスパラメータ
account_id
:snowflake
- アカウントのID
出力
200 OK
[
{
"id": "18342938400393",
"title": "Pulsate Developers",
"public": false,
"assignees": [
{
"id": "1838933554",
"name": "@[email protected]"
}
]
}
]
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NOT_FOUND
: ユーザーが存在しませんLIST_NOTFOUND
: リストが見つかりません
POST /lists/{list_id}
リストにアカウントをアサインします
入力
- パスパラメータ
list_id
:string
- リストのID
- body:
application/json
- account_id:
Array<
snowflake>
- アカウントID
- 一度にアサインできる最大アカウント数: 30
- account_id:
出力
200 OK
{
"account_id": [
"389384553329569",
"586039500493885",
"4847377595"
]
}
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
TOO_MANY_TARGETS
: アサインするアカウント数が多すぎます
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NOT_FOUND
: アカウントが存在しませんLIST_NOTFOUND
: リストが見つかりません
DELETE /lists/{list_id}
リストからアカウントを削除します
入力
- パスパラメータ
list_id
:string
- リストのID
- body:
application/json
- account_id:
Array<
snowflake>
- 削除するアカウントのID
- 一度に削除できるアカウント数: 30
- account_id:
入力例
{
"account_id": [
"389384553329569",
"586039500493885",
"4847377595"
]
}
出力
204 No Content
削除しました.
※レスポンスボディは空になります.
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
TOO_MANY_TARGETS
: 削除するアカウント数が多すぎます
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ACCOUNT_NOT_FOUND
: アカウントが存在しませんLIST_NOTFOUND
: リストが見つかりません
DELETE /lists/{list_id}
リストを削除します.
入力
- パスパラメータ
list_id
:string
- リストのID
出力
204 No Content
削除しました.
※レスポンスボディは空になります.
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
LIST_NOTFOUND
: リストが見つかりません
GET /lists/{list_id}/notes
リストのノートを取得します.
入力
- パスパラメータ
list_id
:snowflake
- リストのID
- クエリパラメータ
has_attachment
:bool | undefined
- デフォルト:
false
- ファイルを含む投稿のみを返します
- デフォルト:
no_nsfw
:bool | undefined
- デフォルト:
false
- NSFWフラグの立っているファイルを含む投稿を返さなくなります
- デフォルト:
before_id
: ``snowflake| undefined
- デフォルト:
undefined
- デフォルトでは現在取得できる最新の投稿から20件取得します.
- 指定したIDより古い投稿を返します.指定したIDの投稿は含まれません
- デフォルト:
出力
200 OK
取得に成功しました
[
{
"id": "3893974892",
"content": "hello world!",
"cw_comment": "",
"visibility": "public",
"created_at": "2023-09-27T14:17:29.169Z",
"attachment_files": [
{
"id": "204980348583",
"filename": "hello.png",
"content_type": "image/png",
"url": "https://example.com/images/hello.png",
"blur": "eoig:woi!our@nj/d",
"nsfw": false
}
],
"reactions": [
{
"emoji": "<:alias:11938437>",
"reacted_by": "3085763644"
},
{
"emoji": "🎉",
"reacted_by": "494984128"
}
],
"author": {
"id": "2874987398",
"name": "@[email protected]",
"display_name": "John Doe",
"bio": "I am Test User.",
"avatar": "https://example.com/images/avatar.png",
"header": "https://example.com/images/header.png",
"followed_count": 200,
"following_count": 10
}
}
]
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
LIST_NOTFOUND
: リストが見つかりませんNOTHING_LEFT
: これ以上古い投稿はありません- 1つでも古い投稿がある場合は投稿を返します
投稿API
note
投稿は”ノート”, 再投稿は”リノート”と言い換えられている場合があります.
ダイレクト投稿: ノートのうち,公開範囲がダイレクトに指定されているもの
POST /notes
ノートを作成
入力
- body:
application/json
項目名 | 型 | 制約/説明 | 数制約 | 例 |
---|---|---|---|---|
content | string | 投稿本文添付ファイルが存在する場合は0文字を許容 | 1≤3000[文字] | |
visibility | string , undefined | 投稿の公開範囲 デフォルト: public とり得る値: public home followers , direct | - | |
attachment_file_ids | Array<snowflake>, undefined | 投稿の添付ファイルID 1つでもファイルが存在しない場合はエラー終了する | 0≤N≤16[個] | |
cw_comment | string | CW時の注釈. CW/CWフラグ 参照デフォルトは空文字列 | 0≤N≤256[文字] | |
send_to | snowflake , undefined | ダイレクト投稿の宛先公開範囲がdirect のときのみ指定可能 |
入力例
{
"content": "hello world!",
"visibility": "public",
"attachment_file_ids": [
"11938472"
],
"cw_comment": ""
}
{
"content": "hello world!",
"visibility": "direct",
"attachment_file_ids": [
"11938472"
],
"cw_comment": "",
"send_to": "8585030584"
}
出力
201 Created
投稿を作成しました.
{
"id": "3893974892",
"content": "hello world!",
"cw_comment": "",
"visibility": "public",
"created_at": "2023-09-27T14:17:29.169Z",
"attachment_files": [
{
"id": "11938472",
"filename": "hello.png",
"content_type": "image/png",
"url": "https://example.com/images/hello.png",
"blur": "eoig:woi!our@nj/d",
"nsfw": false
}
]
}
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
content | string | 投稿本文. 添付ファイルが存在する場合は0文字を許容 | 1≤3000[文字] | |
visibility | string | 投稿の公開範囲. デフォルト: public . とり得る値: public home followers direct | - | |
attachment_files | Array<object> | 投稿の添付ファイルのオブジェクト (後述) | 0≤N≤16[個] | |
cw_comment | string | CW時の注釈, 参照 デフォルトは空文字列 | 0≤N≤256[文字] | |
send_to | Snowflake, undefined | ダイレクト投稿の宛先. 公開範囲がdirectのときのみ指定可能 | ||
created_at | string | 投稿の送信日時 |
- 添付ファイル (
attchment_files
)
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
filename | string | ファイル名 | 3≤N≤256[文字] | |
content_type | string | mimeタイプ | ||
url | string | 添付ファイルのURL | ||
blurhash | string , undefined | 添付ファイルが画像であるときのサムネイルの blurhash | ||
nsfw | boolean | ToDo |
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
TOO_MANY_ATTACHMENTS
: 添付ファイルが制限を超過TOO_MANY_C
ONTENT`` : CW注釈/投稿本文の文字数制限を超過NO_DESTINATION
: 公開範囲がdirectのノートでsend_toが指定されていないINVALID_VISIBILITY
: 公開範囲が正しい形式でない
403 Forbidden
{
"error": "TEST_ERROR_CODE"
}
YOU_ARE_SILENCED
: サイレンスされている際に公開範囲をpublic
に指定した場合
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ATTACHMENT_NOT_FOUND
: 添付したファイルが存在しないACCOUNT_NOT_FOUND
: 宛先(send_to)に指定したアカウントが存在しない
GET /notes/{note_id}
特定の投稿を取得します.
入力
- パスパラメータ
note_id
:string
- 取得したい投稿のID
出力例
200 OK
投稿を取得しました.
{
"id": "3893974892",
"content": "hello world!",
"cw_comment": "",
"visibility": "public",
"created_at": "2023-09-27T14:17:29.169Z",
"attachment_files": [
{
"id": "204980348583",
"filename": "hello.png",
"content_type": "image/png",
"url": "https://example.com/images/hello.png",
"blur": "eoig:woi!our@nj/d",
"nsfw": false
}
],
"reactions": [
{
"emoji": "<:alias:11938437>",
"reacted_by": "3085763644"
},
{
"emoji": "🎉",
"reacted_by": "494984128"
}
],
"author": {
"id": "2874987398",
"name": "@[email protected]",
"display_name": "John Doe",
"bio": "I am Test User.",
"avatar": "https://example.com/images/avatar.png",
"header": "https://example.com/images/header.png",
"followed_count": 200,
"following_count": 10
}
}
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID |
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
NOTE_NOT_FOUND
: 指定したIDのノートが存在しない- 指定したIDのノートを(アクセスしたユーザーが)取得できない場合もこのエラーを返す.
POST /notes/{note_id}/renote
指定したIDのノートをリノートします.
入力
- パスパラメータ
note_id
:string
- 取得したい投稿のID
- body:
application/json
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
content | string | 投稿本文. 添付ファイルが存在する場合は0文字を許容 | 1≤3000[文字] | |
visibility | string | 投稿の公開範囲. デフォルト: public . とり得る値: public home followers direct | - | |
attachment_files | Array<object> | 投稿の添付ファイルのオブジェクト (後述) | 0≤N≤16[個] | |
cw_comment | string | CW時の注釈, 参照 デフォルトは空文字列 | 0≤N≤256[文字] | |
send_to | Snowflake, undefined | ダイレクト投稿の宛先. 公開範囲がdirectのときのみ指定可能 | ||
created_at | string | 投稿の送信日時 |
attachment_files
:
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
filename | string | ファイル名 | ToDo | |
content_type | string | mimeタイプ | ||
ToDo | ||||
url | string | 添付ファイルのURL | ||
blur | string | 添付ファイルが画像であるときのサムネイルの blurhash | ||
nsfw | boolean | ToDo |
入力例
{
"content": "hello world!",
"visibility": "public",
"attachment_file_ids": [
"11938472"
],
"cw_comment": ""
}
出力
200 OK
リノートしました
{
"id": "3893974892",
"content": "hello world!",
"cw_comment": "",
"visibility": "public",
"renote_id": "4973874850",
"created_at": "2023-09-27T14:17:29.169Z",
"attachment_files": [
{
"id": "11938472",
"filename": "hello.png",
"content_type": "image/png",
"url": "https://example.com/images/hello.png",
"blur": "eoig:woi!our@nj/d",
"nsfw": false
}
]
}
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
content | string | 投稿本文. 添付ファイルが存在する場合は0文字を許容 | 1≤3000[文字] | |
visibility | string | 投稿の公開範囲. デフォルト: public . とり得る値: public home followers direct | - | |
attachment_files | Array<object> | 投稿の添付ファイルのオブジェクト (後述) | 0≤N≤16[個] | |
cw_comment | string | CW時の注釈, 参照 デフォルトは空文字列 | 0≤N≤256[文字] | |
send_to | Snowflake, undefined | ダイレクト投稿の宛先. 公開範囲がdirectのときのみ指定可能 | ||
created_at | string | 投稿の送信日時 |
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
filename | string | ファイル名 | ToDo | |
content_type | string | mimeタイプ | ||
ToDo | ||||
url | string | 添付ファイルのURL | ||
blur | string | 添付ファイルが画像であるときのサムネイルの blurhash | ||
nsfw | boolean | ToDo |
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
TOO_MANY_CHAR_LENGTH
: CW注釈/投稿本文の文字数制限を超過INVALID_VISIBILITY
: 公開範囲が正しい形式でないNO_DESTINATION
: 公開範囲がdirectのノートでsend_toが指定されていない
403 Forbidden
{
"error": "TEST_ERROR_CODE"
}
YOU_ARE_SILENCED
: サイレンスされている際に公開範囲をpublic
に指定した
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ATTACHMENT_NOT_FOUND
: 添付したファイルが存在しないNOTE_NOT_FOUND
: ノートが存在しない
POST /notes/{note_id}/reply
指定したIDのノートに返信します.
入力
- パスパラメータ
note_id
:string
- 取得したい投稿のID
- body:
application/json
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
content | string | 投稿本文. 添付ファイルが存在する場合は0文字を許容 | 1≤3000[文字] | |
visibility | string | 投稿の公開範囲. デフォルト: public . とり得る値: public home followers direct | - | |
attachment_files | Array<object> | 投稿の添付ファイルのオブジェクト (後述) | 0≤N≤16[個] | |
cw_comment | string | CW時の注釈, 参照 デフォルトは空文字列 | 0≤N≤256[文字] | |
created_at | string | 投稿の送信日時 |
attachment_files
:
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
filename | string | ファイル名 | ToDo | |
content_type | string | mimeタイプ | ||
ToDo | ||||
url | string | 添付ファイルのURL | ||
blur | string | 添付ファイルが画像であるときのサムネイルの blurhash | ||
nsfw | boolean | ToDo |
入力例
{
"content": "hello world!",
"visibility": "public",
"attachment_file_ids": [
"11938472"
],
"cw_comment": ""
}
出力
200 OK
投稿を作成しました.
{
"id": "3893974892",
"content": "hello world!",
"cw_comment": "",
"reply_to": "2948933000",
"visibility": "public",
"created_at": "2023-09-27T14:17:29.169Z",
"attachment_files": [
{
"id": "11938472",
"filename": "hello.png",
"content_type": "image/png",
"url": "https://example.com/images/hello.png",
"blur": "eoig:woi!our@nj/d",
"nsfw": false
}
]
}
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
content | string | 投稿本文. 添付ファイルが存在する場合は0文字を許容 | 1≤3000[文字] | |
visibility | string | 投稿の公開範囲. デフォルト: public . とり得る値: public home followers direct | - | |
attachment_files | Array<object> | 投稿の添付ファイルのオブジェクト (後述) | 0≤N≤16[個] | |
cw_comment | string | CW時の注釈, 参照 デフォルトは空文字列 | 0≤N≤256[文字] | |
send_to | Snowflake, undefined | ダイレクト投稿の宛先. 公開範囲がdirectのときのみ指定可能 | ||
created_at | string | 投稿の送信日時 |
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
filename | string | ファイル名 | ToDo | |
content_type | string | mimeタイプ | ||
ToDo | ||||
url | string | 添付ファイルのURL | ||
blur | string | 添付ファイルが画像であるときのサムネイルの blurhash | ||
nsfw | boolean | ToDo |
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
TOO_MANY_CHAR_LENGTH
: CW注釈/投稿本文の文字数制限を超過INVALID_VISIBILITY
: 公開範囲が正しい形式でない
403 Forbidden
{
"error": "TEST_ERROR_CODE"
}
YOU_ARE_SILENCED
: サイレンスされている際に公開範囲をpublic
に指定したYOU_ARE_BLOCKED
: 返信先ユーザーにブロックされている
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
ATTACHMENT_NOT_FOUND
: 添付したファイルが存在しないNOTE_NOT_FOUND
: ノートが存在しない
DELETE /notes/{note_id}
投稿を削除します.
NOTICE: 自分以外のノートを削除する場合はモデレータ以上の資格情報が必要です.
入力
- パスパラメータ
note_id
:string
- 削除するノートのID
出力
204 No Content
削除しました.
※ レスポンスボディは空になります
403 Forbidden
{
"error": "TEST_ERROR_CODE"
}
NO_PERMISSION
: ノートの投稿者でないため削除できない
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
NOTE_NOT_FOUND
: 削除するノートが存在しない
POST /notes/{note_id}/reaction
指定したノートにリアクションします.
入力
- パスパラメータ
note_id
:string
- リアクションしたい投稿のID
body: application/json
項目名 | 型 | 説明 | 例 |
---|---|---|---|
emoji | string | 絵文字 | |
表記法は カスタム絵文字 を参照 | <:alias:11938437> | ||
🎉 |
入力例
{
"emoji": "🎉"
}
{
"emoji": "<:awesome:489395643749>"
}
出力
200 OK
リアクションしました.
{
"id": "3893974892",
"content": "hello world!",
"cw_comment": "",
"visibility": "public",
"created_at": "2023-09-27T14:17:29.169Z",
"attachment_files": [
{
"id": "204980348583",
"filename": "hello.png",
"content_type": "image/png",
"url": "https://example.com/images/hello.png",
"blur": "eoig:woi!our@nj/d",
"nsfw": false
}
],
"reactions": [
{
"emoji": "<:alias:11938437>",
"reacted_by": "3085763644"
},
{
"emoji": "🎉",
"reacted_by": "494984128"
}
],
"author": {
"id": "2874987398",
"name": "@[email protected]",
"display_name": "John Doe",
"bio": "I am Test User.",
"avatar": "https://example.com/images/avatar.png",
"header": "https://example.com/images/header.png",
"followed_count": 200,
"following_count": 10
}
}
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
content | string | 投稿本文. 添付ファイルが存在する場合は0文字を許容 | 1≤3000[文字] | |
visibility | string | 投稿の公開範囲. デフォルト: public . とり得る値: public home followers direct | - | |
attachment_files | Array<object> | 投稿の添付ファイルのオブジェクト (後述) | 0≤N≤16[個] | |
cw_comment | string | CW時の注釈, 参照 デフォルトは空文字列 | 0≤N≤256[文字] | |
send_to | Snowflake, undefined | ダイレクト投稿の宛先. 公開範囲がdirectのときのみ指定可能 | ||
created_at | string | 投稿の送信日時 |
attachment_files
:
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
filename | string | ファイル名 | ToDo | |
content_type | string | mimeタイプ | ||
ToDo | ||||
url | string | 添付ファイルのURL | ||
blur | string | 添付ファイルが画像であるときのサムネイルの blurhash | ||
nsfw | boolean | ToDo |
reactions
:
項目名 | 型 | 説明 | 例 |
---|---|---|---|
emoji | string | 絵文字. 表記法は 絵文字の扱い を参照 | 🎉 , <:alias:11938437> |
reacted_by | snowflake | リアクションしたアカウントID | 48499372 |
400
Bad Request
{
"error": "TEST_ERROR_CODE"
}
ALREADY_REACTED
: すでにリアクション済みEMOJI_NOT_FOUND
: 指定した絵文字が存在しない(カスタム絵文字のみ)/複数指定している(Unicode絵文字
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
NOTE_NOT_FOUND
: リアクションするノートが存在しない
DELETE /notes/{note_id}/reaction
指定したノートにつけたリアクションを解除します
入力
- パスパラメータ
note_id
:string
- リアクションしたい投稿のID
出力
204 No Content
削除しました.
※ レスポンスボディは空になります.
400 Forbidden
{
"error": "TEST_ERROR_CODE"
}
NOT_REACTED
: リアクションしていない
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
NOTE_NOT_FOUND
: ノートが存在しない
POST /notes/{note_id}/bookmark
指定した投稿をブックマークします
入力
- パスパラメータ
note_id
:string
- リアクションしたい投稿のID
出力
200 OK
ブックマークしました
{
"id": "3893974892",
"content": "hello world!",
"cw_comment": "",
"visibility": "public",
"created_at": "2023-09-27T14:17:29.169Z",
"attachment_files": [
{
"id": "204980348583",
"filename": "hello.png",
"content_type": "image/png",
"url": "https://example.com/images/hello.png",
"blur": "eoig:woi!our@nj/d",
"nsfw": false
}
],
"reactions": [
{
"emoji": "<:alias:11938437>",
"reacted_by": "3085763644"
},
{
"emoji": "🎉",
"reacted_by": "494984128"
}
],
"author": {
"id": "2874987398",
"name": "@[email protected]",
"display_name": "John Doe",
"bio": "I am Test User.",
"avatar": "https://example.com/images/avatar.png",
"header": "https://example.com/images/header.png",
"followed_count": 200,
"following_count": 10
}
}
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
content | string | 投稿本文. 添付ファイルが存在する場合は0文字を許容 | 1≤3000[文字] | |
visibility | string | 投稿の公開範囲. デフォルト: public . とり得る値: public home followers direct | - | |
attachment_files | Array<object> | 投稿の添付ファイルのオブジェクト (後述) | 0≤N≤16[個] | |
cw_comment | string | CW時の注釈, 参照 デフォルトは空文字列 | 0≤N≤256[文字] | |
send_to | Snowflake, undefined | ダイレクト投稿の宛先. 公開範囲がdirectのときのみ指定可能 | ||
created_at | string | 投稿の送信日時 |
attachment_files
:
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
filename | string | ファイル名 | ToDo | |
content_type | string | mimeタイプ | ||
ToDo | ||||
url | string | 添付ファイルのURL | ||
blur | string | 添付ファイルが画像であるときのサムネイルの blurhash | ||
nsfw | boolean | ToDo |
reactions
:
項目名 | 型 | 説明 | 例 |
---|---|---|---|
emoji | string | 絵文字 | |
表記法は 絵文字の扱い を参照 | <:alias:11938437 | ||
🎉 | |||
reacted_by | snowflake | リアクションしたアカウントID | 48499372 |
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
NOTE_NOT_FOUND
: ノートが存在しない
DELETE /notes/{note_id}/bookmark
指定した投稿をブックマーク解除します
入力
- パスパラメータ
note_id
:string
- ブックマーク解除したい投稿のID
出力
204 No Content
ブックマーク解除しました
※レスポンスボディは空になります.
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
NOTE_NOT_FOUND
: ノートが存在しない
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
content | string | 投稿本文. 添付ファイルが存在する場合は0文字を許容 | 1≤3000[文字] | |
visibility | string | 投稿の公開範囲. デフォルト: public . とり得る値: public home followers direct | - | |
attachment_files | Array<object> | 投稿の添付ファイルのオブジェクト (後述) | 0≤N≤16[個] | |
cw_comment | string | CW時の注釈, 参照 デフォルトは空文字列 | 0≤N≤256[文字] | |
send_to | Snowflake, undefined | ダイレクト投稿の宛先. 公開範囲がdirectのときのみ指定可能 | ||
created_at | string | 投稿の送信日時 |
項目名 | 型 | 説明 | 数制約 | 例 |
---|---|---|---|---|
id | snowflake | 投稿のID | ||
filename | string | ファイル名 | ToDo | |
content_type | string | mimeタイプ | ||
ToDo | ||||
url | string | 添付ファイルのURL | ||
blur | string | 添付ファイルが画像であるときのサムネイルの blurhash | ||
nsfw | boolean | ToDo |
項目名 | 型 | 説明 | 例 |
---|---|---|---|
emoji | string | 絵文字 | |
表記法は 絵文字の扱い を参照 | <:alias:11938437 | ||
🎉 | |||
reacted_by | snowflake | リアクションしたアカウントID | 48499372 |
通知API
お知らせ(Announce)の種類
種類コード | 説明 |
---|---|
info | 一般的なお知らせ |
warn | ユーザー全体への警告 |
通知の種類
種類コード | 通知が発生する条件 |
---|---|
followed | フォローされたとき |
followRequested | フォローをリクエストされたとき |
followAccepted | 自分が行ったフォローリクエストが承認されたとき |
mentioned | メンションされたとき |
renoted | リノートされたとき |
reacted | リアクションされたとき |
followed
- フォローされた
取り得るactor
の種類:
account
{
"id": "20923084093774",
"type": "followed",
"actor": {
"type": "account",
"account": {
"id": "209384",
"name": "@[email protected]",
"nickname": "John Doe🌤",
"avatar": "https://cdn.example.com/johndoe/avatar"
}
},
"createdAt": "2024-08-01T00:00:00.000Z"
}
followRequested
- フォローをリクエストされた
取り得るactor
の種類:
account
{
"id": "20923084093774",
"type": "followRequested",
"actor": {
"type": "account",
"account": {
"id": "209384",
"name": "@[email protected]",
"nickname": "John Doe🌤",
"avatar": "https://cdn.example.com/johndoe/avatar"
}
},
"createdAt": "2024-08-01T00:00:00.000Z"
}
followAccepted
- (自分が行った)フォローリクエストが承認されたとき
取り得るactor
の種類:
account
{
"id": "20923084093774",
"type": "followAccepted",
"actor": {
"type": "account",
"account": {
"id": "209384",
"name": "@[email protected]",
"nickname": "John Doe🌤",
"avatar": "https://cdn.example.com/johndoe/avatar"
}
},
"createdAt": "2024-08-01T00:00:00.000Z"
}
mentioned
- メンションされた
取り得るactor
の種類:
account
{
"id": "20923084093774",
"type": "mentioned",
// 言及元ノートID
"noteId": "29847304533478",
"actor": {
"type": "account",
"account": {
"id": "209384",
"name": "@[email protected]",
"nickname": "John Doe🌤",
"avatar": "https://cdn.example.com/johndoe/avatar"
}
},
"createdAt": "2024-08-01T00:00:00.000Z"
}
renoted
- リノートされた
取り得るactor
の種類:
account
{
"id": "20923084093774",
"type": "renoted",
// リノートされたノートID
"noteId": "1032809844545437574",
// 投稿本文(CW指定の場合は空になる)
"content": "",
"actor": {
"type": "account",
"account": {
"id": "209384",
"name": "@[email protected]",
"nickname": "John Doe🌤",
"avatar": "https://cdn.example.com/johndoe/avatar"
}
},
"createdAt": "2024-08-01T00:00:00.000Z"
}
reacted
- リアクションされた
取り得るactor
の種類:
account
{
"id": "20923084093774",
"type": "reacted",
// リアクションされたノートのID
"noteId": "3094320840856",
// リアクションの内容
"content": "🐭",
"actor": {
"type": "account",
"account": {
"id": "209384",
"name": "@[email protected]",
"nickname": "John Doe🌤",
"avatar": "https://cdn.example.com/johndoe/avatar"
}
},
"createdAt": "2024-08-01T00:00:00.000Z"
}
actor
について
通知を発生させた主体の種類
account
: (内部外部問わず) (ユーザー)アカウント- botの場合も含む
system
: システム通知- モデレーター等からの警告通知など
account
:
{
"type": "account",
"account": {
// アカウントID
"id": "209384",
// アカウント名
"name": "@[email protected]",
// アカウントニックネーム
"nickname": "John Doe🌤",
// アバター画像のURL
"avatar": "https://cdn.example.com/johndoe/avatar"
}
}
system
:
{
"type": "system",
"system": {
// ToDo
}
}
APIエンドポイント一覧
GET /notifications
届いているすべての通知を取得します。
入力
パスパラメータ:
limit
: number, 返す通知の最大数- デフォルト: 30 / 最大: 50
after
: string(date), この日以降の通知を返します- デフォルト: "1970-01-01"
include_read
: boolean, trueの時は既読の通知も返します- デフォルト: false
出力
- body:
application/json
{
// インスタンス全体へのお知らせ
"announcements": [
{
"id": "308205359",
"title": "Service maintenance",
"description": "scheduled: 2024 Sep. 10 00:00 ~ 01:00(UTC)\nduring this period, all services will be unavailable.",
// お知らせの種類
"type": "info",
"createdAt": "2024-08-01T00:00:00.000Z",
// optional
"updatedAt": "2024-08-01T10:00:00.000Z",
// 既読か
"unread": false
}
],
// 自分宛ての通知
"notifications": [
{
"id": "20923084093774",
"type": "followed",
"actor": {
"type": "account",
"account": {
"id": "209384",
"name": "@[email protected]",
"nickname": "John Doe🌤",
"avatar": "https://cdn.example.com/johndoe/avatar"
}
},
"createdAt": "2024-08-01T00:00:00.000Z"
}
]
}
POST /notifications/{id}/read
通知を既読にします
入力
-
パスパラメータ
id
: string- 既読にする通知ID
-
body:
application/json
{}
出力
204 No Content
通知を既読にしました。
検索API
クエリ
ベースは Twitter v2 のクエリ.
オペレータ
Operator | 意味 | 例 | 単独使用可能 |
---|---|---|---|
keyword | 特定のキーワードを含む | 松江 AND どこ | yes |
"" | 文字列の完全一致 | "造幣局 桜" | yes |
from: | 特定ユーザーによるノート | from:@[email protected] or from:@[email protected] | yes |
is:renote | リノート | hello world -is:renote | no |
is:quote | 引用リノート | #筑後川花火大会 is:quote | no |
has:link | 本文にリンクを含むノート | nowplaying has:link | no |
has:media | 添付ファイルを含むノート | #徳川家康 has:media | no |
演算子
演算子 | 説明 |
---|---|
AND | スペースを挟んで連続する演算子はAND 論理演算となり、両方の条件を満たしたノートが返されます |
OR | OR を挟んで連続する演算子はOR論理演算となり、どちらかの条件を満たしたノートが返されます. |
- | キーワードの前にハイフンをつけると、そのキーワードを論理否定することができます. |
() | カッコでくくると演算子をグループ化できます. AND>ORの順で適用されます. |
GET /search/notes
投稿を検索します. 返す最大件数は100件です
入力
- クエリパラメータ
query
:string
- 検索クエリ
- 上記の内容を受け取ります
出力
200 OK
検索に成功
出力の内容はタイムラインと同一である.
[
{
"id": "3893974892",
"content": "hello world!",
"cw_comment": "",
"visibility": "public",
"created_at": "2023-09-27T14:17:29.169Z",
"attachment_files": [
{
"id": "204980348583",
"filename": "hello.png",
"content_type": "image/png",
"url": "https://example.com/images/hello.png",
"blur": "eoig:woi!our@nj/d",
"nsfw": false
}
],
"reactions": [
{
"emoji": "<:alias:11938437>",
"reacted_by": "3085763644"
},
{
"emoji": "🎉",
"reacted_by": "494984128"
}
],
"author": {
"id": "2874987398",
"name": "@[email protected]",
"display_name": "John Doe",
"bio": "I am Test User.",
"avatar": "https://example.com/images/avatar.png",
"header": "https://example.com/images/header.png",
"followed_count": 200,
"following_count": 10
}
}
]
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
INVALID_QUERY
: クエリが正しくありません
タイムラインAPI
warning
取得した投稿は時系列順にソートされた状態で返されます.
一度に取得できる件数は最大20件です.
GET /timeline/{timeline_type}
タイムラインを取得します.
入力
- パスパラメータ
timeline_type
:string
,undefined
- とり得る値:
home
/global
- デフォルト:
home
- とり得る値:
- クエリパラメータ
has_attachment
:bool | undefined
- デフォルト:
false
- ファイルを含む投稿のみを返します
- デフォルト:
no_nsfw
:bool | undefined
- デフォルト:
false
- NSFWフラグの立っているファイルを含む投稿を返さなくなります
- デフォルト:
before_id
:snowflake| undefined
- デフォルト:
undefined
デフォルトでは現在取得できる最新の投稿から20件取得します.
- 指定したIDより古い投稿を返します.指定したIDの投稿は含まれません
- デフォルト:
出力
200 OK
タイムラインを取得します.
[
{
"id": "3893974892",
"content": "hello world!",
"cw_comment": "",
"visibility": "public",
"created_at": "2023-09-27T14:17:29.169Z",
"attachment_files": [
{
"id": "204980348583",
"filename": "hello.png",
"content_type": "image/png",
"url": "https://example.com/images/hello.png",
"blur": "eoig:woi!our@nj/d",
"nsfw": false
}
],
"reactions": [
{
"emoji": "<:alias:11938437>",
"reacted_by": "3085763644"
},
{
"emoji": "🎉",
"reacted_by": "494984128"
}
],
"author": {
"id": "2874987398",
"name": "@[email protected]",
"display_name": "John Doe",
"bio": "I am Test User.",
"avatar": "https://example.com/images/avatar.png",
"header": "https://example.com/images/header.png",
"followed_count": 200,
"following_count": 10
}
}
]
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
INVALID_TIMELINE_TYPE
: 指定したタイムラインタイプは存在しません
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
NOTHING_LEFT
: これ以上古い投稿はありません- 1つでも古い投稿がある場合は投稿を返します
GET /timeline/accounts/{account_id|account_name}
特定のユーザーの投稿を取得します.
入力
- パスパラメータ
account_id,account_name
:snowflake
|string
- アカウント名かアカウントのIDを指定できます.
- クエリパラメータ
has_attachment
:bool | undefined
- デフォルト:
false
- ファイルを含む投稿のみを返します
- デフォルト:
no_nsfw
:bool | undefined
- デフォルト:
false
- NSFWフラグの立っているファイルを1つでも含む投稿を返さなくなります
- デフォルト:
before_id
:snowflake
| undefined`- デフォルト:
undefined
- デフォルトでは現在取得できる最新の投稿から20件取得します.
- 指定したIDより古い投稿を返します.指定したIDの投稿は含まれません
- デフォルト:
出力
200 OK
タイムラインを取得します.
[
{
"id": "3893974892",
"content": "hello world!",
"cw_comment": "",
"visibility": "public",
"created_at": "2023-09-27T14:17:29.169Z",
"attachment_files": [
{
"id": "204980348583",
"filename": "hello.png",
"content_type": "image/png",
"url": "https://example.com/images/hello.png",
"blur": "eoig:woi!our@nj/d",
"nsfw": false
}
],
"reactions": [
{
"emoji": "<:alias:11938437>",
"reacted_by": "3085763644"
},
{
"emoji": "🎉",
"reacted_by": "494984128"
}
],
"author": {
"id": "2874987398",
"name": "@[email protected]",
"display_name": "John Doe",
"bio": "I am Test User.",
"avatar": "https://example.com/images/avatar.png",
"header": "https://example.com/images/header.png",
"followed_count": 200,
"following_count": 10
}
}
]
400 Bad Request
{
"error": "TEST_ERROR_CODE"
}
INVALID_TIMELINE_TYPE
: 指定したタイムラインタイプは存在しません
403 Forbidden
{
"error": "TEST_ERROR_CODE"
}
YOU_ARE_BLOCKED
: 指定したアカウントにブロックされています.
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
NOTHING_LEFT
: これ以上古い投稿はありません- 1つでも古い投稿がある場合は投稿を返します
ACCOUNT_NOT_FOUND
: 指定したアカウントが見つかりませんでした
GET /timeline/conversations
ダイレクト投稿を受け取った、もしくは送った相手を取得します.
入力
なし
出力
200 OK
[
{
"account": {
"accountId": "20983985",
"accountName": "@[email protected]",
"nickname": "John Doe",
"avatar": "https://example.com/images/avatar.png"
},
"updatedAt": "2023-09-10T00:00:00.000Z"
}
]
GET /timeline/conversations/{account_id}
指定したアカウントとのダイレクト投稿を取得します.
入力
- パスパラメータ
account_id
:snowflake
- ダイレクト投稿を取得したいアカウントID.
- クエリパラメータ
before_id
:snowflake
|undefined
- デフォルト:
undefined
- 指定したIDより古い投稿を返します.指定したIDの投稿は含まれません
- デフォルト:
出力
200 OK
タイムラインを取得します.
[
{
"id": "3893974892",
"content": "hello world!",
"cw_comment": "",
"visibility": "public",
"created_at": "2023-09-27T14:17:29.169Z",
"attachment_files": [
{
"id": "204980348583",
"filename": "hello.png",
"content_type": "image/png",
"url": "https://example.com/images/hello.png",
"blur": "eoig:woi!our@nj/d",
"nsfw": false
}
],
"reactions": [
{
"emoji": "<:alias:11938437>",
"reacted_by": "3085763644"
},
{
"emoji": "🎉",
"reacted_by": "494984128"
}
],
"author": {
"id": "2874987398",
"name": "@[email protected]",
"display_name": "John Doe",
"bio": "I am Test User.",
"avatar": "https://example.com/images/avatar.png",
"header": "https://example.com/images/header.png",
"followed_count": 200,
"following_count": 10
}
}
]
404 Not Found
{
"error": "TEST_ERROR_CODE"
}
NOTHING_LEFT
: これ以上古い投稿はありません- 1つでも古い投稿がある場合は投稿を返します
ACCOUNT_NOT_FOUND
: 指定したアカウントが見つかりませんでした
内部フロー・モジュール間通信
Mermaid code
sequenceDiagram
autonumber
actor ユーザー
ユーザー ->> NoteModule: 投稿
opt ダイレクト投稿の場合
NoteModule ->> AccountModule: 送信先アカウントの存在チェック
end
NoteModule ->> AccountModule: 投稿者情報取得
opt リノートの場合
NoteModule ->> NoteModule: 引用 or リノート先情報取得
end
NoteModule ->> ユーザー: 投稿完了
NoteModule ->> SearchModule: 検索インデックス投入
NoteModule ->> TimelineModule: 投稿作成イベント発火
TimelineModule ->> AccountModule: フォロー関係情報取得
TimelineModule ->> NotificationModule: 通知イベント発火
TimelineModule ->> ActivityPubModule: 投稿作成イベント発火
モジュールの概要
Pulsateのバックエンドでは"モジュール"と呼ばれる単位にプログラムを大まかに分け、機能や責任範囲を分離しています.
現在時点でPulsateのバックエンドには以下のモジュールが存在しています.
Note Module
- ノート(投稿)の作成を行う.
- ノートの削除を行う.
- ノート情報を返す.
- 投稿に紐づいたメディアの管理をする
Account Module
- アカウント情報の管理を行う.
- アカウントを凍結/解除する.
- アカウントをサイレンスする.
- ログイン時に発行されるトークンの生成/検証を行う.
- アカウントのフォロー/フォロー解除を行う.
Timeline Module
- (ホーム/リスト)タイムラインを構築する.
- 特定のアカウントが見ることのできる投稿をフォロー関係と公開範囲から決定する.
Drive Module
- メディアファイルの投稿を受け付ける
- メディアファイルを適切に圧縮する
- メディアファイル情報を返す
- メディアファイルの保存を行う
モジュールの内部構造
モジュールの内部は以下のようになっています
.
├── adaptor/
│ ├── controller コントローラー
│ ├── presenter APIのエラー定義
│ ├── repository Repositoryの実装
│ └── validator APIスキーマ定義ファイル
├── model/
│ ├── errors.ts モジュール内で使うエラーの定義
│ ├── <moduleName>.ts モジュールで使うモデル
│ └── repository.ts RepositoryのInterface定義
├── service 実際の処理を行うApplication Serviceの実装
├── mod.ts APIのハンドラ (エントリーポイント)
└── router.ts APIのルーティング定義
処理の大まかな流れは以下の通りです
- mod.ts がトップレベル(pkg/main.ts)から呼び出され、APIのリクエストを受ける
- router.tsの定義に従ってmod.tsで定義されたハンドラーにリクエストの内容が渡される
- adaptor/validatorで定義されたスキーマに沿ったリクエストかを判定する
- ハンドラーからadaptor/controllerを呼び出す
- controllerからserviceを呼び出す
- serviceが処理を行い、model/repository.tsで定義されたInterfaceを呼び出し、データの取得や保存を行う
- Intermoduleパッケージを利用して他のモジュールの機能を呼び出して使うこともできる
Intermoduleパッケージ (モジュール間通信)
モジュールが他のモジュールのServiceをimportすることは禁止されています. そのため,
他のモジュールの機能を利用したい場合には Intermodule
パッケージを利用します.
Intermodule
パッケージはそれぞれのモジュールごとに定義されるインタフェースで、モジュールが他のモジュールから参照される機能を公開するようになっています.
Intermodule
の内部ではそのモジュールのServiceのメソッドを呼び出すよう実装されています.
アクセス制御
アカウントのロール
ロールは自分以外のアカウントに対して操作を実行可能であるかを制御する.
表1: 可能な操作一覧
操作名 | 備考 | Admin | Moderator |
---|---|---|---|
他アカウント | 凍結、サイレンスの実行、アカウント情報の閲覧 | Y | Y |
ノート/メディア | ノート/メディアの削除 | Y | Y |
通報に関する操作 | 通報の確認/解決、警告の送信 | Y | Y |
不要メディア削除 | どこからも参照されないメディアの削除 | Y | N |
統計情報 | 稼働統計情報の閲覧 | Y | Y |
システム設定 | 設定値の閲覧/変更 | Y | N |
お知らせ | お知らせの送信、編集 | Y | N |
カスタム絵文字 | 登録、無効化、削除 | Y | Y |
他インスタンス | インスタンスのサイレンス/ブロック | Y | N |
※ Y: 操作、閲覧が可能 / N: 操作、閲覧が不可能
Admin (管理者)
管理者.
全ての操作が実行可能.
Moderator (モデレーター)
モデレーター.
(自他問わず)インスタンス、お知らせ以外の操作を実行可能
important
モデレーターは一般ロールのアカウントに対する操作のみ実行可能.
Normal (一般)
通常のユーザー.
自分のリソースに対する操作のみを行うことが可能.
メールアドレス検証状態
メールアドレスの検証が行われたかを示すもの.
notActivated (メールアドレス未検証)
メールアドレスの検証が行われていない状態.
検証が行われていないアカウントは一定期間(設定可能)経過後に自動で削除される(設定で行わないことも可能)
メールアドレス検証トークンの再送信のリクエスト と アカウント削除 のみ実行可能.
Active (メールアドレス検証済み)
メールアドレスの検証が行われたことを示す状態.
設定されているロールの権限に基づく全ての操作が実行可能になる.
アカウント状態
アカウントの現在の状態を示すもの.
Notmal(通常)
通常の状態.
設定されているロール権限に基づく全ての操作が実行可能.
important
ただし、メールアドレス検証状態がnotActivated
である場合はそちらが優先される
Frozen(凍結済み)
凍結済み状態.
ログインを含む全ての操作が実行不可能.
そのアカウントの投稿,
メディアはAdmin
/Moderator
ロール以外のアカウントから閲覧できなくなる.
important
凍結状態が解除(解凍)された場合は、投稿やメディアは他のアカウントから閲覧可能になる.
Silenced(サイレンス済み)
サイレンス済み状態.
そのアカウントは新規投稿の公開範囲でPUBLIC
を選択できなくなる
important
サイレンス状態が解除されても、サイレンス中に行われた投稿の公開範囲は変更されない.