Files
Paul HittandClaude Opus 5 19cb56b3a1 A report reaches a person, instead of a row nobody reads
`submitReport` wrote one row to `content_reports` and stopped. No mail, no
alert, and no route or script to read the table back: a report of abuse sat on
disk until somebody thought to look, which for a report of abuse is the same
as nowhere. Meanwhile the safety page has been telling people for months that
a report "goes to the people who run this server". It went to their disk.

So `FN_REPORTS_TO` gets a mail per report -- kind, reason, who it is about,
who filed it, what they wrote and the copy they attached -- and
`Scripts/reports.sh` is the record, because mail is not one.

The mail is detached and cannot fail the report. If SMTP is down the row is
still filed and the script still finds it: refusing a report because a mail
server is unwell would lose the one copy of something somebody went out of
their way to send. With no address configured it says so in the log rather
than filing in silence, which is the state it has been in all along.

The script leads with accounts reported by more than one person. One person
reporting one thing may be a disagreement; several people reporting the same
account is the thing to look at first.

698 tests in 63 suites.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 14:29:27 -04:00

72 lines
2.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Content reports filed on this relay.
#
# Reports used to land in a table and stop there: no mail, no alert, and no way
# to read them back short of writing SQL. They are mailed now when
# FN_REPORTS_TO is set, but mail is not a record. This is the record.
#
# Scripts/reports.sh reports from the last 30 days
# Scripts/reports.sh 365 a year
# Scripts/reports.sh 30 full the full text of each, not a summary
set -euo pipefail
days="${1:-30}"
mode="${2:-summary}"
cd /srv/prod/messenger
q() { docker compose exec -T db psql -U flamenet -d flamenet -qAt -c "$1"; }
cutoff=$(( $(date +%s) - days * 86400 ))
echo "== reports filed in the last ${days} days =="
echo
total=$(q "select count(*) from content_reports where created_at > ${cutoff};")
echo " ${total} in total"
echo
if [ "$total" = "0" ]; then
echo " Nothing. Worth knowing whether that is because nobody reported anything"
echo " or because the route is not reachable: Scripts/call-health.sh is the"
echo " same question for calls."
exit 0
fi
echo "-- by reason --"
q "select reason, count(*) from content_reports
where created_at > ${cutoff} group by reason order by 2 desc;" |
awk -F'|' '{printf " %-16s %s\n", $1, $2}'
echo
echo "-- accounts reported more than once --"
q "select about_id, count(*), count(distinct reporter_id)
from content_reports where created_at > ${cutoff}
group by about_id having count(*) > 1 order by 2 desc limit 20;" |
awk -F'|' '{printf " user %-8s %s reports from %s different people\n", $1, $2, $3}'
echo
echo " More than one person reporting the same account is the signal worth"
echo " acting on first. One person reporting one thing may be a disagreement."
echo
if [ "$mode" = "full" ]; then
echo "-- each report --"
q "select id, to_char(to_timestamp(created_at), 'YYYY-MM-DD HH24:MI'), kind, reason,
about_id, reporter_id, replace(note, E'\n', ' '), replace(content, E'\n', ' ')
from content_reports where created_at > ${cutoff} order by created_at desc;" |
while IFS='|' read -r id when kind reason about by note content; do
echo " [$id] $when $reason ($kind) about user $about, from user $by"
[ -n "$note" ] && echo " said: ${note:0:300}"
[ -n "$content" ] && echo " attached: ${content:0:300}"
echo
done
else
echo "-- most recent --"
q "select id, to_char(to_timestamp(created_at), 'MM-DD HH24:MI'), reason, about_id,
replace(note, E'\n', ' ')
from content_reports where created_at > ${cutoff} order by created_at desc limit 15;" |
awk -F'|' '{printf " [%s] %s %-14s user %-8s %s\n", $1, $2, $3, $4, substr($5,1,60)}'
echo
echo " Add 'full' as the second argument for the whole text of each."
fi