atprotoモデレーション概論 bsky編
bsky lexiconにおけるlabelやlabelerの扱いについて説明する。
内容は大体公式ドキュメントに書いてある。
ここで定義されている概念の幾つかは、将来的にはatproto全体で使われるようになるかもしれない。
実装固有の話題はBluesky Social編にて。具体的にはOzoneとかautomodとか。
takedownの反映
対象が存在しなかった(DB上に存在しない)かのように扱う。APIによって400になったり404になったり省略されたり様々だが、特段語るべきところは無い。
labelの分類
bsky固有の概念として、global labelとcustom labelという分類がある。
global label
global labelはlexiconレベルで意味が定められている特殊なlabelのこと。内訳として2種類ある。
普通のlabelには無い効果を持つlabel値[^redactlabel]
!hide: 強制的に非表示にする (self label不可)
!warn: 強制的に警告を表示する (self label不可)
!no-unauthenticated: 非ログインユーザには非表示にすることをappviewに要求する
共通でself labelに使えるlabel値
porn, sexual, graphic-media, nudity
[^redactlabel]: !takedownと!suspendもここに含むと考えてもよいかもしれない。ここではbsky側の公式ドキュメントに倣い、global labelから外した。公式で出てこないのは、atproto層で定義されたものであるためか、クライアントと無関係なためだと推測している。
後者は少し分かりにくいと思うので補足しておく。label編で説明したように、通常はlabel値の意味は発行者毎に異なり、self labelの発行者はrecordの持ち主である。つまり、同じlabel値であっても、self labelの意味はアカウント毎に異なる。そして実のところ、bskyにおいてself labelの意味を定義する方法は無い。詳細は後述する。\ 一方で、性的なコンテンツのゾーニングは法律やプラットフォームからの要請があるわけで、一律で対応できなければいけない。というわけで、仕様レベルで意味を定義したlabel値を用意して、簡便かつ確実に対処できるようにしている。
custom label
custom labelは発行者によって意味が定義されたlabelのこと。具体的な定義の仕方は後述する。
!が付かないglobal labelは、同じlabel値を独自定義で上書きして、custom labelとして使うこともできる。
大体のlabelはcustom labelに分類されるが、公式クライアントから自分で設定できるのはglobal labelだと思っておけば大体合ってる。
意味の定義
labelの意味は、com.atproto.label.defs#labelValueDefinitionで定義する。これはlabel値(identifier)に対して以下を指定する。
severity: クライアント上のメッセージ要否
alert: 危険なコンテンツとして警告を表示する
inform: 中立的な情報としてメッセージを表示する
none: メッセージを表示しない
blurs: 隠す対象
content: 投稿本文やリスト等、record情報そのものを隠す
media: アイコンや動画等、recordに付随するメディアを隠す
none: 何も隠さない
defaultSetting: labelの対応(クライアントで上書き可能)
hide: 投稿やアカウントそのものを非表示にする
warn: blursの対象を隠して警告を出す
ignore: 何もしない
adultOnly: 未成年者の閲覧可否
true: 未成年者は強制的にhideになる
false: 年齢による区別をしない
locales: 言語毎の表示名と詳細説明
後述するように、この定義はrecordとして示される。
labelの反映
labelはappviewが取得し、APIレスポンスでそれぞれのlabelsフィールドにラベル値が埋め込まれる。この時、署名は省略される。対象毎のlabel位置は以下。
post
labeler: postView/labels
self: postView/record/labels(元recordが丸ごと入る)
profile
labeler&self: profileView/labels, profileViewBasic/labels, profileViewDetailed/labels
generator
labeler&self: generatorView/labels
service
labeler&self: labelerView/labels, labelerViewDetailed/labels
list
labeler&self: listView/labels, listViewBasic/labels
starterpack
labeler: starterPackView/labels, starterPackViewBasic/labels
汎用
通知対象: notification/labels
引用(embed)対象: viewRecord/labels
アカウント
labeler: 未確認(上記全部?)
profileのlabelとアカウントのlabelが別物である点に注意。前者はあくまでアイコンやプロフィール文などprofile recordで定義している範囲に対するlabelであり、アカウントそのものへのlabelではない。
appviewでは!の付かないlabelはAPIレスポンスにlabelを含めるのみで、label毎の処理はクライアント側で行う。\
定義毎の取り扱いは公式SDKに頼るのが楽。global labelもここで定義されている。ちゃんと文書化してほしい。
labeler
labeler→appviewは標準メソッドで伝達され、lexicon固有の方法は無い。
どちらのAPIをどう使うかは規定されていないが、リアルタイムでなくても取得(backfill)できることが重要だとされている。
labelerの発見およびlabel定義公開のため、service recordが用意されている。これを持つアカウントのDIDはlabeler自身と同じものを使う[^labelerdid]。このアカウントは基本的にlabeler専用として、普段使いしないことが推奨されている[^labeleraccount]。
[^labelerdid]: 比較対象として、feed generator(generator record)の場合はBlueskyアカウントとサーバのDIDが異なっていてもよい。feed generatorは必ずしも作者がサーバを所有しているわけではないというのは、SkyFeed等を考えれば分かるだろう。
[^labeleraccount]: モデレータ保護のためや引継ぎを容易にするため等の理由があった気がするが、ソースは忘れた。labeler管理サービスへのログインにも専用アカウントを推奨するくらいなので、大きく間違ってはいないはず。
serviceには前述のlabel定義が並ぶ。labelerはここで定義したlabelとglobal labelのみを発行することが期待されるが、厳密な仕様は未だ無い。
self label
既に大体説明してしまったが、各recordのlabelsフィールドにlabel値を含める。
self labelを定義する仕組みは存在していないため、self labelにはcustom labelは使えない。具体的には、使えるlabel値は!no-unauthenticated(profileのみ), porn, sexual, nudity, graphic-mediaの5つのみ。
とはいえappview実装上、APIではその他のself labelも見せるため、クライアントの独自解釈で取り扱うこともある。