title | emoji | type | topics | published | published_at | |||
---|---|---|---|---|---|---|---|---|
TUIなTwitterクライアント「nekome」を作った |
🐈 |
tech |
|
true |
2022-08-13 18:00 |
↑ こんなの
タブ形式でページを複数開くことができ、 Vim っぽいキーバインドでさくさく使える TUI の Twitter クライアントです。
以下のことができます。
- ホーム・メンション・リストタイムラインの閲覧
- アンケートの投票状況・ピン留めツイートの表示
- ツイートの投稿・削除
- ツイートに対してのいいね・RT・QT・リプライ
- ユーザーのフォロー・ブロック・ミュート
- アカウントの切り替え(マルチアカウント)
これに加えて最近、ストリームモード という Tweet Deck のようにツイートが流れる機能を実装しました!
かなりアプローチは違いますが、今は亡き UserStream の雰囲気を味わうことができます。..
macOS で Homebrew 導入済みなら
brew tap arrow2nd/tap
brew install nekome
Windows で Scoop 導入済みなら
scoop bucket add arrow2nd https://github.com/arrow2nd/scoop-bucket.git
scoop install arrow2nd/nekome
Linux または上記以外の環境なら Releases からビルド済みバイナリをダウンロードできます。
:::message
go install github.com/arrow2nd/nekome/v2@latest
でもインストールできますが、コンシューマーキーが内蔵されていない為そのままでは使うことができません。
詳しくは README をご覧ください。
:::
初回起動時は認証ページにアクセスして PIN コードを取得し、nekome に入力する必要があります。
ここで取得したアクセストークン等は ~/.config/nekome/.cred.toml
内に保存されます。
基本的にキーバインドとコマンドを使って操作します。
たとえば、キーバインドのヘルプページは ?
を入力するか、:docs keybindings
で開くことができます。
また、コマンドは入力補完が効きます。 ちょっと便利です 👍
アカウント・設定ファイルの操作のほか、ツイートの投稿ができます。
omekasyで英字を変換してツイートする例
tweet
コマンドは標準入力にも対応しているので「ツールの出力をそのままツイートする」みたいなことも可能です。
nekome edit
で任意の設定ファイルを開くことができます。
設定に関しての詳細は 設定ファイルについて をご覧ください。
~/.config/nekome/preferences.toml
を編集することで設定をカスタマイズできます。
設定できる項目は以下の通りです。
:::details preferences.toml
[feature]
# メインで使用するユーザ
main_user = "user_name"
# 1度に読み込むツイート数
load_tweets_limit = 25
# 1ページで蓄積する最大ツイート数
accmulate_tweets_limit = 250
# ツイート編集に外部エディタを使用するか
use_external_editor = false
# 実行環境のロケールが CJK かどうか
is_locale_cjk = true
# TUI 起動時に実行するコマンド
startup_cmds = ["home", "mention --unfocus"]
# 確認モーダルを表示するか
[comfirm]
block = true
delete = true
follow = true
like = true
mute = true
quit = true
retweet = true
tweet = true
unblock = true
unfollow = true
unlike = true
unmute = true
unretweet = true
[appearance]
# 読み込むスタイル定義ファイル
style_file = "style_default.toml"
# 日付のフォーマット
date_fmt = "2006/01/02"
# 時刻のフォーマット
time_fmt = "15:04:05"
# ユーザ / BIO の最大行数
user_bio_max_row = 3
# ユーザ / プロフィール表示域の左右パディング
user_profile_padding_x = 4
# ユーザ / 詳細情報のセパレータ
user_detail_separator = " | "
# ツイート / ツイート間のセパレータを非表示
hide_tweet_separator = false
# ツイート / 引用ツイートのセパレータを非表示
hide_quote_tweet_separator = false
# グラフ / 表示に使用する文字
graph_char = "█"
# グラフ / 最大表示幅
graph_max_width = 30
# タブ / セパレータ
tab_separator = "|"
# タブ / 最大表示幅
tab_max_width = 20
[layout]
# ツイート表示
tweet = "{annotation}\n{user_info}\n{text}\n{poll}\n{detail}"
# アノテーション
tweet_anotation = "{text} {author_name} {author_username}"
# ツイート詳細
tweet_detail = "{created_at} | via {via}\n{metrics}"
# 投票表示
tweet_poll = "{graph}\n{detail}"
# 投票グラフ
tweet_poll_graph = "{label}\n{graph} {per} {votes}"
# 投票詳細
tweet_poll_detail = "{status} | {all_votes} votes | ends on {end_date}"
# ユーザプロフィール表示
user = "{user_info}\n{bio}\n{user_detail}"
# ユーザ詳細
user_info = "{name} {username} {badge}"
[text]
# いいねの単位
like = "Like"
# リツイートの単位
retweet = "RT"
# 読み込み中表示
loading = "Loading..."
# ツイート無し表示
no_tweets = "No tweets ฅ^-ω-^ฅ"
# タブ表示
tab_home = "Home"
tab_mention = "Mention"
tab_list = "List: {name}"
tab_user = "User: @{name}"
tab_search = "Search: {query}"
tab_likes = "Likes: @{name}"
tab_docs = "Docs: {name}"
[icon]
# 位置情報
geo = "📍"
# リンク
link = "🔗"
# ピン留めツイート
pinned = "📌"
# 認証済みバッジ
verified = "✅"
# 非公開バッジ
private = "🔒"
[keybinding]
# アプリ全体
[keybinding.global]
quit = ["ctrl+q"]
# メインビュー
[keybinding.view]
close_page = ["ctrl+w"]
focus_cmdline = [":"]
redraw = ["ctrl+l"]
select_next_tab = ["l", "Right"]
select_prev_tab = ["h", "Left"]
show_help = ["?"]
# 全ページ共通
[keybinding.page]
reload_page = ["."]
# ホームタイムラインページ
[keybinding.home_timeline]
stream_mode_start = ["s"]
stream_mode_stop = ["S"]
# ツイートビュー
[keybinding.tweet]
copy_url = ["c"]
cursor_bottom = ["G", "End"]
cursor_down = ["j", "Down"]
cursor_top = ["g", "Home"]
cursor_up = ["k", "Up"]
open_browser = ["o"]
open_user_likes = ["I"]
open_user_page = ["i"]
quote = ["q"]
reply = ["r"]
scroll_down = ["ctrl+k", "PageDown"]
scroll_up = ["ctrl+j", "PageUp"]
tweet = ["n"]
tweet_delete = ["D"]
tweet_like = ["f"]
tweet_retweet = ["t"]
tweet_unlike = ["F"]
tweet_unretweet = ["T"]
user_block = ["x"]
user_follow = ["w"]
user_mute = ["u"]
user_unblock = ["X"]
user_unfollow = ["W"]
user_unmute = ["U"]
:::
~/.config/nekome/style_default.toml
にデフォルトの配色設定があります。
これを編集するか、preferences.toml
内の appearance.style_file
に新しいファイル名を指定すれば自動でファイルが作成されます。
文字色の指定には tview の color tag がそのまま使用できるので、W3C の色名や 16 進数カラーコードが使えるほか、bold, italic などスタイルの指定も可能です。
:::message 以下の内容は記事執筆時点での状況です。 現在は変更・改善されている場合があります。 :::
絵文字などの一部の文字を表示すると画面が乱れるという問題がありました。
これは曖昧文字幅と呼ばれる「表示幅が決まっていない文字」が原因でした。
rivo/tview#693 mattn/go-runewidth#59
この件についてはすでに issue が立って議論されていたので、様子をみたいと思います。
nekome では暫定的な対応として、Ctrl + l
で画面を再描画できるようにしました。
ブックマークや Spaces のエンドポイントを叩くためには OAuth 2.0 Authorization Code Flow with PKCE という認証方法を使う必要があります。
当初、nekome ではブックマーク周りの操作にも対応する予定だったので、この認証方法を採用しました。 しかし、ある程度実装し終わった段階で 複数端末でのログインできない ということに気がつきました。
コミュニティの投稿 にもあるように、「A でログインした後に B でログインすると、A のトークンが失効する」という挙動になってしまいます。
かなり悩んだのですが、複数端末でログインできないのは許容しがたいと判断したので、ブックマーク周りの機能は見送り、認証方法を OAuth 1.0a に変更することで解決しました。
https://github.com/arrow2nd/nekome/tree/oauth2.0
実装自体はすべてここに残してあります。何かの参考になれば…
nekome を作る前に twnyan という CLI ベースの Twitter クライアントを開発していました。
これには Twitter API v1.1 を使用しており v2 が主要 API になった今、v1.1 がいつまでサポートされるのかが分からず、そう遠くない未来に使えなくなってしまう可能性がありました。
このような経緯があり v2 を採用したのですが、開発を進めていくうちにいくつかの問題に直面しました。
v1.1 ではツイートを表すレスポンス内に favorited
や retweeted
というフィールドがあったのですが、v2 には ありません。
https://developer.twitter.com/en/docs/twitter-api/data-dictionary/object-model/tweet
リファレンスや開発者コミュニティの投稿に片っ端から目を通しましたが、どこにも見当たりませんでした。
なんで??????????????
v1.1 ではメディアをアップロードする media/upload
というエンドポイントが用意されていたのですが、v2 にはありませんでした。
https://developer.twitter.com/en/docs/twitter-api/migrate/twitter-api-endpoint-map
ただし、こちらは COMING SOON となっており ロードマップ にも記載されているので、いずれは移行されるものと思います。
nekome では media/upload
をたたく処理を自前で実装し対応しました。
https://github.com/arrow2nd/nekome/blob/4ec129356832e740d30e9ae81c7fd0a211740b54/api/media.go
Tweet caps とは、v2 の一部エンドポイントからのツイート取得数を月単位で制限するというものです。
https://developer.twitter.com/en/docs/twitter-api/tweet-caps
1 つのプロジェクトでひと月に 200 万ツイートまで取得可能です。
しかし、基本的にプロジェクトは 1 つしか作成できない ため、この制限を超えてしまうと自身の管理するすべてのアプリケーションが対象のエンドポイントにアクセスできなくなってしまいます。
nekome では、仮に制限を超えてしまってもユーザー側で対処できるよう、コンシューマーキーを自由に差し替えられるようにしています。
また、ひと月最大で 1000 万ツイートまで取得可能な Elevated+ というアクセスレベルが用意されるようですので、今後の動きを注視していきたいです。
ターミナルで動く Twitter クライアントはすでにいろいろ存在していますが、Twitter API v2 に対応している TUI なクライアントは探した限りでは見当たりませんでした。
「ターミナルからツイートしたい!」「ターミナルに引き篭もりたい!」という方、ぜひお試しいただけたらなと思います…!