fix(twitter): migrate from deleted bird CLI to twitter-cli (#231)

steipete/bird repo has been deleted (404). Migrates to twitter-cli
(public-clis/twitter-cli, 2137 stars, Python, actively maintained).

Changes:
- twitter.py: check() now detects `twitter` binary, parses `twitter status` YAML
- cli.py: install via pipx/uv instead of npm, test via `twitter status`
- cli.py: remove bird/xfetch credential sync, use TWITTER_AUTH_TOKEN/CT0 env
- skill references: bird commands → twitter-cli commands
- tests: 5 new tests covering all check() paths

104 tests passing.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pnant
2026-03-31 15:51:26 +08:00
committed by GitHub
parent 66883060bc
commit c5a304dc20
5 changed files with 106 additions and 109 deletions
+30 -28
View File
@@ -13,58 +13,60 @@ def _cp(stdout="", stderr="", returncode=0):
return m
def test_check_bird_found_and_auth_ok():
"""bird found + bird check returns 0 → ok."""
def test_check_twitter_cli_found_and_auth_ok():
"""twitter-cli found + twitter status ok → ok."""
channel = TwitterChannel()
with patch("shutil.which", side_effect=lambda name: "/usr/local/bin/bird" if name == "bird" else None), patch(
with patch("shutil.which", return_value="/usr/local/bin/twitter"), patch(
"subprocess.run",
return_value=_cp(stdout="Authenticated as @user\n", returncode=0),
return_value=_cp(stdout="ok: true\nusername: testuser\n", returncode=0),
):
status, message = channel.check()
assert status == "ok"
assert "完整可用" in message
def test_check_bird_found_auth_missing():
"""bird found + bird check returns 1 with 'Missing credentials' → warn about auth."""
def test_check_twitter_cli_found_auth_missing():
"""twitter-cli found + not_authenticated → warn about auth."""
channel = TwitterChannel()
with patch("shutil.which", side_effect=lambda name: "/usr/local/bin/bird" if name == "bird" else None), patch(
with patch("shutil.which", return_value="/usr/local/bin/twitter"), patch(
"subprocess.run",
return_value=_cp(stderr="Missing credentials: AUTH_TOKEN and CT0 required\n", returncode=1),
return_value=_cp(
stderr="ok: false\nerror:\n code: not_authenticated\n",
returncode=1,
),
):
status, message = channel.check()
assert status == "warn"
assert "配置认证" in message
assert "未认证" in message
def test_check_bird_not_found():
"""bird not found → warn with install hint for @steipete/bird."""
def test_check_twitter_cli_not_found():
"""twitter-cli not found → warn with install hint."""
channel = TwitterChannel()
with patch("shutil.which", return_value=None):
status, message = channel.check()
assert status == "warn"
assert "@steipete/bird" in message
assert "twitter-cli" in message
def test_check_birdx_binary_accepted():
"""birdx symlink is accepted as an alternative binary name."""
def test_check_twitter_cli_generic_failure():
"""twitter status returns 1 without not_authenticated → generic warn."""
channel = TwitterChannel()
with patch("shutil.which", side_effect=lambda name: "/usr/local/bin/birdx" if name == "birdx" else None), patch(
with patch("shutil.which", return_value="/usr/local/bin/twitter"), patch(
"subprocess.run",
return_value=_cp(stdout="Authenticated as @user\n", returncode=0),
):
status, message = channel.check()
assert status == "ok"
assert "完整可用" in message
def test_check_bird_auth_failure_generic():
"""bird check returns 1 without 'Missing credentials' → generic auth failure warn."""
channel = TwitterChannel()
with patch("shutil.which", side_effect=lambda name: "/usr/local/bin/bird" if name == "bird" else None), patch(
"subprocess.run",
return_value=_cp(stderr="Error: token expired\n", returncode=1),
return_value=_cp(stderr="some error\n", returncode=1),
):
status, message = channel.check()
assert status == "warn"
assert "认证检查失败" in message
def test_check_twitter_cli_exception():
"""twitter status throws exception → warn."""
channel = TwitterChannel()
with patch("shutil.which", return_value="/usr/local/bin/twitter"), patch(
"subprocess.run", side_effect=Exception("timeout"),
):
status, message = channel.check()
assert status == "warn"
assert "连接失败" in message