From d8038557293d063ed0ee1afb64153fad18bce632 Mon Sep 17 00:00:00 2001 From: Alan Myers Date: Tue, 21 Jul 2026 21:02:28 -0400 Subject: [PATCH 1/2] 24h account age chat restriction --- backend/websockets/chat.js | 10 ++++++++++ runserver.js | 6 ++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/backend/websockets/chat.js b/backend/websockets/chat.js index a2cd460b..4764b89b 100644 --- a/backend/websockets/chat.js +++ b/backend/websockets/chat.js @@ -170,6 +170,16 @@ module.exports = async function(ws, data, send, broadcast, server, ctx) { return; } + if(user.authenticated && user.date_joined) { + var accountAge = Date.now() - user.date_joined; + var minimumAge = 24 * 60 * 60 * 1000; // 24 hours + if(accountAge < minimumAge) { + var timeRemaining = calculateTimeDiff(minimumAge - accountAge); + serverChatResponse("Your account is too new. You must wait " + timeRemaining + " before chatting.", location); + return; + } + } + var isTestMessage = false; var isMuted = ( diff --git a/runserver.js b/runserver.js index 929c83d4..570ae458 100644 --- a/runserver.js +++ b/runserver.js @@ -1322,10 +1322,11 @@ async function getUserInfo(cookies, is_websocket, dispatch) { user = JSON.parse(s_data.session_data); if(cookies.csrftoken == user.csrftoken) { // verify csrftoken user.authenticated = true; - var userauth = (await db.get("SELECT level, is_active, email FROM auth_user WHERE id=?", user.id)); + var userauth = (await db.get("SELECT level, is_active, email, date_joined FROM auth_user WHERE id=?", user.id)); var level = userauth.level; user.is_active = !!userauth.is_active; user.email = userauth.email; + user.date_joined = userauth.date_joined; user.operator = level == 3; user.superuser = level == 2 || level == 3; @@ -1357,7 +1358,7 @@ async function getUserInfo(cookies, is_websocket, dispatch) { var session = await uvias.get("SELECT * FROM accounts.get_session($1::bigint, $2::bytea)", [uid, session_id]); if(session) { // both guests and users are included - var user_account = await uvias.get("SELECT to_hex(uid) as uid, username, rank_id FROM accounts.users WHERE uid=$1::bigint", uid); + var user_account = await uvias.get("SELECT to_hex(uid) as uid, username, rank_id, created FROM accounts.users WHERE uid=$1::bigint", uid); if(user_account) { success = true; var session_expire = session.expires.getTime(); @@ -1375,6 +1376,7 @@ async function getUserInfo(cookies, is_websocket, dispatch) { user.authenticated = true; user.display_username = user_account.username; user.uv_rank = user_account.rank_id; + user.date_joined = user_account.created.getTime(); if(links_local) { user.is_active = links_local.email_verified; user.email = links_local.email; From 6714ff07a9a7f648e21a0f46a8b2fffb42dc5541 Mon Sep 17 00:00:00 2001 From: Alan Myers Date: Tue, 21 Jul 2026 21:26:17 -0400 Subject: [PATCH 2/2] Add settings option for chat_age_restriction --- backend/pages/admin/administrator.js | 8 +++++++- backend/websockets/chat.js | 15 +++++++++------ frontend/templates/administrator.html | 4 ++++ runserver.js | 3 ++- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/backend/pages/admin/administrator.js b/backend/pages/admin/administrator.js index 3653a7ff..cb4ca8ff 100644 --- a/backend/pages/admin/administrator.js +++ b/backend/pages/admin/administrator.js @@ -87,7 +87,8 @@ module.exports.GET = async function(req, write, server, ctx, params) { client_version: getClientVersion(), csrftoken, global_chat_enabled: getServerSetting("chatGlobalEnabled") == "1", - global_chat_no_anon: getServerSetting("chatGlobalNoAnon") == "1" + global_chat_no_anon: getServerSetting("chatGlobalNoAnon") == "1", + chat_age_restriction: getServerSetting("chatAgeRestriction") }; write(render("administrator.html", data)); @@ -143,6 +144,11 @@ module.exports.POST = async function(req, write, server, ctx) { } else { updateServerSetting("chatGlobalNoAnon", "0"); } + if("set_chat_age_restriction" in post_data) { + var ageRestrictionHours = parseInt(post_data.set_chat_age_restriction); + if(isNaN(ageRestrictionHours) || ageRestrictionHours < 0) ageRestrictionHours = 0; + updateServerSetting("chatAgeRestriction", ageRestrictionHours.toString()); + } } if("announcement" in post_data) { var new_announcement = post_data.announcement; diff --git a/backend/websockets/chat.js b/backend/websockets/chat.js index 4764b89b..f4719163 100644 --- a/backend/websockets/chat.js +++ b/backend/websockets/chat.js @@ -171,12 +171,15 @@ module.exports = async function(ws, data, send, broadcast, server, ctx) { } if(user.authenticated && user.date_joined) { - var accountAge = Date.now() - user.date_joined; - var minimumAge = 24 * 60 * 60 * 1000; // 24 hours - if(accountAge < minimumAge) { - var timeRemaining = calculateTimeDiff(minimumAge - accountAge); - serverChatResponse("Your account is too new. You must wait " + timeRemaining + " before chatting.", location); - return; + var ageRestrictionHours = parseInt(getServerSetting("chatAgeRestriction")); + if(ageRestrictionHours > 0) { + var accountAge = Date.now() - user.date_joined; + var minimumAge = ageRestrictionHours * 60 * 60 * 1000; + if(accountAge < minimumAge) { + var timeRemaining = calculateTimeDiff(minimumAge - accountAge); + serverChatResponse("Your account is too new. You must wait " + timeRemaining + " before chatting.", location); + return; + } } } diff --git a/frontend/templates/administrator.html b/frontend/templates/administrator.html index 6ea97c19..ccf79521 100644 --- a/frontend/templates/administrator.html +++ b/frontend/templates/administrator.html @@ -306,6 +306,10 @@

Administrator Panel

Global chat: prevent anonymous users: +
+ Required account age for chat (hours): + +
diff --git a/runserver.js b/runserver.js index 570ae458..3f44b805 100644 --- a/runserver.js +++ b/runserver.js @@ -116,7 +116,8 @@ var sql_edits_init = "./backend/edits.sql"; var serverSettings = { announcement: "", chatGlobalEnabled: "1", - chatGlobalNoAnon: "0" + chatGlobalNoAnon: "0", + chatAgeRestriction: "0" }; var serverSettingsStatus = {};