From e7ab0be33ba79d21b173fc91c5e1ed99c85950ec Mon Sep 17 00:00:00 2001 From: Aevann Date: Tue, 26 Sep 2023 23:05:39 +0300 Subject: [PATCH] make /notifications/messages LIVE --- files/assets/js/chat.js | 40 +-------- files/assets/js/flash.js | 35 ++++++++ files/assets/js/messages.js | 11 +++ files/routes/chat.py | 125 ++++++++++++++++++++++++++++- files/routes/users.py | 107 ------------------------ files/templates/chat.html | 2 +- files/templates/notifications.html | 6 ++ files/templates/orgy.html | 2 +- nginx.conf | 4 + 9 files changed, 183 insertions(+), 149 deletions(-) create mode 100644 files/assets/js/flash.js create mode 100644 files/assets/js/messages.js diff --git a/files/assets/js/chat.js b/files/assets/js/chat.js index 81dfbaf27..b9ff2f8b0 100644 --- a/files/assets/js/chat.js +++ b/files/assets/js/chat.js @@ -13,43 +13,11 @@ const socket = io() const chatline = document.getElementsByClassName('chat-line')[0] const box = document.getElementById('chat-window') const ta = document.getElementById('input-text') -const icon = document.querySelector("link[rel~='icon']") const vid = document.getElementById('vid').value -const site_name = document.getElementById('site_name').value const slurreplacer = document.getElementById('slurreplacer').value -let notifs = 0; -let focused = true; let is_typing = false; -let alert=true; - -function flash(){ - let title = document.getElementsByTagName('title')[0] - if (notifs >= 1 && !focused){ - title.innerHTML = `[+${notifs}] Chat`; - if (alert) { - icon.href = `/i/${site_name}/alert.ico?v=3009` - alert=false; - } - else { - icon.href = `/i/${site_name}/icon.webp?x=6` - alert=true; - } - setTimeout(flash, 500) - } - else { - icon.href = `/i/${site_name}/icon.webp?x=6` - notifs = 0 - title.innerHTML = 'Chat'; - } - - if (is_typing) { - is_typing = false - socket.emit('typing', false); - } -} - const blocked_user_ids = document.getElementById('blocked_user_ids').value.split(', ') @@ -77,7 +45,7 @@ socket.on('speak', function(json) { notifs = notifs + 1; if (notifs == 1) { - setTimeout(flash, 500); + flash(); } const users = document.getElementsByClassName('user_id'); @@ -247,12 +215,6 @@ socket.on('online', function(data){ bs_trigger(document.getElementById('online3')) }) -addEventListener('blur', function(){ - focused = false -}) -addEventListener('focus', function(){ - focused = true -}) let timer_id; function remove_typing() { diff --git a/files/assets/js/flash.js b/files/assets/js/flash.js new file mode 100644 index 000000000..8d63b468c --- /dev/null +++ b/files/assets/js/flash.js @@ -0,0 +1,35 @@ +const SITE_NAME = document.querySelector("[property='og:site_name']").content +const icon = document.querySelector("link[rel~='icon']") +const page_title = document.getElementsByTagName('title')[0].innerHTML + +let notifs = 0 +let focused = true +let alert = true; + +addEventListener('blur', function(){ + focused = false +}) +addEventListener('focus', function(){ + focused = true +}) + +function flash(){ + let title = document.getElementsByTagName('title')[0] + if (notifs >= 1 && !focused){ + title.innerHTML = `[+${notifs}] ${page_title}` + if (alert) { + icon.href = `/i/${SITE_NAME}/alert.ico?v=3009` + alert = false + } + else { + icon.href = `/i/${SITE_NAME}/icon.webp?x=6` + alert = true + } + setTimeout(flash, 500) + } + else { + icon.href = `/i/${SITE_NAME}/icon.webp?x=6` + notifs = 0 + title.innerHTML = page_title + } +} diff --git a/files/assets/js/messages.js b/files/assets/js/messages.js new file mode 100644 index 000000000..0695e5e9d --- /dev/null +++ b/files/assets/js/messages.js @@ -0,0 +1,11 @@ +const socket = io() + +socket.on('insert_reply', function(data) { + const replies = document.getElementById(`replies-of-c_${data[0]}`) + replies.insertAdjacentHTML('beforeend', data[1]); + + notifs = notifs + 1; + if (notifs == 1) { + flash(); + } +}) diff --git a/files/routes/chat.py b/files/routes/chat.py index 4cee40e6f..7a3810c54 100644 --- a/files/routes/chat.py +++ b/files/routes/chat.py @@ -27,7 +27,10 @@ socketio = SocketIO( muted = cache.get(f'muted') or {} -ALLOWED_REFERRERS = {f'{SITE_FULL}/chat'} +ALLOWED_REFERRERS = { + f'{SITE_FULL}/chat', + f'{SITE_FULL}/notifications/messages', +} messages = cache.get(f'messages') or { f'{SITE_FULL}/chat': {}, @@ -192,6 +195,10 @@ def connect(v): if request.referrer not in ALLOWED_REFERRERS: return '', 400 + if request.referrer == f'{SITE_FULL}/notifications/messages': + join_room(v.id) + return '' + join_room(request.referrer) if [v.username, v.id, v.name_color, v.patron] not in online[request.referrer]: @@ -213,6 +220,10 @@ def disconnect(v): if request.referrer not in ALLOWED_REFERRERS: return '', 400 + if request.referrer == f'{SITE_FULL}/notifications/messages': + leave_room(v.id) + return '' + leave_room(request.referrer) for val in online.values(): @@ -262,3 +273,115 @@ def close_running_threads(): cache.set('messages', messages) cache.set('muted', muted) atexit.register(close_running_threads) + + +@app.post("/reply") +@limiter.limit('1/second', scope=rpath) +@limiter.limit('1/second', scope=rpath, key_func=get_ID) +@limiter.limit("6/minute;50/hour;200/day", deduct_when=lambda response: response.status_code < 400) +@limiter.limit("6/minute;50/hour;200/day", deduct_when=lambda response: response.status_code < 400, key_func=get_ID) +@auth_required +def messagereply(v): + body = request.values.get("body", "") + body = body[:COMMENT_BODY_LENGTH_LIMIT].strip() + + id = request.values.get("parent_id") + parent = get_comment(id, v=v) + + if parent.parent_post or parent.wall_user_id: + abort(403, "You can only reply to messages!") + + user_id = parent.author.id + + if v.is_permabanned and parent.sentto != MODMAIL_ID: + abort(403, "You are permabanned and may not reply to messages!") + elif v.is_muted and parent.sentto == MODMAIL_ID: + abort(403, "You are forbidden from replying to modmail!") + + if parent.sentto == MODMAIL_ID: user_id = None + elif v.id == user_id: user_id = parent.sentto + + user = None + + if user_id: + user = get_account(user_id, v=v, include_blocks=True) + if hasattr(user, 'is_blocking') and user.is_blocking: + abort(403, f"You're blocking @{user.username}") + elif (v.admin_level <= PERMS['MESSAGE_BLOCKED_USERS'] + and hasattr(user, 'is_blocked') and user.is_blocked): + abort(403, f"You're blocked by @{user.username}") + + if user.has_muted(v): + abort(403, f"@{user.username} is muting notifications from you, so messaging them is pointless!") + + if not g.is_tor and get_setting("dm_media"): + body = process_files(request.files, v, body, is_dm=True, dm_user=user) + body = body[:COMMENT_BODY_LENGTH_LIMIT].strip() #process_files potentially adds characters to the post + + if not body: abort(400, "Message is empty!") + + body_html = sanitize(body) + + if len(body_html) > COMMENT_BODY_HTML_LENGTH_LIMIT: + abort(400, "Message too long!") + + if parent.sentto == MODMAIL_ID: + sentto = MODMAIL_ID + else: + sentto = user_id + + c = Comment(author_id=v.id, + parent_post=None, + parent_comment_id=id, + top_comment_id=parent.top_comment_id, + level=parent.level + 1, + sentto=sentto, + body=body, + body_html=body_html, + ) + g.db.add(c) + g.db.flush() + execute_blackjack(v, c, c.body_html, 'message') + execute_under_siege(v, c, c.body_html, 'message') + + if user_id and user_id not in {v.id, MODMAIL_ID} | BOT_IDs: + notif = g.db.query(Notification).filter_by(comment_id=c.id, user_id=user_id).one_or_none() + if not notif: + notif = Notification(comment_id=c.id, user_id=user_id) + g.db.add(notif) + + title = f'New message from @{c.author_name}' + + url = f'{SITE_FULL}/notifications/messages' + + push_notif({user_id}, title, body, url) + + top_comment = c.top_comment + + if top_comment.sentto == MODMAIL_ID: + admin_ids = [x[0] for x in g.db.query(User.id).filter(User.admin_level >= PERMS['NOTIFICATIONS_MODMAIL'], User.id != v.id)] + + if SITE == 'watchpeopledie.tv': + if AEVANN_ID in admin_ids: + admin_ids.remove(AEVANN_ID) + if 'delete' in top_comment.body.lower() and 'account' in top_comment.body.lower(): + admin_ids.remove(15447) + + if parent.author.id not in admin_ids + [v.id]: + admin_ids.append(parent.author.id) + + #Don't delete unread notifications, so the replies don't get collapsed and they get highlighted + ids = [top_comment.id] + [x.id for x in top_comment.replies(sort="old")] + notifications = g.db.query(Notification).filter(Notification.read == True, Notification.comment_id.in_(ids), Notification.user_id.in_(admin_ids)) + for n in notifications: + g.db.delete(n) + + for admin in admin_ids: + notif = Notification(comment_id=c.id, user_id=admin) + g.db.add(notif) + else: + c.unread = True + rendered = render_template("comments.html", v=get_account(top_comment.sentto), comments=[c]) + emit('insert_reply', [parent.id, rendered], namespace='/', to=top_comment.sentto) + + return {"comment": render_template("comments.html", v=v, comments=[c])} diff --git a/files/routes/users.py b/files/routes/users.py index 0112eb178..305dce9a1 100644 --- a/files/routes/users.py +++ b/files/routes/users.py @@ -684,113 +684,6 @@ def message2(v, username=None, id=None): return {"message": "Message sent!"} -@app.post("/reply") -@limiter.limit('1/second', scope=rpath) -@limiter.limit('1/second', scope=rpath, key_func=get_ID) -@limiter.limit("6/minute;50/hour;200/day", deduct_when=lambda response: response.status_code < 400) -@limiter.limit("6/minute;50/hour;200/day", deduct_when=lambda response: response.status_code < 400, key_func=get_ID) -@auth_required -def messagereply(v): - body = request.values.get("body", "") - body = body[:COMMENT_BODY_LENGTH_LIMIT].strip() - - id = request.values.get("parent_id") - parent = get_comment(id, v=v) - - if parent.parent_post or parent.wall_user_id: - abort(403, "You can only reply to messages!") - - user_id = parent.author.id - - if v.is_permabanned and parent.sentto != MODMAIL_ID: - abort(403, "You are permabanned and may not reply to messages!") - elif v.is_muted and parent.sentto == MODMAIL_ID: - abort(403, "You are forbidden from replying to modmail!") - - if parent.sentto == MODMAIL_ID: user_id = None - elif v.id == user_id: user_id = parent.sentto - - user = None - - if user_id: - user = get_account(user_id, v=v, include_blocks=True) - if hasattr(user, 'is_blocking') and user.is_blocking: - abort(403, f"You're blocking @{user.username}") - elif (v.admin_level <= PERMS['MESSAGE_BLOCKED_USERS'] - and hasattr(user, 'is_blocked') and user.is_blocked): - abort(403, f"You're blocked by @{user.username}") - - if user.has_muted(v): - abort(403, f"@{user.username} is muting notifications from you, so messaging them is pointless!") - - if not g.is_tor and get_setting("dm_media"): - body = process_files(request.files, v, body, is_dm=True, dm_user=user) - body = body[:COMMENT_BODY_LENGTH_LIMIT].strip() #process_files potentially adds characters to the post - - if not body: abort(400, "Message is empty!") - - body_html = sanitize(body) - - if len(body_html) > COMMENT_BODY_HTML_LENGTH_LIMIT: - abort(400, "Message too long!") - - if parent.sentto == MODMAIL_ID: - sentto = MODMAIL_ID - else: - sentto = user_id - - c = Comment(author_id=v.id, - parent_post=None, - parent_comment_id=id, - top_comment_id=parent.top_comment_id, - level=parent.level + 1, - sentto=sentto, - body=body, - body_html=body_html, - ) - g.db.add(c) - g.db.flush() - execute_blackjack(v, c, c.body_html, 'message') - execute_under_siege(v, c, c.body_html, 'message') - - if user_id and user_id not in {v.id, MODMAIL_ID} | BOT_IDs: - notif = g.db.query(Notification).filter_by(comment_id=c.id, user_id=user_id).one_or_none() - if not notif: - notif = Notification(comment_id=c.id, user_id=user_id) - g.db.add(notif) - - title = f'New message from @{c.author_name}' - - url = f'{SITE_FULL}/notifications/messages' - - push_notif({user_id}, title, body, url) - - top_comment = c.top_comment - - if top_comment.sentto == MODMAIL_ID: - admin_ids = [x[0] for x in g.db.query(User.id).filter(User.admin_level >= PERMS['NOTIFICATIONS_MODMAIL'], User.id != v.id)] - - if SITE == 'watchpeopledie.tv': - if AEVANN_ID in admin_ids: - admin_ids.remove(AEVANN_ID) - if 'delete' in top_comment.body.lower() and 'account' in top_comment.body.lower(): - admin_ids.remove(15447) - - if parent.author.id not in admin_ids + [v.id]: - admin_ids.append(parent.author.id) - - #Don't delete unread notifications, so the replies don't get collapsed and they get highlighted - ids = [top_comment.id] + [x.id for x in top_comment.replies(sort="old")] - notifications = g.db.query(Notification).filter(Notification.read == True, Notification.comment_id.in_(ids), Notification.user_id.in_(admin_ids)) - for n in notifications: - g.db.delete(n) - - for admin in admin_ids: - notif = Notification(comment_id=c.id, user_id=admin) - g.db.add(notif) - - return {"comment": render_template("comments.html", v=v, comments=[c])} - @app.get("/2faqr/") @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) diff --git a/files/templates/chat.html b/files/templates/chat.html index 4edc90693..fa5e18258 100644 --- a/files/templates/chat.html +++ b/files/templates/chat.html @@ -31,11 +31,11 @@ - + diff --git a/files/templates/notifications.html b/files/templates/notifications.html index 36e5f4a6d..badb63f55 100644 --- a/files/templates/notifications.html +++ b/files/templates/notifications.html @@ -140,6 +140,12 @@ +{% if request.path == '/notifications/messages' %} + + + +{% endif %} + {% endblock %} {% block GIFpicker %}{% endblock %} diff --git a/files/templates/orgy.html b/files/templates/orgy.html index d40c3a289..3601e2698 100644 --- a/files/templates/orgy.html +++ b/files/templates/orgy.html @@ -47,11 +47,11 @@ - + diff --git a/nginx.conf b/nginx.conf index cdd81d54b..659aa46d7 100644 --- a/nginx.conf +++ b/nginx.conf @@ -30,6 +30,10 @@ server { proxy_pass http://localhost:5001/refresh_chat; include includes/headers; } + location /reply { + proxy_pass http://localhost:5001/reply; + include includes/headers; + } location =/offline.html { alias /d/files/templates/errors/offline.html;