Add tournament and multiplayer community chat moderation
All checks were successful
Deploy MineGeo to Dokploy / deploy (push) Successful in 12m17s

This commit is contained in:
2026-10-27 12:54:15 +05:00
parent b8a430d4c2
commit 9c314ecb4e
19 changed files with 1070 additions and 16 deletions

View File

@@ -88,6 +88,129 @@ try {
[dataset, secret(1), account],
)
).rows[0].id;
{
const moderationRoom = await accountRoom();
await db.query("select public.join_multiplayer_room($1,$2,$3,$4)", [
moderationRoom,
secret(2),
secondAccount,
"Ignored",
]);
await db.query("select public.join_multiplayer_room($1,$2,null,$3)", [
moderationRoom,
secret(3),
"Guest",
]);
const roster = (await act(moderationRoom, 1)).players;
const friend = roster.find((p) => p.name === "Account friend");
let guest = roster.find((p) => p.name === "Guest");
const community = async (who, action, payload = {}) =>
(
await db.query("select public.party_community($1,$2,$3,$4) as value", [
moderationRoom,
secret(who),
action,
JSON.stringify(payload),
])
).rows[0].value;
const chat = (who, text, clientId) =>
db.query("select public.multiplayer_chat($1,$2,$3,$4)", [
moderationRoom,
secret(who),
text,
clientId,
]);
await assert.rejects(
community(2, "ban", { playerId: guest.id }),
/moderator permission/,
);
await community(1, "admin", { playerId: friend.id });
await assert.rejects(
community(2, "ban", { playerId: roster[0].id }),
/owner or yourself/,
);
await community(2, "tag", { playerId: guest.id, tag: "Explorer" });
await community(2, "slow", { seconds: 10 });
const chatId = randomUUID();
await chat(3, "Hello", chatId);
await chat(3, "Hello", chatId);
await assert.rejects(chat(3, "Too fast", randomUUID()), /slow mode/);
await db.query(
"update public.party_chat_cooldowns set last_message_at=clock_timestamp()-interval '11 seconds' where room_id=$1 and access_hash=$2",
[moderationRoom, secret(3)],
);
await db.query(
"update public.multiplayer_messages set created_at=clock_timestamp()-interval '11 seconds' where room_id=$1 and player_id=$2",
[moderationRoom, guest.id],
);
await chat(3, "Hello", chatId);
await chat(3, "A new message", randomUUID());
await act(moderationRoom, 3, "leave");
await db.query("select public.join_multiplayer_room($1,$2,null,$3)", [
moderationRoom,
secret(3),
"Guest",
]);
guest = (await act(moderationRoom, 1)).players.find(
(p) => p.name === "Guest",
);
assert.ok(guest);
await assert.rejects(
chat(3, "Rejoined too fast", randomUUID()),
/slow mode/,
);
await db.query(
"update public.party_chat_cooldowns set last_message_at=clock_timestamp()-interval '11 seconds' where room_id=$1 and access_hash=$2",
[moderationRoom, secret(3)],
);
await assert.rejects(chat(3, "", randomUUID()), /Invalid chat message/);
await chat(3, "Cooldown expired", randomUUID());
const state = await community(2, "ban", { playerId: guest.id });
await assert.rejects(
db.query("select public.join_multiplayer_room($1,$2,null,$3)", [
moderationRoom,
secret(3),
"Guest",
]),
/banned/,
);
await community(2, "unban", { banId: state.bans[0].id });
await db.query("select public.join_multiplayer_room($1,$2,null,$3)", [
moderationRoom,
secret(3),
"Guest",
]);
await community(1, "ban", { playerId: friend.id });
await assert.rejects(
db.query("select public.join_multiplayer_room($1,$2,$3,$4)", [
moderationRoom,
secret(8),
secondAccount,
"Ignored",
]),
/banned/,
);
await assert.rejects(community(2, "slow", { seconds: 0 }), /membership/);
const privileges = (
await db.query(`select
has_table_privilege('anon','public.party_chat_cooldowns','select') as anon_read,
has_table_privilege('authenticated','public.party_chat_cooldowns','select') as authenticated_read`)
).rows[0];
assert.equal(privileges.anon_read, false);
assert.equal(privileges.authenticated_read, false);
await db.query("delete from public.multiplayer_rooms where id=$1", [
moderationRoom,
]);
assert.equal(
(
await db.query(
"select count(*)::integer as n from public.party_chat_cooldowns where room_id=$1",
[moderationRoom],
)
).rows[0].n,
0,
);
}
await assert.rejects(
db.query(
"select public.create_map_room($1,'normal','Guest host',4,$2,null)",

View File

@@ -68,6 +68,63 @@ try {
JSON.stringify({ ...base, ...payload }),
])
)[0].id;
const communityEvent = await create();
const eventAction = (account, action, payload = {}) =>
q("select public.tournament_action($1,$2,$3,$4)", [
communityEvent,
account,
action,
JSON.stringify(payload),
]);
const community = (account, action, payload = {}) =>
q("select public.tournament_community($1,$2,$3,$4) value", [
communityEvent,
account,
action,
JSON.stringify(payload),
]);
const chat = (account, text = null, clientId = null) =>
q("select public.tournament_chat($1,$2,$3,$4) value", [
communityEvent,
account,
text,
clientId,
]);
await eventAction(users[1], "join");
await eventAction(users[2], "join");
await assert.rejects(
community(users[1], "ban", { accountId: users[2] }),
/moderator permission/,
);
await community(users[0], "admin", { accountId: users[1] });
await assert.rejects(
community(users[1], "ban", { accountId: users[0] }),
/owner or yourself/,
);
await community(users[1], "tag", { accountId: users[2], tag: "Explorer" });
await community(users[1], "slow", { seconds: 10 });
const messageId = randomUUID();
await chat(users[2], "Hello", messageId);
const repeated = await chat(users[2], "Hello", messageId);
assert.equal(repeated[0].value.messages.length, 1);
assert.equal(repeated[0].value.messages[0].tag, "Explorer");
await assert.rejects(chat(users[2], "Too fast", randomUUID()), /slow mode/);
await community(users[1], "ban", { accountId: users[2] });
await assert.rejects(chat(users[2]), /membership/);
await assert.rejects(eventAction(users[2], "join"), /banned/);
await community(users[1], "unban", { accountId: users[2] });
await eventAction(users[2], "join");
await eventAction(users[1], "leave");
await assert.rejects(
community(users[1], "slow", { seconds: 0 }),
/membership/,
);
await eventAction(users[1], "join");
await assert.rejects(
community(users[1], "slow", { seconds: 0 }),
/moderator permission/,
);
await q("delete from public.tournaments where id=$1", [communityEvent]);
const voidEvent = await create({
difficulty: "void",
roundTimeSeconds: 10,