mirror of
https://github.com/ihabunek/toot
synced 2024-12-22 07:01:46 +01:00
parent
3ff9bc7942
commit
76bb1b5484
@ -7,6 +7,7 @@ from os import path
|
||||
|
||||
from tests.integration.conftest import ASSETS_DIR, Run, assert_ok, posted_status_id
|
||||
from toot import CLIENT_NAME, CLIENT_WEBSITE, api, cli
|
||||
from toot.cache import clear_last_post_id, get_last_post_id
|
||||
from toot.utils import get_text
|
||||
from unittest import mock
|
||||
|
||||
@ -340,7 +341,7 @@ def test_media_attachment_without_text(mock_read, mock_ml, app, user, run):
|
||||
assert attachment["meta"]["original"]["size"] == "50x50"
|
||||
|
||||
|
||||
def test_reply_thread(app, user, friend, run):
|
||||
def test_reply_to(app, user, friend, run):
|
||||
status = api.post_status(app, friend, "This is the status").json()
|
||||
|
||||
result = run(cli.post.post, "--reply-to", status["id"], "This is the reply")
|
||||
@ -362,3 +363,38 @@ def test_reply_thread(app, user, friend, run):
|
||||
assert user.username in s2
|
||||
assert status["id"] in s1
|
||||
assert reply["id"] in s2
|
||||
|
||||
|
||||
def test_reply_last(app, user, run):
|
||||
result_1 = run(cli.post.post, "one")
|
||||
status_id_1 = posted_status_id(result_1.stdout)
|
||||
assert get_last_post_id(app, user) == status_id_1
|
||||
|
||||
result_2 = run(cli.post.post, "two", "--reply-last")
|
||||
status_id_2 = posted_status_id(result_2.stdout)
|
||||
assert get_last_post_id(app, user) == status_id_2
|
||||
|
||||
result_3 = run(cli.post.post, "two", "--reply-last")
|
||||
status_id_3 = posted_status_id(result_3.stdout)
|
||||
assert get_last_post_id(app, user) == status_id_3
|
||||
|
||||
status_1 = api.fetch_status(app, user, status_id_1).json()
|
||||
status_2 = api.fetch_status(app, user, status_id_2).json()
|
||||
status_3 = api.fetch_status(app, user, status_id_3).json()
|
||||
|
||||
assert status_1["in_reply_to_id"] is None
|
||||
assert status_2["in_reply_to_id"] == status_id_1
|
||||
assert status_3["in_reply_to_id"] == status_id_2
|
||||
|
||||
|
||||
def test_reply_last_fails_if_no_last_id(app, user, run: Run):
|
||||
clear_last_post_id(app, user)
|
||||
result = run(cli.post.post, "one", "--reply-last")
|
||||
assert result.exit_code == 1
|
||||
assert result.stderr.strip() == f"Error: Cannot reply-last, no previous post ID found for {user.username}@{app.instance}"
|
||||
|
||||
|
||||
def test_reply_last_and_reply_to_are_exclusive(app, user, run: Run):
|
||||
result = run(cli.post.post, "one", "--reply-last", "--reply-to", "123")
|
||||
assert result.exit_code == 1
|
||||
assert result.stderr.strip() == f"Error: --reply-last and --reply-to are mutually exclusive"
|
||||
|
61
toot/cache.py
Normal file
61
toot/cache.py
Normal file
@ -0,0 +1,61 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from toot import App, User
|
||||
|
||||
CACHE_SUBFOLDER = "toot"
|
||||
|
||||
|
||||
def save_last_post_id(app: App, user: User, id: str) -> None:
|
||||
"""Save ID of the last post posted to this instance"""
|
||||
path = _last_post_id_path(app, user)
|
||||
with open(path, "w") as f:
|
||||
f.write(id)
|
||||
|
||||
|
||||
def get_last_post_id(app: App, user: User) -> Optional[str]:
|
||||
"""Retrieve ID of the last post posted to this instance"""
|
||||
path = _last_post_id_path(app, user)
|
||||
if path.exists():
|
||||
with open(path, "r") as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def clear_last_post_id(app: App, user: User) -> None:
|
||||
"""Delete the cached last post ID for this instance"""
|
||||
path = _last_post_id_path(app, user)
|
||||
path.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def _last_post_id_path(app: App, user: User):
|
||||
return get_cache_dir("last_post_ids") / f"{user.username}_{app.instance}"
|
||||
|
||||
|
||||
def get_cache_dir(subdir: Optional[str] = None) -> Path:
|
||||
path = _cache_dir_path()
|
||||
if subdir:
|
||||
path = path / subdir
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
|
||||
def _cache_dir_path() -> Path:
|
||||
"""Returns the path to the cache directory"""
|
||||
|
||||
# Windows
|
||||
if sys.platform == "win32" and "LOCALAPPDATA" in os.environ:
|
||||
return Path(os.environ["LOCALAPPDATA"], CACHE_SUBFOLDER)
|
||||
|
||||
# Mac OS
|
||||
if sys.platform == "darwin":
|
||||
return Path.home() / "Library" / "Caches" / CACHE_SUBFOLDER
|
||||
|
||||
# Respect XDG_CONFIG_HOME env variable if set
|
||||
# https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
if "XDG_CACHE_HOME" in os.environ:
|
||||
return Path(os.environ["XDG_CACHE_HOME"], CACHE_SUBFOLDER)
|
||||
|
||||
return Path.home() / ".cache" / CACHE_SUBFOLDER
|
@ -7,6 +7,7 @@ from time import sleep, time
|
||||
from typing import BinaryIO, Optional, Tuple
|
||||
|
||||
from toot import api, config
|
||||
from toot.cache import get_last_post_id, save_last_post_id
|
||||
from toot.cli import AccountParamType, cli, json_option, pass_context, Context
|
||||
from toot.cli import DURATION_EXAMPLES, VISIBILITY_CHOICES
|
||||
from toot.tui.constants import VISIBILITY_OPTIONS # move to top-level ?
|
||||
@ -60,6 +61,12 @@ from toot.utils.datetime import parse_datetime
|
||||
"--reply-to", "-r",
|
||||
help="ID of the status being replied to, if status is a reply.",
|
||||
)
|
||||
@click.option(
|
||||
"--reply-last", "-R",
|
||||
help="Reply to the last posted status to continue the thread.",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
)
|
||||
@click.option(
|
||||
"--language", "-l",
|
||||
help="ISO 639-1 language code of the toot, to skip automatic detection.",
|
||||
@ -137,7 +144,8 @@ def post(
|
||||
poll_multiple: bool,
|
||||
poll_hide_totals: bool,
|
||||
json: bool,
|
||||
using: str
|
||||
using: str,
|
||||
reply_last: bool,
|
||||
):
|
||||
"""Post a new status"""
|
||||
if len(media) > 4:
|
||||
@ -153,6 +161,7 @@ def post(
|
||||
media_ids = _upload_media(app, user, media, descriptions, thumbnails)
|
||||
status_text = _get_status_text(text, editor, media)
|
||||
scheduled_at = _get_scheduled_at(scheduled_at, scheduled_in)
|
||||
reply_to = _get_reply_to(app, user, reply_to, reply_last)
|
||||
|
||||
if not status_text and not media_ids:
|
||||
raise click.ClickException("You must specify either text or media to post.")
|
||||
@ -186,6 +195,8 @@ def post(
|
||||
else:
|
||||
click.echo(f"Toot posted: {status['url']}")
|
||||
|
||||
save_last_post_id(app, user, status["id"])
|
||||
|
||||
delete_tmp_status_file()
|
||||
|
||||
|
||||
@ -245,6 +256,20 @@ def _get_scheduled_at(scheduled_at, scheduled_in):
|
||||
return None
|
||||
|
||||
|
||||
def _get_reply_to(app, user, reply_to, reply_last):
|
||||
if reply_last and reply_to:
|
||||
raise click.ClickException("--reply-last and --reply-to are mutually exclusive")
|
||||
|
||||
if reply_last:
|
||||
last_id = get_last_post_id(app, user)
|
||||
if last_id:
|
||||
return last_id
|
||||
else:
|
||||
raise click.ClickException(f"Cannot reply-last, no previous post ID found for {user.username}@{app.instance}")
|
||||
|
||||
return reply_to
|
||||
|
||||
|
||||
def _upload_media(app, user, media, descriptions, thumbnails):
|
||||
# Match media to corresponding descriptions and thumbnail
|
||||
media = media or []
|
||||
|
Loading…
Reference in New Issue
Block a user