From: Michael Tremer Date: Fri, 9 Jan 2026 15:59:33 +0000 (+0000) Subject: reports: Send am email to moderators if there are pending reports X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=803ba7b9e83cd18942ef3303bab27f1db9e80eb0;p=dbl.git reports: Send am email to moderators if there are pending reports Signed-off-by: Michael Tremer --- diff --git a/src/database.sql b/src/database.sql index df50992..4dea301 100644 --- a/src/database.sql +++ b/src/database.sql @@ -2,7 +2,7 @@ -- PostgreSQL database dump -- -\restrict 80aOA5yV31bCsEeXZQpelpLcW1cJfgnz27c1dg1p4ydlMSFyDUMztrDG4WdSfbB +\restrict 8GAoi0Gc8SzP92GNEI0astiMcEG2ZIy6XPaWB8DUlAso6LnUJFKe8gAEh9fb47M -- Dumped from database version 17.6 (Debian 17.6-0+deb13u1) -- Dumped by pg_dump version 17.6 (Debian 17.6-0+deb13u1) @@ -281,7 +281,8 @@ CREATE TABLE public.reports ( closed_by text, comment text DEFAULT ''::text NOT NULL, block boolean DEFAULT true, - accepted boolean DEFAULT false + accepted boolean DEFAULT false, + notified_at timestamp with time zone ); @@ -641,5 +642,5 @@ ALTER TABLE ONLY public.sources -- PostgreSQL database dump complete -- -\unrestrict 80aOA5yV31bCsEeXZQpelpLcW1cJfgnz27c1dg1p4ydlMSFyDUMztrDG4WdSfbB +\unrestrict 8GAoi0Gc8SzP92GNEI0astiMcEG2ZIy6XPaWB8DUlAso6LnUJFKe8gAEh9fb47M diff --git a/src/dnsbl/lists.py b/src/dnsbl/lists.py index 6dc4d9c..7e57493 100644 --- a/src/dnsbl/lists.py +++ b/src/dnsbl/lists.py @@ -171,6 +171,18 @@ class List(sqlmodel.SQLModel, database.BackendMixin, table=True): return hash(self.id) + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.id == other.id + + return NotImplemented + + def __lt__(self, other): + if isinstance(other, self.__class__): + return self.name < other.name + + return NotImplemented + # ID id : int = sqlmodel.Field(primary_key=True, exclude=True) diff --git a/src/dnsbl/reports.py b/src/dnsbl/reports.py index ba93e77..6a4e2ad 100644 --- a/src/dnsbl/reports.py +++ b/src/dnsbl/reports.py @@ -18,6 +18,7 @@ # # ############################################################################### +import babel.dates import datetime import logging import pydantic @@ -25,6 +26,7 @@ import sqlmodel import uuid from . import database +from .i18n import _ # Setup logging log = logging.getLogger(__name__) @@ -75,6 +77,92 @@ class Reports(object): return report + def notify(self): + """ + Notifies moderators about any pending reports + """ + # Fetch all pending reports that have not been notified, yet + reports = self.backend.db.fetch( + sqlmodel + .update( + Report, + ) + .values( + notified_at = sqlmodel.func.current_timestamp(), + ) + .where( + # Reports must be open + Report.closed_at == None, + + # Reports must not have been notified, yet + Report.notified_at == None, + ) + .returning( + Report, + ) + ) + + lists = {} + + # Group reports by list + for report in reports: + try: + lists[report.list].append(report) + except KeyError: + lists[report.list] = [report] + + # Nothing to do + if not lists: + log.debug("No pending reports, won't send anything") + return + + # Send an email to all moderators + for moderator in self.backend.users.moderators: + lines = [ + _("Dear moderator,"), + "", + _("The following reports have been filed and are awaiting your review:"), + "", + ] + + for list in sorted(lists): + # Headline + lines.append("%s" % list) + + # List all reports + for report in sorted(lists[list]): + if report.block: + headline = _("%s should be blocked") % report.name + else: + headline = _("%s should be allowed") % report.name + + # Add the headline + lines.append(" * %s" % headline) + + # URL + lines.append(" %s" % report.url) + + # Fetch the reporter + reporter = self.backend.users.get_by_uid(report.reported_by) + if reporter: + lines.append(" %s" % (_("Reported by %s") % reporter)) + + # Report Time + lines.append(" %s" % babel.dates.format_datetime(report.reported_at)) + + # Empty line + lines.append("") + + # Send the message + moderator.sendmail("\n".join(lines), headers={ + "Subject" : _("[IPFire DNSBL] Pending Reports"), + + # Mark that this email is auto-generated + "Auto-Submitted" : "auto-generated", + "X-Auto-Response-Suppress" : "OOF, AutoReply", + "Precedence" : "bulk", + }) + class Report(sqlmodel.SQLModel, database.BackendMixin, table=True): __tablename__ = "reports" @@ -82,6 +170,19 @@ class Report(sqlmodel.SQLModel, database.BackendMixin, table=True): def __str__(self): return "%s" % self.id + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.id == other.id + + return NotImplemented + + def __lt__(self, other): + if isinstance(other, self.__class__): + return (self.list, self.reported_at, self.name) \ + < (other.list, other.reported_at, other.name) + + return NotImplemented + # ID id : uuid.UUID = sqlmodel.Field(primary_key=True, default=sqlmodel.func.gen_random_uuid()) @@ -163,3 +264,12 @@ class Report(sqlmodel.SQLModel, database.BackendMixin, table=True): # Update the list's stats self.list.update_stats() + + # Notified At + notified_at: datetime.datetime | None = None + + # URL + + @property + def url(self): + return "https://www.ipfire.org/dnsbl/reports/%s" % self.id diff --git a/src/scripts/dnsbl.in b/src/scripts/dnsbl.in index f5b996d..28fe050 100644 --- a/src/scripts/dnsbl.in +++ b/src/scripts/dnsbl.in @@ -207,6 +207,11 @@ class CLI(object): help=_("Reject the report")) close_report.set_defaults(func=self.__close_report) + # Notify + notify = subparsers.add_parser("notify", + help=_("Notifies moderators about any pending reports")) + notify.set_defaults(func=self.__notify) + # Parse all arguments args = parser.parse_args() @@ -604,6 +609,14 @@ class CLI(object): accept = args.accept and not args.reject, ) + # Notify + + def __notify(self, backend, args): + """ + Notifies moderators about any pending reports + """ + backend.reports.notify() + def main(): c = CLI()