feat: Implement a Discord bot for RSS feed checking with slash commands and add a Gitea Actions workflow for Docker image build and push.
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 16s
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 16s
This commit is contained in:
37
.gitea/workflows/build.yml
Normal file
37
.gitea/workflows/build.yml
Normal file
@@ -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
|
||||||
51
src/bot.py
51
src/bot.py
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
from html import unescape
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
@@ -12,6 +14,28 @@ from .feed_checker import check_feeds, initialize_feed
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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):
|
class RSSBot(discord.Client):
|
||||||
"""Discord client with RSS feed monitoring."""
|
"""Discord client with RSS feed monitoring."""
|
||||||
@@ -138,18 +162,13 @@ class RSSBot(discord.Client):
|
|||||||
logger.exception("Could not fetch target channel")
|
logger.exception("Could not fetch target channel")
|
||||||
return
|
return
|
||||||
|
|
||||||
title = entry["title"]
|
embed = _build_embed(entry["title"], entry["link"], url)
|
||||||
link = entry["link"]
|
|
||||||
if link:
|
|
||||||
message = f"📢 **{title}**\n{link}"
|
|
||||||
else:
|
|
||||||
message = f"📢 **{title}**"
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await channel.send(message)
|
await channel.send(embed=embed)
|
||||||
logger.info("Posted newest entry from new feed: %s", title)
|
logger.info("Posted newest entry from new feed: %s", entry["title"])
|
||||||
except Exception:
|
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:
|
async def _check_and_post(self) -> None:
|
||||||
"""Check feeds and post new entries to the target channel."""
|
"""Check feeds and post new entries to the target channel."""
|
||||||
@@ -164,19 +183,13 @@ class RSSBot(discord.Client):
|
|||||||
new_entries = check_feeds()
|
new_entries = check_feeds()
|
||||||
|
|
||||||
for entry in new_entries:
|
for entry in new_entries:
|
||||||
title = entry["title"]
|
embed = _build_embed(entry["title"], entry["link"], entry["feed_url"])
|
||||||
link = entry["link"]
|
|
||||||
|
|
||||||
if link:
|
|
||||||
message = f"📢 **{title}**\n{link}"
|
|
||||||
else:
|
|
||||||
message = f"📢 **{title}**"
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await channel.send(message)
|
await channel.send(embed=embed)
|
||||||
logger.info("Posted: %s", title)
|
logger.info("Posted: %s", entry["title"])
|
||||||
except Exception:
|
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
|
@tasks.loop(seconds=300) # default, overridden in on_ready
|
||||||
async def check_loop(self) -> None:
|
async def check_loop(self) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user