Add featured tag commands

This commit is contained in:
Ivan Habunek 2023-12-13 07:50:45 +01:00
parent 743dfd715e
commit 381e3583ef
No known key found for this signature in database
GPG Key ID: F5F0623FF5EBCB3D
5 changed files with 188 additions and 17 deletions

View File

@ -1,11 +1,14 @@
from toot import cli
from toot.entities import Tag, from_dict, from_dict_list
import re
from typing import List
from toot import api, cli
from toot.entities import FeaturedTag, Tag, from_dict, from_dict_list
def test_tags(run, base_url):
def test_tags(run):
result = run(cli.tags, "followed")
assert result.exit_code == 0
assert result.stdout.strip() == "You're not following any hashtags."
assert result.stdout.strip() == "You're not following any hashtags"
result = run(cli.tags, "follow", "foo")
assert result.exit_code == 0
@ -13,7 +16,7 @@ def test_tags(run, base_url):
result = run(cli.tags, "followed")
assert result.exit_code == 0
assert result.stdout.strip() == f"* #foo\t{base_url}/tags/foo"
assert _find_tags(result.stdout) == ["#foo"]
result = run(cli.tags, "follow", "bar")
assert result.exit_code == 0
@ -21,10 +24,7 @@ def test_tags(run, base_url):
result = run(cli.tags, "followed")
assert result.exit_code == 0
assert result.stdout.strip() == "\n".join([
f"* #bar\t{base_url}/tags/bar",
f"* #foo\t{base_url}/tags/foo",
])
assert _find_tags(result.stdout) == ["#bar", "#foo"]
result = run(cli.tags, "unfollow", "foo")
assert result.exit_code == 0
@ -32,7 +32,7 @@ def test_tags(run, base_url):
result = run(cli.tags, "followed")
assert result.exit_code == 0
assert result.stdout.strip() == f"* #bar\t{base_url}/tags/bar"
assert _find_tags(result.stdout) == ["#bar"]
result = run(cli.tags, "unfollow", "bar")
assert result.exit_code == 0
@ -40,7 +40,7 @@ def test_tags(run, base_url):
result = run(cli.tags, "followed")
assert result.exit_code == 0
assert result.stdout.strip() == "You're not following any hashtags."
assert result.stdout.strip() == "You're not following any hashtags"
def test_tags_json(run_json):
@ -82,3 +82,82 @@ def test_tags_json(run_json):
result = run_json(cli.tags, "followed", "--json")
assert result == []
def test_tags_featured(run, app, user):
result = run(cli.tags, "featured")
assert result.exit_code == 0
assert result.stdout.strip() == "You don't have any featured hashtags"
result = run(cli.tags, "feature", "foo")
assert result.exit_code == 0
assert result.stdout.strip() == "✓ Tag #foo is now featured"
result = run(cli.tags, "featured")
assert result.exit_code == 0
assert _find_tags(result.stdout) == ["#foo"]
result = run(cli.tags, "feature", "bar")
assert result.exit_code == 0
assert result.stdout.strip() == "✓ Tag #bar is now featured"
result = run(cli.tags, "featured")
assert result.exit_code == 0
assert _find_tags(result.stdout) == ["#bar", "#foo"]
# Unfeature by Name
result = run(cli.tags, "unfeature", "foo")
assert result.exit_code == 0
assert result.stdout.strip() == "✓ Tag #foo is no longer featured"
result = run(cli.tags, "featured")
assert result.exit_code == 0
assert _find_tags(result.stdout) == ["#bar"]
# Unfeature by ID
tag = api.find_featured_tag(app, user, "bar")
assert tag is not None
result = run(cli.tags, "unfeature", tag["id"])
assert result.exit_code == 0
assert result.stdout.strip() == "✓ Tag #bar is no longer featured"
result = run(cli.tags, "featured")
assert result.exit_code == 0
assert result.stdout.strip() == "You don't have any featured hashtags"
def test_tags_featured_json(run_json):
result = run_json(cli.tags, "featured", "--json")
assert result == []
result = run_json(cli.tags, "feature", "foo", "--json")
tag = from_dict(FeaturedTag, result)
assert tag.name == "foo"
result = run_json(cli.tags, "featured", "--json")
[tag] = from_dict_list(FeaturedTag, result)
assert tag.name == "foo"
result = run_json(cli.tags, "feature", "bar", "--json")
tag = from_dict(FeaturedTag, result)
assert tag.name == "bar"
result = run_json(cli.tags, "featured", "--json")
tags = from_dict_list(FeaturedTag, result)
[bar, foo] = sorted(tags, key=lambda t: t.name)
assert foo.name == "foo"
assert bar.name == "bar"
result = run_json(cli.tags, "unfeature", "foo", "--json")
assert result == {}
result = run_json(cli.tags, "unfeature", "bar", "--json")
assert result == {}
result = run_json(cli.tags, "featured", "--json")
assert result == []
def _find_tags(txt: str) -> List[str]:
return sorted(re.findall(r"#\w+", txt))

View File

@ -531,6 +531,29 @@ def followed_tags(app, user):
return _get_response_list(app, user, path)
def featured_tags(app, user):
return http.get(app, user, "/api/v1/featured_tags")
def feature_tag(app, user, tag: str) -> Response:
return http.post(app, user, "/api/v1/featured_tags", data={"name": tag})
def unfeature_tag(app, user, tag_id: str) -> Response:
return http.delete(app, user, f"/api/v1/featured_tags/{tag_id}")
def find_featured_tag(app, user, tag) -> Optional[dict]:
"""Find a featured tag by tag name or ID"""
return next(
(
t for t in featured_tags(app, user).json()
if t["name"].lower() == tag.lstrip("#").lower() or t["id"] == tag
),
None
)
def whois(app, user, account):
return http.get(app, user, f'/api/v1/accounts/{account}').json()

View File

@ -20,7 +20,10 @@ def followed(ctx: Context, json: bool):
if json:
click.echo(pyjson.dumps(tags))
else:
print_tag_list(tags)
if tags:
print_tag_list(tags)
else:
click.echo("You're not following any hashtags")
@tags.command()
@ -51,6 +54,58 @@ def unfollow(ctx: Context, tag: str, json: bool):
click.secho(f"✓ You are no longer following #{tag}", fg="green")
@tags.command()
@json_option
@pass_context
def featured(ctx: Context, json: bool):
"""List hashtags featured on your profile."""
response = api.featured_tags(ctx.app, ctx.user)
if json:
click.echo(response.text)
else:
tags = response.json()
if tags:
print_tag_list(tags)
else:
click.echo("You don't have any featured hashtags")
@tags.command()
@click.argument("tag")
@json_option
@pass_context
def feature(ctx: Context, tag: str, json: bool):
"""Feature a hashtag on your profile"""
tag = tag.lstrip("#")
response = api.feature_tag(ctx.app, ctx.user, tag)
if json:
click.echo(response.text)
else:
click.secho(f"✓ Tag #{tag} is now featured", fg="green")
@tags.command()
@click.argument("tag")
@json_option
@pass_context
def unfeature(ctx: Context, tag: str, json: bool):
"""Unfollow a hashtag
TAG can either be a tag name like "#foo" or "foo" or a tag ID.
"""
featured_tag = api.find_featured_tag(ctx.app, ctx.user, tag)
# TODO: should this be idempotent?
if not featured_tag:
raise click.ClickException(f"Tag {tag} is not featured")
response = api.unfeature_tag(ctx.app, ctx.user, featured_tag["id"])
if json:
click.echo(response.text)
else:
click.secho(f"✓ Tag #{featured_tag['name']} is no longer featured", fg="green")
# -- Deprecated commands -------------------------------------------------------
@cli.command(name="tags_followed", hidden=True)

View File

@ -411,6 +411,10 @@ class Relationship:
@dataclass
class TagHistory:
"""
Usage statistics for given days (typically the past week).
https://docs.joinmastodon.org/entities/Tag/#history
"""
day: str
uses: str
accounts: str
@ -428,6 +432,19 @@ class Tag:
following: Optional[bool]
@dataclass
class FeaturedTag:
"""
Represents a hashtag that is featured on a profile.
https://docs.joinmastodon.org/entities/FeaturedTag/
"""
id: str
name: str
url: str
statuses_count: int
last_status_at: datetime
# Generic data class instance
T = TypeVar("T")

View File

@ -115,11 +115,8 @@ def print_acct_list(accounts):
def print_tag_list(tags):
if tags:
for tag in tags:
click.echo(f"* {format_tag_name(tag)}\t{tag['url']}")
else:
click.echo("You're not following any hashtags.")
for tag in tags:
click.echo(f"* {format_tag_name(tag)}\t{tag['url']}")
def print_lists(lists):