Add functionality to attach feeds to webhooks from the index page
This commit is contained in:
parent
9b685e4980
commit
b025d5b136
3 changed files with 133 additions and 12 deletions
|
|
@ -308,6 +308,52 @@ async def post_create_feed(
|
||||||
return RedirectResponse(url=f"/feed?feed_url={urllib.parse.quote(clean_feed_url)}", status_code=303)
|
return RedirectResponse(url=f"/feed?feed_url={urllib.parse.quote(clean_feed_url)}", status_code=303)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/attach_feed_webhook")
|
||||||
|
async def post_attach_feed_webhook(
|
||||||
|
feed_url: Annotated[str, Form()],
|
||||||
|
webhook_dropdown: Annotated[str, Form()],
|
||||||
|
reader: Annotated[Reader, Depends(get_reader_dependency)],
|
||||||
|
redirect_to: Annotated[str, Form()] = "",
|
||||||
|
) -> RedirectResponse:
|
||||||
|
"""Attach an existing feed to one of the configured webhooks.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
feed_url: The feed URL to update.
|
||||||
|
webhook_dropdown: The webhook name selected from the dropdown.
|
||||||
|
reader: The Reader instance.
|
||||||
|
redirect_to: Optional redirect URL after update.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
RedirectResponse: Redirect to index or feed page.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
HTTPException: If feed or webhook cannot be found.
|
||||||
|
"""
|
||||||
|
clean_feed_url: str = urllib.parse.unquote(feed_url.strip())
|
||||||
|
selected_webhook_name: str = webhook_dropdown.strip()
|
||||||
|
|
||||||
|
try:
|
||||||
|
reader.get_feed(clean_feed_url)
|
||||||
|
except FeedNotFoundError as e:
|
||||||
|
raise HTTPException(status_code=404, detail="Feed not found") from e
|
||||||
|
|
||||||
|
webhook_url: str = ""
|
||||||
|
hooks = cast("list[dict[str, str]]", list(reader.get_tag((), "webhooks", [])))
|
||||||
|
for hook in hooks:
|
||||||
|
if hook.get("name") == selected_webhook_name:
|
||||||
|
webhook_url = hook.get("url", "").strip()
|
||||||
|
break
|
||||||
|
|
||||||
|
if not webhook_url:
|
||||||
|
raise HTTPException(status_code=404, detail="Webhook not found")
|
||||||
|
|
||||||
|
reader.set_tag(clean_feed_url, "webhook", webhook_url) # pyright: ignore[reportArgumentType]
|
||||||
|
commit_state_change(reader, f"Attach feed {clean_feed_url} to webhook {selected_webhook_name}")
|
||||||
|
|
||||||
|
redirect_url: str = redirect_to.strip() or f"/feed?feed_url={urllib.parse.quote(clean_feed_url)}"
|
||||||
|
return RedirectResponse(url=redirect_url, status_code=303)
|
||||||
|
|
||||||
|
|
||||||
@app.post("/pause")
|
@app.post("/pause")
|
||||||
async def post_pause_feed(
|
async def post_pause_feed(
|
||||||
feed_url: Annotated[str, Form()],
|
feed_url: Annotated[str, Form()],
|
||||||
|
|
|
||||||
|
|
@ -133,20 +133,41 @@
|
||||||
<ul class="list-group text-danger">
|
<ul class="list-group text-danger">
|
||||||
Feeds without attached webhook:
|
Feeds without attached webhook:
|
||||||
{% for feed in feeds_without_attached_webhook %}
|
{% for feed in feeds_without_attached_webhook %}
|
||||||
<a class="text-muted" href="/feed?feed_url={{ feed.url|encode_url }}">
|
<li class="list-group-item bg-dark border-dark text-danger">
|
||||||
{# Display username@youtube for YouTube feeds #}
|
<div class="d-flex flex-wrap align-items-center gap-2">
|
||||||
{% if "youtube.com/feeds/videos.xml" in feed.url %}
|
<a class="text-muted" href="/feed?feed_url={{ feed.url|encode_url }}">
|
||||||
{% if "user=" in feed.url %}
|
{# Display username@youtube for YouTube feeds #}
|
||||||
{{ feed.url.split("user=")[1] }}@youtube
|
{% if "youtube.com/feeds/videos.xml" in feed.url %}
|
||||||
{% elif "channel_id=" in feed.url %}
|
{% if "user=" in feed.url %}
|
||||||
{{ feed.title if feed.title else feed.url.split("channel_id=")[1] }}@youtube
|
{{ feed.url.split("user=")[1] }}@youtube
|
||||||
|
{% elif "channel_id=" in feed.url %}
|
||||||
|
{{ feed.title if feed.title else feed.url.split("channel_id=")[1] }}@youtube
|
||||||
|
{% else %}
|
||||||
|
{{ feed.url }}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{{ feed.url }}
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
{% if webhooks %}
|
||||||
|
<form action="/attach_feed_webhook"
|
||||||
|
method="post"
|
||||||
|
class="d-flex flex-wrap align-items-center gap-2 ms-md-auto">
|
||||||
|
<input type="hidden" name="feed_url" value="{{ feed.url }}" />
|
||||||
|
<input type="hidden" name="redirect_to" value="/" />
|
||||||
|
<select name="webhook_dropdown"
|
||||||
|
class="form-select form-select-sm bg-dark border-dark text-muted"
|
||||||
|
required>
|
||||||
|
<option value="" selected disabled>Select webhook...</option>
|
||||||
|
{% for hook in webhooks %}<option value="{{ hook.name }}">{{ hook.name }}</option>{% endfor %}
|
||||||
|
</select>
|
||||||
|
<button class="btn btn-outline-light btn-sm" type="submit">Attach</button>
|
||||||
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ feed.url }}
|
<span class="text-muted small">Add a webhook first to attach this feed.</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
</div>
|
||||||
{{ feed.url }}
|
</li>
|
||||||
{% endif %}
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -559,6 +559,60 @@ def test_delete_webhook() -> None:
|
||||||
assert webhook_name not in response3.text, f"Webhook found in /webhooks: {response3.text}"
|
assert webhook_name not in response3.text, f"Webhook found in /webhooks: {response3.text}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_attach_feed_webhook_from_index() -> None:
|
||||||
|
"""Feeds without attached webhook should be attachable from the index page."""
|
||||||
|
original_webhook_name = "original-webhook"
|
||||||
|
original_webhook_url = "https://discord.com/api/webhooks/111/original"
|
||||||
|
replacement_webhook_name = "replacement-webhook"
|
||||||
|
replacement_webhook_url = "https://discord.com/api/webhooks/222/replacement"
|
||||||
|
|
||||||
|
# Start clean.
|
||||||
|
client.post(url="/remove", data={"feed_url": feed_url})
|
||||||
|
client.post(url="/delete_webhook", data={"webhook_url": original_webhook_url})
|
||||||
|
client.post(url="/delete_webhook", data={"webhook_url": replacement_webhook_url})
|
||||||
|
|
||||||
|
# Add a webhook and a feed attached to it.
|
||||||
|
response = client.post(
|
||||||
|
url="/add_webhook",
|
||||||
|
data={"webhook_name": original_webhook_name, "webhook_url": original_webhook_url},
|
||||||
|
)
|
||||||
|
assert response.status_code == 200, f"Failed to add original webhook: {response.text}"
|
||||||
|
|
||||||
|
response = client.post(url="/add", data={"feed_url": feed_url, "webhook_dropdown": original_webhook_name})
|
||||||
|
assert response.status_code == 200, f"Failed to add feed: {response.text}"
|
||||||
|
|
||||||
|
# Remove the original webhook so feed becomes "without attached webhook".
|
||||||
|
response = client.post(url="/delete_webhook", data={"webhook_url": original_webhook_url})
|
||||||
|
assert response.status_code == 200, f"Failed to delete original webhook: {response.text}"
|
||||||
|
|
||||||
|
# Add a replacement webhook we can attach to.
|
||||||
|
response = client.post(
|
||||||
|
url="/add_webhook",
|
||||||
|
data={"webhook_name": replacement_webhook_name, "webhook_url": replacement_webhook_url},
|
||||||
|
)
|
||||||
|
assert response.status_code == 200, f"Failed to add replacement webhook: {response.text}"
|
||||||
|
|
||||||
|
# The feed should now be listed in "Feeds without attached webhook" section.
|
||||||
|
response = client.get(url="/")
|
||||||
|
assert response.status_code == 200, f"Failed to get /: {response.text}"
|
||||||
|
assert "Feeds without attached webhook:" in response.text
|
||||||
|
assert "/attach_feed_webhook" in response.text
|
||||||
|
|
||||||
|
# Attach the feed to the new webhook.
|
||||||
|
response = client.post(
|
||||||
|
url="/attach_feed_webhook",
|
||||||
|
data={"feed_url": feed_url, "webhook_dropdown": replacement_webhook_name, "redirect_to": "/"},
|
||||||
|
)
|
||||||
|
assert response.status_code == 200, f"Failed to attach feed to webhook: {response.text}"
|
||||||
|
|
||||||
|
reader = get_reader_dependency()
|
||||||
|
assert reader.get_tag(feed_url, "webhook", "") == replacement_webhook_url
|
||||||
|
|
||||||
|
# Cleanup.
|
||||||
|
client.post(url="/remove", data={"feed_url": feed_url})
|
||||||
|
client.post(url="/delete_webhook", data={"webhook_url": replacement_webhook_url})
|
||||||
|
|
||||||
|
|
||||||
def test_update_feed_not_found() -> None:
|
def test_update_feed_not_found() -> None:
|
||||||
"""Test updating a non-existent feed."""
|
"""Test updating a non-existent feed."""
|
||||||
# Generate a feed URL that does not exist
|
# Generate a feed URL that does not exist
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue