2024/12/18 18:00:00
Blueskyカスタムフィード作成ツールを作ってみた
Bluesky Advent Calendar 2024
本記事はBluesky Advent Calendar 2024の12/18の記事になります。作ってくれたgirigiribauerさんありがとうございます。先日、ご本人にもお会いしましたが至極まっとうな方でした。私とは大違い。イナバウアーでもベッケンバウアーでもなくジャックバウアーだって教えてもらいました。
背景
自前のカスタムフィードはaerostream2というRustのcrateをこれまた自作して作ってます。コードはそこそこ楽に書けるようにしていますがこれの利用者はこじらさんだけの想定。ブルスコちゃんをRustで書き直すのに使いたいっていう話があったけどどうなったのかなぁ。
世の中の人はSkyFeedを使ってカスタムフィードを作成している人が多そう。SkyFeedは勝手カスタムフィードのホストも担ってくれるし本当に素晴しいサービスだと思います。過去のポストを検索するような運用もしていてバックエンドに巨大なデータベースを抱えてるのだと思うと尊敬しかありません。
最近ユーザが増えてきたってことでSkyFeedさんも磐石ではなさそうに拝見するので、ちょっとでも負荷軽減するために私もカスタムフィードのデザインとフィードジェネレーターをホスティングするようなツールを提供しても良いかなって思ったのがきっかけです。特に日本語の形態素解析やってるのでフィルタ作る時に精度高くなる可能性もあるなと思ってました。
システム構成
いずれにせよフィードジェネレータをホスティングしないといけないので、サーバは用意せねばなりません。既存の私のカスタムフィード提供用のサーバは上記の図のようにfirehoseからデータを引っ張ってくるのですが、これと同じものを別でもう1つ用意するとトラフィックが大変。ということで、私のカスタムフィード提供サーバの後段に別のサーバを用意して、元々あるサーバのからfirehoseのデータをスルーパスしてもらうことにしました。
SkyFeedはデータベースに蓄積したデータを検索して結果を返しているように見えるのですが同じことを私がやると大変なので、firehoseをの中から条件を満たすポストをカスタムフィード毎に蓄積して、カスタムフィードにアクセスがあったら蓄積していたポストの一覧を返してあげる、そんな挙動でいいなと思っていました。過去ポストのデータベースはN-FeedやU-Feedを実現するのに持ってますがこれに負荷かけるとN-FeedやU-Feedがうまく動かなくなるリスクがあるので、過去ポストのデータベースは使わない仕様にしようと思いました。
ということで、カスタムフィードの本体であるフィードジェネレータの機能は、
- カスタムフィードの定義情報(名前、説明文、URL、条件式)を受け取ってカスタムフィードをPDSに登録する
- 常時firehoseを監視して1.の条件式に合致するポストをカスタムフィード毎に記憶する
- カスタムフィードへのリクエストがあったら2.で記憶しておいたポストの一覧を返却する
上記を達成すれば完成することになります。
ただし、条件式を手動で書くのはかなり大変だろうと思うので、条件式を書くところだけはブラウザアプリを書かないといけません。私はブラウザアプリを作成するのは得意ではないですが今回は書きました。ReactではなくSvelteでもなくSolidJSで書きました。ReactよりSolidの方が組込上がりの私にはしっくりきます。CSSは便利なフレームワーク使っても良いのですが何となくtsxに全部書けてしまうのでTailWindCSSにしてみました。結果的に言えば考えないといけない事が増えて大変ではありました。まぁCSSなんてデザインできる人の使うものです、組込エンジニアにはUIがわからんのです。
構成としてはバックエンドのフィードジェネレータと、ブラウザ内で動作する条件式作成ツールの組み合わせになりますが、まずは、バックエンドのフィードジェネレータから作成する事にしました。条件式としてどこまで記述可能にするのかを先に定義しないと、ブラウザアプリの開発の手戻りが発生するので。いや、手戻りは実際発生しまくりなんですけども。
条件式のパース
あまり私はパーザとか書いた事がなかったのですが条件式をフィルタに変換するにはパーザを書かねばなりません。過去にnomは使ったことがあったのですが、いまいち思った通りに使えなかった(私がたぶん何か理解が及んでいなかった)ので別のを使ってみようと思って今回はpestを使ってみることにしました。PEGを書いておくといい具合に処理してくれるということなのでこりゃ良いわいっていう話です。で、実際にりようしているPEGは以下です。
word = { (!")" ~ ANY)+ }
operation = { "FROM" | "TEXT" | "MENTION" | "LINK" | "TAG" | "RECORD" | "EXTERNAL" | "IMAGE" | "VIDEO" | "LABEL" | "WORD" | "COMPLEX" }
flag = { "HAS_MENTION" | "HAS_LINK" | "HAS_TAG" | "HAS_RECORD" | "HAS_FEEDGEN" | "HAS_POST" | "HAS_LIST" |
"HAS_STARTERPACK" | "HAS_EXTERNAL" | "HAS_IMAGE" | "HAS_VIDEO" | "HAS_LABEL" | "IS_REPLY" }
regex_op = { "REGEX" }
regex_word = { "/" ~ (!"/" ~ ANY)+ ~ "/" }
single_condition = { (operation ~ "(" ~ word ~ ")") | flag | (regex_op ~ "(" ~ regex_word ~ ")") }
not_condition = { "!" ~ ((operation ~ "(" ~ word ~ ")") | flag)}
and_condition = { some_condition ~ WHITE_SPACE* ~ "&&" ~ WHITE_SPACE* ~ some_condition ~
(WHITE_SPACE* ~ "&&" ~ WHITE_SPACE* ~ some_condition)* }
or_condition = { some_condition ~ WHITE_SPACE* ~ "||" ~ WHITE_SPACE* ~ some_condition ~
(WHITE_SPACE* ~ "||" ~ WHITE_SPACE* ~ some_condition)* }
some_condition = _{ ("(" ~ WHITE_SPACE* ~ (or_condition | and_condition) ~ WHITE_SPACE* ~ ")") | single_condition | not_condition }
condition = _{ WHITE_SPACE* ~ (or_condition | and_condition | single_condition | not_condition) ~ WHITE_SPACE* }
こんなもん読めるかいな!ってなると思うので日本語で解釈しておくと…
- 条件式の要素の書き方は
処理の種類(パラメータ)
もしくは処理の種類
- ↑の条件の文字列には
)
は含まない - 処理の種類が正規表現(
REGEX
)の場合は '/' で囲む - 処理の種類が非成立の場合を条件成立とみなす場合は条件式の要素の頭に
!
をつける - 複数の条件式の要素の全てが成立した場合のみ条件式が成立したとみなす場合は条件式の要素を
&&
でつなぐ - 複数の条件式の要素のいずれかが成立した場合に条件式が成立したとみなす場合は条件式の要素を
||
でつなぐ - && や || で複数の条件式を接続する場合には複数の条件式を
()
で囲む(省略不可)
()
の省略はできないけれど &&
と ||
のネストを許容する感じです。TEXT(ブルスコ)
と書けばブルスコを含むポストを、TEXT(ブルスコ) && TEXT(初)
と書けばブルスコと初を両方含むポストを、TEXT(ブルスコ) || TEXT(ブルスカ)
と書けばブルスコとブルスカのいずれかを含むポストを、(TEXT(ブルスコ) || TEXT(ブルスカ)) && TEXT(初)
と書けばブルスコかブルスカのいずれかを含み初も含むポストを示す条件式になります。
条件式の処理の種類
条件式では以下のような設定が処理できます
FROM(パラメータ)
ポストを投稿したアカウントのDIDがパラメータと一致する場合に成立TEXT(パラメータ)
ポストのテキストにパラメータの文字列が含まれる場合に成立MENTION(パラメータ)
ポストに含まれるメンションの宛先のアカウントのDIDがパラメータと一致する場合に成立LINK(パラメータ)
ポストに含まれるリンク先のURLがパラメータを含む場合に成立TAG(パラメータ)
ポストに含まれるタグがパラメータを含む場合に成立RECORD(パラメータ)
ポストに含まれる他のBluesky内データへのURLがパラメータを含む場合に成立EXTERNAL(パラメータ)
ポストに含まれるBluesky外データへのURLもしくはタイトルもしくは説明文がパラメータを含む場合に成立IMAGE(パラメータ)
ポストに含まれる画像のALTがパラメータを含む場合に成立VIDEO(パラメータ)
ポストに含まれる動画のALTがパラメータを含む場合に成立LABEL(パラメータ)
ポストに含まれるラベルのいずれかがパラメータと一致する場合に成立WORD(パラメータ)
ポストのテキストを形態素解析した結果の単語の一覧の中に、単語の原形とパラメータが一致するものがある場合に成立COMPLEX([パラメータ1,パラメータ2,...])
(単語登録機能を作成したため非推奨)ポストのテキストを形態素解析した結果の単語の一覧の中に、パラメータで示される文字列のリストと同じ単語の原形が同じ順序で同じ数だけ見つかった場合に成立REGEX(/パラメータ/)
ポストのテキストとパラメータで表現される正規表現が一致する場合に成立HAS_MENTION
ポストがメンションを含む場合に成立HAS_LINK
ポストがリンクを含む場合に成立HAS_TAG
ポストがタグを含む場合に成立HAS_RECORD
ポストが他のBluesky内データへのURLを含む場合に成立HAS_FEEDGEN
ポストがフィードジェネレータを含む場合に成立HAS_POST
引用リポストの場合に成立HAS_LIST
ポストがリストを含む場合に成立HAS_STARTERPACK
ポストがスターターパックを含む場合に成立HAS_EXTERNAL
ポストがBluesky外データを含む場合に成立HAS_IMAGE
ポストが画像を含む場合に成立HAS_VIDEO
ポストが動画を含む場合に成立HAS_LABEL
ポストがラベルを含む場合に成立IS_REPLY
ポストが返信の場合に成立
フィードジェネレータの作成
aerostream2にはフィードジェネレータを作成するのに必要な処理をある程度カバーしてくれる仕組みを用意しています。特に今回のように「カスタムフィードにアクセスがあった時に全員に同じタイムラインを見せる」系のカスタムフィードであればポストのリストにポストをどんどん追加していけば、勝手にカスタムフィードとして見せてくれる仕組みがあります。aerostream2の説明はまたcrates.ioにpublishする覚悟が決まったらまた別途説明しますが、要はたいして時間かからずにフィードジェネレータは作成できちゃいました。フィードジェネレータの挙動を変えるためのAPIはおおよそ以下のようなものを揃えました。
/register
サーバ側でカスタムフィードのレコードをBlueskyに登録し、条件式をフィードジェネレータに登録して、カスタムフィードとして公開/unregister
フィードジェネレータに登録した条件式を削除して、Blueskyに登録したカスタムフィードのレコードを削除/reg
ブラウザ側でカスタムフィードのレコードをBlueskyに登録済みである場合に、条件式をフィードジェネレータに登録して、カスタムフィードとして公開/unreg
カスタムフィードのレコードはブラウザアプリ側で削除する前提で、フィードジェネレータに登録した条件式を削除/tag
カスタムフィードの所有者であるかどうかを判定するためのレコードへの埋め込むためのランダムな文字列を返す/condition
既存のカスタムフィードの条件式を読み出す/analyze
ブラウザアプリ向けに形態素解析する/test
条件式とポストから条件式に合致するかどうかの判定結果を返す/add_word
形態素解析エンジンに新規に単語を登録する/rm_word
形態素解析エンジンに過去に登録した単語を削除する
カスタムフィードを作成するのにこれとブラウザアプリがセットになるのですが、正しいブラウザアプリかどうかの判別は特には行わない想定で、今はCORSの設定をしています。ブラウザアプリを私が作るよりもきっとうまく作ることができるとBluesky meetup in Fukuokaで宣言されたしのさんのような方が自由にブラウザアプリを作成するのを阻害するつもりもありませんので。もし、しのさんみたいな申し出があれば上記のAPIの詳細は公開しますので連絡いただければと思います。喜んでブラウザアプリの開発から私は手を引きます。
とはいえ、自分が作ったカスタムフィードを他人が改変するというような事がないようにするところだけは配慮しました。App Passwordを使ってサーバ側でカスタムフィードのレコードを作成する場合は良いのですが、OAuth認証の場合はカスタムフィードのレコードをブラウザアプリの中で作成する実装にしています。その場合でもサーバサイドでそのカスタムフィードを作成できたかどうかを識別できるようにするために、サーバから渡した適当な文字列をカスタムフィードのレコードの中に埋め込んでもらった事をサーバ側で確認した上でサーバ側にエンジンを登録できるようにしています。
実際のところフィードジェネレータの作成は比較的とんとん拍子でできてしまいました。
ブラウザアプリの作成
ブラウザアプリの作成方針としては、APIの全機能を引き出す事は当初から考えていませんでした。そんなことをするとUIが複雑になってしまうので、きっと利用する事が難しくなるだろうと思っていました。一方で、私のフィードジェネレータは形態素解析をした結果を用いているというところは特徴だと思っていたので、形態素解析を前面に押し出すようなUIにしたいと思っていました。
それらを踏まえ、カスタムフィードを作成する時の流れとしては以下のような感じにしようと思っていました。
- カスタムフィードの作成者にBlueskyにログインしてもらう
- ログインしたアカウントに関連づけられた何らかのタイムライン(例えばFollowing)に基づいてポストの一覧を取得する
- そのタイムラインの中から特定のポストを選択してもらう
- そのポストのテキストの形態素解析結果を表示する
- 形態素解析した結果の単語のリストから単語をいくつか選択する
- 選択された複数の単語をANDもしくはORの条件で結合しフィードジェネレータに登録する条件式を作成する
- カスタムフィードを作成するにあたって必要な追加情報(URLの末尾部分(=rkey)、表示名、説明文)を入力する
- 6.の条件式と7.の追加情報をフィードジェネレータに登録してカスタムフィードを完成させる
これでできることよりも複雑な条件式は、もはやテキストで入力してもらってAPIをたたく、くらいの雑な作りで良いと割り切っていました。そこまで割り切る事でなんとか苦手なブラウザアプリを開発するモチベーションを維持していたというのが正直なところです。
先ほどフィードジェネレータの方でOAuth認証をあたかも最初から想定して作成していたかのような書き方をしていましたが、実際にはブラウザアプリを作成し始めてから「あ、OAuthでも認証できんじゃん」と思って後からOAuth認証に対応しました。なので、ブラウザアプリを作成してから/reg
や/unreg
といったAPIを作成していますのでAPIの名前が雑になっています。
OAuthもサーバ側で認証プロセスを仲介する方法としない方法と両方あるようで、通常ブラウザアプリであってもWebサーバくらいあるでしょ、だから前者推奨だとみたいな事をどっかに書いていた気もしましたが、面倒なので後者で対応しちゃいました。というか認証のプロセスそのものはライブラリ依存にしちゃってますが、ライブラリの使い方とかいちいちきっちりおさえにいくのがぶっちゃけ面倒になってしまったのです。こういう面倒だからもういいじゃん、というあきらめは、個人開発においては非常に重要だと思っています。まぁ仕事でも枝葉の仕様はそれくらいの割り切りはあって良いと思いますけど。OAuthの認証方式が枝葉かどうかはともかくとして。
そんなこんなでブラウザアプリも上記程度のものであればどうにか動くようになるところまではこぎつけることができました。
Bluesky meetup in Fuokuoka
で、まぁ、この時点ではまだ一般に公開できるようなものではなかったと思っています。実際、考慮漏れいっぱいあったんで、フィードジェネレータはともかく、ブラウザアプリはレベル的にはまぁいまいちでした。
とはいえ、これ以上のレベルを求めるとなると、私以外の方にも使ってもらいたいなぁというのはちょっと思っていました。ですので、ひで部でちょろっと公開したんですが、反応はいまいちでした。ないへぶさんが、ポスト選択したら形態素解析できる!うひょー!、とカスタムフィードを作成できる機能とは別なところで感動していた程度で。
そう思ってるところでBluesky meetup in Fukuokaが開催されました。世の中一般に公開するとなんかちょっと面倒だなぁと思っていたのですが、meetupの参加者だけに公開して反応もらうのはありだなと思いましたので、事前に想定されていたプログラムには含まれなかったのですが、Bluesky meetup in Fukuokaの懇親会の時間の一部を借りて、ちょっと触ってもらうことにしました。
歴史的建造物を流用したエンジニアカフェという素敵な場所で、おいしそうな山盛りのケータリングを前にして、カスタムフィード作成ツールの説明をする私と、その話の内容に従って黙々とスマホを操作する参加者、という非常にシュールな光景が展開されてはいましたが、参加された方は比較的興味をもっていじってくださいました。しろるさんは、広めようと思ってます!という「ブルスこんにちは」という挨拶を含むカスタムフィードを作成しておられました。ブルスこんにちは、非常に良い挨拶だなと思ってます。世界に広げようブルスこんにちはの輪。
けっこう簡単に作れるねっていうところは好評でしたが、作った瞬間は何もカスタムフィードに表示されないので、該当するであろうポストをテスト的に投稿しないとわかんないね、というのは事前にわかっていたものの課題でした。あと、モバイルでの利用を想定したUIになってなかったのでそこも使いにくいポイントではありました。今でも使いにくいですけど。ただまぁデザインセンスは皆無なので課題についてはすっぱりあきらめました。個人開発はあきらめが肝心。
単語登録
で、実は私はこのカスタムフィード作成ツールで実現したい事が1つありました。それは既存の私の提供するカスタムフィードや似た者システムとも共通で利用できる「ユーザによる単語登録機能」です。
N-FeedやU-Feedや似た者システムについては、形態素解析した結果の単語を使って「似たポスト」「あまり話題にされない単語」「その人固有の単語」といった特徴を抽出しているのですが、利用している辞書は教科書的な単語のみを収録しているので、SNSでよく使われるような単語であったりとか、新しく作られた固有名詞みたいなものは辞書に単語が存在していません。
例えば、みりめいさんが、鉄道漫画×位置情報ゲーム「あみてつ」というアプリを開発されていますが、この「あみてつ」という単語は形態素解析にかけると「あみ」と「てつ」の2単語に分割されてしまいます。ですので「あみてつ」という単語を特徴とするアカウントと似ているかどうかを判定して欲しいにもかかわらず、辞書にない言葉なのでそれができないために、みりめいさんが似た者システムを利用しても似てる人が見つからないという悲しい結果になってしまっていました。これは改善したい。
単語登録をするところはまぁえっちらおっちらやったらなんとか実現できました。N-FeedやU-Feedは過去のポストのデータベース(Elasticsearch)で形態素解析をしていますが、この形態素解析のエンジンに単語登録する、とか、カスタムフィード作成ツールのWORD()によるポストの収集処理でも、登録した単語を利用するというのは実現できました。
ただ、ここで問題が発生します。
先ほども紹介をした「ブルスこんにちは」というカスタムフィードは「ブルス」と「こんにちは」という2つの単語が連続して現れた場合(条件式でいうところのCOMPLEX())にポストを抽出するように条件式が作成されているのですが、「ブルスこんにちは」という単語を登録すると形態素解析の結果として2単語ではなく1単語を返してしまうために、元のカスタムフィードが機能しなくなってしまうのです。
これは非常にマズいので、どうにかして解決できないかを考えてはみたのですが、またしても面倒になったので、COMPLEX()の利用は非推奨にする、既存のCOMPLEXを利用しているカスタムフィードは手動で私の方で単語登録してWORD(<登録した単語>)という条件式のカスタムフィードに書き換える、ということで対応することにしました。単語の登録削除は誰でもできるようにはしていますが、複合語による検索を除外することで「単語登録すれば機能する」し「単語削除すれば機能しなくなる」というわかりやすい仕様にすることでもういいやという風に結論づけるとこにしました。単語登録と条件式の書き換えは私の方で手動でやるってここには書いていますがまだ実際にはやっていません。面倒になっちゃったんで。またどこかで時間見つけてやっておきます。
ということで、ここまででおおよそ現在のカスタムフィード作成ツールの形になりました。
使い方紹介
まずURLはhttps://customfeed.shigepon.net/になります。あくまで永遠のα版です。
メニューバーは↑のような構成になっています。私のアイコンは私の印象を植えつけるための刷り込みですので気にしないでください。カスタムフィード作成ツールという名前はダサいですが考えるの面倒なので見てわかる形にしています。未ログインの部分はOAuthで認証が通っているとハンドル名になります。ここまでは表示しているだけのメニューです。
まずは「ログイン(OAuth)」と書かれたエリアを押してください。ボタンに見えないのは私のデザインセンスがないからです。気にしないで先にすすみましょう。
ここにカスタムフィードを公開する際に利用するハンドルを入力してください。その上で認証ボタンを押します。そうするとBlueskyの認証画面に遷移します。
ここはApp Passwordではなくアカウントのマスターパスワード(で呼ぶんでいいんだけ?)を入力してください。OAuth認証で権限許諾するにはApp PasswordではできないようにBluesky側の仕組みができています。認証に成功すると認可画面に遷移します。
認可画面は何書いてんだかわかんないと思いますが「あなたが誰であるかを識別する、ということと、App Passwordで利用できる権限相当をこのWebサイトに許可してええか?」と聞いてはります。App Passwordをアプリに入力するのと同じ事だと理解してください。良かったらAcceptを押します。
そうするとこんな感じで右上の「未ログイン」が「<入力したハンドル名>でログイン中」に変わります。同時にFollowingのポストが一覧表示されます。ここから適当に1つポストを選択します。そうすると形態素解析が実行されます。
カスタムフィードを作成する時はこの右側の形態素解析済みの単語リストから単語を選択します。黄色に帯がかかっている単語はN-Feed等のカスタムフィードでこの人はこういう単語を発言しているという認識をする際に使う名詞になっています。あまり気にしなくて大丈夫ですが、特徴をつかみやすい単語になってるケースが多いです。ここでは「豚肉」と「もやし」を選択して、条件式の上にあるボタンのうち「単語(全てを含む)を追加」を選択します。
そうするとこんな感じで条件式のところに、選択した単語にWORDという処理を介したものを&&で結合した条件式が出現しました。これで「豚肉」と「もやし」の両方を含むポストを選択する条件式が作成されました。この&&の左右の条件式の要素はボタンになっていて押すと条件式から削除されるようになっています。今回はこのままカスタムフィードにしちゃいましょう。黒い帯にある「フィードの公開」というボタンに見えないボタンを押します。
そうすると公開するのに必要な情報を入力する画面になります。パスの末尾にはカスタムフィードを識別するための名称を入力してください。アカウント×パスの末尾、の情報でカスタムフィードが唯一に決まるので、この組み合わせを同じにして公開しなおせば、カスタムフィードの条件式を上書きして公開する事が可能です。名前は外から見た時に見える名前ですのでわかりやすい名前を入力してください。説明文は名前の下に表示される説明文ですのでこちらもわかりやすいように入力してください。説明文は省略する事も可能です。ここまで入力できれば「公開」ボタンを押します。これでカスタムフィードが完成します。クライアントで見てみると確かにカスタムフィードができています。
ではこのカスタムフィードを表示してみます。
何も表示されません。サーバのリソースをできるだけ使わないようにするために、カスタムフィードを作成してから条件に合致するポストを発言した場合にのみ蓄積しているからです。では、この条件にひっかかるポストを実験的に発言してみましょう。
はい、こんな感じで条件に合致するポストをするとちゃんとカスタムフィードとして機能していることが確認できます。簡単でしたよね?おおよそこんな流れでカスタムフィードを作ることができます。
それ以外にできること
メニューに表示されているボタンの機能をします。
- 「Following」と書かれているリストにはログインしたアカウントがフォローしているカスタムフィードの一覧が並んでいますので、リストから目的のカスタムフィードを選択することでFollowing以外のタイムラインを表示することができます
- 「タイムラインの更新」はカスタムフィード作成ツールは自動的にタイムラインの新しいものを取得したりはしないので最新のポストを表示したい場合には「タイムラインの更新」ボタンを押してください
- 「ポストの読み込み」は特定のポスト、リスト、カスタムフィードを表示したい場合に押すボタンです。押すとダイアログが表示されますので、そこにポストやリストやカスタムフィードのURLを貼りつけると合致するタイムラインを表示することができます
- 「フィードの読み込み」は過去にこのツールで公開したカスタムフィード、あるいは、実験的にSkyFeedのカスタムフィードの条件式を読み込むことができるかもしれません
- 「フィードの公開」は↑で説明した通りです
条件式の上に並んでいるボタンの機能についても説明します。
- 「テキストで編集」は条件式を直接テキストで編集するもので、この日記の前半部分を読んで理解できる人はここから直接条件式を記入して公開していただいてもかまいませんが、WORD()を利用する場合は、単語の境界がどこになるかは形態素解析しないと見誤ることもあるので&&や||を複雑に組み合わせたい場合等必要に応じて利用してください
- 「新規作成」は押すと条件式をまっさらにします、危険です
- 「1つの単語として辞書登録」は形態素解析をした結果の単語を複数組み合わせて新しい単語としてフィードジェネレータに登録する機能で、ここで登録した単語は他のN-FeedやU-Feedや似た者システムでも同様に単語として扱われるようになります
- 「登録済みの単語を辞書から削除」は上記の単語登録を取り消す機能で、削除してしまうとバラバラの単語として再び扱われるようになります
- 「単語(全てを含む)を追加」は↑で説明しましたが全ての単語をANDで接続するような条件式を作成します
- 「単語(いずれかを含む)を追加」は↑の説明とは違って全ての単語をORで接続するような条件式を作成します
だいたいできることはこんなもんです。UIで入力するのがまどろっこしかったら、もはやテキストで直接入力するほぼUIにした意味ねーじゃんみたいなツールになってしまいます。α版なので良いのです。もし何か要望等あればBlueskyで@shigepon.netまでご連絡ください。なんだか1月から仕事が急に忙しくなりそうで、土日を侵食してきそうな勢いなのですが、気が向いたら対応します。
おわりに
明日12/19はShin-ichi Kamikura (sinnchan.k)lさんの「Bluesky 2年目の様子など書いてみます」の予定です。おたのしみに。
皆様、お身体にお気をつけて年末年始お過ごしください。私はちょっと覚悟決めて仕事に望みたいと思います。