mirror of
https://github.com/maubot/rss.git
synced 2025-03-14 19:16:35 -04:00
allow to filter entries by title per subscription
Signed-off-by: Sebastian Hoß <seb@xn--ho-hia.de>
This commit is contained in:
parent
81ec8ed864
commit
05687597aa
40
rss/bot.py
40
rss/bot.py
@ -23,6 +23,7 @@ import asyncio
|
||||
import hashlib
|
||||
import html
|
||||
import io
|
||||
import re
|
||||
|
||||
import aiohttp
|
||||
import attr
|
||||
@ -106,7 +107,12 @@ class RSSBot(Plugin):
|
||||
except Exception:
|
||||
self.log.exception("Fatal error while polling feeds")
|
||||
|
||||
async def _send(self, feed: Feed, entry: Entry, sub: Subscription) -> EventID:
|
||||
async def _send(self, feed: Feed, entry: Entry, sub: Subscription) -> EventID | None:
|
||||
title_exclude_filter = sub.title_exclude_filter
|
||||
if title_exclude_filter:
|
||||
if re.search(title_exclude_filter, entry.title):
|
||||
return None
|
||||
|
||||
message = sub.notification_template.safe_substitute(
|
||||
{
|
||||
"feed_url": feed.url,
|
||||
@ -368,6 +374,31 @@ class RSSBot(Plugin):
|
||||
)
|
||||
await evt.reply(f"Subscribed to {feed_info}")
|
||||
|
||||
@rss.subcommand(
|
||||
"set-filter", aliases=("f", "filter"), help="Set title exclude filter for a specific feed."
|
||||
)
|
||||
@command.argument("feed_id", "feed ID", parser=int)
|
||||
@command.argument("title_exclude_filter", "title exclude filter", pass_raw=True)
|
||||
async def set_filter(self, evt: MessageEvent, feed_id: int, title_exclude_filter: str) -> None:
|
||||
if not await self.can_manage(evt):
|
||||
return
|
||||
sub, feed = await self.dbm.get_subscription(feed_id, evt.room_id)
|
||||
if not sub:
|
||||
await evt.reply("This room is not subscribed to that feed")
|
||||
return
|
||||
try:
|
||||
re.compile(title_exclude_filter)
|
||||
except re.error:
|
||||
await evt.reply(f"Filter is not a valid regular expression")
|
||||
return
|
||||
await self.dbm.update_title_filter(feed.id, evt.room_id, title_exclude_filter)
|
||||
if title_exclude_filter:
|
||||
await evt.reply(
|
||||
f"Feed {feed_id} will now exclude titles matching: {title_exclude_filter}"
|
||||
)
|
||||
else:
|
||||
await evt.reply(f"Removed title exclude filter from feed {feed_id}")
|
||||
|
||||
@rss.subcommand(
|
||||
"unsubscribe", aliases=("u", "unsub"), help="Unsubscribe this room from a feed."
|
||||
)
|
||||
@ -441,11 +472,13 @@ class RSSBot(Plugin):
|
||||
await evt.reply(f"Updates for feed ID {feed.id} will now be sent as `{send_type}`")
|
||||
|
||||
@staticmethod
|
||||
def _format_subscription(feed: Feed, subscriber: str) -> str:
|
||||
def _format_subscription(feed: Feed, subscriber: str, title_exclude_filter: str) -> str:
|
||||
msg = (
|
||||
f"* {feed.id} - [{feed.title}]({feed.url}) "
|
||||
f"(subscribed by [{subscriber}](https://matrix.to/#/{subscriber}))"
|
||||
)
|
||||
if title_exclude_filter:
|
||||
msg += f" (excludes titles matching: {title_exclude_filter})"
|
||||
if feed.error_count > 1:
|
||||
msg += f" \n ⚠️ The last {feed.error_count} attempts to fetch the feed have failed!"
|
||||
return msg
|
||||
@ -463,7 +496,8 @@ class RSSBot(Plugin):
|
||||
await evt.reply(
|
||||
"**Subscriptions in this room:**\n\n"
|
||||
+ "\n".join(
|
||||
self._format_subscription(feed, subscriber) for feed, subscriber in subscriptions
|
||||
self._format_subscription(feed, subscriber, title_exclude_filter)
|
||||
for feed, subscriber, title_exclude_filter in subscriptions
|
||||
)
|
||||
)
|
||||
|
||||
|
21
rss/db.py
21
rss/db.py
@ -39,6 +39,7 @@ class Subscription:
|
||||
user_id: UserID
|
||||
notification_template: Template
|
||||
send_notice: bool
|
||||
title_exclude_filter: str
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row: Record | None) -> Subscription | None:
|
||||
@ -51,12 +52,14 @@ class Subscription:
|
||||
return None
|
||||
send_notice = bool(row["send_notice"])
|
||||
tpl = Template(row["notification_template"])
|
||||
exclude_filter = row["title_exclude_filter"]
|
||||
return cls(
|
||||
feed_id=feed_id,
|
||||
room_id=room_id,
|
||||
user_id=user_id,
|
||||
notification_template=tpl,
|
||||
send_notice=send_notice,
|
||||
title_exclude_filter=exclude_filter,
|
||||
)
|
||||
|
||||
|
||||
@ -82,6 +85,7 @@ class Feed:
|
||||
data.pop("user_id", None)
|
||||
data.pop("send_notice", None)
|
||||
data.pop("notification_template", None)
|
||||
data.pop("title_exclude_filter", None)
|
||||
return cls(**data, subscriptions=[])
|
||||
|
||||
|
||||
@ -121,7 +125,7 @@ class DBManager:
|
||||
async def get_feeds(self) -> list[Feed]:
|
||||
q = """
|
||||
SELECT id, url, title, subtitle, link, next_retry, error_count,
|
||||
room_id, user_id, notification_template, send_notice
|
||||
room_id, user_id, notification_template, send_notice, title_exclude_filter
|
||||
FROM feed INNER JOIN subscription ON feed.id = subscription.feed_id
|
||||
"""
|
||||
rows = await self.db.fetch(q)
|
||||
@ -134,13 +138,14 @@ class DBManager:
|
||||
feed.subscriptions.append(Subscription.from_row(row))
|
||||
return list(feeds.values())
|
||||
|
||||
async def get_feeds_by_room(self, room_id: RoomID) -> list[tuple[Feed, UserID]]:
|
||||
async def get_feeds_by_room(self, room_id: RoomID) -> list[tuple[Feed, UserID, str]]:
|
||||
q = """
|
||||
SELECT id, url, title, subtitle, link, next_retry, error_count, user_id FROM feed
|
||||
SELECT id, url, title, subtitle, link, next_retry, error_count, user_id, title_exclude_filter FROM feed
|
||||
INNER JOIN subscription ON feed.id = subscription.feed_id AND subscription.room_id = $1
|
||||
ORDER BY id
|
||||
"""
|
||||
rows = await self.db.fetch(q, room_id)
|
||||
return [(Feed.from_row(row), row["user_id"]) for row in rows]
|
||||
return [(Feed.from_row(row), row["user_id"], row["title_exclude_filter"]) for row in rows]
|
||||
|
||||
async def get_entries(self, feed_id: int) -> list[Entry]:
|
||||
q = "SELECT feed_id, id, date, title, summary, link FROM entry WHERE feed_id = $1"
|
||||
@ -173,7 +178,7 @@ class DBManager:
|
||||
) -> tuple[Subscription | None, Feed | None]:
|
||||
q = """
|
||||
SELECT id, url, title, subtitle, link, next_retry, error_count,
|
||||
room_id, user_id, notification_template, send_notice
|
||||
room_id, user_id, notification_template, send_notice, title_exclude_filter
|
||||
FROM feed LEFT JOIN subscription ON feed.id = subscription.feed_id AND room_id = $2
|
||||
WHERE feed.id = $1
|
||||
"""
|
||||
@ -238,3 +243,9 @@ class DBManager:
|
||||
async def set_send_notice(self, feed_id: int, room_id: RoomID, send_notice: bool) -> None:
|
||||
q = "UPDATE subscription SET send_notice=$3 WHERE feed_id=$1 AND room_id=$2"
|
||||
await self.db.execute(q, feed_id, room_id, send_notice)
|
||||
|
||||
async def update_title_filter(
|
||||
self, feed_id: int, room_id: RoomID, title_exclude_filter: str
|
||||
) -> None:
|
||||
q = "UPDATE subscription SET title_exclude_filter=$3 WHERE feed_id=$1 AND room_id=$2"
|
||||
await self.db.execute(q, feed_id, room_id, title_exclude_filter)
|
||||
|
@ -18,7 +18,7 @@ from mautrix.util.async_db import Connection, Scheme, UpgradeTable
|
||||
upgrade_table = UpgradeTable()
|
||||
|
||||
|
||||
@upgrade_table.register(description="Latest revision", upgrades_to=3)
|
||||
@upgrade_table.register(description="Latest revision", upgrades_to=4)
|
||||
async def upgrade_latest(conn: Connection, scheme: Scheme) -> None:
|
||||
gen = "GENERATED ALWAYS AS IDENTITY" if scheme != Scheme.SQLITE else ""
|
||||
await conn.execute(
|
||||
@ -44,6 +44,7 @@ async def upgrade_latest(conn: Connection, scheme: Scheme) -> None:
|
||||
|
||||
notification_template TEXT,
|
||||
send_notice BOOLEAN DEFAULT true,
|
||||
title_exclude_filter TEXT DEFAULT '',
|
||||
|
||||
PRIMARY KEY (feed_id, room_id),
|
||||
FOREIGN KEY (feed_id) REFERENCES feed (id)
|
||||
@ -72,3 +73,8 @@ async def upgrade_v2(conn: Connection) -> None:
|
||||
async def upgrade_v3(conn: Connection) -> None:
|
||||
await conn.execute("ALTER TABLE feed ADD COLUMN next_retry BIGINT DEFAULT 0")
|
||||
await conn.execute("ALTER TABLE feed ADD COLUMN error_count BIGINT DEFAULT 0")
|
||||
|
||||
|
||||
@upgrade_table.register(description="Add title exclude filter to subscriptions")
|
||||
async def upgrade_v4(conn: Connection) -> None:
|
||||
await conn.execute("ALTER TABLE subscription ADD COLUMN title_exclude_filter TEXT DEFAULT ''")
|
||||
|
Loading…
x
Reference in New Issue
Block a user