MarseyWorld/files/routes/groups.py

474 lines
17 KiB
Python
Raw Normal View History

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 *
2024-08-12 18:07:55 +00:00
from files.helpers.actions import execute_under_siege
2023-02-24 06:31:06 +00:00
from files.routes.wrappers import *
from files.__main__ import app, limiter
@app.get("/ping_groups")
@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):
groups = g.db.query(Group).order_by(Group.created_utc)
owner = request.values.get('owner')
if owner:
owner_id = get_user(owner, attributes=[User.id]).id
groups = groups.filter_by(owner_id=owner_id)
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)
@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
2023-02-24 06:31:06 +00:00
def create_group(v):
name = request.values.get("name", "").strip().lstrip("!").strip().lower()
2024-08-11 20:11:04 +00:00
if not name: stop(400)
2023-02-24 06:31:06 +00:00
if name.startswith('slots') or name.startswith('remindme'):
2024-08-11 20:11:04 +00:00
stop(400, "You can't make a group with that name!")
2023-03-04 21:45:12 +00:00
if not hole_group_name_regex.fullmatch(name):
2024-08-11 20:11:04 +00:00
stop(400, "Name does not match the required format!")
2023-02-24 06:31:06 +00:00
if name in {'everyone', 'jannies', 'holejannies', 'followers', 'commenters'} or g.db.get(Group, name):
2024-08-11 20:11:04 +00:00
stop(400, "This group already exists!")
2023-03-02 19:21:38 +00:00
2024-03-06 02:04:25 +00:00
charge_reason = f'Cost of creating <a href="/!{name}">!{name}</a>'
2024-03-09 12:19:38 +00:00
if not v.charge_account('coins/marseybux', GROUP_COST, charge_reason):
2024-08-11 20:11:04 +00:00
stop(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)
2024-08-11 20:11:04 +00:00
if v.shadowbanned: stop(500)
2023-03-02 19:21:38 +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(),
is_mod=True,
2023-03-02 19:21:38 +00:00
)
2023-03-16 06:27:58 +00:00
g.db.add(group_membership)
2023-03-02 19:21:38 +00:00
g.db.flush() #Necessary, to make linkfying the ping group in the notification work
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
2024-03-06 00:11:35 +00:00
cache.delete("group_count")
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)
@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)
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()
2024-03-27 14:17:24 +00:00
if group_name == 'verifiedrich' and not v.patron:
2024-08-11 20:11:04 +00:00
stop(403, f"Only {patron}s can join !verifiedrich")
2024-03-27 14:17:24 +00:00
if group_name in {'verifiedrich', 'focusgroup'}:
join = GroupMembership(
user_id=v.id,
group_name=group_name,
approved_utc = time.time()
)
g.db.add(join)
2024-03-27 14:17:24 +00:00
return {"message": f"You have joined !{group_name} successfully!"}
2023-03-16 06:27:58 +00:00
group = g.db.get(Group, group_name)
2024-08-11 20:11:04 +00:00
if not group: stop(404)
2024-10-16 23:00:39 +00:00
blacklist = g.db.query(GroupBlacklist.user_id).filter_by(user_id=v.id, group_name=group_name).one_or_none()
if blacklist:
stop(403, f"You're blacklisted from !{group_name}")
2024-08-12 18:07:55 +00:00
execute_under_siege(v, group, "ping group application")
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)
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))
2024-03-02 08:10:59 +00:00
if 2249 in notified_ids:
notified_ids.remove(2249)
2024-03-03 20:58:32 +00:00
text = f"@{v.username} has applied to join !{group}. You can approve or reject the application [here](/!{group})."
cid = notif_comment(text)
for uid in notified_ids:
add_notif(cid, uid, text, pushnotif_url=f'{SITE_FULL}/!{group}')
2023-02-24 06:31:06 +00:00
return {"message": f"Application to !{group} submitted successfully!"}
2023-02-24 06:31:06 +00:00
2024-11-09 20:28:24 +00:00
def delete_group(membership, group, v):
g.db.delete(membership)
for blacklist in g.db.query(GroupBlacklist).filter_by(group_name=group.name):
g.db.delete(blacklist)
g.db.delete(group)
text = f':marseydisintegrate: !{group} has been deleted by @{v.username} :!marseydisintegrate:'
alert_active_users(text, None, User.group_creation_notifs == True)
return {"message": f"You have deleted !{group} successfully!"}
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)
@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)
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)
2024-08-11 20:11:04 +00:00
if not group: stop(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!"
func = send_repeatable_notification
2023-02-24 19:29:07 +00:00
else:
text = f"@{v.username} has cancelled their application to !{group}"
msg = f"You have cancelled your application to !{group} successfully!"
func = send_notification
2023-02-24 19:29:07 +00:00
2024-10-16 06:22:18 +00:00
if v.id == group.owner_id:
new_owner_id = g.db.query(GroupMembership.user_id).filter(
GroupMembership.user_id != group.owner_id,
GroupMembership.group_name == group.name,
GroupMembership.approved_utc != None,
).order_by(GroupMembership.is_mod.desc(), GroupMembership.approved_utc).first()
if new_owner_id:
new_owner_id = new_owner_id[0]
send_repeatable_notification(new_owner_id, f"@{group.owner.username} (!{group}'s owner) has left the group, You're now the new owner!")
group.owner_id = new_owner_id
g.db.add(group)
2024-11-09 20:28:24 +00:00
else:
return delete_group(existing, group, v)
2024-10-16 06:22:18 +00:00
else:
func(group.owner_id, text)
2024-10-16 06:22:18 +00:00
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>")
@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):
2024-11-08 19:23:37 +00:00
page = get_page()
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)
2024-08-11 20:11:04 +00:00
if not group: stop(404)
2023-02-24 19:53:40 +00:00
2024-11-09 10:38:40 +00:00
members = g.db.query(GroupMembership).join(GroupMembership.user).options(
joinedload(GroupMembership.user),
joinedload(GroupMembership.approver),
).filter(
GroupMembership.group_name == group_name,
GroupMembership.approved_utc != None,
GroupMembership.user_id != group.owner_id,
)
2024-11-08 19:23:37 +00:00
total = members.count()
2024-11-08 19:38:21 +00:00
if page == 1: size = 499
else: size = 500
members = members.order_by(GroupMembership.is_mod.desc(), GroupMembership.approved_utc).offset(size * (page - 1)).limit(size).all()
2024-11-08 19:23:37 +00:00
if page == 1:
2024-11-09 10:38:40 +00:00
owner = [g.db.query(GroupMembership).options(
joinedload(GroupMembership.user),
joinedload(GroupMembership.approver),
).filter(
2024-11-08 19:23:37 +00:00
GroupMembership.group_name == group_name,
GroupMembership.approved_utc != None,
GroupMembership.user_id == group.owner_id,
).one()]
members = owner + members
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, page=page, total=total, size=size)
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)
@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)
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)
2024-08-11 20:11:04 +00:00
if not group: stop(404)
2023-05-05 21:45:25 +00:00
if not v.mods_group(group):
2024-08-11 20:11:04 +00:00
stop(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:
2024-08-11 20:11:04 +00:00
stop(404, "There is no application to approve!")
2023-02-24 06:31:06 +00:00
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()
2024-10-16 05:08:17 +00:00
application.approver_id = v.id
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:
send_repeatable_notification(application.user_id, f"@{v.username} has approved your application to !{group}")
2023-02-24 06:31:06 +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)
@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)
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)
2024-08-11 20:11:04 +00:00
if not group: stop(404)
2023-05-05 21:45:25 +00:00
if not v.mods_group(group):
2024-08-11 20:11:04 +00:00
stop(403, f"Only the group owner and its mods can reject memberships!")
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()
if not membership:
2024-08-11 20:11:04 +00:00
stop(404, "There is no membership to reject!")
2024-10-16 23:00:39 +00:00
uid = membership.user_id
2023-02-24 06:31:06 +00:00
2024-10-16 06:22:18 +00:00
if membership.user_id == group.owner_id and v.admin_level < PERMS["MODS_EVERY_GROUP"]:
stop(403, "You can't kick the group owner!")
2023-12-06 20:54:42 +00:00
2024-10-16 06:22:18 +00:00
if v.id != group.owner_id and membership.is_mod and v.admin_level < PERMS["MODS_EVERY_GROUP"]:
stop(403, "Only the group owner can kick mods!")
2023-08-10 10:17:27 +00:00
2024-10-16 06:22:18 +00:00
if membership.approved_utc:
text = f"@{v.username} has kicked you from !{group}"
msg = f"You have kicked @{membership.user.username} successfully!"
else:
2024-10-16 06:22:18 +00:00
text = f"@{v.username} has rejected your application to !{group}"
msg = f"You have rejected @{membership.user.username} successfully!"
send_repeatable_notification(membership.user_id, text)
2024-09-28 10:51:05 +00:00
if membership.user_id == group.owner_id:
new_owner_id = g.db.query(GroupMembership.user_id).filter(
GroupMembership.user_id != group.owner_id,
GroupMembership.group_name == group.name,
GroupMembership.approved_utc != None,
2024-10-07 16:16:34 +00:00
).order_by(GroupMembership.is_mod.desc(), GroupMembership.approved_utc).first()
2024-09-28 10:51:05 +00:00
if new_owner_id:
2024-10-07 16:16:34 +00:00
new_owner_id = new_owner_id[0]
2024-10-16 06:22:18 +00:00
send_repeatable_notification(new_owner_id, f"@{group.owner.username} (!{group}'s owner) has been kicked from the group by site admins, You're now the new owner!")
2024-09-28 10:51:05 +00:00
group.owner_id = new_owner_id
g.db.add(group)
2024-11-09 20:28:24 +00:00
else:
return delete_group(membership, group, v)
2024-09-28 10:51:05 +00:00
2023-03-16 06:27:58 +00:00
g.db.delete(membership)
2023-02-24 19:43:27 +00:00
2024-11-09 20:28:24 +00:00
if v.id != uid:
2024-10-16 23:00:39 +00:00
group_blacklist = GroupBlacklist(
user_id=uid,
group_name=group.name,
blacklister_id=v.id,
)
g.db.add(group_blacklist)
return {"message": msg}
@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)
2024-08-11 20:11:04 +00:00
if not group: stop(404)
if v.id != group.owner_id:
2024-08-11 20:11:04 +00:00
stop(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:
2024-08-11 20:11:04 +00:00
stop(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)
2024-08-11 20:11:04 +00:00
if not group: stop(404)
if v.id != group.owner_id:
2024-08-11 20:11:04 +00:00
stop(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:
2024-08-11 20:11:04 +00:00
stop(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!'}
@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)
2024-08-11 20:11:04 +00:00
if not group: stop(404)
if v.mods_group(group):
2024-11-09 20:12:30 +00:00
stop(403, f"You're a mod of !{group.name} and can't usurp it!")
if not v.is_member_of_group(group):
2024-08-11 20:11:04 +00:00
stop(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>'''
two_mo_ago = time.time() - 86400 * 60
2024-02-18 21:57:20 +00:00
is_active = g.db.query(Notification.created_utc).join(Notification.comment).filter(
Notification.created_utc > two_mo_ago,
2024-02-18 21:57:20 +00:00
Comment.author_id == AUTOJANNY_ID,
Comment.parent_post == None,
Comment.body_html.like(search_html),
).first()
2024-02-11 11:57:17 +00:00
two_mo_old_applications = g.db.query(GroupMembership.user_id).join(GroupMembership.user).filter(
GroupMembership.group_name == group.name,
GroupMembership.approved_utc == None,
GroupMembership.created_utc < two_mo_ago,
2024-07-24 01:23:35 +00:00
User.shadowbanned == None,
).first()
if is_active or not two_mo_old_applications:
send_notification(group.owner_id, f"@{v.username} has tried to usurp control of !{group.name} from you and failed because you reviewed a membership application in the past 2 months!")
2024-05-22 23:23:48 +00:00
g.db.commit()
2024-08-11 20:11:04 +00:00
stop(403, "The current regime has reviewed a membership application in the past 2 months, so you can't usurp them!")
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 2 months not reviewing membership applications. Be active next time sweaty :!marseycheeky:")
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)
2024-08-11 20:11:04 +00:00
if not group: stop(404)
2024-02-13 13:00:58 +00:00
if v.id != group.owner_id:
2024-08-11 20:11:04 +00:00
stop(403, f"Only the group owner (@{group.owner.username}) can change the description!")
2024-02-13 13:00:58 +00:00
description = request.values.get('description')
if description:
description = description.strip()
if len(description) > 100:
2024-08-11 20:11:04 +00:00
stop(400, "New description is too long (max 100 characters)")
description_html = filter_emojis_only(description, link=True)
2024-02-15 19:01:40 +00:00
if len(description_html) > 1000:
2024-08-11 20:11:04 +00:00
stop(400, "Rendered description is too long!")
2024-02-13 13:00:58 +00:00
else:
description = None
description_html = None
2024-02-13 13:00:58 +00:00
group.description = description
group.description_html = description_html
2024-02-13 13:00:58 +00:00
g.db.add(group)
return {"message": 'Description changed successfully!'}
2024-10-16 23:00:39 +00:00
@app.post("/!<group_name>/<user_id>/unblacklist")
@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_unblacklist(v, group_name, user_id):
group_name = group_name.strip().lower()
group = g.db.get(Group, group_name)
if not group: stop(404)
if not v.mods_group(group):
stop(403, "Only the group owner and its mods can unblacklist someone!")
blacklist = g.db.query(GroupBlacklist).filter_by(user_id=user_id, group_name=group.name).one_or_none()
if not blacklist:
stop(404, "This user is not blacklisted!")
g.db.delete(blacklist)
send_repeatable_notification(user_id, f"@{v.username} has unblacklisted you from !{group}")
2024-10-16 23:00:39 +00:00
u = get_account(user_id)
return {"message": f"@{u.username} has been unblacklisted from !{group} successfully!"}