2022-11-15 09:19:08 +00:00
|
|
|
from os import path, rename
|
|
|
|
from shutil import copyfile, move
|
2022-09-10 07:38:23 +00:00
|
|
|
|
2023-03-18 13:34:04 +00:00
|
|
|
from files.classes.emoji import *
|
2022-11-15 09:19:08 +00:00
|
|
|
from files.classes.hats import Hat, HatDef
|
|
|
|
from files.classes.mod_logs import ModAction
|
2023-09-15 00:07:17 +00:00
|
|
|
from files.helpers.cloudflare import purge_files_in_cloudflare_cache
|
2022-12-11 23:44:34 +00:00
|
|
|
from files.helpers.config.const import *
|
2022-09-13 18:00:11 +00:00
|
|
|
from files.helpers.get import *
|
2022-11-15 09:19:08 +00:00
|
|
|
from files.helpers.media import *
|
|
|
|
from files.helpers.useractions import *
|
|
|
|
from files.routes.wrappers import *
|
|
|
|
from files.__main__ import app, cache, limiter
|
|
|
|
|
2023-03-18 13:34:04 +00:00
|
|
|
ASSET_TYPES = (Emoji, HatDef)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-18 14:54:01 +00:00
|
|
|
@app.get("/submit/marseys")
|
|
|
|
def submit_marseys_redirect():
|
|
|
|
return redirect("/submit/emojis")
|
|
|
|
|
2023-03-18 13:34:04 +00:00
|
|
|
@app.get("/submit/emojis")
|
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)
|
2022-11-15 09:19:08 +00:00
|
|
|
@auth_required
|
2023-07-30 00:42:06 +00:00
|
|
|
def submit_emojis(v):
|
2023-09-05 12:30:16 +00:00
|
|
|
emojis = g.db.query(Emoji).filter(Emoji.submitter_id != None)
|
2022-12-08 15:05:17 +00:00
|
|
|
|
2023-03-18 14:39:26 +00:00
|
|
|
emojis = emojis.order_by(Emoji.created_utc.desc()).all()
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-18 14:39:26 +00:00
|
|
|
for emoji in emojis:
|
|
|
|
emoji.author = g.db.query(User.username).filter_by(id=emoji.author_id).one()[0]
|
|
|
|
emoji.submitter = g.db.query(User.username).filter_by(id=emoji.submitter_id).one()[0]
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-08-11 21:50:23 +00:00
|
|
|
return render_template("submit_emojis.html", v=v, emojis=emojis)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
|
2023-08-17 15:37:30 +00:00
|
|
|
emoji_modifiers = ('pat', 'talking', 'genocide', 'love')
|
2023-08-17 15:28:14 +00:00
|
|
|
|
2023-03-18 13:34:04 +00:00
|
|
|
@app.post("/submit/emojis")
|
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)
|
2022-11-15 09:19:08 +00:00
|
|
|
@auth_required
|
2023-07-30 00:42:06 +00:00
|
|
|
def submit_emoji(v):
|
2022-11-15 09:19:08 +00:00
|
|
|
file = request.files["image"]
|
|
|
|
name = request.values.get('name', '').lower().strip()
|
|
|
|
tags = request.values.get('tags', '').lower().strip()
|
|
|
|
username = request.values.get('author', '').lower().strip()
|
2023-03-18 13:34:04 +00:00
|
|
|
kind = request.values.get('kind', '').strip()
|
2023-10-02 02:46:59 +00:00
|
|
|
over_18 = bool(request.values.get("over_18"))
|
2023-03-18 13:34:04 +00:00
|
|
|
|
2023-08-17 15:28:14 +00:00
|
|
|
for modifier in emoji_modifiers:
|
|
|
|
if name.endswith(modifier):
|
|
|
|
abort(400, f'Submitted emoji names should NOT end with the word "{modifier}"')
|
|
|
|
|
2023-03-21 16:50:22 +00:00
|
|
|
if kind not in EMOJI_KINDS:
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "Invalid emoji kind!")
|
2023-03-18 15:21:20 +00:00
|
|
|
|
2023-07-07 23:25:07 +00:00
|
|
|
if kind in {"Platy", "Wolf", "Tay", "Carp", "Capy"} and not name.startswith(kind.lower()):
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, f'The name of this emoji should start with the word "{kind.lower()}"')
|
2023-03-18 15:21:20 +00:00
|
|
|
|
2023-07-07 23:25:07 +00:00
|
|
|
if kind == "Marsey" and not name.startswith("marsey") and not name.startswith("marcus"):
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, 'The name of this emoji should start with the word "Marsey" or "Marcus"')
|
2023-07-07 23:25:07 +00:00
|
|
|
|
2023-03-18 15:21:20 +00:00
|
|
|
if kind == "Marsey Flags" and not name.startswith("marseyflag"):
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, 'The name of this emoji should start with the word "marseyflag"')
|
2023-03-18 15:21:20 +00:00
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
if g.is_tor:
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "Image uploads are not allowed through TOR!")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
if not file or not file.content_type.startswith('image/'):
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "You need to submit an image!")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-18 14:53:00 +00:00
|
|
|
if not emoji_name_regex.fullmatch(name):
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "Invalid name!")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-18 13:34:04 +00:00
|
|
|
existing = g.db.query(Emoji.name).filter_by(name=name).one_or_none()
|
2022-11-15 09:19:08 +00:00
|
|
|
if existing:
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "Someone already submitted an emoji with this name!")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
if not tags_regex.fullmatch(tags):
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "Invalid tags!")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-09-06 18:13:32 +00:00
|
|
|
author = get_user(username, v=v)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-21 14:16:18 +00:00
|
|
|
highquality = f'/asset_submissions/emojis/{name}'
|
2022-11-15 09:19:08 +00:00
|
|
|
file.save(highquality)
|
2023-09-14 22:40:25 +00:00
|
|
|
process_image(highquality, v) #to ensure not malware
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-21 14:16:18 +00:00
|
|
|
filename = f'/asset_submissions/emojis/{name}.webp'
|
2022-11-15 09:19:08 +00:00
|
|
|
copyfile(highquality, filename)
|
2023-09-23 17:58:04 +00:00
|
|
|
process_image(filename, v, resize=300, trim=True)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-09-29 07:15:29 +00:00
|
|
|
emoji = Emoji(
|
|
|
|
name=name,
|
|
|
|
kind=kind,
|
|
|
|
author_id=author.id,
|
|
|
|
tags=tags,
|
|
|
|
count=0,
|
|
|
|
submitter_id=v.id,
|
|
|
|
over_18=over_18,
|
|
|
|
)
|
2023-03-18 14:53:00 +00:00
|
|
|
g.db.add(emoji)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-08-11 21:50:23 +00:00
|
|
|
return {"message": f"'{name}' submitted successfully!"}
|
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-07-30 00:42:06 +00:00
|
|
|
def verify_permissions_and_get_asset(cls, asset_type, v, name, make_lower=False):
|
2022-11-15 09:19:08 +00:00
|
|
|
if cls not in ASSET_TYPES: raise Exception("not a valid asset type")
|
|
|
|
name = name.strip()
|
|
|
|
if make_lower: name = name.lower()
|
|
|
|
asset = None
|
|
|
|
if cls == HatDef:
|
2023-03-16 06:27:58 +00:00
|
|
|
asset = g.db.query(cls).filter_by(name=name).one_or_none()
|
2022-11-15 09:19:08 +00:00
|
|
|
else:
|
2023-03-16 06:27:58 +00:00
|
|
|
asset = g.db.get(cls, name)
|
2022-11-15 09:19:08 +00:00
|
|
|
if not asset:
|
|
|
|
abort(404, f"This {asset} '{name}' doesn't exist!")
|
|
|
|
return asset
|
|
|
|
|
2023-03-18 13:34:04 +00:00
|
|
|
@app.post("/admin/approve/emoji/<name>")
|
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-01-22 08:04:49 +00:00
|
|
|
@admin_level_required(PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'])
|
2023-03-18 14:53:00 +00:00
|
|
|
def approve_emoji(v, name):
|
|
|
|
emoji = verify_permissions_and_get_asset(Emoji, "emoji", v, name, True)
|
2022-11-15 09:19:08 +00:00
|
|
|
tags = request.values.get('tags').lower().strip()
|
|
|
|
if not tags:
|
|
|
|
abort(400, "You need to include tags!")
|
|
|
|
|
|
|
|
new_name = request.values.get('name').lower().strip()
|
|
|
|
if not new_name:
|
|
|
|
abort(400, "You need to include name!")
|
|
|
|
|
2023-03-18 13:34:04 +00:00
|
|
|
new_kind = request.values.get('kind').strip()
|
|
|
|
if not new_kind:
|
|
|
|
abort(400, "You need to include kind!")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-18 14:53:00 +00:00
|
|
|
if not emoji_name_regex.fullmatch(new_name):
|
2022-11-15 09:19:08 +00:00
|
|
|
abort(400, "Invalid name!")
|
2023-03-18 13:34:04 +00:00
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
if not tags_regex.fullmatch(tags):
|
|
|
|
abort(400, "Invalid tags!")
|
|
|
|
|
2023-03-21 16:50:22 +00:00
|
|
|
if new_kind not in EMOJI_KINDS:
|
2023-03-18 13:34:04 +00:00
|
|
|
abort(400, "Invalid kind!")
|
|
|
|
|
2023-10-02 02:46:59 +00:00
|
|
|
over_18 = request.values.get("over_18") == 'true'
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-18 14:53:00 +00:00
|
|
|
emoji.name = new_name
|
|
|
|
emoji.kind = new_kind
|
|
|
|
emoji.tags = tags
|
2023-09-29 07:15:29 +00:00
|
|
|
emoji.over_18 = over_18
|
2023-03-18 14:53:00 +00:00
|
|
|
g.db.add(emoji)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-18 14:53:00 +00:00
|
|
|
author = get_account(emoji.author_id)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-18 15:13:36 +00:00
|
|
|
if emoji.kind == "Marsey":
|
2023-06-06 21:39:38 +00:00
|
|
|
all_by_author = g.db.query(Emoji).filter_by(kind="Marsey", author_id=author.id).count()
|
2023-03-18 15:25:56 +00:00
|
|
|
|
2023-08-20 02:00:35 +00:00
|
|
|
if all_by_author >= 99:
|
2023-03-18 15:13:36 +00:00
|
|
|
badge_grant(badge_id=143, user=author)
|
2023-08-20 02:00:35 +00:00
|
|
|
elif all_by_author >= 9:
|
2023-03-18 15:13:36 +00:00
|
|
|
badge_grant(badge_id=16, user=author)
|
|
|
|
else:
|
2023-03-18 15:25:56 +00:00
|
|
|
badge_grant(badge_id=17, user=author)
|
2023-06-22 16:19:05 +00:00
|
|
|
elif emoji.kind == "Capy":
|
|
|
|
all_by_author = g.db.query(Emoji).filter_by(kind="Capy", author_id=author.id).count()
|
2023-08-20 02:00:35 +00:00
|
|
|
if all_by_author >= 9:
|
2023-06-22 16:19:05 +00:00
|
|
|
badge_grant(badge_id=115, user=author)
|
|
|
|
badge_grant(badge_id=114, user=author)
|
2023-06-23 16:34:29 +00:00
|
|
|
elif emoji.kind == "Carp":
|
|
|
|
all_by_author = g.db.query(Emoji).filter_by(kind="Carp", author_id=author.id).count()
|
2023-08-20 02:00:35 +00:00
|
|
|
if all_by_author >= 9:
|
2023-06-30 23:10:50 +00:00
|
|
|
badge_grant(badge_id=288, user=author)
|
|
|
|
badge_grant(badge_id=287, user=author)
|
2023-03-18 15:13:36 +00:00
|
|
|
elif emoji.kind == "Wolf":
|
|
|
|
all_by_author = g.db.query(Emoji).filter_by(kind="Wolf", author_id=author.id).count()
|
2023-08-20 02:00:35 +00:00
|
|
|
if all_by_author >= 9:
|
2023-03-18 15:13:36 +00:00
|
|
|
badge_grant(badge_id=111, user=author)
|
2023-08-06 07:35:30 +00:00
|
|
|
badge_grant(badge_id=110, user=author)
|
2023-03-18 15:13:36 +00:00
|
|
|
elif emoji.kind == "Platy":
|
|
|
|
all_by_author = g.db.query(Emoji).filter_by(kind="Platy", author_id=author.id).count()
|
2023-08-20 02:00:35 +00:00
|
|
|
if all_by_author >= 9:
|
2023-03-18 15:13:36 +00:00
|
|
|
badge_grant(badge_id=113, user=author)
|
2023-08-06 07:35:30 +00:00
|
|
|
badge_grant(badge_id=112, user=author)
|
2023-05-05 21:45:25 +00:00
|
|
|
|
2023-09-29 07:15:29 +00:00
|
|
|
if not Emoji.over_18:
|
|
|
|
cache.delete("emojis")
|
|
|
|
cache.delete(f"emoji_list_{emoji.kind}")
|
2023-03-18 13:34:04 +00:00
|
|
|
|
2023-09-15 00:07:17 +00:00
|
|
|
purge_files_in_cloudflare_cache(f"{SITE_FULL_IMAGES}/e/{emoji.name}/webp")
|
2023-03-18 13:34:04 +00:00
|
|
|
|
2023-03-21 14:16:18 +00:00
|
|
|
move(f"/asset_submissions/emojis/{name}.webp", f"files/assets/images/emojis/{emoji.name}.webp")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-21 14:16:18 +00:00
|
|
|
highquality = f"/asset_submissions/emojis/{name}"
|
2022-11-15 09:19:08 +00:00
|
|
|
with Image.open(highquality) as i:
|
2023-03-21 14:16:18 +00:00
|
|
|
new_path = f'/asset_submissions/emojis/original/{name}.{i.format.lower()}'
|
2022-11-15 09:19:08 +00:00
|
|
|
rename(highquality, new_path)
|
|
|
|
|
2022-11-20 10:50:02 +00:00
|
|
|
author.pay_account('coins', 250)
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.add(author)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
if v.id != author.id:
|
2023-03-18 14:53:00 +00:00
|
|
|
msg = f"@{v.username} (a site admin) has approved an emoji you made: :{emoji.name}:\n\nYou have received 250 coins as a reward!"
|
2022-11-15 09:19:08 +00:00
|
|
|
send_repeatable_notification(author.id, msg)
|
|
|
|
|
2023-03-18 14:53:00 +00:00
|
|
|
if v.id != emoji.submitter_id and author.id != emoji.submitter_id:
|
|
|
|
msg = f"@{v.username} (a site admin) has approved an emoji you submitted: :{emoji.name}:"
|
|
|
|
send_repeatable_notification(emoji.submitter_id, msg)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-18 14:53:00 +00:00
|
|
|
emoji.submitter_id = None
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-02-18 21:34:39 +00:00
|
|
|
ma = ModAction(
|
2023-03-18 14:53:00 +00:00
|
|
|
kind="approve_emoji",
|
2023-02-18 21:34:39 +00:00
|
|
|
user_id=v.id,
|
2023-07-22 16:24:16 +00:00
|
|
|
_note=f'<img loading="lazy" data-bs-toggle="tooltip" alt=":{name}:" title=":{name}:" src="{SITE_FULL_IMAGES}/e/{name}.webp">'
|
2023-02-18 21:34:39 +00:00
|
|
|
)
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.add(ma)
|
2023-02-18 21:34:39 +00:00
|
|
|
|
2023-09-29 07:15:29 +00:00
|
|
|
if emoji.over_18:
|
|
|
|
OVER_18_EMOJIS.append(emoji.name)
|
|
|
|
|
2023-03-18 14:53:00 +00:00
|
|
|
return {"message": f"'{emoji.name}' approved!"}
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-07-30 00:42:06 +00:00
|
|
|
def remove_asset(cls, type_name, v, name):
|
2022-11-15 09:19:08 +00:00
|
|
|
if cls not in ASSET_TYPES: raise Exception("not a valid asset type")
|
2023-03-18 14:02:03 +00:00
|
|
|
should_make_lower = cls == Emoji
|
2022-11-15 09:19:08 +00:00
|
|
|
if should_make_lower: name = name.lower()
|
|
|
|
name = name.strip()
|
|
|
|
if not name:
|
|
|
|
abort(400, f"You need to specify a {type_name}!")
|
|
|
|
asset = None
|
|
|
|
if cls == HatDef:
|
2023-03-16 06:27:58 +00:00
|
|
|
asset = g.db.query(cls).filter_by(name=name).one_or_none()
|
2022-11-15 09:19:08 +00:00
|
|
|
else:
|
2023-03-16 06:27:58 +00:00
|
|
|
asset = g.db.get(cls, name)
|
2022-11-15 09:19:08 +00:00
|
|
|
if not asset:
|
|
|
|
abort(404, f"This {type_name} '{name}' doesn't exist!")
|
2023-01-22 08:04:49 +00:00
|
|
|
if v.id != asset.submitter_id and v.admin_level < PERMS['MODERATE_PENDING_SUBMITTED_ASSETS']:
|
|
|
|
abort(403)
|
2022-11-15 09:19:08 +00:00
|
|
|
name = asset.name
|
2023-02-28 17:15:49 +00:00
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
if v.id != asset.submitter_id:
|
2023-09-29 07:59:27 +00:00
|
|
|
reason = request.values.get("reason")
|
2022-11-15 09:19:08 +00:00
|
|
|
msg = f"@{v.username} has rejected a {type_name} you submitted: `'{name}'`"
|
2023-09-29 07:59:27 +00:00
|
|
|
if reason:
|
|
|
|
msg += f"\nReason: `{reason}`"
|
2022-11-15 09:19:08 +00:00
|
|
|
send_repeatable_notification(asset.submitter_id, msg)
|
2023-02-28 17:15:49 +00:00
|
|
|
|
|
|
|
ma = ModAction(
|
|
|
|
kind=f"reject_{type_name}",
|
|
|
|
user_id=v.id,
|
|
|
|
_note=name
|
|
|
|
)
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.add(ma)
|
2023-02-28 17:15:49 +00:00
|
|
|
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.delete(asset)
|
2023-03-18 14:56:34 +00:00
|
|
|
|
2023-03-25 15:07:12 +00:00
|
|
|
os.remove(f"/asset_submissions/{type_name}s/{name}.webp")
|
|
|
|
os.remove(f"/asset_submissions/{type_name}s/{name}")
|
2023-02-18 21:34:39 +00:00
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
return {"message": f"'{name}' removed!"}
|
|
|
|
|
2023-03-18 13:34:04 +00:00
|
|
|
@app.post("/remove/emoji/<name>")
|
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)
|
2022-11-15 09:19:08 +00:00
|
|
|
@auth_required
|
2023-07-30 00:42:06 +00:00
|
|
|
def remove_emoji(v, name):
|
2023-03-18 14:53:00 +00:00
|
|
|
return remove_asset(Emoji, "emoji", v, name)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
@app.get("/submit/hats")
|
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)
|
2022-11-15 09:19:08 +00:00
|
|
|
@auth_required
|
2023-07-30 00:42:06 +00:00
|
|
|
def submit_hats(v):
|
2023-08-16 23:20:31 +00:00
|
|
|
hats = g.db.query(HatDef).filter(HatDef.submitter_id != None).order_by(HatDef.created_utc.desc()).all()
|
2023-03-18 13:34:04 +00:00
|
|
|
|
2023-08-11 21:50:23 +00:00
|
|
|
return render_template("submit_hats.html", v=v, hats=hats)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
@app.post("/submit/hats")
|
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)
|
2022-11-15 09:19:08 +00:00
|
|
|
@auth_required
|
2023-07-30 00:42:06 +00:00
|
|
|
def submit_hat(v):
|
2022-11-15 09:19:08 +00:00
|
|
|
name = request.values.get('name', '').strip()
|
|
|
|
description = request.values.get('description', '').strip()
|
|
|
|
username = request.values.get('author', '').strip()
|
|
|
|
|
|
|
|
if g.is_tor:
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "Image uploads are not allowed through TOR!")
|
2022-09-10 07:38:23 +00:00
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
file = request.files["image"]
|
|
|
|
if not file or not file.content_type.startswith('image/'):
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "You need to submit an image!")
|
2022-09-10 07:38:23 +00:00
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
if not hat_regex.fullmatch(name):
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "Invalid name!")
|
2022-09-10 07:38:23 +00:00
|
|
|
|
2023-03-16 06:27:58 +00:00
|
|
|
existing = g.db.query(HatDef.name).filter_by(name=name).one_or_none()
|
2022-11-15 09:19:08 +00:00
|
|
|
if existing:
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "A hat with this name already exists!")
|
2022-09-10 07:38:23 +00:00
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
if not description_regex.fullmatch(description):
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "Invalid description!")
|
2022-09-10 07:38:23 +00:00
|
|
|
|
2023-09-06 18:13:32 +00:00
|
|
|
author = get_user(username, v=v)
|
2022-09-10 07:38:23 +00:00
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
highquality = f'/asset_submissions/hats/{name}'
|
|
|
|
file.save(highquality)
|
2023-09-14 22:40:25 +00:00
|
|
|
process_image(highquality, v) #to ensure not malware
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
with Image.open(highquality) as i:
|
|
|
|
if i.width > 100 or i.height > 130:
|
2023-03-25 15:07:12 +00:00
|
|
|
os.remove(highquality)
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "Images must be 100x130")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
if len(list(Iterator(i))) > 1: price = 1000
|
|
|
|
else: price = 500
|
|
|
|
|
|
|
|
filename = f'/asset_submissions/hats/{name}.webp'
|
|
|
|
copyfile(highquality, filename)
|
|
|
|
process_image(filename, v, resize=100)
|
|
|
|
|
|
|
|
hat = HatDef(name=name, author_id=author.id, description=description, price=price, submitter_id=v.id)
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.add(hat)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-08-11 21:50:23 +00:00
|
|
|
return {"message": f"'{name}' submitted successfully!"}
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
@app.post("/admin/approve/hat/<name>")
|
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("120/minute;200/hour;1000/day", deduct_when=lambda response: response.status_code < 400)
|
|
|
|
@limiter.limit("120/minute;200/hour;1000/day", deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
|
2023-01-22 08:04:49 +00:00
|
|
|
@admin_level_required(PERMS['MODERATE_PENDING_SUBMITTED_ASSETS'])
|
2022-11-15 09:19:08 +00:00
|
|
|
def approve_hat(v, name):
|
|
|
|
hat = verify_permissions_and_get_asset(HatDef, "hat", v, name, False)
|
|
|
|
description = request.values.get('description').strip()
|
|
|
|
if not description: abort(400, "You need to include a description!")
|
|
|
|
|
|
|
|
new_name = request.values.get('name').strip()
|
|
|
|
if not new_name: abort(400, "You need to include a name!")
|
|
|
|
if not hat_regex.fullmatch(new_name): abort(400, "Invalid name!")
|
|
|
|
if not description_regex.fullmatch(description): abort(400, "Invalid description!")
|
|
|
|
|
|
|
|
try:
|
|
|
|
hat.price = int(request.values.get('price'))
|
|
|
|
if hat.price < 0: raise ValueError("Invalid hat price")
|
|
|
|
except:
|
|
|
|
abort(400, "Invalid hat price")
|
|
|
|
hat.name = new_name
|
|
|
|
hat.description = description
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.add(hat)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
author = hat.author
|
|
|
|
|
2023-03-16 06:27:58 +00:00
|
|
|
all_by_author = g.db.query(HatDef).filter_by(author_id=author.id).count()
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-08-20 02:00:35 +00:00
|
|
|
if all_by_author >= 249:
|
2022-11-15 09:19:08 +00:00
|
|
|
badge_grant(badge_id=166, user=author)
|
2023-08-20 02:00:35 +00:00
|
|
|
elif all_by_author >= 99:
|
2022-11-15 09:19:08 +00:00
|
|
|
badge_grant(badge_id=165, user=author)
|
2023-08-20 02:00:35 +00:00
|
|
|
elif all_by_author >= 49:
|
2022-11-15 09:19:08 +00:00
|
|
|
badge_grant(badge_id=164, user=author)
|
2023-08-20 02:00:35 +00:00
|
|
|
elif all_by_author >= 9:
|
2022-11-15 09:19:08 +00:00
|
|
|
badge_grant(badge_id=163, user=author)
|
|
|
|
|
|
|
|
hat_copy = Hat(
|
|
|
|
user_id=author.id,
|
|
|
|
hat_id=hat.id
|
|
|
|
)
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.add(hat_copy)
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
if v.id != author.id:
|
2022-12-13 17:11:26 +00:00
|
|
|
msg = f"@{v.username} (a site admin) has approved a hat you made: '{hat.name}'"
|
2022-11-15 09:19:08 +00:00
|
|
|
send_repeatable_notification(author.id, msg)
|
|
|
|
|
|
|
|
if v.id != hat.submitter_id and author.id != hat.submitter_id:
|
2022-12-13 17:11:26 +00:00
|
|
|
msg = f"@{v.username} (a site admin) has approved a hat you submitted: '{hat.name}'"
|
2022-11-15 09:19:08 +00:00
|
|
|
send_repeatable_notification(hat.submitter_id, msg)
|
|
|
|
|
|
|
|
hat.submitter_id = None
|
|
|
|
|
|
|
|
move(f"/asset_submissions/hats/{name}.webp", f"files/assets/images/hats/{hat.name}.webp")
|
|
|
|
|
|
|
|
highquality = f"/asset_submissions/hats/{name}"
|
|
|
|
with Image.open(highquality) as i:
|
|
|
|
new_path = f'/asset_submissions/hats/original/{name}.{i.format.lower()}'
|
|
|
|
rename(highquality, new_path)
|
|
|
|
|
2023-02-18 21:34:39 +00:00
|
|
|
ma = ModAction(
|
|
|
|
kind="approve_hat",
|
|
|
|
user_id=v.id,
|
2023-03-19 16:28:19 +00:00
|
|
|
_note=f'<a href="{SITE_FULL_IMAGES}/i/hats/{name}.webp">{name}</a>'
|
2023-02-18 21:34:39 +00:00
|
|
|
)
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.add(ma)
|
2023-02-18 21:34:39 +00:00
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
return {"message": f"'{hat.name}' approved!"}
|
|
|
|
|
|
|
|
@app.post("/remove/hat/<name>")
|
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)
|
2022-11-15 09:19:08 +00:00
|
|
|
@auth_required
|
2023-07-30 00:42:06 +00:00
|
|
|
def remove_hat(v, name):
|
2022-11-15 09:19:08 +00:00
|
|
|
return remove_asset(HatDef, 'hat', v, name)
|
|
|
|
|
2023-03-18 13:34:04 +00:00
|
|
|
@app.get("/admin/update/emojis")
|
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-01-22 08:04:49 +00:00
|
|
|
@admin_level_required(PERMS['UPDATE_ASSETS'])
|
2023-03-18 14:53:00 +00:00
|
|
|
def update_emojis(v):
|
2023-03-21 17:03:11 +00:00
|
|
|
return render_template("admin/update_assets.html", v=v, type="Emoji")
|
2022-09-10 07:38:23 +00:00
|
|
|
|
|
|
|
|
2023-03-18 13:34:04 +00:00
|
|
|
@app.post("/admin/update/emojis")
|
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-01-22 08:04:49 +00:00
|
|
|
@admin_level_required(PERMS['UPDATE_ASSETS'])
|
2023-03-18 14:53:00 +00:00
|
|
|
def update_emoji(v):
|
2022-11-15 09:19:08 +00:00
|
|
|
file = request.files["image"]
|
|
|
|
name = request.values.get('name', '').lower().strip()
|
|
|
|
tags = request.values.get('tags', '').lower().strip()
|
2023-03-21 16:50:22 +00:00
|
|
|
kind = request.values.get('kind', '').strip()
|
2022-09-10 07:38:23 +00:00
|
|
|
|
2023-03-18 13:34:04 +00:00
|
|
|
existing = g.db.get(Emoji, name)
|
2022-11-15 09:19:08 +00:00
|
|
|
if not existing:
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "An emoji with this name doesn't exist!")
|
2023-03-18 13:34:04 +00:00
|
|
|
|
|
|
|
updated = False
|
2022-09-18 17:38:53 +00:00
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
if file:
|
|
|
|
if g.is_tor:
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "Image uploads are not allowed through TOR!")
|
2022-11-15 09:19:08 +00:00
|
|
|
if not file.content_type.startswith('image/'):
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "You need to submit an image!")
|
2023-01-01 11:36:20 +00:00
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
for x in IMAGE_FORMATS:
|
2023-03-21 14:16:18 +00:00
|
|
|
if path.isfile(f'/asset_submissions/emojis/original/{name}.{x}'):
|
2023-03-25 15:07:12 +00:00
|
|
|
os.remove(f'/asset_submissions/emojis/original/{name}.{x}')
|
2022-10-08 01:00:27 +00:00
|
|
|
|
2023-03-21 14:16:18 +00:00
|
|
|
highquality = f"/asset_submissions/emojis/{name}"
|
2022-10-08 01:00:27 +00:00
|
|
|
file.save(highquality)
|
2023-09-14 22:40:25 +00:00
|
|
|
process_image(highquality, v) #to ensure not malware
|
2022-10-08 01:00:27 +00:00
|
|
|
with Image.open(highquality) as i:
|
|
|
|
format = i.format.lower()
|
2023-03-21 14:16:18 +00:00
|
|
|
new_path = f'/asset_submissions/emojis/original/{name}.{format}'
|
2022-10-08 01:00:27 +00:00
|
|
|
rename(highquality, new_path)
|
2022-09-18 17:38:53 +00:00
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
filename = f"files/assets/images/emojis/{name}.webp"
|
2022-10-08 01:00:27 +00:00
|
|
|
copyfile(new_path, filename)
|
2023-09-23 17:58:04 +00:00
|
|
|
process_image(filename, v, resize=300, trim=True)
|
2023-09-15 00:07:17 +00:00
|
|
|
purge_files_in_cloudflare_cache([f"{SITE_FULL_IMAGES}/e/{name}.webp", f"{SITE_FULL_IMAGES}/asset_submissions/emojis/original/{name}.{format}"])
|
2023-03-18 13:34:04 +00:00
|
|
|
updated = True
|
|
|
|
|
2023-01-01 11:36:20 +00:00
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
if tags and existing.tags != tags and tags != "none":
|
2023-03-18 13:34:04 +00:00
|
|
|
if not tags_regex.fullmatch(tags):
|
|
|
|
abort(400, "Invalid tags!")
|
2023-02-17 17:06:22 +00:00
|
|
|
existing.tags += f" {tags}"
|
2023-03-21 16:50:22 +00:00
|
|
|
updated = True
|
|
|
|
|
|
|
|
if kind and existing.kind != kind and kind != "none":
|
|
|
|
if kind not in EMOJI_KINDS:
|
|
|
|
abort(400, "Invalid kind!")
|
|
|
|
existing.kind = kind
|
2023-03-18 13:34:04 +00:00
|
|
|
updated = True
|
|
|
|
|
|
|
|
if not updated:
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "You need to actually update something!")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-21 16:50:22 +00:00
|
|
|
g.db.add(existing)
|
|
|
|
|
2022-11-15 09:19:08 +00:00
|
|
|
ma = ModAction(
|
2023-03-18 14:53:00 +00:00
|
|
|
kind="update_emoji",
|
2022-11-15 09:19:08 +00:00
|
|
|
user_id=v.id,
|
2023-07-22 16:24:16 +00:00
|
|
|
_note=f'<img loading="lazy" data-bs-toggle="tooltip" alt=":{name}:" title=":{name}:" src="{SITE_FULL_IMAGES}/e/{name}.webp">'
|
2022-11-15 09:19:08 +00:00
|
|
|
)
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.add(ma)
|
2023-03-21 16:50:22 +00:00
|
|
|
|
2023-09-29 07:15:29 +00:00
|
|
|
if not Emoji.over_18:
|
|
|
|
cache.delete("emojis")
|
|
|
|
cache.delete(f"emoji_list_{existing.kind}")
|
2023-03-21 16:50:22 +00:00
|
|
|
|
2023-08-11 21:50:23 +00:00
|
|
|
return {"message": f"'{name}' updated successfully!"}
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
@app.get("/admin/update/hats")
|
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-01-22 08:04:49 +00:00
|
|
|
@admin_level_required(PERMS['UPDATE_ASSETS'])
|
2022-11-15 09:19:08 +00:00
|
|
|
def update_hats(v):
|
2023-03-04 18:55:07 +00:00
|
|
|
return render_template("admin/update_assets.html", v=v, type="Hat")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
@app.post("/admin/update/hats")
|
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-01-22 08:04:49 +00:00
|
|
|
@admin_level_required(PERMS['UPDATE_ASSETS'])
|
2022-11-15 09:19:08 +00:00
|
|
|
def update_hat(v):
|
|
|
|
file = request.files["image"]
|
|
|
|
name = request.values.get('name', '').strip()
|
|
|
|
|
|
|
|
if g.is_tor:
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "Image uploads are not allowed through TOR!")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
if not file or not file.content_type.startswith('image/'):
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "You need to submit an image!")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
if not hat_regex.fullmatch(name):
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "Invalid name!")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
2023-03-16 06:27:58 +00:00
|
|
|
existing = g.db.query(HatDef.name).filter_by(name=name).one_or_none()
|
2022-11-15 09:19:08 +00:00
|
|
|
if not existing:
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "A hat with this name doesn't exist!")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
highquality = f"/asset_submissions/hats/{name}"
|
|
|
|
file.save(highquality)
|
2023-09-14 22:40:25 +00:00
|
|
|
process_image(highquality, v) #to ensure not malware
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
with Image.open(highquality) as i:
|
|
|
|
if i.width > 100 or i.height > 130:
|
2023-03-25 15:07:12 +00:00
|
|
|
os.remove(highquality)
|
2023-08-11 21:50:23 +00:00
|
|
|
abort(400, "Images must be 100x130")
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
format = i.format.lower()
|
|
|
|
new_path = f'/asset_submissions/hats/original/{name}.{format}'
|
|
|
|
|
|
|
|
for x in IMAGE_FORMATS:
|
|
|
|
if path.isfile(f'/asset_submissions/hats/original/{name}.{x}'):
|
2023-03-25 15:07:12 +00:00
|
|
|
os.remove(f'/asset_submissions/hats/original/{name}.{x}')
|
2022-11-15 09:19:08 +00:00
|
|
|
|
|
|
|
rename(highquality, new_path)
|
|
|
|
|
|
|
|
filename = f"files/assets/images/hats/{name}.webp"
|
|
|
|
copyfile(new_path, filename)
|
|
|
|
process_image(filename, v, resize=100)
|
2023-09-15 00:07:17 +00:00
|
|
|
purge_files_in_cloudflare_cache([f"{SITE_FULL_IMAGES}/i/hats/{name}.webp", f"{SITE_FULL_IMAGES}/asset_submissions/hats/original/{name}.{format}"])
|
2022-11-15 09:19:08 +00:00
|
|
|
ma = ModAction(
|
|
|
|
kind="update_hat",
|
|
|
|
user_id=v.id,
|
2023-03-19 16:28:19 +00:00
|
|
|
_note=f'<a href="{SITE_FULL_IMAGES}/i/hats/{name}.webp">{name}</a>'
|
2022-11-15 09:19:08 +00:00
|
|
|
)
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.add(ma)
|
2023-08-11 21:50:23 +00:00
|
|
|
return {"message": f"'{name}' updated successfully!"}
|