-- 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)
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
);
-- PostgreSQL database dump complete
--
-\unrestrict 80aOA5yV31bCsEeXZQpelpLcW1cJfgnz27c1dg1p4ydlMSFyDUMztrDG4WdSfbB
+\unrestrict 8GAoi0Gc8SzP92GNEI0astiMcEG2ZIy6XPaWB8DUlAso6LnUJFKe8gAEh9fb47M
# #
###############################################################################
+import babel.dates
import datetime
import logging
import pydantic
import uuid
from . import database
+from .i18n import _
# Setup logging
log = logging.getLogger(__name__)
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"
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())
# 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
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()
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()