Add support for extensions
Some checks failed
Test and build Docker image / docker (push) Failing after 3s
Some checks failed
Test and build Docker image / docker (push) Failing after 3s
This commit is contained in:
parent
64a116d947
commit
793f67db42
39 changed files with 3670 additions and 1309 deletions
|
|
@ -202,7 +202,7 @@
|
|||
{% raw %}
|
||||
{{entry_text}}
|
||||
{% endraw %}
|
||||
</code> Same as entry_content if it exists, otherwise entry_summary
|
||||
</code>{{ entry.content[0].value|discord_markdown if entry and entry.content else (entry.summary|discord_markdown if entry and entry.summary else '') }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
|
|
@ -217,18 +217,22 @@
|
|||
{% raw %}
|
||||
{{image_1}}
|
||||
{% endraw %}
|
||||
</code>First image in the entry if it exists
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="list-inline">
|
||||
<li>Examples:</li>
|
||||
<li>
|
||||
<code>
|
||||
{% raw %}
|
||||
{{feed_title}}\n{{entry_content}}
|
||||
{% endraw %}
|
||||
</code>
|
||||
</code>{{ first_image }}
|
||||
</li>
|
||||
{% if extension_variables %}
|
||||
<br />
|
||||
<li>Extension variables (from enabled extensions):</li>
|
||||
{% for var_name in extension_variables %}
|
||||
<li>
|
||||
<code>{% raw %}{{{% endraw %}{{ var_name }}{% raw %}}}{% endraw %}</code>
|
||||
{% if var_name in extension_values and extension_values[var_name] %}
|
||||
{{ extension_values[var_name] }}
|
||||
{% else %}
|
||||
(empty)
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% else %}
|
||||
Something went wrong, there was no entry found. If this feed has entries and you still see this
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@
|
|||
{% raw %}
|
||||
{{entry_text}}
|
||||
{% endraw %}
|
||||
</code> Same as entry_content if it exists, otherwise entry_summary
|
||||
</code>{{ entry.content[0].value|discord_markdown if entry and entry.content else (entry.summary|discord_markdown if entry and entry.summary else '') }}
|
||||
</li>
|
||||
<li>
|
||||
<code>
|
||||
|
|
@ -210,13 +210,27 @@
|
|||
{% raw %}
|
||||
{{image_1}}
|
||||
{% endraw %}
|
||||
</code>First image in the entry if it exists
|
||||
</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
Something went wrong, there was no entry found. If this feed has entries and you still see this
|
||||
message, please contact the developer.
|
||||
{% endif %}
|
||||
</code>{{ first_image }}
|
||||
</li>
|
||||
{% if extension_variables %}
|
||||
<br />
|
||||
<li>Extension variables (from enabled extensions):</li>
|
||||
{% for var_name in extension_variables %}
|
||||
<li>
|
||||
<code>{% raw %}{{{% endraw %}{{ var_name }}{% raw %}}}{% endraw %}</code>
|
||||
{% if var_name in extension_values and extension_values[var_name] %}
|
||||
{{ extension_values[var_name] }}
|
||||
{% else %}
|
||||
(empty)
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% else %}
|
||||
Something went wrong, there was no entry found. If this feed has entries and you still see this
|
||||
message, please contact the developer.
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="form-text">
|
||||
<ul class="list-inline">
|
||||
|
|
@ -251,17 +265,16 @@
|
|||
<label for="thumbnail_url" class="col-sm-6 col-form-label">Thumbnail</label>
|
||||
<input name="thumbnail_url" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="thumbnail_url" {% if thumbnail_url %} value="{{- thumbnail_url -}}" {% endif %} />
|
||||
{% if is_steam_feed %}
|
||||
<div class="form-check mt-2">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="show_steam_game_icon_in_thumbnail"
|
||||
name="show_steam_game_icon_in_thumbnail"
|
||||
value="true"
|
||||
{% if show_steam_game_icon_in_thumbnail %}checked{% endif %}
|
||||
/>
|
||||
<label class="form-check-label" for="show_steam_game_icon_in_thumbnail"> Use Steam game icon as thumbnail when available </label>
|
||||
<input class="form-check-input" type="checkbox" name="show_steam_game_icon_in_thumbnail"
|
||||
id="show_steam_game_icon_in_thumbnail" value="true"
|
||||
{% if show_steam_game_icon_in_thumbnail %}checked{% endif %}>
|
||||
<label class="form-check-label" for="show_steam_game_icon_in_thumbnail">
|
||||
Use Steam game icon as thumbnail override
|
||||
</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
<label for="footer_text" class="col-sm-6 col-form-label">Footer text</label>
|
||||
<input name="footer_text" type="text" class="form-control bg-dark border-dark text-muted"
|
||||
id="footer_text" {% if footer_text %} value="{{- footer_text -}}" {% endif %} />
|
||||
|
|
|
|||
68
discord_rss_bot/templates/extensions.html
Normal file
68
discord_rss_bot/templates/extensions.html
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}
|
||||
Extensions: {{ feed.title if feed.title else feed.url }} | discord-rss-bot
|
||||
{% endblock title %}
|
||||
{% block description %}
|
||||
Enable or disable content extension plugins for {{ feed.title if feed.title else feed.url }}.
|
||||
{% endblock description %}
|
||||
{% block content %}
|
||||
<div class="p-2 border border-dark">
|
||||
<form action="/extensions" method="post">
|
||||
<div class="row pb-2">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-text">
|
||||
<ul class="list-inline">
|
||||
<li>Extensions extract additional variables from feed entries that you can use in your message template or embed.</li>
|
||||
<li>Each extension creates a template variable like <code>{% raw %}{{ extension_variable_name }}{% endraw %}</code>.</li>
|
||||
<li>Enable the ones you need and use their variables in the <a href="/custom?feed_url={{ feed.url|encode_url }}">message template</a> or <a href="/embed?feed_url={{ feed.url|encode_url }}">embed settings</a>.</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% if not discovered_extensions %}
|
||||
<div class="alert alert-info mt-3" role="alert">
|
||||
<p class="mb-0">
|
||||
No extension plugins were found.
|
||||
Place <code>.py</code> files containing <code>FeedExtension</code> subclasses
|
||||
in the extensions directory
|
||||
(<code>{{ extensions_dir }}</code>).
|
||||
</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="mt-3">
|
||||
<p class="text-muted small mb-2">
|
||||
Discovered extensions in <code>{{ extensions_dir }}</code>:
|
||||
</p>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for name, cls in discovered_extensions|dictsort %}
|
||||
<div class="list-group-item bg-transparent border-secondary text-light px-0">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input class="form-check-input"
|
||||
type="checkbox"
|
||||
id="ext-{{ name }}"
|
||||
name="enabled_extensions"
|
||||
value="{{ name }}"
|
||||
{% if name in enabled_extensions %}checked{% endif %} />
|
||||
<label class="form-check-label flex-grow-1" for="ext-{{ name }}">
|
||||
<code>{{ name }}</code>
|
||||
{% if cls.description %}
|
||||
<br />
|
||||
<span class="text-muted small">{{ cls.description }}</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% 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 mt-3">
|
||||
<button class="btn btn-dark btn-sm"
|
||||
{% if not discovered_extensions %}disabled{% endif %}>Save extensions</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
<div class="feed-page__content">
|
||||
<h2 class="h3 mb-1">
|
||||
<a class="text-muted text-decoration-none feed-page__wrap"
|
||||
href="{{ feed.url }}">{{ feed.title }}</a>
|
||||
href="{{ feed.url }}">{{ feed | feed_display_name }}</a>
|
||||
</h2>
|
||||
<p class="text-muted mb-0">{{ total_entries }} entries</p>
|
||||
</div>
|
||||
|
|
@ -30,18 +30,16 @@
|
|||
Text
|
||||
{% endif %}
|
||||
</span>
|
||||
{% if not "youtube.com/feeds/videos.xml" in feed.url %}
|
||||
<span class="badge status-chip bg-secondary">
|
||||
Images:
|
||||
{% if media_gallery_image_limit == 0 %}
|
||||
No images
|
||||
{% elif media_gallery_image_limit == 1 %}
|
||||
First image only
|
||||
{% else %}
|
||||
Up to {{ media_gallery_image_limit }} images
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="badge status-chip bg-secondary">
|
||||
Images:
|
||||
{% if media_gallery_image_limit == 0 %}
|
||||
No images
|
||||
{% elif media_gallery_image_limit == 1 %}
|
||||
First image only
|
||||
{% else %}
|
||||
Up to {{ media_gallery_image_limit }} images
|
||||
{% endif %}
|
||||
</span>
|
||||
{% if delivery_mode == "screenshot" %}
|
||||
<span class="badge status-chip bg-secondary">
|
||||
Screenshot layout:
|
||||
|
|
@ -177,10 +175,8 @@
|
|||
<br>
|
||||
Embed: a Discord embed with title, description, and images.
|
||||
<br>
|
||||
{% if not "youtube.com/feeds/videos.xml" in feed.url %}
|
||||
Screenshot: full-page screenshot of the entry link.
|
||||
<br>
|
||||
{% endif %}
|
||||
Screenshot: full-page screenshot of the entry link.
|
||||
<br>
|
||||
Text: plain message.
|
||||
</p>
|
||||
<div class="d-flex" role="group" aria-label="Delivery mode">
|
||||
|
|
@ -197,23 +193,21 @@
|
|||
style="border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0">Embed</span>
|
||||
{% endif %}
|
||||
{% if not "youtube.com/feeds/videos.xml" in feed.url %}
|
||||
{% if chromium_installed %}
|
||||
{% if delivery_mode != "screenshot" %}
|
||||
<form action="/use_screenshot" method="post" class="d-inline">
|
||||
<button class="btn btn-outline-light btn-sm rounded-0"
|
||||
name="feed_url"
|
||||
value="{{ feed.url }}">Screenshot</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<span class="btn btn-primary btn-sm disabled rounded-0">Screenshot</span>
|
||||
{% endif %}
|
||||
{% if chromium_installed %}
|
||||
{% if delivery_mode != "screenshot" %}
|
||||
<form action="/use_screenshot" method="post" class="d-inline">
|
||||
<button class="btn btn-outline-light btn-sm rounded-0"
|
||||
name="feed_url"
|
||||
value="{{ feed.url }}">Screenshot</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<span class="btn btn-outline-light btn-sm rounded-0"
|
||||
title="Playwright Chromium is not installed. Run 'uv run playwright install chromium' to enable."
|
||||
style="opacity: 0.5;
|
||||
cursor: not-allowed">Screenshot</span>
|
||||
<span class="btn btn-primary btn-sm disabled rounded-0">Screenshot</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="btn btn-outline-light btn-sm rounded-0"
|
||||
title="Playwright Chromium is not installed. Run 'uv run playwright install chromium' to enable."
|
||||
style="opacity: 0.5;
|
||||
cursor: not-allowed">Screenshot</span>
|
||||
{% endif %}
|
||||
{% if delivery_mode != "text" %}
|
||||
<form action="/use_text" method="post" class="d-inline">
|
||||
|
|
@ -229,14 +223,14 @@
|
|||
border-bottom-left-radius: 0">Text</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if not chromium_installed and not "youtube.com/feeds/videos.xml" in feed.url %}
|
||||
{% if not chromium_installed %}
|
||||
<div class="mt-2 small text-muted">
|
||||
Screenshot mode requires Chromium for Playwright.
|
||||
Run <code class="text-light">uv run playwright install chromium</code> to enable it.
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% if delivery_mode == "screenshot" and chromium_installed and not "youtube.com/feeds/videos.xml" in feed.url %}
|
||||
{% if delivery_mode == "screenshot" and chromium_installed %}
|
||||
<hr class="border-secondary" />
|
||||
<section class="mb-3">
|
||||
<h4 class="h6 text-muted mb-2">Screenshot Layout</h4>
|
||||
|
|
@ -259,54 +253,52 @@
|
|||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% if not "youtube.com/feeds/videos.xml" in feed.url %}
|
||||
<hr class="border-secondary" />
|
||||
<section class="mb-3">
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-2">
|
||||
<h4 class="h6 text-muted mb-0">Image Delivery</h4>
|
||||
<span class="badge {{ 'bg-info' if media_gallery_image_limit < max_media_gallery_items else 'bg-secondary' }}">
|
||||
{% if media_gallery_image_limit == 0 %}
|
||||
No images
|
||||
{% elif media_gallery_image_limit == 1 %}
|
||||
First image only
|
||||
{% else %}
|
||||
Up to {{ media_gallery_image_limit }} images
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<p id="imageDeliveryHelp" class="text-muted small mb-2">
|
||||
How many images to include per entry. Only applies to embed mode.
|
||||
</p>
|
||||
<form action="/set_feed_media_gallery_image_limit"
|
||||
method="post"
|
||||
class="mb-0">
|
||||
<input type="hidden" name="feed_url" value="{{ feed.url }}" />
|
||||
<label class="form-label small text-muted mb-2" for="image_limit">
|
||||
Images per entry:
|
||||
<output name="image_limit_value" for="image_limit">{{ media_gallery_image_limit }}</output>
|
||||
</label>
|
||||
<div class="d-flex flex-wrap align-items-center gap-3">
|
||||
<div class="flex-grow-1 image-limit-control">
|
||||
<input id="image_limit"
|
||||
class="form-range"
|
||||
type="range"
|
||||
name="image_limit"
|
||||
min="0"
|
||||
max="{{ max_media_gallery_items }}"
|
||||
step="1"
|
||||
value="{{ media_gallery_image_limit }}"
|
||||
aria-describedby="imageDeliveryHelp"
|
||||
oninput="this.form.elements.image_limit_value.value = this.value" />
|
||||
<div class="d-flex justify-content-between text-muted small">
|
||||
<span>0</span>
|
||||
<span>{{ max_media_gallery_items }}</span>
|
||||
</div>
|
||||
<hr class="border-secondary" />
|
||||
<section class="mb-3">
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-2">
|
||||
<h4 class="h6 text-muted mb-0">Image Delivery</h4>
|
||||
<span class="badge {{ 'bg-info' if media_gallery_image_limit < max_media_gallery_items else 'bg-secondary' }}">
|
||||
{% if media_gallery_image_limit == 0 %}
|
||||
No images
|
||||
{% elif media_gallery_image_limit == 1 %}
|
||||
First image only
|
||||
{% else %}
|
||||
Up to {{ media_gallery_image_limit }} images
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<p id="imageDeliveryHelp" class="text-muted small mb-2">
|
||||
How many images to include per entry. Only applies to embed mode.
|
||||
</p>
|
||||
<form action="/set_feed_media_gallery_image_limit"
|
||||
method="post"
|
||||
class="mb-0">
|
||||
<input type="hidden" name="feed_url" value="{{ feed.url }}" />
|
||||
<label class="form-label small text-muted mb-2" for="image_limit">
|
||||
Images per entry:
|
||||
<output name="image_limit_value" for="image_limit">{{ media_gallery_image_limit }}</output>
|
||||
</label>
|
||||
<div class="d-flex flex-wrap align-items-center gap-3">
|
||||
<div class="flex-grow-1 image-limit-control">
|
||||
<input id="image_limit"
|
||||
class="form-range"
|
||||
type="range"
|
||||
name="image_limit"
|
||||
min="0"
|
||||
max="{{ max_media_gallery_items }}"
|
||||
step="1"
|
||||
value="{{ media_gallery_image_limit }}"
|
||||
aria-describedby="imageDeliveryHelp"
|
||||
oninput="this.form.elements.image_limit_value.value = this.value" />
|
||||
<div class="d-flex justify-content-between text-muted small">
|
||||
<span>0</span>
|
||||
<span>{{ max_media_gallery_items }}</span>
|
||||
</div>
|
||||
<button class="btn btn-outline-light btn-sm" type="submit">Save image limit</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
{% endif %}
|
||||
<button class="btn btn-outline-light btn-sm" type="submit">Save image limit</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<hr class="border-secondary" />
|
||||
<section>
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-2">
|
||||
|
|
@ -489,15 +481,12 @@
|
|||
<div class="btn-group btn-group-sm" role="group">
|
||||
<a class="btn {{ 'btn-primary' if delivery_mode == 'text' else 'btn-outline-light' }}"
|
||||
href="/custom?feed_url={{ feed.url|encode_url }}">Customize message</a>
|
||||
{% if not "youtube.com/feeds/videos.xml" in feed.url %}
|
||||
<a class="btn {{ 'btn-primary' if delivery_mode == 'embed' else 'btn-outline-light' }}"
|
||||
href="/embed?feed_url={{ feed.url|encode_url }}">Customize embed</a>
|
||||
{% endif %}
|
||||
<a class="btn {{ 'btn-primary' if delivery_mode == 'embed' else 'btn-outline-light' }}"
|
||||
href="/embed?feed_url={{ feed.url|encode_url }}">Customize embed</a>
|
||||
</div>
|
||||
<a class="btn btn-sm btn-outline-light"
|
||||
href="/extensions?feed_url={{ feed.url|encode_url }}">Extensions</a>
|
||||
</div>
|
||||
{% if is_steam_feed %}
|
||||
<div class="form-text small mt-2">Steam feeds can enable thumbnails in embed settings.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -73,11 +73,7 @@
|
|||
{% for feed in domain_feeds %}
|
||||
<li>
|
||||
<a class="text-muted" href="/feed?feed_url={{ feed.url|encode_url }}">
|
||||
{% if feed.title %}
|
||||
{{ feed.title }}
|
||||
{% else %}
|
||||
{{ feed.url }}
|
||||
{% endif %}
|
||||
{{ feed | feed_display_name }}
|
||||
</a>
|
||||
{% if not feed.updates_enabled %}<span class="text-warning">Disabled</span>{% endif %}
|
||||
{% if feed.last_exception %}<span class="text-danger">({{ feed.last_exception.value_str }})</span>{% endif %}
|
||||
|
|
@ -116,18 +112,7 @@
|
|||
{% for broken_feed in broken_feeds %}
|
||||
<a class="text-muted"
|
||||
href="/feed?feed_url={{ broken_feed.url|encode_url }}">
|
||||
{# Display username@youtube for YouTube feeds #}
|
||||
{% if "youtube.com/feeds/videos.xml" in broken_feed.url %}
|
||||
{% if "user=" in broken_feed.url %}
|
||||
{{ broken_feed.url.split("user=")[1] }}@youtube
|
||||
{% elif "channel_id=" in broken_feed.url %}
|
||||
{{ broken_feed.title if broken_feed.title else broken_feed.url.split("channel_id=")[1] }}@youtube
|
||||
{% else %}
|
||||
{{ broken_feed.url }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ broken_feed.url }}
|
||||
{% endif %}
|
||||
{{ broken_feed | feed_display_name }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
@ -142,18 +127,7 @@
|
|||
<li class="list-group-item bg-dark border-dark text-danger">
|
||||
<div class="d-flex flex-wrap align-items-center gap-2">
|
||||
<a class="text-muted" href="/feed?feed_url={{ feed.url|encode_url }}">
|
||||
{# Display username@youtube for YouTube feeds #}
|
||||
{% if "youtube.com/feeds/videos.xml" in feed.url %}
|
||||
{% if "user=" in feed.url %}
|
||||
{{ 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 %}
|
||||
{{ feed | feed_display_name }}
|
||||
</a>
|
||||
{% if webhooks %}
|
||||
<form action="/attach_feed_webhook"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue