2023-02-24 06:31:06 +00:00
from files . classes import *
from files . helpers . alerts import *
from files . helpers . regex import *
from files . helpers . get import *
from files . routes . wrappers import *
from files . __main__ import app , limiter
@app.get ( " /ping_groups " )
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-02-24 06:31:06 +00:00
@auth_required
2023-07-30 00:42:06 +00:00
def ping_groups ( v ) :
2023-03-16 06:27:58 +00:00
groups = g . db . query ( Group ) . order_by ( Group . created_utc ) . all ( )
2023-08-11 21:50:23 +00:00
return render_template ( ' groups.html ' , v = v , groups = groups , cost = GROUP_COST )
2023-02-24 06:31:06 +00:00
@app.post ( " /create_group " )
2023-02-27 05:33:45 +00:00
@limiter.limit ( ' 1/second ' , scope = rpath )
2023-04-02 06:52:26 +00:00
@limiter.limit ( ' 1/second ' , scope = rpath , key_func = get_ID )
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
2023-02-24 06:31:06 +00:00
def create_group ( v ) :
name = request . values . get ( ' name ' )
if not name : abort ( 400 )
name = name . strip ( ) . lower ( )
2023-04-29 21:27:45 +00:00
if name . startswith ( ' slots ' ) or name . startswith ( ' remindme ' ) :
2023-08-11 21:50:23 +00:00
abort ( 400 , " You can ' t make a group with that name! " )
2023-03-04 21:45:12 +00:00
2023-09-08 22:31:18 +00:00
if not hole_group_name_regex . fullmatch ( name ) :
2023-08-11 21:50:23 +00:00
abort ( 400 , " Name does not match the required format! " )
2023-02-24 06:31:06 +00:00
2023-11-04 21:28:44 +00:00
if name in { ' everyone ' , ' jannies ' , ' holejannies ' , ' followers ' , ' commenters ' } or g . db . get ( Group , name ) :
2023-08-11 21:50:23 +00:00
abort ( 400 , " This group already exists! " )
2023-03-02 19:21:38 +00:00
2024-01-12 08:12:35 +00:00
if not v . charge_account ( ' coins/marseybux ' , GROUP_COST ) [ 0 ] :
2023-08-11 21:50:23 +00:00
abort ( 403 , " You don ' t have enough coins or marseybux! " )
2023-03-02 19:21:38 +00:00
2023-03-16 06:27:58 +00:00
g . db . add ( v )
2023-03-02 19:21:38 +00:00
if v . shadowbanned : abort ( 500 )
2024-02-11 13:39:16 +00:00
group = Group (
name = name ,
owner_id = v . id ,
)
2023-03-16 06:27:58 +00:00
g . db . add ( group )
2023-03-02 19:21:38 +00:00
group_membership = GroupMembership (
user_id = v . id ,
group_name = group . name ,
created_utc = time . time ( ) ,
approved_utc = time . time ( )
)
2023-03-16 06:27:58 +00:00
g . db . add ( group_membership )
2023-03-02 19:21:38 +00:00
2023-10-12 18:57:35 +00:00
g . db . flush ( ) #Necessary, to make linkfying the ping group in the notification work
2024-02-08 03:35:37 +00:00
text = f " :!marseyparty: ! { group } has been created by @ { v . username } :marseyparty: "
2024-02-10 15:34:45 +00:00
alert_active_users ( text , v . id , User . group_creation_notifs == True )
2023-02-24 06:31:06 +00:00
2023-08-11 21:50:23 +00:00
return { " message " : f " ! { group } created successfully! " }
2023-02-24 06:31:06 +00:00
2023-02-24 19:29:07 +00:00
@app.post ( " /!<group_name>/apply " )
2023-02-27 05:33:45 +00:00
@limiter.limit ( ' 1/second ' , scope = rpath )
2023-04-02 06:52:26 +00:00
@limiter.limit ( ' 1/second ' , scope = rpath , key_func = get_ID )
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-02-24 06:31:06 +00:00
@auth_required
2023-07-30 00:42:06 +00:00
def join_group ( v , group_name ) :
2023-03-07 18:55:17 +00:00
group_name = group_name . strip ( ) . lower ( )
2023-08-01 22:18:57 +00:00
if group_name == ' verifiedrich ' :
if not v . patron :
abort ( 403 , f " Only { patron } s can join !verifiedrich " )
join = GroupMembership (
user_id = v . id ,
group_name = group_name ,
approved_utc = time . time ( )
)
g . db . add ( join )
return { " message " : " You have joined !verifiedrich successfully! " }
2023-08-01 21:56:13 +00:00
2023-03-16 06:27:58 +00:00
group = g . db . get ( Group , group_name )
2023-02-24 06:31:06 +00:00
if not group : abort ( 404 )
2023-08-01 22:18:57 +00:00
2023-03-16 06:27:58 +00:00
existing = g . db . query ( GroupMembership ) . filter_by ( user_id = v . id , group_name = group_name ) . one_or_none ( )
2023-02-24 06:31:06 +00:00
if not existing :
join = GroupMembership ( user_id = v . id , group_name = group_name )
2023-03-16 06:27:58 +00:00
g . db . add ( join )
2024-02-12 13:12:42 +00:00
notified_ids = [ group . owner_id ]
notified_ids + = [ x [ 0 ] for x in g . db . query ( GroupMembership . user_id ) . filter_by ( group_name = group_name , is_mod = True ) . all ( ) ]
notified_ids = list ( set ( notified_ids ) )
for uid in notified_ids :
send_notification ( uid , f " @ { v . username } has applied to join ! { group } . You can approve or reject the application [here](/! { group } ). " )
2023-02-24 06:31:06 +00:00
2024-02-18 20:46:43 +00:00
return { " message " : f " Application to ! { group } submitted successfully! " }
2023-02-24 06:31:06 +00:00
2023-02-24 19:29:07 +00:00
@app.post ( " /!<group_name>/leave " )
2023-02-27 05:33:45 +00:00
@limiter.limit ( ' 1/second ' , scope = rpath )
2023-04-02 06:52:26 +00:00
@limiter.limit ( ' 1/second ' , scope = rpath , key_func = get_ID )
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-02-24 06:31:06 +00:00
@auth_required
2023-07-30 00:42:06 +00:00
def leave_group ( v , group_name ) :
2023-03-07 18:55:17 +00:00
group_name = group_name . strip ( ) . lower ( )
2023-03-16 06:27:58 +00:00
group = g . db . get ( Group , group_name )
2023-02-24 06:31:06 +00:00
if not group : abort ( 404 )
2023-03-16 06:27:58 +00:00
existing = g . db . query ( GroupMembership ) . filter_by ( user_id = v . id , group_name = group_name ) . one_or_none ( )
2023-02-24 19:29:07 +00:00
if existing :
if existing . approved_utc :
text = f " @ { v . username } has left ! { group } "
msg = f " You have left ! { group } successfully! "
else :
text = f " @ { v . username } has cancelled their application to ! { group } "
msg = f " You have cancelled your application to ! { group } successfully! "
2024-02-11 11:00:37 +00:00
send_notification ( group . owner_id , text )
2023-03-16 06:27:58 +00:00
g . db . delete ( existing )
2023-05-05 21:45:25 +00:00
2023-02-25 21:44:02 +00:00
return { " message " : msg }
2023-02-24 19:29:07 +00:00
2023-02-24 19:43:27 +00:00
return { " message " : ' ' }
2023-02-24 19:29:07 +00:00
2023-02-25 22:06:43 +00:00
@app.get ( " /!<group_name> " )
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-02-24 19:29:07 +00:00
@auth_required
2023-07-30 00:42:06 +00:00
def memberships ( v , group_name ) :
2023-03-07 18:55:17 +00:00
group_name = group_name . strip ( ) . lower ( )
2023-03-16 06:27:58 +00:00
group = g . db . get ( Group , group_name )
2023-02-24 19:29:07 +00:00
if not group : abort ( 404 )
2023-02-24 19:53:40 +00:00
2024-02-11 14:47:06 +00:00
members = \
[ g . db . query ( GroupMembership ) . filter (
2023-02-25 21:44:02 +00:00
GroupMembership . group_name == group_name ,
2024-02-11 14:47:06 +00:00
GroupMembership . approved_utc != None ,
GroupMembership . user_id == group . owner_id ,
) . one ( ) ] + \
g . db . query ( GroupMembership ) . filter (
GroupMembership . group_name == group_name ,
GroupMembership . approved_utc != None ,
GroupMembership . user_id != group . owner_id ,
) . order_by ( GroupMembership . is_mod . desc ( ) , GroupMembership . approved_utc ) . all ( )
2023-02-24 19:53:40 +00:00
2023-03-16 06:27:58 +00:00
applications = g . db . query ( GroupMembership ) . filter (
2023-02-25 21:44:02 +00:00
GroupMembership . group_name == group_name ,
GroupMembership . approved_utc == None
) . order_by ( GroupMembership . created_utc ) . all ( )
return render_template ( ' group_memberships.html ' , v = v , group = group , members = members , applications = applications )
2023-02-24 06:31:06 +00:00
@app.post ( " /!<group_name>/<user_id>/approve " )
2023-02-27 05:33:45 +00:00
@limiter.limit ( ' 1/second ' , scope = rpath )
2023-04-02 06:52:26 +00:00
@limiter.limit ( ' 1/second ' , scope = rpath , key_func = get_ID )
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-02-24 06:31:06 +00:00
@auth_required
2023-07-30 00:42:06 +00:00
def group_approve ( v , group_name , user_id ) :
2023-03-07 18:55:17 +00:00
group_name = group_name . strip ( ) . lower ( )
2023-03-16 06:27:58 +00:00
group = g . db . get ( Group , group_name )
2023-02-24 06:31:06 +00:00
if not group : abort ( 404 )
2023-05-05 21:45:25 +00:00
2023-08-08 18:08:29 +00:00
if not v . mods_group ( group ) :
abort ( 403 , f " Only the group owner and its mods can approve applications! " )
2023-05-05 21:45:25 +00:00
2023-03-16 06:27:58 +00:00
application = g . db . query ( GroupMembership ) . filter_by ( user_id = user_id , group_name = group . name ) . one_or_none ( )
2023-02-24 06:31:06 +00:00
if not application :
abort ( 404 , " There is no application to approve! " )
2023-12-07 18:45:19 +00:00
if not application . approved_utc :
2023-02-24 06:31:06 +00:00
application . approved_utc = time . time ( )
2023-03-16 06:27:58 +00:00
g . db . add ( application )
2023-12-07 18:45:19 +00:00
if v . id != application . user_id :
2024-02-18 20:46:43 +00:00
send_repeatable_notification ( application . user_id , f " @ { v . username } has approved your application to ! { group } " )
2023-02-24 06:31:06 +00:00
2023-02-24 19:41:25 +00:00
return { " message " : f ' You have approved @ { application . user . username } successfully! ' }
2023-02-24 06:31:06 +00:00
@app.post ( " /!<group_name>/<user_id>/reject " )
2023-02-27 05:33:45 +00:00
@limiter.limit ( ' 1/second ' , scope = rpath )
2023-04-02 06:52:26 +00:00
@limiter.limit ( ' 1/second ' , scope = rpath , key_func = get_ID )
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-02-24 06:31:06 +00:00
@auth_required
2023-07-30 00:42:06 +00:00
def group_reject ( v , group_name , user_id ) :
2023-03-07 18:55:17 +00:00
group_name = group_name . strip ( ) . lower ( )
2023-03-16 06:27:58 +00:00
group = g . db . get ( Group , group_name )
2023-02-24 06:31:06 +00:00
if not group : abort ( 404 )
2023-05-05 21:45:25 +00:00
2023-08-08 18:08:29 +00:00
if not v . mods_group ( group ) :
abort ( 403 , f " Only the group owner and its mods can reject memberships! " )
2023-02-24 19:41:25 +00:00
2023-03-16 06:27:58 +00:00
membership = g . db . query ( GroupMembership ) . filter_by ( user_id = user_id , group_name = group . name ) . one_or_none ( )
2023-02-24 19:41:25 +00:00
if not membership :
abort ( 404 , " There is no membership to reject! " )
2023-02-24 06:31:06 +00:00
2023-12-06 20:54:42 +00:00
if v . id != membership . user_id : #implies kicking and not leaving
2024-02-11 11:00:37 +00:00
if membership . user_id == group . owner_id :
2023-12-06 20:54:42 +00:00
abort ( 403 , " You can ' t kick the group owner! " )
2024-02-11 11:00:37 +00:00
if v . id != group . owner_id and membership . is_mod :
2023-12-06 20:54:42 +00:00
abort ( 403 , " Only the group owner can kick mods! " )
2023-08-10 10:17:27 +00:00
2023-05-16 10:40:57 +00:00
if v . id == membership . user_id :
msg = f " You have left ! { group } successfully! "
2024-02-11 13:44:10 +00:00
if v . id == group . owner_id :
new_owner_id = g . db . query ( GroupMembership . user_id ) . filter (
GroupMembership . user_id != v . id ,
GroupMembership . group_name == group . name ,
GroupMembership . approved_utc != None ,
) . order_by ( GroupMembership . is_mod . desc ( ) , GroupMembership . approved_utc ) . first ( ) [ 0 ]
if new_owner_id :
send_repeatable_notification ( new_owner_id , f " @ { v . username } (! { group } ' s owner) has left the group, You ' re now the new owner! " )
group . owner_id = new_owner_id
g . db . add ( group )
2023-02-24 19:41:25 +00:00
else :
2023-05-16 10:40:57 +00:00
if membership . approved_utc :
2024-02-18 20:46:43 +00:00
text = f " @ { v . username } has kicked you from ! { group } "
2023-05-16 10:40:57 +00:00
msg = f " You have kicked @ { membership . user . username } successfully! "
else :
2024-02-18 20:46:43 +00:00
text = f " @ { v . username } has rejected your application to ! { group } "
2023-05-16 10:40:57 +00:00
msg = f " You have rejected @ { membership . user . username } successfully! "
2023-05-16 10:28:50 +00:00
send_repeatable_notification ( membership . user_id , text )
2023-02-24 19:41:25 +00:00
2023-03-16 06:27:58 +00:00
g . db . delete ( membership )
2023-02-24 19:43:27 +00:00
2023-08-20 03:03:07 +00:00
g . db . flush ( )
2023-07-07 00:37:17 +00:00
count = g . db . query ( GroupMembership ) . filter_by ( group_name = group . name ) . count ( )
if not count :
g . db . commit ( ) #need it to fix "Dependency rule tried to blank-out primary key column 'group_memberships.group_name' on instance"
g . db . delete ( group )
2023-02-24 19:41:25 +00:00
return { " message " : msg }
2023-08-08 18:08:29 +00:00
@app.post ( " /!<group_name>/<user_id>/add_mod " )
@limiter.limit ( ' 1/second ' , scope = rpath )
@limiter.limit ( ' 1/second ' , scope = rpath , key_func = get_ID )
@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 group_add_mod ( v , group_name , user_id ) :
group_name = group_name . strip ( ) . lower ( )
group = g . db . get ( Group , group_name )
if not group : abort ( 404 )
2024-02-11 11:00:37 +00:00
if v . id != group . owner_id :
2023-08-08 18:08:29 +00:00
abort ( 403 , f " Only the group owner (@ { group . owner . username } ) can add mods! " )
membership = g . db . query ( GroupMembership ) . filter_by ( user_id = user_id , group_name = group . name ) . one_or_none ( )
if not membership :
abort ( 404 , " The user is not a member of the group! " )
membership . is_mod = True
g . db . add ( membership )
send_repeatable_notification ( membership . user_id , f " @ { v . username } (! { group } ' s owner) has added you as a mod! " )
return { " message " : f ' You have added @ { membership . user . username } as a mod successfully! ' }
@app.post ( " /!<group_name>/<user_id>/remove_mod " )
@limiter.limit ( ' 1/second ' , scope = rpath )
@limiter.limit ( ' 1/second ' , scope = rpath , key_func = get_ID )
@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 group_remove_mod ( v , group_name , user_id ) :
group_name = group_name . strip ( ) . lower ( )
group = g . db . get ( Group , group_name )
if not group : abort ( 404 )
2024-02-11 11:00:37 +00:00
if v . id != group . owner_id :
2023-08-08 18:08:29 +00:00
abort ( 403 , f " Only the group owner (@ { group . owner . username } ) can remove mods! " )
membership = g . db . query ( GroupMembership ) . filter_by ( user_id = user_id , group_name = group . name ) . one_or_none ( )
if not membership :
abort ( 404 , " The user is not a member of the group! " )
membership . is_mod = False
g . db . add ( membership )
send_repeatable_notification ( membership . user_id , f " @ { v . username } (! { group } ' s owner) has removed you as a mod! " )
return { " message " : f ' You have removed @ { membership . user . username } as a mod successfully! ' }
2024-02-11 11:31:46 +00:00
@app.post ( " /!<group_name>/usurp " )
@limiter.limit ( ' 1/second ' , scope = rpath )
@limiter.limit ( ' 1/second ' , scope = rpath , key_func = get_ID )
@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 group_usurp ( v , group_name ) :
group_name = group_name . strip ( ) . lower ( )
group = g . db . get ( Group , group_name )
if not group : abort ( 404 )
2024-02-15 22:17:04 +00:00
if v . mods_group ( group ) :
2024-02-18 23:22:09 +00:00
abort ( 403 , f " You ' re a mod of ! { group . name } can ' t usurp it! " )
2024-02-15 22:17:04 +00:00
2024-02-11 11:31:46 +00:00
if not v . is_member_of_group ( group ) :
abort ( 403 , " Only members of groups can usurp them! " )
2024-02-18 21:57:20 +00:00
search_html = f ''' %</a> has % your application to <a href= " /! { group . name } " rel= " nofollow " >! { group . name } </a></p> '''
one_month_ago = time . time ( ) - 2629800
is_active = g . db . query ( Notification . created_utc ) . join ( Notification . comment ) . filter (
Notification . created_utc > one_month_ago ,
Comment . author_id == AUTOJANNY_ID ,
Comment . parent_post == None ,
Comment . body_html . like ( search_html ) ,
2024-02-11 11:53:30 +00:00
) . first ( )
2024-02-11 11:57:17 +00:00
2024-02-18 23:09:09 +00:00
month_old_applications = g . db . query ( GroupMembership . user_id ) . filter (
GroupMembership . group_name == group . name ,
GroupMembership . approved_utc == None ,
GroupMembership . created_utc < one_month_ago ,
) . first ( )
if is_active or not month_old_applications :
2024-02-13 09:41:38 +00:00
abort ( 403 , " The current regime has reviewed a membership application in the past month, so you can ' t usurp them! " )
2024-02-11 11:53:30 +00:00
2024-02-13 09:41:38 +00:00
send_repeatable_notification ( group . owner_id , f " @ { v . username } has usurped control of ! { group . name } from you. This was possible because you (and your mods) have spent more than a month not reviewing membership applications. Be active next time sweaty :!marseycheeky: " )
2024-02-11 11:31:46 +00:00
group . owner_id = v . id
g . db . add ( group )
return { " message " : f ' You have usurped control of ! { group . name } successfully! ' }
2024-02-13 13:00:58 +00:00
@app.post ( " /!<group_name>/description " )
@limiter.limit ( ' 1/second ' , scope = rpath )
@limiter.limit ( ' 1/second ' , scope = rpath , key_func = get_ID )
@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 group_change_description ( v , group_name ) :
group_name = group_name . strip ( ) . lower ( )
group = g . db . get ( Group , group_name )
if not group : abort ( 404 )
if v . id != group . owner_id :
abort ( 403 , f " Only the group owner (@ { group . owner . username } ) can change the description! " )
description = request . values . get ( ' description ' )
if description :
description = description . strip ( )
2024-02-15 18:48:36 +00:00
if len ( description ) > 100 :
abort ( 400 , " New description is too long (max 100 characters) " )
2024-02-15 19:35:38 +00:00
description_html = filter_emojis_only ( description , link = True )
2024-02-15 19:01:40 +00:00
if len ( description_html ) > 1000 :
2024-02-15 18:48:36 +00:00
abort ( 400 , " Rendered description is too long! " )
2024-02-13 13:00:58 +00:00
else :
description = None
2024-02-15 18:48:36 +00:00
description_html = None
2024-02-13 13:00:58 +00:00
group . description = description
2024-02-15 18:48:36 +00:00
group . description_html = description_html
2024-02-13 13:00:58 +00:00
g . db . add ( group )
return { " message " : ' Description changed successfully! ' }