centralize chat logic

master
Aevann 2024-04-06 06:31:51 +02:00
parent c37a0de7db
commit 1a60e5a7a2
14 changed files with 212 additions and 359 deletions

View File

@ -206,7 +206,7 @@ ta.addEventListener("keydown", function(e) {
socket.on('online', function(data) {
const online_li = data[0]
if (location.pathname.startsWith('/chat/')) {
if (location.pathname != '/chat/1') {
for (const marker of document.getElementsByClassName('online-marker')) {
marker.classList.add('d-none')
}

View File

@ -121,7 +121,7 @@ function postToastSwitch(t, url, button1, button2, cls, extraActionsOnSuccess) {
});
}
if (!location.pathname.endsWith('/submit') && location.pathname != '/chat')
if (!location.pathname.endsWith('/submit') && !location.pathname.startsWith('/chat/'))
{
document.addEventListener('keydown', (e) => {
if (!((e.ctrlKey || e.metaKey) && e.key === "Enter")) return;
@ -401,7 +401,7 @@ if (is_pwa) {
}
const gbrowser = document.getElementById('gbrowser').value
if (!location.pathname.startsWith('/chat') && (gbrowser == 'iphone' || gbrowser == 'mac')) {
if (!location.pathname.startsWith('/chat/') && (gbrowser == 'iphone' || gbrowser == 'mac')) {
const videos = document.querySelectorAll('video')
for (const video of videos) {

View File

@ -4,7 +4,7 @@ function update_ghost_div_textarea(text)
{
let ghostdiv
if (location.pathname.startsWith('/chat') )
if (location.pathname.startsWith('/chat/') )
ghostdiv = document.getElementById("ghostdiv-chat");
else
ghostdiv = text.parentNode.getElementsByClassName("ghostdiv")[0];

View File

@ -1,4 +1,3 @@
import atexit
import time
import uuid
from hashlib import md5
@ -31,19 +30,10 @@ socketio = SocketIO(
)
muted = cache.get(f'muted') or {}
online = {"messages": set()}
typing = {}
messages = cache.get(f'messages') or {}
online = {
"chat": {},
"messages": set()
}
typing = {
"chat": []
}
cache.set('loggedin_chat', len(online["chat"]), timeout=86400)
cache.set('loggedin_chat', 0, timeout=86400)
def auth_required_socketio(f):
def wrapper(*args, **kwargs):
@ -61,45 +51,31 @@ def is_not_banned_socketio(f):
wrapper.__name__ = f.__name__
return wrapper
def commit_and_close():
try: g.db.commit()
except: g.db.rollback()
g.db.close()
stdout.flush()
CHAT_ERROR_MESSAGE = f"To prevent spam, you'll need {TRUESCORE_MINIMUM} truescore (this is {TRUESCORE_MINIMUM} votes, either up or down, on any threads or comments you've made) in order to access chat. Sorry! I love you 💖"
@app.post('/refresh_chat')
def refresh_chat():
emit('refresh_chat', namespace='/', to="chat")
emit('refresh_chat', namespace='/', to=f'{SITE_FULL}/chat/1')
return ''
@app.get("/chat/")
def chat_redirect():
return redirect("/chat")
@app.get("/chat")
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def chat(v):
if not v.allowed_in_chat:
abort(403, CHAT_ERROR_MESSAGE)
displayed_messages = {k: val for k, val in messages.items() if val["user_id"] not in v.userblocks}
orgy = get_running_orgy(v)
if orgy:
x = secrets.token_urlsafe(8)
orgies = g.db.query(Orgy).order_by(Orgy.start_utc).all()
return render_template("orgy.html", v=v, messages=displayed_messages, orgy=orgy, x=x, orgies=orgies)
return render_template("chat.html", v=v, messages=displayed_messages)
@socketio.on('speak')
@is_not_banned_socketio
def speak(data, v):
if request.referrer.startswith(f'{SITE_FULL}/chat/'):
chat_id = int(data['chat_id'])
chat = g.db.get(Chat, chat_id)
if not chat:
abort(404, "Chat not found!")
if chat.id == 1:
if not v.allowed_in_chat: return ''
else:
is_member = g.db.query(ChatMembership.user_id).filter_by(user_id=v.id, chat_id=chat_id).one_or_none()
if not is_member: return ''
@ -114,6 +90,26 @@ def speak(data, v):
if image: text += f'\n\n{image}'
if not text: return ''
if chat.id == 1:
vname = v.username.lower()
if vname in muted:
if time.time() < muted[vname]:
return ''
else:
del muted[vname]
refresh_online(f'{SITE_FULL}/chat/1')
if v.admin_level >= PERMS['USER_BAN']:
text = text.lower()
for i in mute_regex.finditer(text):
username = i.group(1).lower()
muted_until = int(int(i.group(2)) * 60 + time.time())
muted[username] = muted_until
refresh_online(f'{SITE_FULL}/chat/1')
for i in unmute_regex.finditer(text):
username = i.group(1).lower()
muted.pop(username, None)
refresh_online(f'{SITE_FULL}/chat/1')
text_html = sanitize(text, count_emojis=True, chat=True)
if isinstance(text_html , tuple): return ''
@ -204,106 +200,9 @@ def speak(data, v):
emit('speak', data, room=request.referrer, broadcast=True)
try: g.db.commit()
except: g.db.rollback()
g.db.close()
stdout.flush()
typing[request.referrer] = []
return ''
if not v.allowed_in_chat: return ''
image = None
if data['file']:
name = f'/chat_images/{time.time()}'.replace('.','') + '.webp'
with open(name, 'wb') as f:
f.write(data['file'])
image = process_image(name, v)
global messages
text = data['message'].strip()[:CHAT_LENGTH_LIMIT]
if image: text += f'\n\n{image}'
if not text: return ''
text_html = sanitize(text, count_emojis=True, chat=True)
if isinstance(text_html , tuple): return ''
execute_under_siege(v, None, text, "chat")
quotes = data['quotes']
id = secrets.token_urlsafe(5)
self_only = False
vname = v.username.lower()
if vname in muted:
if time.time() < muted[vname]:
self_only = True
else:
del muted[vname]
refresh_online("chat")
if SITE == 'rdrama.net' and v.admin_level < PERMS['BYPASS_ANTISPAM_CHECKS']:
def shut_up():
self_only = True
muted_until = int(time.time() + 600)
muted[vname] = muted_until
refresh_online("chat")
if not self_only:
identical = [x for x in list(messages.values())[-5:] if v.id == x['user_id'] and text == x['text']]
if len(identical) >= 3: shut_up()
if not self_only:
count = len([x for x in list(messages.values())[-12:] if v.id == x['user_id']])
if count >= 10: shut_up()
if not self_only:
count = len([x for x in list(messages.values())[-25:] if v.id == x['user_id']])
if count >= 20: shut_up()
data = {
"id": id,
"quotes": quotes if messages.get(quotes) else '',
"hat": v.hat_active(None)[0],
"user_id": v.id,
"username": v.username,
"namecolor": v.name_color,
"patron": v.patron,
"pride_username": bool(v.has_badge(303)),
"text": text,
"text_censored": censor_slurs_profanities(text, 'chat', True),
"text_html": text_html,
"text_html_censored": censor_slurs_profanities(text_html, 'chat'),
"created_utc": int(time.time()),
}
if v.admin_level >= PERMS['USER_BAN']:
text = text.lower()
for i in mute_regex.finditer(text):
username = i.group(1).lower()
muted_until = int(int(i.group(2)) * 60 + time.time())
muted[username] = muted_until
refresh_online("chat")
for i in unmute_regex.finditer(text):
username = i.group(1).lower()
muted.pop(username, None)
refresh_online("chat")
if self_only or v.shadowbanned or execute_blackjack(v, None, text, "chat"):
emit('speak', data)
else:
emit('speak', data, room="chat", broadcast=True)
messages[id] = data
messages = dict(list(messages.items())[-250:])
typing["chat"] = []
commit_and_close()
return ''
@ -316,22 +215,20 @@ def refresh_online(room):
data = [list(online[room].values()), muted]
emit("online", data, room=room, broadcast=True)
if room == "chat":
if room == f'{SITE_FULL}/chat/1':
cache.set('loggedin_chat', len(online[room]), timeout=86400)
@socketio.on('connect')
@auth_required_socketio
def connect(v):
if request.referrer == f'{SITE_FULL}/notifications/messages':
if not request.referrer: return
room = request.referrer
if room == f'{SITE_FULL}/notifications/messages':
join_room(v.id)
online["messages"].add(v.id)
return ''
if request.referrer and request.referrer.startswith(f'{SITE_FULL}/chat/'):
room = request.referrer
else:
room = "chat"
join_room(room)
if not typing.get(room):
@ -341,21 +238,22 @@ def connect(v):
typing[room].remove(v.username)
emit('typing', typing[room], room=room)
commit_and_close()
return ''
@socketio.on('disconnect')
@auth_required_socketio
def disconnect(v):
if not request.referrer: return
room = request.referrer
if request.referrer == f'{SITE_FULL}/notifications/messages':
leave_room(v.id)
online["messages"].remove(v.id)
return ''
if request.referrer and request.referrer.startswith(f'{SITE_FULL}/chat/'):
room = request.referrer
else:
room = "chat"
online[room].pop(v.id, None)
if v.username in typing[room]:
@ -364,15 +262,15 @@ def disconnect(v):
leave_room(room)
refresh_online(room)
commit_and_close()
return ''
@socketio.on('heartbeat')
@auth_required_socketio
def heartbeat(v):
if request.referrer and request.referrer.startswith(f'{SITE_FULL}/chat/'):
if not request.referrer: return
room = request.referrer
else:
room = "chat"
if not online.get(room):
online[room] = {}
@ -383,15 +281,15 @@ def heartbeat(v):
if not already_there:
refresh_online(room)
commit_and_close()
return ''
@socketio.on('typing')
@is_not_banned_socketio
def typing_indicator(data, v):
if request.referrer and request.referrer.startswith(f'{SITE_FULL}/chat/'):
if not request.referrer: return
room = request.referrer
else:
room = "chat"
if not typing.get(room):
typing[room] = []
@ -402,23 +300,24 @@ def typing_indicator(data, v):
typing[room].remove(v.username)
emit('typing', typing[room], room=room, broadcast=True)
commit_and_close()
return ''
@socketio.on('delete')
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
def delete(id, v):
messages.pop(id, None)
emit('delete', id, room="chat", broadcast=True)
message = g.db.get(ChatMessage, id)
g.db.delete(message)
emit('delete', id, room=f'{SITE_FULL}/chat/1', broadcast=True)
commit_and_close()
return ''
def close_running_threads():
cache.set('messages', messages, timeout=86400)
cache.set('muted', muted, timeout=86400)
atexit.register(close_running_threads)
@app.post("/reply")
@limiter.limit('1/second', scope=rpath)
@limiter.limit('1/second', scope=rpath, key_func=get_ID)

View File

@ -82,8 +82,6 @@ def selected_tab(request):
return 'comments'
elif request.path == '/casino':
return 'casino'
elif request.path == '/chat':
return 'chat'
elif request.path.startswith('/shop/'):
return 'shop'

View File

@ -6,6 +6,11 @@ from files.helpers.get import *
from files.__main__ import app, limiter
@app.get("/chat")
@app.get("/orgy")
def chat_redirect():
return redirect("/chat/1")
@app.post("/@<username>/chat")
@limiter.limit('1/second', scope=rpath)
@limiter.limit('1/second', scope=rpath, key_func=get_ID)

View File

@ -439,14 +439,6 @@ def transfers(v):
def donate(v):
return render_template(f'donate.html', v=v)
@app.get("/orgy")
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def orgy(v):
return redirect("/chat")
@app.get("/sidebar_images")
@app.get("/banners")
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)

View File

@ -1,39 +0,0 @@
{%- extends 'root.html' -%}
{% block pagetitle -%}Chat{%- endblock %}
{% block pagetype %}chat{% endblock %}
{% block body_attributes %}class="has_header"{% endblock %}
{% block body %}
<link rel="stylesheet" href="{{'css/chat.css' | asset}}">
{% include "header.html" %}
{% include "modals/expanded_image.html" %}
{% include "modals/emoji.html" %}
{% include "modals/gif.html" %}
{% set vlink = '<a href="/id/' ~ v.id ~ '"' %}
<div class="container pb-4 pb-md-2">
<div class="row justify-content-around" id="main-content-row">
<div class="col h-100 {% block customPadding %}custom-gutters{% endblock %}" id="main-content-col">
<div id="chat-group-template" class="d-none">
{{macros.chat_group_template()}}
</div>
</div>
<div id="chat-line-template" class="d-none">
{{macros.chat_line_template()}}
</div>
{{macros.chat_window(vlink)}}
</div>
{{macros.chat_users_list()}}
</div>
<input id="vid" hidden value="{{v.id}}">
<input id="slurreplacer" hidden value="{{v.slurreplacer}}">
<input id="admin_level" hidden value="{{v.admin_level}}">
<input id="blocked_user_ids" hidden value="{{(v.userblocks|string)[1:-1]}}">
<script defer src="{{'js/vendor/socketio.js' | asset}}"></script>
<script defer src="{{'js/flash.js' | asset}}"></script>
<script defer src="{{'js/vendor/lozad.js' | asset}}"></script>
<script defer src="{{'js/vendor/lite-youtube.js' | asset}}"></script>
<script defer src="{{'js/chat.js' | asset}}"></script>
{% endblock %}

View File

@ -24,7 +24,7 @@
{% set class = 'site-banner-hole' %}
{% elif get_running_orgy(v) and os_path.exists(path ~ "/orgy_banners") %}
{% set src = macros.random_image("assets/images/" ~ SITE_NAME ~ "/orgy_banners") %}
{% set href = "/chat" %}
{% set href = "/chat/1" %}
{% set expand = false %}
{% elif IS_DKD() or IS_BIRTHGAY() or IS_BIRTHDEAD() %}
{% set src = macros.random_image("assets/events/" ~ IS_EVENT() ~ "/images/banners") %}

View File

@ -281,7 +281,7 @@
{%- if FEATURES['CHAT'] -%}
<li class="nav-item d-none d-lg-flex align-items-center justify-content-center text-center mx-1" id="header--chat--item">
<a class="nav-link position-relative" href="/chat" style="margin-top: {% if get_running_orgy(v) %}-4px{% else %}-1.6px{% endif %} !important">
<a class="nav-link position-relative" href="/chat/1" style="margin-top: {% if get_running_orgy(v) %}-4px{% else %}-1.6px{% endif %} !important">
{% if get_running_orgy(v) %}
<img src="{{SITE_FULL_IMAGES}}/i/orgy.webp" width="30">
{% else %}
@ -490,7 +490,7 @@
</li>
{% endif %}
<li class="mt-3">
{% if request.path == '/chat' %}
{% if request.path == '/chat/1' %}
{% if SITE_NAME == 'WPD' %}
<h5 class="ml-3">Chat Rules</h5>
<ul class="ml-3">
@ -502,7 +502,7 @@
{% endif %}
<div class="mx-2">
<h5 class="mt-3">Users Online (<span class="chat-count"></span>)</h5>
<h5 class="mt-3 mb-1">Users Online (<span class="chat-count"></span>)</h5>
<div id="online3" class="col text-left d-lg-none bg-white mb-4 pb-2" style="max-width:300px"></div>
{% if (orgies)|length > 1 %}
<h5>Orgy Playlist</h5>
@ -532,7 +532,7 @@
<br><br><br><br><br><br><br><br>
{% elif request.path.startswith('/chat/') %}
<div class="mx-2">
<h5 class="mt-2 mb-3">Members ({{sorted_memberships|length}})</h5>
<h5 class="mt-3 mb-1">Members ({{sorted_memberships|length}})</h5>
<ul class="col text-left d-lg-none bg-white mb-4 pb-2" style="max-width:300px;list-style-type:none">
{% for membership in sorted_memberships %}
{% set user = membership.user %}

View File

@ -48,7 +48,7 @@
{% if v %}
{%- if FEATURES['CHAT'] -%}
<button type="button" class="nobackground border-0 col px-0 btn btn-dead m-0 {% if not get_running_orgy(v) %}pt-0{% endif %}">
<a href="/chat" class="text-decoration-none">
<a href="/chat/1" class="text-decoration-none">
<div class="text-center {% if request|selected_tab=='chat' %}text-primary{% else %}text-muted{% endif %}">
{%- if get_running_orgy(v) -%}
<img src="{{SITE_FULL_IMAGES}}/i/orgy.webp" width="30">

View File

@ -12,6 +12,7 @@
<div class="container pb-4 pb-md-2">
<div class="row justify-content-around" id="main-content-row">
<div class="col h-100 {% block customPadding %}custom-gutters{% endblock %}" id="main-content-col">
{% if chat.id != 1 %}
<h5 class="mt-2 mb-3 ml-1 toggleable d-mob-none" style="display:inline-block">{{chat.name}}</h5>
<b class="mt-2 mb-2 ml-1 text-small toggleable d-md-none" style="display:inline-block">{{chat.name}}</b>
@ -28,6 +29,7 @@
{% else %}
<button id="leave-private-chat" type="submit" class="btn btn-danger ml-1 ml-md-3 px-2 text-small-sm" data-nonce="{{g.nonce}}" data-onclick="areyousure(this)" data-areyousure="postToastReload(this, '/chat/{{chat.id}}/leave')">Leave</button>
{% endif %}
{% endif %}
<div id="chat-group-template" class="d-none">
{{macros.chat_group_template()}}
@ -42,6 +44,22 @@
</div>
<div class="col text-left d-none d-lg-block pt-3 pb-5" style="max-width:313px">
{% if chat.id == 1 %}
<div class="col text-left d-none d-lg-block pt-3 pb-5" style="max-width:300px">
{% if SITE_NAME == 'WPD' %}
<h5>Chat Rules</h5>
<ul class="mt-2 mb-4">
<li>Follow the rules mentioned on <a href="/sidebar">the sidebar</a>.</li>
<li>Do not try to crash the chatroom with malicious code (CSS).</li>
<li>Do not send porn in chat.</li>
<li>Do not spam.</li>
</ul>
{% endif %}
<h5>Users Online (<span class="chat-count"></span>)</h5>
<div id="online" class="mt-2 {% if SITE_NAME == 'WPD' %}online-wpd{% endif %}"></div>
</div>
{% else %}
<h5>Members ({{sorted_memberships|length}})</h5>
<div id="members" class="mt-2">
{% for membership in sorted_memberships %}
@ -67,6 +85,7 @@
</li>
{% endfor %}
</div>
{% endif %}
</div>
</div>

View File

@ -329,7 +329,7 @@
{%- endif -%}
</span>
<i class="quote btn fas fa-reply ml-auto" data-nonce="{{g.nonce}}" data-onclick="quote(this)"></i>
{% if not request.path.startswith("/chat/") and v.admin_level >= PERMS.POST_COMMENT_MODERATION %}
{% if chat.id == 1 and v.admin_level >= PERMS.POST_COMMENT_MODERATION %}
<i class="btn del delconfirm fas fa-trash-alt"></i>
<i class="btn d-none del delmsg fas fa-trash-alt text-danger" data-nonce="{{g.nonce}}" data-onclick="del(this)"></i>
{% endif %}
@ -382,23 +382,6 @@
</div>
{% endmacro %}
{% macro chat_users_list() %}
<div class="col text-left d-none d-lg-block pt-3 pb-5" style="max-width:300px">
{% if SITE_NAME == 'WPD' %}
<h5>Chat Rules</h5>
<ul class="mt-2 mb-4">
<li>Follow the rules mentioned on <a href="/sidebar">the sidebar</a>.</li>
<li>Do not try to crash the chatroom with malicious code (CSS).</li>
<li>Do not send porn in chat.</li>
<li>Do not spam.</li>
</ul>
{% endif %}
<h5>Users Online (<span class="chat-count"></span>)</h5>
<div id="online" class="mt-2 {% if SITE_NAME == 'WPD' %}online-wpd{% endif %}"></div>
</div>
{% endmacro %}
{% macro time_filter_buttons() %}
<div class="dropdown dropdown-actions mr-2">
<button type="button" class="btn btn-secondary dropdown-toggle" id="dropdownMenuButton" data-bs-toggle="dropdown">

View File

@ -20,10 +20,6 @@ server {
proxy_pass http://localhost:5001/socket.io;
include includes/headers;
}
location =/chat {
proxy_pass http://localhost:5001/chat;
include includes/headers;
}
location =/reply {
proxy_pass http://localhost:5001/reply;
include includes/headers;