Implement "report abuse" command.

This commit is contained in:
Condorra 2023-07-27 21:38:29 +10:00
parent 590d4640dd
commit a1495e6731
5 changed files with 117 additions and 10 deletions

View File

@ -193,7 +193,7 @@ impl DBPool {
let conn = self.get_conn().await?;
Ok(conn
.query(
"SELECT item, session, listener, message FROM sendqueue ORDER BY item ASC LIMIT 10",
"SELECT item, session, listener, message FROM sendqueue WHERE sent_at IS NULL ORDER BY item ASC LIMIT 10",
&[],
)
.await?
@ -204,7 +204,18 @@ impl DBPool {
pub async fn delete_from_sendqueue(self: &DBPool, item: &SendqueueItem) -> DResult<()> {
let conn = self.get_conn().await?;
conn.execute("DELETE FROM sendqueue WHERE item=$1", &[&item.item])
conn.execute(
"UPDATE sendqueue SET sent_at = NOW() WHERE item=$1",
&[&item.item],
)
.await?;
conn.execute("DELETE FROM sendqueue WHERE item IN (\
WITH item_rows AS (\
SELECT item, row_number() OVER (ORDER BY sent_at DESC) AS rn FROM sendqueue
WHERE sent_at IS NOT NULL AND \
session = $1 AND listener = $2) \
SELECT item FROM item_rows WHERE rn > 80 \
)", &[&item.session.session, &item.session.listener])
.await?;
Ok(())
}
@ -1670,6 +1681,42 @@ impl DBTrans {
.collect())
}
pub async fn sendqueue_to_abusereport(
&self,
uuid: &Uuid,
username: &str,
session: &ListenerSession,
) -> DResult<()> {
self.pg_trans()?
.execute(
"INSERT INTO abuselog (id, triggered_by, logdata, expires) \
VALUES ($1, $2, (\
SELECT json_build_object(\
'sendqueue', json_build_array(array_agg(json_build_object(\
'message', message, 'sent_at', sent_at)))) FROM sendqueue WHERE \
listener = $3 AND session = $4), \
NOW() + INTERVAL '60 days')",
&[&uuid, &username, &session.listener, &session.session],
)
.await?;
Ok(())
}
pub async fn clean_and_count_abusereports(&self, username: &str) -> DResult<i64> {
let trans = self.pg_trans()?;
trans
.execute("DELETE FROM abuselog WHERE expires < NOW()", &[])
.await?;
Ok(trans
.query_one(
"SELECT COUNT(*) AS n FROM abuselog \
WHERE triggered_by = $1",
&[&username],
)
.await?
.get("n"))
}
pub async fn commit(mut self: Self) -> DResult<()> {
let trans_opt = self.with_trans_mut(|t| std::mem::replace(t, None));
if let Some(trans) = trans_opt {

View File

@ -1,9 +1,9 @@
use crate::message_handler::ListenerSession;
use crate::DResult;
use crate::db::DBPool;
use crate::message_handler::ListenerSession;
use crate::models::session::Session;
use crate::DResult;
use ansi::ansi;
use std::default::Default;
use crate::models::session::Session;
// ANSI art version of the symbol we are legally required to display per:
// https://www.legislation.gov.au/Details/F2017C00102
@ -35,7 +35,14 @@ const AUS_RATING_SYMBOL: &'static str = "\x1b[48;5;234m \x1b[38;5;252;48;5;235m
\x1b[49;38;5;233m\x1b[49;38;5;235m\x1b[49;38;5;242m\x1b[49;38;5;249m\x1b[49;38;5;254m\x1b[49;38;5;255m\x1b[49;38;5;253m\x1b[49;38;5;255m\x1b[49;38;5;254m\x1b[49;38;5;255m\x1b[49;38;5;254m\x1b[49;38;5;7m\x1b[49;38;5;243m\x1b[49;38;5;235m\x1b[49;38;5;233m\x1b[49;38;5;234m\x1b[49;38;5;233m\x1b[m";
pub async fn handle(session: &ListenerSession, source: String, pool: &DBPool) -> DResult<()> {
pool.start_session(session, &Session { source, ..Default::default() }).await?;
pool.start_session(
session,
&Session {
source,
..Default::default()
},
)
.await?;
pool.queue_for_session(&session, Some(&(ansi!("\
Welcome to <red>BlastMud<reset> - a text-based post-apocalyptic \
game <bold>restricted to adults (18+)<reset>\r\n").to_owned() + AUS_RATING_SYMBOL + ansi!("\r\n\
@ -47,7 +54,8 @@ pub async fn handle(session: &ListenerSession, source: String, pool: &DBPool) ->
\t<bold>help<reset> to learn more.\r\n\
[Please contact staff@blastmud.org with any feedback or suggestions on how to \r\n\
improve Blastmud, to report any inappropriate user generated content or behaviour, or if you \r\n\
need any other help from the game's operators].\r\n\
need any other help from the game's operators; use <bold>report abuse<reset> immediately after \r\n\
receiving any inappropriate message to store evidence].\r\n\
Blastmud's privacy policy: https://blastmud.org/privacy/\r\n")))).await?;
Ok(())
}

View File

@ -52,6 +52,7 @@ mod quit;
pub mod register;
pub mod remove;
pub mod rent;
mod report;
pub mod say;
mod score;
mod sign;
@ -200,9 +201,9 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
"reply" => page::VERB,
"put" => put::VERB,
"remove" => remove::VERB,
"rent" => rent::VERB,
"report" => report::VERB,
"\'" => say::VERB,
"say" => say::VERB,

View File

@ -0,0 +1,41 @@
use super::{get_user_or_fail, user_error, UResult, UserVerb, UserVerbRef, VerbContext};
use ansi::ansi;
use async_trait::async_trait;
use uuid::Uuid;
pub struct Verb;
#[async_trait]
impl UserVerb for Verb {
async fn handle(
self: &Self,
ctx: &mut VerbContext,
_verb: &str,
remaining: &str,
) -> UResult<()> {
if remaining != "abuse" {
user_error(ansi!("Try <bold>report abuse<reset>.").to_owned())?;
}
let user = get_user_or_fail(ctx)?;
let username = user.username.to_lowercase();
if ctx.trans.clean_and_count_abusereports(&username).await? > 10 {
user_error(
ansi!(
"You have too many recent abuse reports logged to record any more. \
Contact staff@blastmud.org for help."
)
.to_owned(),
)?;
}
let uuid = Uuid::new_v4();
ctx.trans
.sendqueue_to_abusereport(&uuid, &username, &ctx.session)
.await?;
ctx.trans.queue_for_session(ctx.session, Some(&format!("Up to the last 80 things we sent you since you logged in will now be retained for at least 60 days under reference {}. Send an email to staff@blastmud.org explaining exactly what happened, and include this reference. We'll be able to look it up and investigate.\n", &uuid))).await?;
Ok(())
}
}
static VERB_INT: Verb = Verb;
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;

View File

@ -7,7 +7,7 @@ CREATE DATABASE blast_schemaonly;
CREATE TABLE listeners (
listener UUID NOT NULL PRIMARY KEY,
last_seen TIMESTAMP WITH TIME ZONE
last_seen TIMESTAMPTZ
);
CREATE TABLE sessions (
@ -48,9 +48,19 @@ CREATE UNLOGGED TABLE sendqueue (
item BIGSERIAL NOT NULL PRIMARY KEY,
session UUID NOT NULL REFERENCES sessions(session),
listener UUID REFERENCES listeners(listener),
message TEXT /* Nullable, null means disconnect */
message TEXT, /* Nullable, null means disconnect */
sent_at TIMESTAMPTZ
);
CREATE TABLE abuselog (
id UUID NOT NULL PRIMARY KEY,
triggered_by TEXT NOT NULL,
logdata JSONB NOT NULL,
expires TIMESTAMPTZ NOT NULL
);
CREATE INDEX abuselog_by_triggerer ON abuselog(triggered_by);
CREATE INDEX abuselog_by_expires ON abuselog(expires);
CREATE TABLE tasks (
task_id BIGSERIAL NOT NULL PRIMARY KEY,
details JSONB NOT NULL