diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..7e0c165 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,37 @@ +name: Build and Push Docker Image + +on: + push: + tags: + - 'v*' + +env: + REGISTRY: git.out.jafre.li + IMAGE_NAME: jafreli/updater + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Login to Gitea Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ var.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Extract version from tag + id: version + run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest diff --git a/src/bot.py b/src/bot.py index 4d38cfa..59e046a 100644 --- a/src/bot.py +++ b/src/bot.py @@ -2,6 +2,8 @@ import asyncio import logging +import re +from html import unescape import discord from discord import app_commands @@ -12,6 +14,28 @@ from .feed_checker import check_feeds, initialize_feed logger = logging.getLogger(__name__) +# Regex to strip HTML tags +_HTML_TAG_RE = re.compile(r"<[^>]+>") + + +def clean_html(text: str) -> str: + """Strip HTML tags and decode HTML entities.""" + text = _HTML_TAG_RE.sub("", text) + return unescape(text).strip() + + +def _build_embed(title: str, link: str, feed_url: str) -> discord.Embed: + """Build a nice Discord embed for an RSS entry.""" + title = clean_html(title) + + embed = discord.Embed( + title=title, + url=link if link else None, + color=0x2B82D4, + ) + embed.set_footer(text=f"📰 {feed_url}") + return embed + class RSSBot(discord.Client): """Discord client with RSS feed monitoring.""" @@ -138,18 +162,13 @@ class RSSBot(discord.Client): logger.exception("Could not fetch target channel") return - title = entry["title"] - link = entry["link"] - if link: - message = f"📢 **{title}**\n{link}" - else: - message = f"📢 **{title}**" + embed = _build_embed(entry["title"], entry["link"], url) try: - await channel.send(message) - logger.info("Posted newest entry from new feed: %s", title) + await channel.send(embed=embed) + logger.info("Posted newest entry from new feed: %s", entry["title"]) except Exception: - logger.exception("Failed to post entry: %s", title) + logger.exception("Failed to post entry: %s", entry["title"]) async def _check_and_post(self) -> None: """Check feeds and post new entries to the target channel.""" @@ -164,19 +183,13 @@ class RSSBot(discord.Client): new_entries = check_feeds() for entry in new_entries: - title = entry["title"] - link = entry["link"] - - if link: - message = f"📢 **{title}**\n{link}" - else: - message = f"📢 **{title}**" + embed = _build_embed(entry["title"], entry["link"], entry["feed_url"]) try: - await channel.send(message) - logger.info("Posted: %s", title) + await channel.send(embed=embed) + logger.info("Posted: %s", entry["title"]) except Exception: - logger.exception("Failed to post entry: %s", title) + logger.exception("Failed to post entry: %s", entry["title"]) @tasks.loop(seconds=300) # default, overridden in on_ready async def check_loop(self) -> None: