MarseyWorld/files/routes/chats.py

406 lines
14 KiB
Python
Raw Normal View History

2024-04-08 06:57:38 +00:00
import isodate
import yt_dlp
2024-03-10 14:27:21 +00:00
2024-04-06 06:27:43 +00:00
from files.classes.chats import *
2024-04-08 06:33:47 +00:00
from files.classes.orgy import *
2024-03-10 14:27:21 +00:00
from files.routes.wrappers import *
from files.helpers.config.const import *
from files.helpers.get import *
from files.__main__ import app, limiter
2024-04-06 04:31:51 +00:00
@app.get("/chat")
@app.get("/orgy")
@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 chat_redirect(v):
2024-04-06 04:31:51 +00:00
return redirect("/chat/1")
2024-03-10 14:27:21 +00:00
@app.post("/@<username>/chat")
@limiter.limit('1/second', scope=rpath)
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
@limiter.limit("10/minute;20/hour;50/day", deduct_when=lambda response: response.status_code < 400)
@limiter.limit("10/minute;20/hour;50/day", deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
@auth_required
def chat_user(v, username):
user = get_user(username, v=v, include_blocks=True)
if hasattr(user, 'is_blocking') and user.is_blocking:
2024-08-11 20:11:04 +00:00
stop(403, f"You're blocking @{user.username}")
2024-03-10 14:27:21 +00:00
if v.admin_level < PERMS['MESSAGE_BLOCKED_USERS'] and hasattr(user, 'is_blocked') and user.is_blocked:
2024-08-11 20:11:04 +00:00
stop(403, f"@{user.username} is blocking you!")
2024-03-10 14:27:21 +00:00
if v.admin_level < PERMS['MESSAGE_BLOCKED_USERS'] and user.has_muted(v):
2024-08-11 20:11:04 +00:00
stop(403, f"@{user.username} is muting notifications from you, so you can't chat with them!")
2024-03-10 14:27:21 +00:00
2024-04-09 06:59:05 +00:00
sq = g.db.query(Chat.id).join(ChatMembership, ChatMembership.chat_id == Chat.id).filter(ChatMembership.user_id.in_((v.id, user.id))).group_by(Chat.id).having(func.count(Chat.id) == 2).subquery()
2024-10-01 19:19:12 +00:00
existing = g.db.query(Chat.id).join(ChatMembership, ChatMembership.chat_id == Chat.id).filter(Chat.id == sq.c.id).group_by(Chat.id).having(func.count(Chat.id) == 2).order_by(Chat.id).first()
2024-03-10 14:27:21 +00:00
if existing:
return redirect(f"/chat/{existing.id}")
2024-04-07 08:46:34 +00:00
chat = Chat(name=f"@{v.username}, @{user.username}")
2024-03-10 14:27:21 +00:00
g.db.add(chat)
g.db.flush()
chat_membership = ChatMembership(
user_id=v.id,
chat_id=chat.id,
is_mod=True,
2024-03-10 14:27:21 +00:00
)
g.db.add(chat_membership)
chat_membership = ChatMembership(
user_id=user.id,
chat_id=chat.id,
)
chat_membership.created_utc += 1
2024-03-10 14:27:21 +00:00
g.db.add(chat_membership)
return redirect(f"/chat/{chat.id}")
@app.get("/chat/<int:chat_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
2024-04-06 06:27:43 +00:00
def chat(v, chat_id):
2024-03-10 14:27:21 +00:00
chat = g.db.get(Chat, chat_id)
if not chat:
2024-08-11 20:11:04 +00:00
stop(404, "Chat not found!")
2024-03-10 14:27:21 +00:00
muting_chat = False
if chat.id == 1:
if not v.allowed_in_chat:
2024-08-11 20:11:04 +00:00
stop(403, f"To prevent spam, you'll need {TRUESCORE_MINIMUM} truescore (this is {TRUESCORE_MINIMUM} votes, either up or down, on any threads or comments you've made) in order to access chat. Sorry! I love you 💖")
2024-08-07 14:57:32 +00:00
membership = True
else:
membership = g.db.query(ChatMembership).filter_by(user_id=v.id, chat_id=chat_id).one_or_none()
if v.admin_level < PERMS['VIEW_CHATS'] and not membership:
2024-08-11 20:11:04 +00:00
stop(403, "You're not a member of this chat!")
2024-07-13 11:20:29 +00:00
if membership:
muting_chat = membership.muted
2024-03-10 14:27:21 +00:00
displayed_messages = g.db.query(ChatMessage).options(joinedload(ChatMessage.quoted_message)).filter(ChatMessage.chat_id == chat.id)
if v.admin_level < PERMS['USER_SHADOWBAN']:
displayed_messages = displayed_messages.join(ChatMessage.user).filter(or_(User.id == v.id, User.shadowbanned == None))
displayed_messages = reversed(displayed_messages.order_by(ChatMessage.id.desc()).limit(250).all())
2024-03-10 14:36:14 +00:00
displayed_messages = {m.id: m for m in displayed_messages}
2024-03-10 14:27:21 +00:00
2024-04-06 04:35:44 +00:00
if chat.id == 1:
sorted_memberships = None
else:
if not session.get("GLOBAL") and membership:
2024-07-13 16:48:33 +00:00
if membership.notification:
membership.notification = False
2024-05-23 23:47:40 +00:00
membership.mentions = 0
g.db.add(membership)
g.db.commit() #to clear notif count
2024-04-07 00:50:51 +00:00
2024-04-06 04:35:44 +00:00
query = g.db.query(ChatMembership).filter_by(chat_id=chat.id)
2024-04-07 00:50:51 +00:00
sorted_memberships = query.filter(ChatMembership.user_id != chat.owner_id).join(ChatMembership.user).order_by(func.lower(User.username)).all()
owner_membership = query.filter_by(user_id=chat.owner_id).one_or_none()
if owner_membership:
sorted_memberships = [owner_membership] + sorted_memberships
2024-04-08 06:33:47 +00:00
orgy = get_running_orgy(v, chat_id)
if orgy:
orgies = g.db.query(Orgy).filter_by(chat_id=chat_id).order_by(Orgy.start_utc).all()
2024-08-07 22:52:53 +00:00
return render_template("orgy.html", v=v, messages=displayed_messages, chat=chat, sorted_memberships=sorted_memberships, muting_chat=muting_chat, orgy=orgy, orgies=orgies, membership=membership, chat_css=chat.css)
2024-04-08 06:33:47 +00:00
2024-08-07 22:52:53 +00:00
return render_template("chat.html", v=v, messages=displayed_messages, chat=chat, sorted_memberships=sorted_memberships, muting_chat=muting_chat, membership=membership, chat_css=chat.css)
2024-03-10 14:27:21 +00:00
@app.post("/chat/<int:chat_id>/name")
2024-04-24 12:14:59 +00:00
@limiter.limit('1/second', scope=rpath)
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
2024-03-10 14:27:21 +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)
@auth_required
def change_chat_name(v, chat_id):
chat = g.db.get(Chat, chat_id)
if not chat:
2024-08-11 20:11:04 +00:00
stop(404, "Chat not found!")
2024-03-10 14:27:21 +00:00
if v.id not in chat.mod_ids:
2024-08-11 20:11:04 +00:00
stop(403, "You need to be the chat owner or a mod to do that!")
2024-03-10 14:27:21 +00:00
new_name = request.values.get("new_name").strip()
2024-03-10 18:21:10 +00:00
if len(new_name) > 40:
2024-08-11 20:11:04 +00:00
stop(400, "New name is too long (max 40 characters)")
2024-03-10 18:21:10 +00:00
2024-03-10 14:27:21 +00:00
chat.name = new_name
g.db.add(chat)
return redirect(f"/chat/{chat.id}")
@app.post("/chat/<int:chat_id>/leave")
2024-04-24 12:14:59 +00:00
@limiter.limit('1/second', scope=rpath)
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
2024-03-10 14:27:21 +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)
@auth_required
def leave_chat(v, chat_id):
chat = g.db.get(Chat, chat_id)
if not chat:
2024-08-11 20:11:04 +00:00
stop(404, "Chat not found!")
2024-03-10 14:27:21 +00:00
membership = g.db.query(ChatMembership).filter_by(user_id=v.id, chat_id=chat_id).one_or_none()
if not membership:
2024-08-11 20:11:04 +00:00
stop(400, "You're not a member of this chat!")
2024-03-10 14:27:21 +00:00
g.db.delete(membership)
g.db.flush()
if not chat.mod_ids:
oldest_membership = g.db.query(ChatMembership).filter_by(chat_id=chat.id).order_by(ChatMembership.created_utc, ChatMembership.user_id).first()
2024-08-11 21:13:52 +00:00
if oldest_membership:
oldest_membership.is_mod = True
g.db.add(oldest_membership)
2024-03-10 14:27:21 +00:00
return {"message": "Chat left successfully!"}
2024-04-08 06:33:47 +00:00
@app.post("/chat/<int:chat_id>/toggle_mute")
@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 mute_chat(v, chat_id):
chat = g.db.get(Chat, chat_id)
if not chat:
2024-08-11 20:11:04 +00:00
stop(404, "Chat not found!")
membership = g.db.query(ChatMembership).filter_by(user_id=v.id, chat_id=chat_id).one_or_none()
if not membership:
2024-08-11 20:11:04 +00:00
stop(400, "You're not a member of this chat!")
if membership.muted:
membership.muted = False
msg = "Chat unmuted successfully (yayyy)"
send_notification(chat.owner_id, f"@{v.username} unmuted your chat [{chat.name}](/chat/{chat.id}).")
else:
membership.muted = True
msg = "Chat muted successfully (die)"
send_notification(chat.owner_id, f"@{v.username} muted your chat [{chat.name}](/chat/{chat.id}), kick him now!")
g.db.add(membership)
return {"message": msg}
2024-04-08 06:33:47 +00:00
2024-08-07 22:52:53 +00:00
@app.get("/chat/<int:chat_id>/custom_css")
@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 chat_custom_css_get(v, chat_id):
chat = g.db.get(Chat, chat_id)
if not chat:
2024-08-11 20:11:04 +00:00
stop(404, "Chat not found!")
2024-08-07 22:52:53 +00:00
if v.id not in chat.mod_ids:
2024-08-11 20:11:04 +00:00
stop(403, "You need to be the chat owner or a mod to do that!")
2024-08-07 22:52:53 +00:00
return render_template("chat_css.html", v=v, chat=chat)
@app.post("/chat/<int:chat_id>/custom_css")
@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 chat_custom_css_post(v, chat_id):
chat = g.db.get(Chat, chat_id)
if not chat:
2024-08-11 20:11:04 +00:00
stop(404, "Chat not found!")
2024-08-07 22:52:53 +00:00
if v.id not in chat.mod_ids:
2024-08-11 20:11:04 +00:00
stop(403, "You need to be the chat owner or a mod to do that!")
2024-08-07 22:52:53 +00:00
2024-08-11 20:11:04 +00:00
if v.shadowbanned: stop(400)
2024-08-07 22:52:53 +00:00
css = request.values.get('css', '').strip()
if len(css) > CSS_LENGTH_LIMIT:
stop(400, f"Chat CSS is too long (max {CSS_LENGTH_LIMIT} characters)")
2024-08-07 22:52:53 +00:00
valid, error = validate_css(css)
if not valid:
2024-08-11 20:11:04 +00:00
stop(400, error)
2024-08-07 22:52:53 +00:00
chat.css = css
g.db.add(chat)
return {"message": "Chat Custom CSS successfully updated!"}
@app.get("/chat/<int:chat_id>/css")
@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 chat_css(v, chat_id):
chat = g.db.query(Chat.css).filter_by(id=chat_id).one_or_none()
if not chat:
2024-08-11 20:11:04 +00:00
stop(404, "Chat not found!")
2024-08-07 22:52:53 +00:00
resp = make_response(chat.css or "")
resp.headers.add("Content-Type", "text/css")
return resp
2024-04-08 06:33:47 +00:00
@app.get("/chat/<int:chat_id>/orgies")
@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)
2024-04-08 06:33:47 +00:00
@auth_required
def orgy_control(v, chat_id):
chat = g.db.get(Chat, chat_id)
if not chat:
2024-08-11 20:11:04 +00:00
stop(404, "Chat not found!")
2024-04-08 06:33:47 +00:00
if chat.id == 1:
if v.admin_level < PERMS["ORGIES"]:
2024-08-11 20:11:04 +00:00
stop(403, "Your admin-level is not sufficient enough for this action!")
elif v.id not in chat.mod_ids:
2024-08-11 20:11:04 +00:00
stop(403, "You need to be the chat owner or a mod to do that!")
2024-04-08 06:33:47 +00:00
orgies = g.db.query(Orgy).filter_by(chat_id=chat_id).order_by(Orgy.start_utc).all()
return render_template("orgy_control.html", v=v, orgies=orgies, chat=chat)
@app.post("/chat/<int:chat_id>/schedule_orgy")
@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)
2024-04-08 06:33:47 +00:00
@auth_required
def schedule_orgy(v, chat_id):
chat = g.db.get(Chat, chat_id)
if not chat:
2024-08-11 20:11:04 +00:00
stop(404, "Chat not found!")
2024-04-08 06:33:47 +00:00
if chat.id == 1:
if v.admin_level < PERMS["ORGIES"]:
2024-08-11 20:11:04 +00:00
stop(403, "Your admin-level is not sufficient enough for this action!")
elif v.id not in chat.mod_ids:
2024-08-11 20:11:04 +00:00
stop(403, "You need to be the chat owner or a mod to do that!")
2024-04-08 06:33:47 +00:00
link = request.values.get("link", "").strip()
title = request.values.get("title", "").strip()
start_utc = request.values.get("start_utc", "").strip()
if not link:
2024-08-11 20:11:04 +00:00
stop(400, "A link is required!")
2024-04-08 06:33:47 +00:00
if not title:
2024-08-11 20:11:04 +00:00
stop(400, "A title is required!")
2024-04-08 06:33:47 +00:00
if len(title) > 50:
2024-08-11 20:11:04 +00:00
stop(400, 'Title is too long (max 50 characters)')
2024-04-08 06:33:47 +00:00
normalized_link = normalize_url(link)
if start_utc:
start_utc = int(start_utc)
else:
2024-05-03 07:44:24 +00:00
last_orgy = g.db.query(Orgy).filter_by(chat_id=chat.id).order_by(Orgy.start_utc.desc()).first()
2024-04-08 06:33:47 +00:00
if last_orgy and last_orgy.end_utc:
start_utc = last_orgy.end_utc
else:
start_utc = int(time.time())
end_utc = None
if bare_youtube_regex.match(normalized_link):
orgy_type = 'youtube'
id, _ = get_youtube_id_and_t(normalized_link)
data = f'https://cdpn.io/pen/debug/NWeVNRj?v={id}&autoplay=1'
if YOUTUBE_KEY != DEFAULT_CONFIG_VALUE:
req = requests.get(f"https://www.googleapis.com/youtube/v3/videos?id={id}&key={YOUTUBE_KEY}&part=contentDetails", headers=HEADERS, timeout=5).json()
2024-08-20 01:40:42 +00:00
duration = req['items'][0]['contentDetails'].get('duration')
if duration and duration != 'P0D':
2024-04-08 06:33:47 +00:00
duration = isodate.parse_duration(duration).total_seconds()
end_utc = int(start_utc + duration)
elif rumble_regex.match(normalized_link):
orgy_type = 'rumble'
data = normalized_link
elif twitch_regex.match(normalized_link):
orgy_type = 'twitch'
data = twitch_regex.search(normalized_link).group(3)
data = f'https://player.twitch.tv/?channel={data}&parent={SITE}'
2024-08-23 18:58:21 +00:00
elif any(normalized_link.lower().endswith(f'.{x}') for x in VIDEO_FORMATS):
2024-04-10 11:23:45 +00:00
domain = tldextract.extract(normalized_link).registered_domain
2024-10-04 17:33:15 +00:00
if domain not in {'archive.org', 'catbox.moe'} and not is_safe_url(normalized_link):
2024-08-11 20:11:04 +00:00
stop(400, "For linking an mp4 file, you can only use archive.org or one of the approved media hosts outlined in https://rdrama.net/formatting#approved")
2024-04-08 06:33:47 +00:00
orgy_type = 'file'
data = normalized_link
2024-06-06 18:37:42 +00:00
video_info = ffmpeg.probe(data)
2024-04-08 06:33:47 +00:00
duration = float(video_info['streams'][0]['duration'])
if duration == 2.0: raise
if duration > 3000:
duration += 300 #account for break
end_utc = int(start_utc + duration)
else:
2024-08-11 20:11:04 +00:00
stop(400)
2024-04-08 06:33:47 +00:00
data = data.strip()
orgy = Orgy(
title=title,
type=orgy_type,
data=data,
start_utc=start_utc,
end_utc=end_utc,
chat_id=chat_id,
)
g.db.add(orgy)
if chat.id == 1:
ma = ModAction(
kind="schedule_orgy",
user_id=v.id,
_note=f'<a href="{orgy.data}" rel="nofollow noopener">{title}</a>',
2024-04-08 06:33:47 +00:00
)
g.db.add(ma)
return redirect(f"/chat/{chat_id}/orgies")
@app.post("/chat/<int:chat_id>/remove_orgy/<int:created_utc>")
@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)
2024-04-08 06:33:47 +00:00
@auth_required
def remove_orgy(v, created_utc, chat_id):
chat = g.db.get(Chat, chat_id)
if not chat:
2024-08-11 20:11:04 +00:00
stop(404, "Chat not found!")
2024-04-08 06:33:47 +00:00
if chat.id == 1:
if v.admin_level < PERMS["ORGIES"]:
2024-08-11 20:11:04 +00:00
stop(403, "Your admin-level is not sufficient enough for this action!")
elif v.id not in chat.mod_ids:
2024-08-11 20:11:04 +00:00
stop(403, "You need to be the chat owner or a mod to do that!")
2024-04-08 06:33:47 +00:00
2024-04-16 21:31:07 +00:00
orgy = g.db.query(Orgy).filter_by(created_utc=created_utc).one_or_none()
2024-04-08 06:33:47 +00:00
2024-04-16 21:31:07 +00:00
if orgy:
if chat.id == 1:
ma = ModAction(
kind="remove_orgy",
user_id=v.id,
_note=f'<a href="{orgy.data}" rel="nofollow noopener">{orgy.title}</a>',
)
g.db.add(ma)
started = orgy.started
g.db.delete(orgy)
g.db.commit()
if started:
requests.post(f'http://localhost:5001/chat/{chat_id}/refresh_chat', headers={"User-Agent": "refreshing_chat", "Host": SITE})
2024-04-08 06:33:47 +00:00
return {"message": "Orgy stopped successfully!"}