diff --git a/discord_rss_bot/custom_message.py b/discord_rss_bot/custom_message.py new file mode 100644 index 0000000..c8f1441 --- /dev/null +++ b/discord_rss_bot/custom_message.py @@ -0,0 +1,95 @@ +from reader import Entry, Feed, Reader, TagNotFoundError + +from discord_rss_bot.settings import get_reader + + +def try_to_replace(custom_message: str, template: str, replace_with: str) -> str: + """Try to replace a tag in custom_message. + + Args: + custom_message: The custom_message to replace tags in. + feed: The feed to get the tags from. + entry: The entry to get the tags from. + tag: The tag to replace. + + Returns: + Returns the custom_message with the tag replaced. + """ + if not template: + return custom_message + if not replace_with: + return custom_message + try: + print(f"custom_message: {custom_message}") + print(f"template: {template}") + print(f"replace_with: {replace_with}") + return custom_message.replace(template, replace_with) + except TypeError: + return custom_message + + +def replace_tags(feed: Feed, entry: Entry) -> str: + """Replace tags in custom_message. + + Args: + feed: The feed to get the tags from. + entry: The entry to get the tags from. + + Returns: + Returns the custom_message with the tags replaced. + """ + custom_reader: Reader = get_reader() + custom_message: str = get_custom_message(feed=feed, custom_reader=custom_reader) + + list_of_replacements = [ + {"{{feed_author}}": feed.author}, + {"{{feed_added}}": feed.added}, + {"{{feed_last_exception}}": feed.last_exception}, + {"{{feed_last_updated}}": feed.last_updated}, + {"{{feed_link}}": feed.link}, + {"{{feed_subtitle}}": feed.subtitle}, + {"{{feed_title}}": feed.title}, + {"{{feed_updated}}": feed.updated}, + {"{{feed_updates_enabled}}": str(feed.updates_enabled)}, + {"{{feed_url}}": feed.url}, + {"{{feed_user_title}}": feed.user_title}, + {"{{feed_version}}": feed.version}, + {"{{entry_added}}": entry.added}, + {"{{entry_author}}": entry.author}, + {"{{entry_content}}": entry.content}, + {"{{entry_id}}": entry.id}, + {"{{entry_important}}": str(entry.important)}, + {"{{entry_link}}": entry.link}, + {"{{entry_published}}": entry.published}, + {"{{entry_read}}": str(entry.read)}, + {"{{entry_read_modified}}": entry.read_modified}, + {"{{entry_summary}}": entry.summary}, + {"{{entry_title}}": entry.title}, + {"{{entry_updated}}": entry.updated}, + ] + + for replacement in list_of_replacements: + for template, replace_with in replacement.items(): + custom_message: str = try_to_replace(custom_message, template, replace_with) + + print(f"custom_message: {custom_message}") + return custom_message + + +def get_custom_message(custom_reader: Reader, feed: Feed) -> str: + """Get custom_message tag from feed. + + Args: + custom_reader: What Reader to use. + feed: The feed to get the tag from. + + Returns: + Returns the contents from the custom_message tag. + """ + try: + custom_message: str = custom_reader.get_tag(feed, "custom_message") # type: ignore + except TagNotFoundError: + custom_message: str = "" + except ValueError: + custom_message: str = "" + return custom_message diff --git a/discord_rss_bot/feeds.py b/discord_rss_bot/feeds.py index c22cf56..e789772 100644 --- a/discord_rss_bot/feeds.py +++ b/discord_rss_bot/feeds.py @@ -4,10 +4,10 @@ from discord_webhook import DiscordWebhook from reader import Entry, Feed, Reader from requests import Response -from discord_rss_bot import settings +from discord_rss_bot import custom_message, settings from discord_rss_bot.filter.blacklist import should_be_skipped -from discord_rss_bot.settings import get_reader from discord_rss_bot.filter.whitelist import has_white_tags, should_be_sent +from discord_rss_bot.settings import get_reader def send_to_discord(custom_reader: Reader | None = None, feed: Feed | None = None, do_once: bool = False) -> None: @@ -50,6 +50,11 @@ def send_to_discord(custom_reader: Reader | None = None, feed: Feed | None = Non webhook: DiscordWebhook = DiscordWebhook(url=webhook_url, content=webhook_message, rate_limit_retry=True) + if custom_message.get_custom_message(reader, entry.feed) != "": + print("Custom message found, replacing tags.") + webhook.content = custom_message.replace_tags(entry=entry, feed=entry.feed) + + print(f"Webhook content: {webhook.content}") if feed is not None and has_white_tags(reader, feed): # Only send the entry if it is whitelisted, otherwise, mark it as read and continue. if should_be_sent(reader, entry): diff --git a/discord_rss_bot/main.py b/discord_rss_bot/main.py index f8663c5..c351501 100644 --- a/discord_rss_bot/main.py +++ b/discord_rss_bot/main.py @@ -12,6 +12,7 @@ from reader import Entry, EntryCounts, EntrySearchCounts, EntrySearchResult, Fee from starlette.responses import RedirectResponse from discord_rss_bot.custom_filters import encode_url, entry_is_blacklisted, entry_is_whitelisted +from discord_rss_bot.custom_message import get_custom_message from discord_rss_bot.feeds import send_to_discord from discord_rss_bot.filter.blacklist import get_blacklist_content, get_blacklist_summary, get_blacklist_title from discord_rss_bot.filter.whitelist import get_whitelist_content, get_whitelist_summary, get_whitelist_title @@ -115,6 +116,8 @@ async def create_feed(feed_url: str = Form(), webhook_dropdown: str = Form()): """ clean_feed_url: str = feed_url.strip() + # TODO: Check if the feed is valid, if not return an error or fix it. + # For example, if the feed is missing the protocol, add it. reader.add_feed(clean_feed_url) reader.update_feed(clean_feed_url) @@ -304,6 +307,62 @@ async def get_blacklist(feed_url: str, request: Request): return templates.TemplateResponse("blacklist.html", context) # type: ignore +@app.post("/custom") +async def set_custom(custom_message: str = Form(""), feed_url: str = Form()): + """ + Set the custom message, this is used when sending the message. + + Args: + custom_message: The custom message. + feed_url: The feed we should set the custom message for. + + Returns: + Redirect to the feed. + """ + + # Add the custom_message to the feed. + if custom_message: + reader.set_tag(feed_url, "custom_message", custom_message) + print(f"Set custom message for {feed_url} to {custom_message}") + else: + print(f"Removing custom message for {feed_url}") + reader.delete_tag(feed_url, "custom_message", missing_ok=True) + + # Clean URL is used to redirect to the feed page. + clean_url: str = urllib.parse.quote(feed_url) + + return RedirectResponse(url=f"/feed/?feed_url={clean_url}", status_code=303) + + +@app.get("/custom", response_class=HTMLResponse) +async def get_custom(feed_url: str, request: Request): + """Get the custom message. This is used when sending the message to Discord. + + Args: + feed_url: What feed we should get the custom message for. + request: The HTTP request. + + Returns: + custom.html + """ + + # Make feed_url a valid URL. + url: str = urllib.parse.unquote(feed_url) + + feed: Feed = reader.get_feed(url) + + # Get previous data, this is used when creating the form. + custom_message: str = get_custom_message(reader, feed) + + # Get the first entry, this is used to show the user what the custom message will look like. + first_entry: Entry = reader.get_entries(feed=feed, limit=1) + for entry in first_entry: + first_entry = entry + + context = {"request": request, "feed": feed, "custom_message": custom_message, "entry": first_entry} + return templates.TemplateResponse("custom.html", context) # type: ignore + + @app.get("/add", response_class=HTMLResponse) def get_add(request: Request): """ diff --git a/discord_rss_bot/templates/custom.html b/discord_rss_bot/templates/custom.html new file mode 100644 index 0000000..e88d5a8 --- /dev/null +++ b/discord_rss_bot/templates/custom.html @@ -0,0 +1,70 @@ +{% extends "base.html" %}( +{% block title %} | Custom message{% endblock %} +{% block content %} + <div class="p-2 border border-dark"> + <form action="/custom" method="post"> + <!-- Feed URL --> + <div class="row pb-2"> + + <div class="col-sm-12"> + <div class="form-text"> + <ul class="list-inline"> + <li>You can modify the message that is sent to Discord.</li> + <br> + <li><code>{% raw %}{{feed_url}}{% endraw %}</code> will be replaced with the feed URL. You can use <code>\n</code> for new lines.</li> + <br> + <li><code>{% raw %}{{feed_author}}{% endraw %}</code> - {{feed.author}}</li> + <li><code>{% raw %}{{feed_added}}{% endraw %}</code> - {{feed.added}}</li> + <li><code>{% raw %}{{feed_last_exception}}{% endraw %}</code> - {{feed.last_exception}}</li> + <li><code>{% raw %}{{feed_last_updated}}{% endraw %}</code> - {{feed.last_updated}}</li> + <li><code>{% raw %}{{feed_link}}{% endraw %}</code> - {{feed.link}}</li> + <li><code>{% raw %}{{feed_subtitle}}{% endraw %}</code> - {{feed.subtitle}}</li> + <li><code>{% raw %}{{feed_title}}{% endraw %}</code> - {{feed.title}}</li> + <li><code>{% raw %}{{feed_updated}}{% endraw %}</code> - {{feed.updated}}</li> + <li><code>{% raw %}{{feed_updates_enabled}}{% endraw %}</code> - {{feed.updates_enabled}}</li> + <li><code>{% raw %}{{feed_url}}{% endraw %}</code> - {{feed.url}}</li> + <li><code>{% raw %}{{feed_user_title}}{% endraw %}</code> - {{feed.user_title}}</li> + <li><code>{% raw %}{{feed_version}}{% endraw %}</code> - {{feed.version}}</li> + <br> + <li><code>{% raw %}{{entry_added}}{% endraw %}</code> - {{entry.added}}</li> + <li><code>{% raw %}{{entry_author}}{% endraw %}</code> - {{entry.author}}</li> + <li><code>{% raw %}{{entry_content}}{% endraw %}</code> - {{entry.content[0].value}}</li> + <li><code>{% raw %}{{entry_id}}{% endraw %}</code> - {{entry.id}}</li> + <li><code>{% raw %}{{entry_important}}{% endraw %}</code> - {{entry.important}}</li> + <li><code>{% raw %}{{entry_link}}{% endraw %}</code> - {{entry.link}}</li> + <li><code>{% raw %}{{entry_published}}{% endraw %}</code> - {{entry.published}}</li> + <li><code>{% raw %}{{entry_read}}{% endraw %}</code> - {{entry.read}}</li> + <li><code>{% raw %}{{entry_read_modified}}{% endraw %}</code> - {{entry.read_modified}}</li> + <li><code>{% raw %}{{entry_summary}}{% endraw %}</code> - {{entry.summary}}</li> + <li><code>{% raw %}{{entry_title}}{% endraw %}</code> - {{entry.title}}</li> + <li><code>{% raw %}{{entry_updated}}{% endraw %}</code> - {{entry.updated}}</li> + </ul> + <ul class="list-inline"> + <li>Examples:</li> + <li><code>{% raw %}Hello {{entry_author}}\n{{feed_title}}\n{{entry_read}}{% endraw %}</code></li> + <br> + <li>Will become:</li> + <li><code style="white-space: pre-line"> + Hello {{entry.author}} + {{feed.title}} + {{entry.read}} + </code></li> + </ul> + </div> + + <label for="custom_message" class="col-sm-6 col-form-label">Message</label> + <input name="custom_message" type="text" class="form-control bg-dark border-dark text-muted" + id="custom_message" value="{% if custom_message %}{{ custom_message }}{% endif %}"> + </div> + </div> + + <!-- Add a hidden feed_url field to the form --> + <input type="hidden" name="feed_url" value="{{ feed.url }}"> + + <!-- Submit button --> + <div class="d-md-flex"> + <button class="btn btn-dark btn-sm">Update message</button> + </div> + </form> + </div> +{% endblock %} \ No newline at end of file diff --git a/discord_rss_bot/templates/feed.html b/discord_rss_bot/templates/feed.html index f5fee11..48efc1f 100644 --- a/discord_rss_bot/templates/feed.html +++ b/discord_rss_bot/templates/feed.html @@ -26,6 +26,7 @@ {% endif %} <a class="text-muted" href="/whitelist?feed_url={{ feed.url|encode_url }}">Whitelist</a> <a class="text-muted" href="/blacklist?feed_url={{ feed.url|encode_url }}">Blacklist</a> + <a class="text-muted" href="/custom?feed_url={{ feed.url|encode_url }}">Custom message</a> </div> {% for entry in entries %} @@ -46,7 +47,8 @@ {% if entry.published %} @ {{ entry.published.strftime('%Y-%m-%d, %T') }} {% endif %} - + + {# TODO: Only show one if both are the same #} {% if entry.summary %} <details> <summary>Summary</summary>