2022-11-15 09:19:08 +00:00
import atexit
2022-03-24 19:44:12 +00:00
import time
2022-09-24 22:05:50 +00:00
import uuid
2023-10-02 06:12:31 +00:00
from hashlib import md5
2022-11-15 09:19:08 +00:00
2023-09-05 15:32:57 +00:00
from flask_socketio import SocketIO , emit , join_room , leave_room
2023-02-16 13:41:58 +00:00
from flask import request
2022-11-15 09:19:08 +00:00
from files . helpers . actions import *
2022-07-11 09:52:59 +00:00
from files . helpers . alerts import *
2022-12-11 23:44:34 +00:00
from files . helpers . config . const import *
2023-09-22 17:01:30 +00:00
from files . helpers . slurs_and_profanities import censor_slurs_profanities
2022-06-24 14:30:59 +00:00
from files . helpers . regex import *
2023-03-06 19:52:31 +00:00
from files . helpers . media import *
2023-02-07 03:31:49 +00:00
from files . helpers . sanitize import *
2022-12-26 03:14:02 +00:00
from files . helpers . alerts import push_notif
2022-11-15 09:19:08 +00:00
from files . routes . wrappers import *
Bring back orgies (watchparties), now controllable by admins, and generally better in all ways (#165)
This PR adds orgies back into rdrama. Long ago, snakes made the original orgy code, and it was super fun. But he had to rush it out, and ended up making it a bit unsustainable, and had a couple questionable coding decisions, which meant that it had to be removed. Hey, the man literally did it in a few hours before the DB trial continued, lmao.
Anyways, I took my own approach to it. I do not use iframes, i just just repurpose code from /chat window. Because I had that freedom, I also moved things around to make the user experience a bit better. I also added a title to give users some context about what's happening. Check it out
![image](/attachments/6719146c-4922-4d75-967d-8d424a09b198)
Most importantly, this is all configurable from the site. Admins with the permission "ORGIES" will see this in their control panel
![image](/attachments/423d6046-a11d-4e84-bd2c-a2a641afd552)
Nigga, idk where to put it, so I made my own category.
If there is no orgy in progress, admins will see this:
![image](/attachments/7c64b9fa-cdf4-4986-a0c4-f2324878062e)
Click the button, and, viola, the orgy begins.
If there is an orgy in progress, the page will look like this:
![image](/attachments/b65be4b3-5db1-43cb-8857-7d3a8ea24ca7)
Click the button, and the orgy stops.
If an orgy is in progress, navigating to /chat will take the user to the orgy seemlessly. But what if they don't want to participate, liek some kind of spoilsport? Just navigate to /old_chat.
That's just about it, it's really that simple. I have lots of ideas for the future, but I'll let that wait til later :).
A few notes about implementation:
- I moved some functionality out of /templates/chat.html and into /templates/util/macros.html. This is just so I could reference the code directly from my new template, /templates/orgy.html.
- The orgy is stored as a single row in the new table "orgies". Okay, I know this is a little silly, but you know what they say: "if it's stupid and it works, it's not stupid". (tbf the oceangate ceo also said that)
Co-authored-by: Chuck Sneed <sneed@formerlychucks.net>
Reviewed-on: https://fsdfsd.net/rDrama/rDrama/pulls/165
Co-authored-by: HeyMoon <heymoon@noreply.fsdfsd.net>
Co-committed-by: HeyMoon <heymoon@noreply.fsdfsd.net>
2023-07-02 23:55:37 +00:00
from files . classes . orgy import *
2022-11-15 09:19:08 +00:00
from files . __main__ import app , cache , limiter
2022-03-20 20:41:54 +00:00
2023-01-21 10:36:21 +00:00
socketio = SocketIO (
app ,
async_mode = ' gevent ' ,
2023-09-05 19:45:00 +00:00
max_http_buffer_size = 8388608 ,
2023-01-21 10:36:21 +00:00
)
2022-03-24 19:44:12 +00:00
2023-01-23 06:04:02 +00:00
muted = cache . get ( f ' muted ' ) or { }
2023-09-05 15:32:57 +00:00
2023-09-26 20:05:39 +00:00
ALLOWED_REFERRERS = {
f ' { SITE_FULL } /chat ' ,
f ' { SITE_FULL } /notifications/messages ' ,
}
2023-09-06 14:29:31 +00:00
2023-09-05 15:32:57 +00:00
messages = cache . get ( f ' messages ' ) or {
f ' { SITE_FULL } /chat ' : { } ,
}
typing = {
f ' { SITE_FULL } /chat ' : [ ] ,
}
online = {
2023-10-04 19:53:54 +00:00
f ' { SITE_FULL } /chat ' : [ ] ,
2023-09-05 15:32:57 +00:00
}
2023-09-17 20:58:08 +00:00
cache . set ( ' loggedin_chat ' , len ( online [ f ' { SITE_FULL } /chat ' ] ) , timeout = 0 )
2022-03-24 19:44:12 +00:00
2023-09-14 17:20:44 +00:00
def auth_required_socketio ( f ) :
2023-07-24 10:33:04 +00:00
def wrapper ( * args , * * kwargs ) :
v = get_logged_in_user ( )
if not v : return ' ' , 401
2023-09-14 17:20:44 +00:00
if v . is_permabanned : return ' ' , 403
2023-09-28 20:47:24 +00:00
if request . referrer :
g . referrer = request . referrer . split ( ' ? ' ) [ 0 ]
2023-10-05 07:33:08 +00:00
if g . referrer not in ALLOWED_REFERRERS :
g . referrer = None
2023-09-28 20:47:24 +00:00
else :
g . referrer = None
2023-07-24 10:33:04 +00:00
return make_response ( f ( * args , v = v , * * kwargs ) )
wrapper . __name__ = f . __name__
return wrapper
2023-09-14 17:20:44 +00:00
def is_not_banned_socketio ( f ) :
2023-07-24 10:33:04 +00:00
def wrapper ( * args , * * kwargs ) :
v = get_logged_in_user ( )
if not v : return ' ' , 401
2023-09-14 17:20:44 +00:00
if v . is_suspended : return ' ' , 403
2023-09-28 20:47:24 +00:00
if request . referrer :
g . referrer = request . referrer . split ( ' ? ' ) [ 0 ]
2023-10-05 07:33:08 +00:00
if g . referrer not in ALLOWED_REFERRERS :
g . referrer = None
2023-09-28 20:47:24 +00:00
else :
g . referrer = None
2023-07-24 10:33:04 +00:00
return make_response ( f ( * args , v = v , * * kwargs ) )
wrapper . __name__ = f . __name__
return wrapper
2023-08-25 21:00:53 +00:00
CHAT_ERROR_MESSAGE = f " To prevent spam, you ' ll need { TRUESCORE_CC_CHAT_MINIMUM } truescore (this is { TRUESCORE_CC_CHAT_MINIMUM } votes, either up or down, on any threads or comments you ' ve made) in order to access chat. Sorry! I love you 💖 "
2023-08-13 15:13:09 +00:00
2023-09-26 15:19:29 +00:00
@app.post ( ' /refresh_chat ' )
2023-09-26 12:29:36 +00:00
def refresh_chat ( ) :
emit ( ' refresh_chat ' , namespace = ' / ' , to = f ' { SITE_FULL } /chat ' )
return ' '
2022-03-24 19:44:12 +00:00
@app.get ( " /chat " )
2023-07-13 13:50:46 +00:00
@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 )
2023-09-14 16:49:46 +00:00
@auth_required
2022-08-17 20:30:07 +00:00
def chat ( v ) :
2023-08-13 15:13:09 +00:00
if not v . allowed_in_chat :
abort ( 403 , CHAT_ERROR_MESSAGE )
2023-09-05 15:32:57 +00:00
displayed_messages = { k : val for k , val in messages [ f " { SITE_FULL } /chat " ] . items ( ) if val [ " user_id " ] not in v . userblocks }
2023-10-02 06:12:31 +00:00
orgy = get_orgy ( v )
if orgy :
m = md5 ( )
with open ( ' files/assets/subtitles.vtt ' , " rb " ) as f :
data = f . read ( )
m . update ( data )
subtitles_hash = m . hexdigest ( )
return render_template ( " orgy.html " , v = v , messages = displayed_messages , orgy = orgy , subtitles_hash = subtitles_hash )
2023-09-24 22:12:42 +00:00
return render_template ( " chat.html " , v = v , messages = displayed_messages )
2022-03-24 19:44:12 +00:00
@socketio.on ( ' speak ' )
2023-07-24 10:33:04 +00:00
@is_not_banned_socketio
2022-03-24 19:44:12 +00:00
def speak ( data , v ) :
2023-10-05 07:33:08 +00:00
if not g . referrer :
2023-09-05 15:32:57 +00:00
return ' ' , 400
2023-01-21 10:36:21 +00:00
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 )
2023-08-13 15:13:09 +00:00
if not v . allowed_in_chat :
2022-12-04 18:39:06 +00:00
return ' ' , 403
2022-03-24 21:01:04 +00:00
2023-01-20 04:10:25 +00:00
global messages
2022-08-14 02:38:07 +00:00
2023-07-29 19:13:37 +00:00
text = data [ ' message ' ] [ : CHAT_LENGTH_LIMIT ]
2023-03-12 13:02:31 +00:00
if image : text + = f ' \n \n { image } '
2022-11-16 14:00:04 +00:00
if not text : return ' ' , 400
2022-08-14 02:38:07 +00:00
2023-03-19 12:01:54 +00:00
text_html = sanitize ( text , count_emojis = True , chat = True )
2023-01-27 07:07:58 +00:00
if isinstance ( text_html , tuple ) :
return text_html
2022-09-24 22:05:50 +00:00
quotes = data [ ' quotes ' ]
2023-09-29 04:44:47 +00:00
id = secrets . token_urlsafe ( 5 )
2023-01-23 02:45:34 +00:00
2023-01-23 06:04:02 +00:00
self_only = False
vname = v . username . lower ( )
if vname in muted :
if time . time ( ) < muted [ vname ] :
self_only = True
else :
del muted [ vname ]
2023-09-27 22:09:09 +00:00
emit ( " online " , [ online [ g . referrer ] , muted ] , room = g . referrer , broadcast = True )
2023-01-23 06:04:02 +00:00
2023-09-23 17:51:20 +00:00
if SITE == ' rdrama.net ' and v . admin_level < PERMS [ ' BYPASS_ANTISPAM_CHECKS ' ] :
2023-01-24 05:28:56 +00:00
def shut_up ( ) :
self_only = True
2023-03-04 18:16:38 +00:00
muted_until = int ( time . time ( ) + 600 )
2023-01-24 05:28:56 +00:00
muted [ vname ] = muted_until
2023-09-27 22:09:09 +00:00
emit ( " online " , [ online [ g . referrer ] , muted ] , room = g . referrer , broadcast = True )
2023-01-23 06:04:02 +00:00
2023-01-24 05:28:56 +00:00
if not self_only :
2023-09-27 22:09:09 +00:00
identical = [ x for x in list ( messages [ g . referrer ] . values ( ) ) [ - 5 : ] if v . id == x [ ' user_id ' ] and text == x [ ' text ' ] ]
2023-01-24 05:28:56 +00:00
if len ( identical ) > = 3 : shut_up ( )
2023-01-23 06:04:02 +00:00
2023-01-24 05:28:56 +00:00
if not self_only :
2023-09-27 22:09:09 +00:00
count = len ( [ x for x in list ( messages [ g . referrer ] . values ( ) ) [ - 12 : ] if v . id == x [ ' user_id ' ] ] )
2023-01-24 05:28:56 +00:00
if count > = 10 : shut_up ( )
2023-01-23 06:04:02 +00:00
2023-01-24 05:28:56 +00:00
if not self_only :
2023-09-27 22:09:09 +00:00
count = len ( [ x for x in list ( messages [ g . referrer ] . values ( ) ) [ - 25 : ] if v . id == x [ ' user_id ' ] ] )
2023-01-24 05:28:56 +00:00
if count > = 20 : shut_up ( )
2023-01-23 02:45:34 +00:00
2022-11-16 14:00:04 +00:00
data = {
2023-01-22 06:00:50 +00:00
" id " : id ,
2023-09-27 22:09:09 +00:00
" quotes " : quotes if messages [ g . referrer ] . get ( quotes ) else ' ' ,
2022-12-24 22:21:49 +00:00
" hat " : v . hat_active ( v ) [ 0 ] ,
2022-09-27 05:15:22 +00:00
" user_id " : v . id ,
2022-03-28 10:06:57 +00:00
" username " : v . username ,
2022-09-17 14:39:50 +00:00
" namecolor " : v . name_color ,
2023-08-14 12:26:42 +00:00
" patron " : v . patron ,
2022-03-28 10:06:57 +00:00
" text " : text ,
2023-09-26 15:37:07 +00:00
" text_censored " : censor_slurs_profanities ( text , ' chat ' , True ) ,
2022-03-28 10:06:57 +00:00
" text_html " : text_html ,
2023-09-22 17:01:30 +00:00
" text_html_censored " : censor_slurs_profanities ( text_html , ' chat ' ) ,
2022-09-24 20:36:56 +00:00
" time " : int ( time . time ( ) ) ,
2022-03-24 19:44:12 +00:00
}
2023-01-01 11:36:20 +00:00
2022-03-24 21:01:04 +00:00
2022-10-06 05:54:48 +00:00
if v . admin_level > = PERMS [ ' USER_BAN ' ] :
2022-03-24 21:01:04 +00:00
text = text . lower ( )
for i in mute_regex . finditer ( text ) :
2022-08-13 09:24:56 +00:00
username = i . group ( 1 ) . lower ( )
2023-01-23 06:04:02 +00:00
muted_until = int ( int ( i . group ( 2 ) ) * 60 + time . time ( ) )
muted [ username ] = muted_until
2023-09-27 22:09:09 +00:00
emit ( " online " , [ online [ g . referrer ] , muted ] , room = g . referrer , broadcast = True )
2023-01-23 06:04:02 +00:00
self_only = True
2023-02-07 03:31:49 +00:00
if self_only or v . shadowbanned or execute_blackjack ( v , None , text , " chat " ) :
2023-09-15 23:54:58 +00:00
emit ( ' speak ' , data )
2023-01-23 06:04:02 +00:00
else :
2023-09-27 22:09:09 +00:00
emit ( ' speak ' , data , room = g . referrer , broadcast = True )
messages [ g . referrer ] [ id ] = data
messages [ g . referrer ] = dict ( list ( messages [ g . referrer ] . items ( ) ) [ - 250 : ] )
2022-03-24 21:01:04 +00:00
2022-08-15 15:02:19 +00:00
typing = [ ]
2022-12-25 20:30:50 +00:00
2023-09-26 21:30:03 +00:00
return ' ' , 204
2022-03-24 19:44:12 +00:00
2023-01-24 05:28:56 +00:00
def refresh_online ( ) :
2023-09-27 22:09:09 +00:00
emit ( " online " , [ online [ g . referrer ] , muted ] , room = g . referrer , broadcast = True )
2023-09-17 20:58:08 +00:00
cache . set ( ' loggedin_chat ' , len ( online [ f ' { SITE_FULL } /chat ' ] ) , timeout = 0 )
2023-01-20 04:25:35 +00:00
2023-10-04 19:53:54 +00:00
def remove_from_online ( v ) :
for li in online . values ( ) :
for entry in li :
if entry [ 0 ] == v . id :
li . remove ( entry )
2023-10-05 07:33:31 +00:00
if g . referrer and v . username in typing [ g . referrer ] :
2023-10-04 19:55:07 +00:00
typing [ g . referrer ] . remove ( v . username )
2023-09-29 07:33:04 +00:00
2022-03-24 19:44:12 +00:00
@socketio.on ( ' connect ' )
2023-09-14 16:49:46 +00:00
@auth_required_socketio
2022-03-24 19:44:12 +00:00
def connect ( v ) :
2023-10-05 07:33:08 +00:00
if not g . referrer :
2023-09-05 15:32:57 +00:00
return ' ' , 400
2023-01-21 10:54:15 +00:00
2023-09-27 22:09:09 +00:00
if g . referrer == f ' { SITE_FULL } /notifications/messages ' :
2023-09-26 20:05:39 +00:00
join_room ( v . id )
return ' '
2023-09-27 22:09:09 +00:00
join_room ( g . referrer )
2023-09-05 15:32:57 +00:00
2023-10-04 19:53:54 +00:00
remove_from_online ( v )
2022-03-24 19:44:12 +00:00
2023-10-04 19:53:54 +00:00
online [ g . referrer ] . append ( [ v . id , v . username , v . name_color , v . patron ] )
2023-10-04 19:02:53 +00:00
2023-10-04 19:53:54 +00:00
refresh_online ( )
emit ( ' typing ' , typing [ g . referrer ] , room = g . referrer )
return ' ' , 204
2023-10-04 19:02:53 +00:00
2022-03-24 19:44:12 +00:00
@socketio.on ( ' disconnect ' )
2023-09-14 16:49:46 +00:00
@auth_required_socketio
2022-03-24 19:44:12 +00:00
def disconnect ( v ) :
2023-10-04 19:53:54 +00:00
if g . referrer != f ' { SITE_FULL } /notifications/messages ' :
remove_from_online ( v )
2023-09-06 14:29:31 +00:00
2023-10-05 07:33:08 +00:00
if not g . referrer :
2023-09-26 20:47:42 +00:00
return ' ' , 400
2023-09-27 22:09:09 +00:00
elif g . referrer == f ' { SITE_FULL } /notifications/messages ' :
2023-09-26 20:47:42 +00:00
leave_room ( v . id )
else :
2023-09-27 22:09:09 +00:00
leave_room ( g . referrer )
2023-09-27 22:04:19 +00:00
refresh_online ( )
2023-09-07 15:26:31 +00:00
2023-09-26 21:30:03 +00:00
return ' ' , 204
2022-03-24 19:44:12 +00:00
@socketio.on ( ' typing ' )
2023-07-24 10:33:04 +00:00
@is_not_banned_socketio
2022-03-24 19:44:12 +00:00
def typing_indicator ( data , v ) :
2023-10-05 07:33:08 +00:00
if not g . referrer :
2023-09-05 15:32:57 +00:00
return ' ' , 400
2022-03-24 19:44:12 +00:00
2023-09-27 22:09:09 +00:00
if data and v . username not in typing [ g . referrer ] :
typing [ g . referrer ] . append ( v . username )
elif not data and v . username in typing [ g . referrer ] :
typing [ g . referrer ] . remove ( v . username )
2023-09-05 15:32:57 +00:00
2023-09-27 22:09:09 +00:00
emit ( ' typing ' , typing [ g . referrer ] , room = g . referrer , broadcast = True )
2023-09-26 21:30:03 +00:00
return ' ' , 204
2022-03-24 19:44:12 +00:00
2022-09-10 09:31:51 +00:00
@socketio.on ( ' delete ' )
2022-10-06 00:57:08 +00:00
@admin_level_required ( PERMS [ ' POST_COMMENT_MODERATION ' ] )
2023-01-21 10:36:21 +00:00
def delete ( id , v ) :
2023-10-05 07:33:08 +00:00
if not g . referrer :
2023-09-05 15:32:57 +00:00
return ' ' , 400
2023-09-27 22:09:09 +00:00
for k , val in messages [ g . referrer ] . items ( ) :
2023-09-05 15:32:57 +00:00
if k == id :
2023-09-27 22:09:09 +00:00
del messages [ g . referrer ] [ k ]
2023-09-05 15:32:57 +00:00
break
2022-09-10 09:31:51 +00:00
2023-09-27 22:09:09 +00:00
emit ( ' delete ' , id , room = g . referrer , broadcast = True )
2022-09-10 09:31:51 +00:00
2023-09-26 21:30:03 +00:00
return ' ' , 204
2022-09-10 09:31:51 +00:00
2022-03-24 19:44:12 +00:00
def close_running_threads ( ) :
2023-09-12 22:00:57 +00:00
cache . set ( ' messages ' , messages )
cache . set ( ' muted ' , muted )
2022-09-24 04:26:44 +00:00
atexit . register ( close_running_threads )
2023-09-26 20:05:39 +00:00
@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 )
2023-09-26 20:35:41 +00:00
elif user_id and user_id not in { v . id , MODMAIL_ID } | BOT_IDs :
2023-09-26 20:05:39 +00:00
c . unread = True
2023-09-26 20:35:41 +00:00
rendered = render_template ( " comments.html " , v = get_account ( user_id ) , comments = [ c ] )
emit ( ' insert_reply ' , [ parent . id , rendered ] , namespace = ' / ' , to = user_id )
2023-09-26 20:05:39 +00:00
return { " comment " : render_template ( " comments.html " , v = v , comments = [ c ] ) }