MarseyWorld/files/routes/admin.py

1518 lines
40 KiB
Python
Raw Normal View History

2022-05-04 23:09:46 +00:00
import time
import re
2022-05-04 23:09:46 +00:00
from os import remove
from PIL import Image as IMAGE
from files.helpers.wrappers import *
from files.helpers.alerts import *
from files.helpers.sanitize import *
from files.helpers.security import *
from files.helpers.get import *
2022-05-22 16:13:19 +00:00
from files.helpers.media import *
2022-05-04 23:09:46 +00:00
from files.helpers.const import *
from files.helpers.actions import *
2022-10-13 07:27:56 +00:00
from files.helpers.cloudflare import *
2022-05-04 23:09:46 +00:00
from files.classes import *
from flask import *
from files.__main__ import app, cache, limiter
from .front import frontlist
from .login import check_for_alts
2022-05-04 23:09:46 +00:00
from files.helpers.discord import add_role
import datetime
2022-05-04 23:09:46 +00:00
import requests
from urllib.parse import quote, urlencode
@app.post('/kippy')
2022-10-06 03:26:39 +00:00
@admin_level_required(PERMS['PRINT_MARSEYBUX_FOR_KIPPY_ON_PCMEMES'])
def kippy(v):
2022-10-06 03:26:39 +00:00
if SITE == 'rdrama.net': abort(404)
2022-06-24 13:19:53 +00:00
kippy = get_account(KIPPY_ID)
kippy.procoins += 10000
g.db.add(kippy)
return '10k marseycoins printed!'
2022-05-25 20:16:26 +00:00
@app.get('/admin/loggedin')
2022-10-06 03:29:14 +00:00
@admin_level_required(PERMS['VIEW_ACTIVE_USERS'])
2022-05-25 20:16:26 +00:00
def loggedin_list(v):
2022-06-20 20:25:03 +00:00
ids = [x for x,val in cache.get(f'{SITE}_loggedin').items() if time.time()-val < LOGGEDIN_ACTIVE_TIME]
2022-05-26 20:31:08 +00:00
users = g.db.query(User).filter(User.id.in_(ids)).order_by(User.admin_level.desc(), User.truecoins.desc()).all()
2022-05-25 21:25:23 +00:00
return render_template("loggedin.html", v=v, users=users)
2022-05-25 20:16:26 +00:00
2022-05-26 20:31:08 +00:00
@app.get('/admin/loggedout')
2022-10-06 03:29:14 +00:00
@admin_level_required(PERMS['VIEW_ACTIVE_USERS'])
2022-05-26 20:31:08 +00:00
def loggedout_list(v):
2022-06-20 20:25:03 +00:00
users = sorted([val[1] for x,val in cache.get(f'{SITE}_loggedout').items() if time.time()-val[0] < LOGGEDIN_ACTIVE_TIME])
2022-05-26 20:31:08 +00:00
return render_template("loggedout.html", v=v, users=users)
2022-05-04 23:09:46 +00:00
@app.get('/admin/merge/<id1>/<id2>')
@admin_level_required(PERMS['USER_MERGE'])
2022-05-04 23:09:46 +00:00
def merge(v, id1, id2):
if v.id != AEVANN_ID: abort(403)
if time.time() - session.get('verified', 0) > 3:
session.pop("lo_user", None)
path = request.path
qs = urlencode(dict(request.values))
argval = quote(f"{path}?{qs}", safe='')
return redirect(f"/login?redirect={argval}")
user1 = get_account(id1)
user2 = get_account(id2)
awards = g.db.query(AwardRelationship).filter_by(user_id=user2.id)
comments = g.db.query(Comment).filter_by(author_id=user2.id)
submissions = g.db.query(Submission).filter_by(author_id=user2.id)
badges = g.db.query(Badge).filter_by(user_id=user2.id)
mods = g.db.query(Mod).filter_by(user_id=user2.id)
exiles = g.db.query(Exile).filter_by(user_id=user2.id)
for award in awards:
award.user_id = user1.id
g.db.add(award)
for comment in comments:
comment.author_id = user1.id
g.db.add(comment)
for submission in submissions:
submission.author_id = user1.id
g.db.add(submission)
for badge in badges:
if not user1.has_badge(badge.badge_id):
badge.user_id = user1.id
g.db.add(badge)
g.db.flush()
2022-05-04 23:09:46 +00:00
for mod in mods:
if not user1.mods(mod.sub):
mod.user_id = user1.id
g.db.add(mod)
g.db.flush()
2022-05-04 23:09:46 +00:00
for exile in exiles:
if not user1.exiled_from(exile.sub):
exile.user_id = user1.id
g.db.add(exile)
g.db.flush()
2022-05-04 23:09:46 +00:00
for kind in ('comment_count', 'post_count', 'winnings', 'received_award_count', 'coins_spent', 'lootboxes_bought', 'coins', 'truecoins', 'procoins'):
2022-05-04 23:09:46 +00:00
amount = getattr(user1, kind) + getattr(user2, kind)
setattr(user1, kind, amount)
setattr(user2, kind, 0)
g.db.add(user1)
g.db.add(user2)
2022-09-01 23:59:56 +00:00
online = cache.get(ONLINE_STR)
2022-05-04 23:09:46 +00:00
cache.clear()
2022-09-01 23:59:56 +00:00
cache.set(ONLINE_STR, online)
2022-05-04 23:09:46 +00:00
return redirect(user1.url)
@app.get('/admin/merge_all/<id>')
@admin_level_required(PERMS['USER_MERGE'])
2022-05-04 23:09:46 +00:00
def merge_all(v, id):
if v.id != AEVANN_ID: abort(403)
if time.time() - session.get('verified', 0) > 3:
session.pop("lo_user", None)
path = request.path
qs = urlencode(dict(request.values))
argval = quote(f"{path}?{qs}", safe='')
return redirect(f"/login?redirect={argval}")
user = get_account(id)
alt_ids = [x.id for x in user.alts_unique]
things = g.db.query(AwardRelationship).filter(AwardRelationship.user_id.in_(alt_ids)).all() + g.db.query(Mod).filter(Mod.user_id.in_(alt_ids)).all() + g.db.query(Exile).filter(Exile.user_id.in_(alt_ids)).all()
for thing in things:
thing.user_id = user.id
g.db.add(thing)
things = g.db.query(Submission).filter(Submission.author_id.in_(alt_ids)).all() + g.db.query(Comment).filter(Comment.author_id.in_(alt_ids)).all()
for thing in things:
thing.author_id = user.id
g.db.add(thing)
badges = g.db.query(Badge).filter(Badge.user_id.in_(alt_ids)).all()
for badge in badges:
if not user.has_badge(badge.badge_id):
badge.user_id = user.id
g.db.add(badge)
g.db.flush()
2022-05-04 23:09:46 +00:00
for alt in user.alts_unique:
for kind in ('comment_count', 'post_count', 'winnings', 'received_award_count', 'coins_spent', 'lootboxes_bought', 'coins', 'truecoins', 'procoins'):
2022-05-04 23:09:46 +00:00
amount = getattr(user, kind) + getattr(alt, kind)
setattr(user, kind, amount)
setattr(alt, kind, 0)
g.db.add(alt)
g.db.add(user)
2022-09-01 23:59:56 +00:00
online = cache.get(ONLINE_STR)
2022-05-04 23:09:46 +00:00
cache.clear()
2022-09-01 23:59:56 +00:00
cache.set(ONLINE_STR, online)
2022-05-04 23:09:46 +00:00
return redirect(user.url)
@app.post("/@<username>/make_admin")
2022-10-06 03:37:18 +00:00
@admin_level_required(PERMS['ADMIN_ADD'])
2022-05-04 23:09:46 +00:00
def make_admin(v, username):
2022-05-29 18:36:51 +00:00
if SITE == 'rdrama.net': abort(403)
2022-05-04 23:09:46 +00:00
user = get_user(username)
2022-10-06 03:37:18 +00:00
user.admin_level = PERMS['ADMIN_ADD_PERM_LEVEL']
2022-05-04 23:09:46 +00:00
g.db.add(user)
ma = ModAction(
kind="make_admin",
user_id=v.id,
target_user_id=user.id
)
g.db.add(ma)
return {"message": f"@{user.username} has been made admin!"}
2022-05-04 23:09:46 +00:00
@app.post("/@<username>/remove_admin")
2022-10-06 03:37:18 +00:00
@admin_level_required(PERMS['ADMIN_REMOVE'])
2022-05-04 23:09:46 +00:00
def remove_admin(v, username):
user = get_user(username)
user.admin_level = 0
g.db.add(user)
ma = ModAction(
kind="remove_admin",
user_id=v.id,
target_user_id=user.id
)
g.db.add(ma)
return {"message": f"@{user.username} has been removed as admin!"}
2022-05-04 23:09:46 +00:00
2022-08-26 21:53:17 +00:00
@app.post("/distribute/<option_id>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
2022-10-06 05:59:27 +00:00
@admin_level_required(PERMS['POST_BETS_DISTRIBUTE'])
2022-08-26 21:53:17 +00:00
def distribute(v, option_id):
autojanny = get_account(AUTOJANNY_ID)
if autojanny.coins == 0: abort(400, "@AutoJanny has 0 coins")
2022-08-26 21:53:17 +00:00
try: option_id = int(option_id)
except: abort(400)
try: option = g.db.get(SubmissionOption, option_id)
except: abort(404)
2022-09-08 18:25:45 +00:00
if option.exclusive != 2: abort(403)
option.exclusive = 3
g.db.add(option)
2022-08-26 21:53:17 +00:00
post = option.post
pool = 0
for o in post.options:
2022-09-30 15:38:47 +00:00
if o.exclusive >= 2: pool += o.upvotes
pool *= POLL_BET_COINS
2022-08-26 21:53:17 +00:00
2022-10-12 16:33:00 +00:00
autojanny.charge_account('coins', pool)
2022-08-26 21:53:17 +00:00
if autojanny.coins < 0: autojanny.coins = 0
g.db.add(autojanny)
votes = option.votes
coinsperperson = int(pool / len(votes))
cid = notif_comment(f"You won {coinsperperson} coins betting on [{post.title}]({post.shortlink}) :marseyparty:")
for vote in votes:
u = vote.user
u.coins += coinsperperson
add_notif(cid, u.id)
cid = notif_comment(f"You lost the {POLL_BET_COINS} coins you bet on [{post.title}]({post.shortlink}) :marseylaugh:")
2022-08-26 21:53:17 +00:00
losing_voters = []
for o in post.options:
2022-09-23 12:09:33 +00:00
if o.exclusive == 2:
2022-08-26 21:53:17 +00:00
losing_voters.extend([x.user_id for x in o.votes])
for uid in losing_voters:
add_notif(cid, uid)
2022-05-04 23:09:46 +00:00
2022-08-26 21:53:17 +00:00
ma = ModAction(
kind="distribute",
user_id=v.id,
target_submission_id=post.id
)
g.db.add(ma)
return {"message": f"Each winner has received {coinsperperson} coins!"}
2022-05-04 23:09:46 +00:00
@app.post("/@<username>/revert_actions")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@admin_level_required(PERMS['ADMIN_ACTIONS_REVERT'])
def revert_actions(v, username):
user = get_user(username)
ma = ModAction(
kind="revert",
user_id=v.id,
target_user_id=user.id
)
g.db.add(ma)
cutoff = int(time.time()) - 86400
posts = [x[0] for x in g.db.query(ModAction.target_submission_id).filter(ModAction.user_id == user.id, ModAction.created_utc > cutoff, ModAction.kind == 'ban_post').all()]
posts = g.db.query(Submission).filter(Submission.id.in_(posts)).all()
comments = [x[0] for x in g.db.query(ModAction.target_comment_id).filter(ModAction.user_id == user.id, ModAction.created_utc > cutoff, ModAction.kind == 'ban_comment').all()]
comments = g.db.query(Comment).filter(Comment.id.in_(comments)).all()
for item in posts + comments:
item.is_banned = False
item.ban_reason = None
item.is_approved = v.id
g.db.add(item)
users = (x[0] for x in g.db.query(ModAction.target_user_id).filter(ModAction.user_id == user.id, ModAction.created_utc > cutoff, ModAction.kind.in_(('shadowban', 'ban_user'))).all())
users = g.db.query(User).filter(User.id.in_(users)).all()
for user in users:
user.shadowbanned = None
user.unban_utc = 0
user.ban_reason = None
if user.is_banned:
user.is_banned = 0
send_repeatable_notification(user.id, f"@{v.username} (Admin) has unbanned you!")
g.db.add(user)
for u in user.alts:
u.shadowbanned = None
u.unban_utc = 0
u.ban_reason = None
if u.is_banned:
u.is_banned = 0
send_repeatable_notification(u.id, f"@{v.username} (Admin) has unbanned you!")
g.db.add(u)
2022-10-08 06:08:45 +00:00
return {"message": f"@{user.username}'s admin actions have been reverted!"}
2022-05-04 23:09:46 +00:00
@app.post("/@<username>/club_allow")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
2022-10-06 03:47:42 +00:00
@admin_level_required(PERMS['USER_CLUB_ALLOW_BAN'])
2022-05-04 23:09:46 +00:00
def club_allow(v, username):
u = get_user(username, v=v)
if u.admin_level >= v.admin_level: abort(403, 'noob')
2022-05-04 23:09:46 +00:00
u.club_allowed = True
g.db.add(u)
for x in u.alts_unique:
x.club_allowed = True
g.db.add(x)
ma = ModAction(
kind="club_allow",
user_id=v.id,
target_user_id=u.id
)
g.db.add(ma)
send_repeatable_notification(u.id, f"@{v.username} (Admin) has inducted you into the {CC_TITLE}!")
return {"message": f"@{u.username} has been allowed into the {CC_TITLE}!"}
2022-05-04 23:09:46 +00:00
@app.post("/@<username>/club_ban")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
2022-10-06 03:47:42 +00:00
@admin_level_required(PERMS['USER_CLUB_ALLOW_BAN'])
2022-05-04 23:09:46 +00:00
def club_ban(v, username):
u = get_user(username, v=v)
if u.admin_level >= v.admin_level: abort(403, 'noob')
2022-05-04 23:09:46 +00:00
u.club_allowed = False
for x in u.alts_unique:
u.club_allowed = False
g.db.add(x)
ma = ModAction(
kind="club_ban",
user_id=v.id,
target_user_id=u.id
)
g.db.add(ma)
send_repeatable_notification(u.id, f"@{v.username} (Admin) has disallowed you from the {CC_TITLE}!")
return {"message": f"@{u.username} has been disallowed from the {CC_TITLE}. Deserved."}
2022-05-04 23:09:46 +00:00
@app.get("/admin/shadowbanned")
2022-10-12 06:53:32 +00:00
@admin_level_required(PERMS['USER_SHADOWBAN'])
2022-05-04 23:09:46 +00:00
def shadowbanned(v):
2022-05-25 20:16:26 +00:00
users = g.db.query(User).filter(User.shadowbanned != None).order_by(User.shadowbanned).all()
2022-05-04 23:09:46 +00:00
return render_template("shadowbanned.html", v=v, users=users)
@app.get("/admin/image_posts")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
2022-05-04 23:09:46 +00:00
def image_posts_listing(v):
try: page = int(request.values.get('page', 1))
except: page = 1
posts = g.db.query(Submission).order_by(Submission.id.desc())
firstrange = 25 * (page - 1)
secondrange = firstrange+26
posts = [x.id for x in posts if x.is_image][firstrange:secondrange]
next_exists = (len(posts) > 25)
posts = get_posts(posts[:25], v=v)
return render_template("admin/image_posts.html", v=v, listing=posts, next_exists=next_exists, page=page, sort="new")
@app.get("/admin/reported/posts")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
2022-05-04 23:09:46 +00:00
def reported_posts(v):
page = max(1, int(request.values.get("page", 1)))
listing = g.db.query(Submission).filter_by(
is_approved=None,
is_banned=False
2022-06-29 07:22:18 +00:00
).join(Submission.flags).order_by(Submission.id.desc()).offset(25 * (page - 1)).limit(26)
2022-05-04 23:09:46 +00:00
listing = [p.id for p in listing]
next_exists = len(listing) > 25
listing = listing[:25]
listing = get_posts(listing, v=v)
return render_template("admin/reported_posts.html",
2022-09-04 23:15:37 +00:00
next_exists=next_exists, listing=listing, page=page, v=v)
2022-05-04 23:09:46 +00:00
@app.get("/admin/reported/comments")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
2022-05-04 23:09:46 +00:00
def reported_comments(v):
page = max(1, int(request.values.get("page", 1)))
listing = g.db.query(Comment
2022-09-04 23:15:37 +00:00
).filter_by(
2022-05-04 23:09:46 +00:00
is_approved=None,
is_banned=False
2022-06-29 07:22:18 +00:00
).join(Comment.flags).order_by(Comment.id.desc()).offset(25 * (page - 1)).limit(26).all()
2022-05-04 23:09:46 +00:00
listing = [c.id for c in listing]
next_exists = len(listing) > 25
listing = listing[:25]
listing = get_comments(listing, v=v)
return render_template("admin/reported_comments.html",
2022-09-04 23:15:37 +00:00
next_exists=next_exists,
listing=listing,
page=page,
v=v,
standalone=True)
2022-05-04 23:09:46 +00:00
@app.get("/admin")
@admin_level_required(PERMS['ADMIN_HOME_VISIBLE'])
2022-05-04 23:09:46 +00:00
def admin_home(v):
2022-07-01 18:19:21 +00:00
under_attack = False
if v.admin_level >= PERMS['SITE_SETTINGS_UNDER_ATTACK']:
2022-10-13 07:27:56 +00:00
under_attack = (get_security_level() or 'high') == 'under_attack'
2022-05-04 23:09:46 +00:00
gitref = admin_git_head()
return render_template("admin/admin_home.html", v=v,
2022-07-01 18:19:21 +00:00
under_attack=under_attack,
gitref=gitref)
def admin_git_head():
short_len = 12
# Note: doing zero sanitization. Git branch names are extremely permissive.
# However, they forbid '..', so I don't see an obvious dir traversal attack.
# Also, a malicious branch name would mean someone already owned the server
# or repo, so I think this isn't a weak link.
try:
with open('.git/HEAD', encoding='utf_8') as head_f:
head_txt = head_f.read()
2022-07-06 11:49:13 +00:00
head_path = git_regex.match(head_txt).group(1)
with open('.git/' + head_path, encoding='utf_8') as ref_f:
gitref = ref_f.read()[0:short_len]
except:
return '<unable to read>'
return gitref
2022-05-04 23:09:46 +00:00
@app.post("/admin/site_settings/<setting>")
@admin_level_required(PERMS['SITE_SETTINGS'])
2022-05-04 23:09:46 +00:00
def change_settings(v, setting):
site_settings = app.config['SETTINGS']
site_settings[setting] = not site_settings[setting]
with open("/site_settings.json", "w", encoding='utf_8') as f:
json.dump(site_settings, f)
2022-05-04 23:09:46 +00:00
if site_settings[setting]: word = 'enable'
else: word = 'disable'
ma = ModAction(
kind=f"{word}_{setting}",
user_id=v.id,
)
g.db.add(ma)
return {'message': f"{setting} {word}d successfully!"}
@app.post("/admin/purge_cache")
@admin_level_required(PERMS['SITE_CACHE_PURGE_CDN'])
2022-05-04 23:09:46 +00:00
def purge_cache(v):
2022-09-01 23:59:56 +00:00
online = cache.get(ONLINE_STR)
2022-05-04 23:09:46 +00:00
cache.clear()
2022-09-01 23:59:56 +00:00
cache.set(ONLINE_STR, online)
2022-10-13 07:27:56 +00:00
if not purge_entire_cache():
abort(400, 'Failed to purge cache')
2022-05-04 23:09:46 +00:00
ma = ModAction(
kind="purge_cache",
user_id=v.id
)
g.db.add(ma)
2022-10-13 07:27:56 +00:00
return {"message": "Cache purged!"}
2022-05-04 23:09:46 +00:00
@app.post("/admin/under_attack")
@admin_level_required(PERMS['SITE_SETTINGS_UNDER_ATTACK'])
2022-05-04 23:09:46 +00:00
def under_attack(v):
2022-10-13 07:27:56 +00:00
response = get_security_level()
if not response:
abort(400, 'Could not retrieve the current security level')
old_under_attack_mode = response == 'under_attack'
enable_disable_str = 'disable' if old_under_attack_mode else 'enable'
new_security_level = 'high' if old_under_attack_mode else 'under_attack'
if not set_security_level(new_security_level):
abort(400, f'Failed to {enable_disable_str} under attack mode')
ma = ModAction(
kind=f"{enable_disable_str}_under_attack",
user_id=v.id,
)
g.db.add(ma)
2022-10-13 07:54:30 +00:00
return {"message": f"Under attack mode {enable_disable_str}d!"}
2022-05-04 23:09:46 +00:00
@app.get("/admin/badge_grant")
2022-10-06 03:50:02 +00:00
@admin_level_required(PERMS['USER_BADGES'])
2022-10-11 07:29:24 +00:00
@feature_required('BADGES')
2022-05-04 23:09:46 +00:00
def badge_grant_get(v):
badges = g.db.query(BadgeDef).order_by(BadgeDef.id).all()
return render_template("admin/badge_grant.html", v=v, badge_types=badges)
@app.post("/admin/badge_grant")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
2022-10-06 03:50:02 +00:00
@admin_level_required(PERMS['USER_BADGES'])
2022-10-11 07:29:24 +00:00
@feature_required('BADGES')
2022-05-04 23:09:46 +00:00
def badge_grant_post(v):
2022-10-11 07:29:24 +00:00
2022-05-04 23:09:46 +00:00
badges = g.db.query(BadgeDef).order_by(BadgeDef.id).all()
user = get_user(request.values.get("username").strip(), graceful=True)
if not user:
return render_template("admin/badge_grant.html", v=v, badge_types=badges, error="User not found.")
try: badge_id = int(request.values.get("badge_id"))
except: abort(400)
if SITE == 'watchpeopledie.co' and badge_id not in {99,101}:
abort(403)
if badge_id in {16,17,21,22,23,24,25,26,27,94,95,96,97,98,109,137,67,68,83,84,87,90,140} and v.id != AEVANN_ID and SITE != 'pcmemes.net':
2022-05-04 23:09:46 +00:00
abort(403)
if user.has_badge(badge_id):
return render_template("admin/badge_grant.html", v=v, badge_types=badges, error="User already has that badge.")
new_badge = Badge(badge_id=badge_id, user_id=user.id)
desc = request.values.get("description")
if desc: new_badge.description = desc
url = request.values.get("url")
2022-05-25 18:29:22 +00:00
if '\\' in url: abort(400)
2022-05-04 23:09:46 +00:00
if url: new_badge.url = url
g.db.add(new_badge)
2022-08-17 17:57:32 +00:00
g.db.flush()
2022-05-04 23:09:46 +00:00
if v.id != user.id:
text = f"@{v.username} (Admin) has given you the following profile badge:\n\n![]({new_badge.path})\n\n**{new_badge.name}**\n\n{new_badge.badge.description}"
2022-09-13 10:27:09 +00:00
send_repeatable_notification(user.id, text)
2022-05-04 23:09:46 +00:00
ma = ModAction(
kind="badge_grant",
user_id=v.id,
target_user_id=user.id,
_note=new_badge.name
)
g.db.add(ma)
2022-09-12 05:23:08 +00:00
return render_template("admin/badge_grant.html", v=v, badge_types=badges, msg=f"{new_badge.name} Badge granted to @{user.username} successfully!")
2022-05-04 23:09:46 +00:00
@app.get("/admin/badge_remove")
2022-10-06 03:50:02 +00:00
@admin_level_required(PERMS['USER_BADGES'])
2022-10-11 07:29:24 +00:00
@feature_required('BADGES')
2022-05-04 23:09:46 +00:00
def badge_remove_get(v):
2022-10-11 07:29:24 +00:00
2022-05-04 23:09:46 +00:00
badges = g.db.query(BadgeDef).order_by(BadgeDef.id).all()
2022-05-04 23:09:46 +00:00
return render_template("admin/badge_remove.html", v=v, badge_types=badges)
@app.post("/admin/badge_remove")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
2022-10-06 03:50:02 +00:00
@admin_level_required(PERMS['USER_BADGES'])
2022-10-11 07:29:24 +00:00
@feature_required('BADGES')
2022-05-04 23:09:46 +00:00
def badge_remove_post(v):
2022-10-11 07:29:24 +00:00
2022-05-04 23:09:46 +00:00
badges = g.db.query(BadgeDef).order_by(BadgeDef.id).all()
user = get_user(request.values.get("username").strip(), graceful=True)
if not user:
return render_template("admin/badge_remove.html", v=v, badge_types=badges, error="User not found.")
try: badge_id = int(request.values.get("badge_id"))
except: abort(400)
if badge_id in {67,68,83,84,87,90,140} and v.id != AEVANN_ID and SITE != 'pcmemes.net':
abort(403)
2022-05-04 23:09:46 +00:00
badge = user.has_badge(badge_id)
if not badge:
return render_template("admin/badge_remove.html", v=v, badge_types=badges, error="User doesn't have that badge.")
2022-09-13 10:27:09 +00:00
if v.id != user.id:
text = f"@{v.username} (Admin) has removed the following profile badge from you:\n\n![]({badge.path})\n\n**{badge.name}**\n\n{badge.badge.description}"
2022-09-13 10:27:09 +00:00
send_repeatable_notification(user.id, text)
2022-05-04 23:09:46 +00:00
ma = ModAction(
kind="badge_remove",
user_id=v.id,
target_user_id=user.id,
_note=badge.name
)
g.db.add(ma)
g.db.delete(badge)
2022-09-12 05:23:08 +00:00
return render_template("admin/badge_remove.html", v=v, badge_types=badges, msg=f"{badge.name} Badge removed from @{user.username} successfully!")
2022-05-04 23:09:46 +00:00
@app.get("/admin/users")
2022-10-06 05:37:50 +00:00
@admin_level_required(PERMS['VIEW_ALL_USERS'])
2022-05-04 23:09:46 +00:00
def users_list(v):
try: page = int(request.values.get("page", 1))
except: page = 1
users = g.db.query(User).order_by(User.id.desc()).offset(25 * (page - 1)).limit(26).all()
next_exists = (len(users) > 25)
users = users[:25]
2022-09-05 20:23:35 +00:00
return render_template("user_cards.html",
2022-09-04 23:15:37 +00:00
v=v,
users=users,
next_exists=next_exists,
page=page,
)
2022-05-04 23:09:46 +00:00
@app.get("/admin/alt_votes")
2022-10-06 05:37:50 +00:00
@admin_level_required(PERMS['VIEW_ALT_VOTES'])
2022-05-04 23:09:46 +00:00
def alt_votes_get(v):
u1 = request.values.get("u1")
u2 = request.values.get("u2")
if not u1 or not u2:
return render_template("admin/alt_votes.html", v=v)
u1 = get_user(u1)
u2 = get_user(u2)
u1_post_ups = g.db.query(
Vote.submission_id).filter_by(
user_id=u1.id,
vote_type=1).all()
u1_post_downs = g.db.query(
Vote.submission_id).filter_by(
user_id=u1.id,
vote_type=-1).all()
u1_comment_ups = g.db.query(
CommentVote.comment_id).filter_by(
user_id=u1.id,
vote_type=1).all()
u1_comment_downs = g.db.query(
CommentVote.comment_id).filter_by(
user_id=u1.id,
vote_type=-1).all()
u2_post_ups = g.db.query(
Vote.submission_id).filter_by(
user_id=u2.id,
vote_type=1).all()
u2_post_downs = g.db.query(
Vote.submission_id).filter_by(
user_id=u2.id,
vote_type=-1).all()
u2_comment_ups = g.db.query(
CommentVote.comment_id).filter_by(
user_id=u2.id,
vote_type=1).all()
u2_comment_downs = g.db.query(
CommentVote.comment_id).filter_by(
user_id=u2.id,
vote_type=-1).all()
data = {}
data['u1_only_post_ups'] = len(
[x for x in u1_post_ups if x not in u2_post_ups])
data['u2_only_post_ups'] = len(
[x for x in u2_post_ups if x not in u1_post_ups])
data['both_post_ups'] = len(list(set(u1_post_ups) & set(u2_post_ups)))
data['u1_only_post_downs'] = len(
[x for x in u1_post_downs if x not in u2_post_downs])
data['u2_only_post_downs'] = len(
[x for x in u2_post_downs if x not in u1_post_downs])
data['both_post_downs'] = len(
list(set(u1_post_downs) & set(u2_post_downs)))
data['u1_only_comment_ups'] = len(
[x for x in u1_comment_ups if x not in u2_comment_ups])
data['u2_only_comment_ups'] = len(
[x for x in u2_comment_ups if x not in u1_comment_ups])
data['both_comment_ups'] = len(
list(set(u1_comment_ups) & set(u2_comment_ups)))
data['u1_only_comment_downs'] = len(
[x for x in u1_comment_downs if x not in u2_comment_downs])
data['u2_only_comment_downs'] = len(
[x for x in u2_comment_downs if x not in u1_comment_downs])
data['both_comment_downs'] = len(
list(set(u1_comment_downs) & set(u2_comment_downs)))
data['u1_post_ups_unique'] = 100 * \
data['u1_only_post_ups'] // len(u1_post_ups) if u1_post_ups else 0
data['u2_post_ups_unique'] = 100 * \
data['u2_only_post_ups'] // len(u2_post_ups) if u2_post_ups else 0
data['u1_post_downs_unique'] = 100 * \
data['u1_only_post_downs'] // len(
u1_post_downs) if u1_post_downs else 0
data['u2_post_downs_unique'] = 100 * \
data['u2_only_post_downs'] // len(
u2_post_downs) if u2_post_downs else 0
data['u1_comment_ups_unique'] = 100 * \
data['u1_only_comment_ups'] // len(
u1_comment_ups) if u1_comment_ups else 0
data['u2_comment_ups_unique'] = 100 * \
data['u2_only_comment_ups'] // len(
u2_comment_ups) if u2_comment_ups else 0
data['u1_comment_downs_unique'] = 100 * \
data['u1_only_comment_downs'] // len(
u1_comment_downs) if u1_comment_downs else 0
data['u2_comment_downs_unique'] = 100 * \
data['u2_only_comment_downs'] // len(
u2_comment_downs) if u2_comment_downs else 0
return render_template("admin/alt_votes.html",
2022-09-04 23:15:37 +00:00
u1=u1,
u2=u2,
v=v,
data=data
)
2022-05-04 23:09:46 +00:00
@app.post("/admin/link_accounts")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
2022-10-06 05:37:50 +00:00
@admin_level_required(PERMS['USER_LINK'])
2022-05-04 23:09:46 +00:00
def admin_link_accounts(v):
u1 = int(request.values.get("u1"))
u2 = int(request.values.get("u2"))
new_alt = Alt(
user1=u1,
user2=u2,
is_manual=True
)
g.db.add(new_alt)
g.db.flush()
check_for_alts(g.db.get(User, u1))
check_for_alts(g.db.get(User, u2))
2022-05-04 23:09:46 +00:00
ma = ModAction(
kind="link_accounts",
user_id=v.id,
target_user_id=u1,
_note=f'with {u2}'
)
g.db.add(ma)
2022-06-24 13:19:53 +00:00
return redirect(f"/admin/alt_votes?u1={get_account(u1).username}&u2={get_account(u2).username}")
2022-05-04 23:09:46 +00:00
@app.get("/admin/removed/posts")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
2022-05-04 23:09:46 +00:00
def admin_removed(v):
try: page = int(request.values.get("page", 1))
except: page = 1
if page < 1: abort(400)
2022-07-03 06:12:53 +00:00
ids = g.db.query(Submission.id).join(Submission.author).filter(or_(Submission.is_banned==True, User.shadowbanned != None)).order_by(Submission.id.desc()).offset(25 * (page - 1)).limit(26).all()
2022-05-04 23:09:46 +00:00
ids=[x[0] for x in ids]
next_exists = len(ids) > 25
ids = ids[:25]
posts = get_posts(ids, v=v)
return render_template("admin/removed_posts.html",
2022-09-04 23:15:37 +00:00
v=v,
listing=posts,
page=page,
next_exists=next_exists
)
2022-05-04 23:09:46 +00:00
@app.get("/admin/removed/comments")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
2022-05-04 23:09:46 +00:00
def admin_removed_comments(v):
try: page = int(request.values.get("page", 1))
except: page = 1
2022-07-03 06:12:53 +00:00
ids = g.db.query(Comment.id).join(Comment.author).filter(or_(Comment.is_banned==True, User.shadowbanned != None)).order_by(Comment.id.desc()).offset(25 * (page - 1)).limit(26).all()
2022-05-04 23:09:46 +00:00
ids=[x[0] for x in ids]
next_exists = len(ids) > 25
ids = ids[:25]
comments = get_comments(ids, v=v)
return render_template("admin/removed_comments.html",
2022-09-04 23:15:37 +00:00
v=v,
listing=comments,
page=page,
next_exists=next_exists
)
2022-05-04 23:09:46 +00:00
@app.post("/agendaposter/<user_id>")
@admin_level_required(PERMS['USER_AGENDAPOSTER'])
2022-05-04 23:09:46 +00:00
def agendaposter(user_id, v):
2022-06-24 13:19:53 +00:00
user = get_account(user_id)
2022-05-04 23:09:46 +00:00
days = request.values.get("days")
2022-05-26 20:04:39 +00:00
if days:
2022-09-10 03:13:12 +00:00
expiry = int(time.time() + float(days)*60*60*24)
else: expiry = 1
2022-05-04 23:09:46 +00:00
user.agendaposter = expiry
g.db.add(user)
if days:
days_txt = str(days).rstrip('.0')
note = f"for {days_txt} days"
2022-09-21 23:52:39 +00:00
else: note = "permanently"
2022-05-04 23:09:46 +00:00
ma = ModAction(
kind="agendaposter",
user_id=v.id,
target_user_id=user.id,
2022-09-29 10:18:27 +00:00
_note=note
2022-05-04 23:09:46 +00:00
)
g.db.add(ma)
badge_grant(user=user, badge_id=28)
2022-05-04 23:09:46 +00:00
send_repeatable_notification(user.id, f"@{v.username} (Admin) has marked you as a chud ({note}).")
2022-05-04 23:09:46 +00:00
return redirect(user.url)
@app.post("/unagendaposter/<user_id>")
@admin_level_required(PERMS['USER_AGENDAPOSTER'])
2022-05-04 23:09:46 +00:00
def unagendaposter(user_id, v):
2022-06-24 13:19:53 +00:00
user = get_account(user_id)
2022-05-04 23:09:46 +00:00
user.agendaposter = 0
g.db.add(user)
for alt in user.alts:
alt.agendaposter = 0
g.db.add(alt)
ma = ModAction(
kind="unagendaposter",
user_id=v.id,
target_user_id=user.id
)
g.db.add(ma)
badge = user.has_badge(28)
if badge: g.db.delete(badge)
send_repeatable_notification(user.id, f"@{v.username} (Admin) has unmarked you as a chud.")
2022-05-04 23:09:46 +00:00
return {"message": f"@{user.username}'s chud theme has been disabled!"}
2022-05-04 23:09:46 +00:00
@app.post("/shadowban/<user_id>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@admin_level_required(PERMS['USER_SHADOWBAN'])
2022-05-04 23:09:46 +00:00
def shadowban(user_id, v):
2022-06-24 13:19:53 +00:00
user = get_account(user_id)
2022-05-04 23:09:46 +00:00
if user.admin_level != 0: abort(403)
user.shadowbanned = v.username
reason = request.values.get("reason").strip()[:256]
user.ban_reason = reason
2022-05-04 23:09:46 +00:00
g.db.add(user)
if request.values.get("alts"):
for alt in user.alts:
if alt.admin_level: continue
alt.shadowbanned = v.username
alt.ban_reason = reason
g.db.add(alt)
2022-05-04 23:09:46 +00:00
ma = ModAction(
kind="shadowban",
user_id=v.id,
target_user_id=user.id,
_note=f'reason: "{reason}"'
2022-05-04 23:09:46 +00:00
)
g.db.add(ma)
cache.delete_memoized(frontlist)
return redirect(user.url)
2022-05-04 23:09:46 +00:00
@app.post("/unshadowban/<user_id>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@admin_level_required(PERMS['USER_SHADOWBAN'])
2022-05-04 23:09:46 +00:00
def unshadowban(user_id, v):
2022-06-24 13:19:53 +00:00
user = get_account(user_id)
2022-05-04 23:09:46 +00:00
user.shadowbanned = None
if not user.is_banned: user.ban_reason = None
2022-05-04 23:09:46 +00:00
g.db.add(user)
for alt in user.alts:
alt.shadowbanned = None
if not alt.is_banned: alt.ban_reason = None
2022-05-04 23:09:46 +00:00
g.db.add(alt)
ma = ModAction(
kind="unshadowban",
user_id=v.id,
target_user_id=user.id,
)
g.db.add(ma)
cache.delete_memoized(frontlist)
return redirect(user.url)
2022-05-04 23:09:46 +00:00
@app.post("/admin/title_change/<user_id>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
2022-10-06 05:45:44 +00:00
@admin_level_required(PERMS['USER_TITLE_CHANGE'])
2022-05-04 23:09:46 +00:00
def admin_title_change(user_id, v):
2022-06-24 13:19:53 +00:00
user = get_account(user_id)
2022-05-04 23:09:46 +00:00
new_name=request.values.get("title").strip()[:256]
user.customtitleplain=new_name
new_name = filter_emojis_only(new_name)
2022-06-24 13:19:53 +00:00
user=get_account(user.id)
2022-05-04 23:09:46 +00:00
user.customtitle=new_name
if request.values.get("locked"): user.flairchanged = int(time.time()) + 2629746
else:
user.flairchanged = None
badge = user.has_badge(96)
if badge: g.db.delete(badge)
g.db.add(user)
if user.flairchanged: kind = "set_flair_locked"
else: kind = "set_flair_notlocked"
ma=ModAction(
kind=kind,
user_id=v.id,
target_user_id=user.id,
_note=f'"{user.customtitleplain}"'
)
g.db.add(ma)
return redirect(user.url)
@app.post("/ban_user/<user_id>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@admin_level_required(PERMS['USER_BAN'])
2022-05-04 23:09:46 +00:00
def ban_user(user_id, v):
2022-06-24 13:19:53 +00:00
user = get_account(user_id)
2022-05-04 23:09:46 +00:00
2022-10-11 13:48:49 +00:00
if user.admin_level > v.admin_level:
abort(403)
2022-05-04 23:09:46 +00:00
days = float(request.values.get("days")) if request.values.get('days') else 0
reason = request.values.get("reason").strip()[:256]
reason = filter_emojis_only(reason)
2022-05-04 23:09:46 +00:00
if reason.startswith("/") and '\\' not in reason:
reason = f'<a href="{reason.split()[0]}">{reason}</a>'
2022-05-04 23:09:46 +00:00
user.ban(admin=v, reason=reason, days=days)
2022-05-04 23:09:46 +00:00
if request.values.get("alts"):
for x in user.alts:
2022-10-11 13:48:49 +00:00
if x.admin_level > v.admin_level:
continue
x.ban(admin=v, reason=reason, days=days)
2022-05-04 23:09:46 +00:00
if days:
days_txt = str(days).rstrip('.0')
if reason: text = f"@{v.username} (Admin) has banned you for **{days_txt}** days for the following reason:\n\n> {reason}"
else: text = f"@{v.username} (Admin) has banned you for **{days_txt}** days."
2022-05-04 23:09:46 +00:00
else:
if reason: text = f"@{v.username} (Admin) has banned you permanently for the following reason:\n\n> {reason}"
else: text = f"@{v.username} (Admin) has banned you permanently."
2022-05-04 23:09:46 +00:00
send_repeatable_notification(user.id, text)
2022-07-12 20:00:19 +00:00
if days == 0: duration = "permanently"
elif days == 1: duration = "for 1 day"
else: duration = f"for {days_txt} days"
2022-05-04 23:09:46 +00:00
note = f'reason: "{reason}", duration: {duration}'
ma=ModAction(
kind="ban_user",
user_id=v.id,
target_user_id=user.id,
_note=note
)
g.db.add(ma)
if 'reason' in request.values:
if request.values["reason"].startswith("/post/"):
2022-07-28 14:23:38 +00:00
try: post = int(request.values["reason"].split("/post/")[1].split(None, 1)[0])
except: abort(400)
2022-06-25 00:11:00 +00:00
post = get_post(post)
post.bannedfor = f'{duration} by @{v.username}'
2022-06-25 00:11:00 +00:00
g.db.add(post)
elif request.values["reason"].startswith("/comment/"):
2022-07-28 14:23:38 +00:00
try: comment = int(request.values["reason"].split("/comment/")[1].split(None, 1)[0])
except: abort(400)
2022-06-25 00:11:00 +00:00
comment = get_comment(comment)
comment.bannedfor = f'{duration} by @{v.username}'
2022-06-25 00:11:00 +00:00
g.db.add(comment)
2022-05-04 23:09:46 +00:00
if 'redir' in request.values: return redirect(user.url)
else: return {"message": f"@{user.username} has been banned!"}
2022-05-04 23:09:46 +00:00
@app.post("/unban_user/<user_id>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@admin_level_required(PERMS['USER_BAN'])
2022-05-04 23:09:46 +00:00
def unban_user(user_id, v):
2022-06-24 13:19:53 +00:00
user = get_account(user_id)
if not user.is_banned:
abort(400)
2022-05-04 23:09:46 +00:00
user.is_banned = 0
user.unban_utc = 0
user.ban_reason = None
send_repeatable_notification(user.id, f"@{v.username} (Admin) has unbanned you!")
2022-05-04 23:09:46 +00:00
g.db.add(user)
for x in user.alts:
if x.is_banned: send_repeatable_notification(x.id, f"@{v.username} (Admin) has unbanned you!")
2022-05-04 23:09:46 +00:00
x.is_banned = 0
x.unban_utc = 0
x.ban_reason = None
g.db.add(x)
ma=ModAction(
kind="unban_user",
user_id=v.id,
target_user_id=user.id,
)
g.db.add(ma)
if "@" in request.referrer: return redirect(user.url)
else: return {"message": f"@{user.username} has been unbanned!"}
2022-05-04 23:09:46 +00:00
@app.post("/mute_user/<int:user_id>/<int:mute_status>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@admin_level_required(PERMS['USER_BAN'])
def mute_user(v, user_id, mute_status):
user = get_account(user_id)
if mute_status != 0 and not user.is_muted:
user.is_muted = True
log_action = 'mod_mute_user'
success_msg = f"@{user.username} has been muted!"
elif mute_status == 0 and user.is_muted:
user.is_muted = False
log_action = 'mod_unmute_user'
success_msg = f"@{user.username} has been un-muted!"
else:
abort(400)
ma = ModAction(
kind=log_action,
user_id=v.id,
target_user_id=user.id,
)
g.db.add(user)
g.db.add(ma)
if 'redir' in request.values:
return redirect(user.url)
else:
return {"message": success_msg}
2022-05-04 23:09:46 +00:00
@app.post("/remove_post/<post_id>")
2022-05-04 23:09:46 +00:00
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
def remove_post(post_id, v):
2022-06-24 13:19:53 +00:00
post = get_post(post_id)
2022-05-04 23:09:46 +00:00
post.is_banned = True
post.is_approved = None
post.stickied = None
post.is_pinned = False
post.ban_reason = v.username
g.db.add(post)
ma=ModAction(
kind="ban_post",
user_id=v.id,
target_submission_id=post.id,
)
g.db.add(ma)
cache.delete_memoized(frontlist)
v.coins += 1
g.db.add(v)
2022-10-13 07:27:56 +00:00
purge_files_in_cache(f"https://{SITE}/logged_out")
2022-05-04 23:09:46 +00:00
return {"message": "Post removed!"}
@app.post("/approve_post/<post_id>")
2022-05-04 23:09:46 +00:00
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
def approve_post(post_id, v):
2022-05-04 23:09:46 +00:00
2022-06-24 13:19:53 +00:00
post = get_post(post_id)
2022-05-04 23:09:46 +00:00
2022-08-19 22:20:25 +00:00
if post.author.id == v.id and post.author.agendaposter and AGENDAPOSTER_PHRASE not in post.body.lower() and post.sub != 'chudrama':
abort(400, "You can't bypass the chud award!")
2022-05-09 14:07:29 +00:00
2022-05-04 23:09:46 +00:00
if post.is_banned:
ma=ModAction(
kind="unban_post",
user_id=v.id,
target_submission_id=post.id,
)
g.db.add(ma)
post.is_banned = False
post.ban_reason = None
post.is_approved = v.id
g.db.add(post)
cache.delete_memoized(frontlist)
2022-10-12 16:33:00 +00:00
v.charge_account('coins', 1)
2022-05-04 23:09:46 +00:00
g.db.add(v)
return {"message": "Post approved!"}
@app.post("/distinguish/<post_id>")
2022-10-06 04:19:11 +00:00
@admin_level_required(PERMS['POST_COMMENT_DISTINGUISH'])
2022-08-11 04:05:23 +00:00
def distinguish_post(post_id, v):
2022-06-24 13:19:53 +00:00
post = get_post(post_id)
2022-05-04 23:09:46 +00:00
if post.author_id != v.id and v.admin_level < PERMS['POST_COMMENT_MODERATION']: abort(403)
2022-05-04 23:09:46 +00:00
if post.distinguish_level:
post.distinguish_level = 0
kind = 'undistinguish_post'
else:
post.distinguish_level = v.admin_level
kind = 'distinguish_post'
g.db.add(post)
ma = ModAction(
kind=kind,
user_id=v.id,
target_submission_id=post.id
)
g.db.add(ma)
if post.distinguish_level: return {"message": "Post distinguished!"}
else: return {"message": "Post undistinguished!"}
@app.post("/sticky/<post_id>")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
2022-10-11 07:29:24 +00:00
@feature_required('PINS')
2022-05-04 23:09:46 +00:00
def sticky_post(post_id, v):
2022-10-11 07:29:24 +00:00
2022-10-14 18:28:20 +00:00
pins = g.db.query(Submission).filter(Submission.stickied != None, Submission.is_banned == False).count()
if pins >= PIN_LIMIT and v.admin_level < PERMS['BYPASS_PIN_LIMIT']:
abort(403, f"Can't exceed {PIN_LIMIT} pinned posts limit!")
2022-06-24 13:19:53 +00:00
post = get_post(post_id)
2022-05-04 23:09:46 +00:00
2022-10-14 18:28:20 +00:00
if not post.stickied_utc:
post.stickied_utc = int(time.time()) + 3600
pin_time = 'for 1 hour'
code = 200
if v.id != post.author_id:
send_repeatable_notification(post.author_id, f"@{v.username} (Admin) has pinned [{post.title}](/post/{post_id})!")
2022-10-14 18:28:20 +00:00
else:
2022-10-15 17:03:51 +00:00
if pins >= PIN_LIMIT:
abort(403, f"Can't exceed {PIN_LIMIT} pinned posts limit!")
2022-10-14 18:28:20 +00:00
post.stickied_utc = None
2022-10-14 18:28:46 +00:00
pin_time = 'permanently'
2022-10-14 18:28:20 +00:00
code = 201
post.stickied = v.username
g.db.add(post)
2022-05-04 23:09:46 +00:00
2022-10-14 18:28:20 +00:00
ma=ModAction(
kind="pin_post",
user_id=v.id,
target_submission_id=post.id,
_note=pin_time
)
g.db.add(ma)
cache.delete_memoized(frontlist)
2022-05-04 23:09:46 +00:00
2022-10-14 18:28:20 +00:00
return {"message": f"Post pinned {pin_time}!"}, code
2022-05-04 23:09:46 +00:00
@app.post("/unsticky/<post_id>")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
2022-05-04 23:09:46 +00:00
def unsticky_post(post_id, v):
2022-06-24 13:19:53 +00:00
post = get_post(post_id)
2022-09-30 22:40:02 +00:00
if post.stickied:
if post.stickied.endswith('(pin award)'): abort(403, "Can't unpin award pins!")
2022-05-04 23:09:46 +00:00
post.stickied = None
post.stickied_utc = None
g.db.add(post)
ma=ModAction(
kind="unpin_post",
user_id=v.id,
target_submission_id=post.id
)
g.db.add(ma)
if v.id != post.author_id:
send_repeatable_notification(post.author_id, f"@{v.username} (Admin) has unpinned [{post.title}](/post/{post_id})!")
2022-05-04 23:09:46 +00:00
cache.delete_memoized(frontlist)
return {"message": "Post unpinned!"}
@app.post("/sticky_comment/<cid>")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
2022-05-04 23:09:46 +00:00
def sticky_comment(cid, v):
comment = get_comment(cid, v=v)
2022-05-26 23:08:23 +00:00
if not comment.stickied:
comment.stickied = v.username
2022-05-04 23:09:46 +00:00
g.db.add(comment)
ma=ModAction(
kind="pin_comment",
user_id=v.id,
target_comment_id=comment.id
)
g.db.add(ma)
if v.id != comment.author_id:
message = f"@{v.username} (Admin) has pinned your [comment]({comment.shortlink})!"
2022-05-04 23:09:46 +00:00
send_repeatable_notification(comment.author_id, message)
return {"message": "Comment pinned!"}
@app.post("/unsticky_comment/<cid>")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
2022-05-04 23:09:46 +00:00
def unsticky_comment(cid, v):
comment = get_comment(cid, v=v)
2022-05-26 23:08:23 +00:00
if comment.stickied:
if comment.stickied.endswith("(pin award)"): abort(403, "Can't unpin award pins!")
2022-05-04 23:09:46 +00:00
2022-05-26 23:08:23 +00:00
comment.stickied = None
2022-05-04 23:09:46 +00:00
g.db.add(comment)
ma=ModAction(
kind="unpin_comment",
user_id=v.id,
target_comment_id=comment.id
)
g.db.add(ma)
if v.id != comment.author_id:
message = f"@{v.username} (Admin) has unpinned your [comment]({comment.shortlink})!"
2022-05-04 23:09:46 +00:00
send_repeatable_notification(comment.author_id, message)
return {"message": "Comment unpinned!"}
@app.post("/remove_comment/<c_id>")
2022-05-04 23:09:46 +00:00
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
2022-08-11 04:05:23 +00:00
def remove_comment(c_id, v):
2022-06-24 13:19:53 +00:00
comment = get_comment(c_id)
2022-05-04 23:09:46 +00:00
comment.is_banned = True
comment.is_approved = None
comment.ban_reason = v.username
g.db.add(comment)
ma=ModAction(
kind="ban_comment",
user_id=v.id,
target_comment_id=comment.id,
)
g.db.add(ma)
2022-05-04 23:09:46 +00:00
return {"message": "Comment removed!"}
@app.post("/approve_comment/<c_id>")
2022-05-04 23:09:46 +00:00
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
2022-08-11 04:05:23 +00:00
def approve_comment(c_id, v):
2022-05-04 23:09:46 +00:00
2022-06-24 13:19:53 +00:00
comment = get_comment(c_id)
2022-05-04 23:09:46 +00:00
if not comment: abort(404)
2022-08-19 22:20:25 +00:00
if comment.author.id == v.id and comment.author.agendaposter and AGENDAPOSTER_PHRASE not in comment.body.lower() and comment.post.sub != 'chudrama':
abort(400, "You can't bypass the chud award!")
2022-05-04 23:09:46 +00:00
if comment.is_banned:
ma=ModAction(
kind="unban_comment",
user_id=v.id,
target_comment_id=comment.id,
)
g.db.add(ma)
comment.is_banned = False
comment.ban_reason = None
comment.is_approved = v.id
g.db.add(comment)
return {"message": "Comment approved!"}
@app.post("/distinguish_comment/<c_id>")
2022-10-06 04:19:11 +00:00
@admin_level_required(PERMS['POST_COMMENT_DISTINGUISH'])
2022-05-04 23:09:46 +00:00
def admin_distinguish_comment(c_id, v):
comment = get_comment(c_id, v=v)
if comment.author_id != v.id: abort(403)
if comment.distinguish_level:
comment.distinguish_level = 0
kind = 'undistinguish_comment'
else:
comment.distinguish_level = v.admin_level
kind = 'distinguish_comment'
g.db.add(comment)
ma = ModAction(
kind=kind,
user_id=v.id,
target_comment_id=comment.id
)
g.db.add(ma)
if comment.distinguish_level: return {"message": "Comment distinguished!"}
else: return {"message": "Comment undistinguished!"}
@app.get("/admin/dump_cache")
@admin_level_required(PERMS['SITE_CACHE_DUMP_INTERNAL'])
2022-05-04 23:09:46 +00:00
def admin_dump_cache(v):
2022-09-01 23:59:56 +00:00
online = cache.get(ONLINE_STR)
2022-05-04 23:09:46 +00:00
cache.clear()
2022-09-01 23:59:56 +00:00
cache.set(ONLINE_STR, online)
2022-05-04 23:09:46 +00:00
ma = ModAction(
kind="dump_cache",
user_id=v.id
)
g.db.add(ma)
return {"message": "Internal cache cleared."}
@app.get("/admin/banned_domains/")
@admin_level_required(PERMS['DOMAINS_BAN'])
2022-05-04 23:09:46 +00:00
def admin_banned_domains(v):
banned_domains = g.db.query(BannedDomain).all()
return render_template("admin/banned_domains.html", v=v, banned_domains=banned_domains)
@app.post("/admin/banned_domains")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@admin_level_required(PERMS['DOMAINS_BAN'])
2022-05-04 23:09:46 +00:00
def admin_toggle_ban_domain(v):
domain=request.values.get("domain", "").strip()
if not domain: abort(400)
reason=request.values.get("reason").strip()
2022-05-04 23:09:46 +00:00
d = g.db.query(BannedDomain).filter_by(domain=domain).one_or_none()
if d:
g.db.delete(d)
ma = ModAction(
kind="unban_domain",
user_id=v.id,
_note=domain
)
g.db.add(ma)
else:
d = BannedDomain(domain=domain, reason=reason)
g.db.add(d)
ma = ModAction(
kind="ban_domain",
user_id=v.id,
_note=f'{domain}, reason: {reason}'
)
g.db.add(ma)
return redirect("/admin/banned_domains/")
@app.post("/admin/nuke_user")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
2022-05-04 23:09:46 +00:00
def admin_nuke_user(v):
user=get_user(request.values.get("user"))
for post in g.db.query(Submission).filter_by(author_id=user.id).all():
if post.is_banned:
continue
post.is_banned = True
post.ban_reason = v.username
g.db.add(post)
for comment in g.db.query(Comment).filter_by(author_id=user.id).all():
if comment.is_banned:
continue
comment.is_banned = True
comment.ban_reason = v.username
g.db.add(comment)
ma=ModAction(
kind="nuke_user",
user_id=v.id,
target_user_id=user.id,
)
g.db.add(ma)
return redirect(user.url)
@app.post("/admin/unnuke_user")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@admin_level_required(PERMS['POST_COMMENT_MODERATION'])
2022-05-04 23:09:46 +00:00
def admin_nunuke_user(v):
user=get_user(request.values.get("user"))
for post in g.db.query(Submission).filter_by(author_id=user.id).all():
if not post.is_banned:
continue
post.is_banned = False
post.ban_reason = None
2022-07-12 20:00:19 +00:00
post.is_approved = v.id
2022-05-04 23:09:46 +00:00
g.db.add(post)
for comment in g.db.query(Comment).filter_by(author_id=user.id).all():
if not comment.is_banned:
continue
comment.is_banned = False
comment.ban_reason = None
2022-07-12 20:00:19 +00:00
comment.is_approved = v.id
2022-05-04 23:09:46 +00:00
g.db.add(comment)
ma=ModAction(
kind="unnuke_user",
user_id=v.id,
target_user_id=user.id,
)
g.db.add(ma)
return redirect(user.url)