diff --git a/files/assets/js/admin/orgies.js b/files/assets/js/admin/orgies.js deleted file mode 100644 index b115f4d53..000000000 --- a/files/assets/js/admin/orgies.js +++ /dev/null @@ -1,8 +0,0 @@ -function remove_orgy(t, created_utc) { - postToast(t, `/admin/remove_orgy/${created_utc}`, - {}, - () => { - t.parentElement.parentElement.remove() - } - ); -} diff --git a/files/assets/js/core.js b/files/assets/js/core.js index f9fb38cf8..a42a24b4e 100644 --- a/files/assets/js/core.js +++ b/files/assets/js/core.js @@ -138,7 +138,7 @@ if (!location.pathname.endsWith('/submit') && !location.pathname.startsWith('/ch return } - if (location.pathname == '/admin/orgies') { + if (location.pathname.endsWith('/orgies')) { document.getElementById('start-orgy').click(); return } diff --git a/files/assets/js/orgies.js b/files/assets/js/orgies.js new file mode 100644 index 000000000..1f38e5671 --- /dev/null +++ b/files/assets/js/orgies.js @@ -0,0 +1,8 @@ +function remove_orgy(t, chat_id, created_utc) { + postToast(t, `/chat/${chat_id}/remove_orgy/${created_utc}`, + {}, + () => { + t.parentElement.parentElement.remove() + } + ); +} diff --git a/files/classes/orgy.py b/files/classes/orgy.py index 80bee5d2f..19735cc56 100644 --- a/files/classes/orgy.py +++ b/files/classes/orgy.py @@ -2,7 +2,7 @@ import time from flask import g, abort import requests -from sqlalchemy import Column, or_ +from sqlalchemy import Column, or_, ForeignKey from sqlalchemy.sql.sqltypes import * from files.classes import Base @@ -20,6 +20,7 @@ class Orgy(Base): start_utc = Column(Integer) end_utc = Column(Integer) started = Column(Boolean, default=False) + chat_id = Column(Integer, ForeignKey("chats.id")) def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) @@ -36,24 +37,24 @@ class Orgy(Base): t += 303 return t -def get_running_orgy(v): +def get_running_orgy(v, chat_id): if not (v and v.allowed_in_chat): return None refresh = False - ended_orgies = g.db.query(Orgy).filter(Orgy.end_utc != None, Orgy.end_utc < time.time()).all() + ended_orgies = g.db.query(Orgy).filter(Orgy.chat_id == chat_id, Orgy.end_utc != None, Orgy.end_utc < time.time()).all() for orgy in ended_orgies: if orgy.started: refresh = True g.db.delete(orgy) - orgy = g.db.query(Orgy).filter(Orgy.start_utc < time.time()).order_by(Orgy.start_utc).first() + orgy = g.db.query(Orgy).filter(Orgy.chat_id == chat_id, Orgy.start_utc < time.time()).order_by(Orgy.start_utc).first() if orgy and not orgy.started: orgy.started = True g.db.add(orgy) refresh = True if refresh: - requests.post('http://localhost:5001/refresh_chat', headers={"Host": SITE}) + requests.post(f'http://localhost:5001/chat/{chat_id}/refresh_chat', headers={"User-Agent": "refreshing_chat", "Host": SITE}) return orgy diff --git a/files/routes/admin.py b/files/routes/admin.py index 767be2047..d1b1c8b07 100644 --- a/files/routes/admin.py +++ b/files/routes/admin.py @@ -1926,118 +1926,6 @@ def admin_reset_password(user_id, v): return {"message": f"@{user.username}'s password has been reset! The new password has been messaged to them and to you!"} -@app.get("/admin/orgies") -@admin_level_required(PERMS['ORGIES']) -def orgy_control(v): - orgies = g.db.query(Orgy).order_by(Orgy.start_utc).all() - return render_template("admin/orgy_control.html", v=v, orgies=orgies) - -@app.post("/admin/schedule_orgy") -@admin_level_required(PERMS['ORGIES']) -def schedule_orgy(v): - link = request.values.get("link", "").strip() - title = request.values.get("title", "").strip() - start_utc = request.values.get("start_utc", "").strip() - - if not link: - abort(400, "A link is required!") - - if not title: - abort(400, "A title is required!") - - normalized_link = normalize_url(link) - - if start_utc: - start_utc = int(start_utc) - else: - last_orgy = g.db.query(Orgy).order_by(Orgy.start_utc.desc()).first() - 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' - data, _ = get_youtube_id_and_t(normalized_link) - if YOUTUBE_KEY != DEFAULT_CONFIG_VALUE: - req = requests.get(f"https://www.googleapis.com/youtube/v3/videos?id={data}&key={YOUTUBE_KEY}&part=contentDetails", headers=HEADERS, timeout=5).json() - duration = req['items'][0]['contentDetails']['duration'] - if duration != 'P0D': - duration = isodate.parse_duration(duration).total_seconds() - end_utc = int(start_utc + duration) - orgy_type = 'file' - - ydl_opts = { - "quiet": True, - "simulate": True, - "forceurl": True, - 'format': 'b', - 'proxy': PROXY_URL - } - - with yt_dlp.YoutubeDL(ydl_opts) as ydl: - info = ydl.extract_info(f"https://www.youtube.com/watch?v={data}") - data = info["url"] - 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) - elif any((normalized_link.lower().endswith(f'.{x}') for x in VIDEO_FORMATS)): - orgy_type = 'file' - data = normalized_link - video_info = ffmpeg.probe(data, headers=f'referer:{SITE_FULL}/chat') - 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: - abort(400) - - data = data.strip() - - orgy = Orgy( - title=title, - type=orgy_type, - data=data, - start_utc=start_utc, - end_utc=end_utc, - ) - g.db.add(orgy) - - ma = ModAction( - kind="schedule_orgy", - user_id=v.id, - _note=f'{title}', - ) - g.db.add(ma) - - return redirect('/admin/orgies') - -@app.post("/admin/remove_orgy/") -@admin_level_required(PERMS['ORGIES']) -def remove_orgy(v, created_utc): - orgy = g.db.query(Orgy).filter_by(created_utc=created_utc).one() - - ma = ModAction( - kind="remove_orgy", - user_id=v.id, - _note=f'{orgy.title}', - ) - g.db.add(ma) - - started = orgy.started - g.db.delete(orgy) - g.db.commit() - if started: - requests.post('http://localhost:5001/refresh_chat', headers={"Host": SITE}) - - return {"message": "Orgy stopped successfully!"} - @app.get("/admin/insert_transaction") @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) diff --git a/files/routes/allroutes.py b/files/routes/allroutes.py index ca08cda8e..cc907eb9b 100644 --- a/files/routes/allroutes.py +++ b/files/routes/allroutes.py @@ -24,7 +24,7 @@ def before_request(): abort(403, "Cloudflare workers are not allowed to access this website!") g.agent = request.headers.get("User-Agent", "") - if not g.agent and request.path not in {'/kofi', '/bm', '/refresh_chat'}: + if not g.agent and request.path not in {'/kofi', '/bm'}: abort(403, 'Please use a "User-Agent" header!') if not get_setting('bots') and request.headers.get("Authorization"): diff --git a/files/routes/chat.py b/files/routes/chat.py index fe7bbf80f..bf2263cf2 100644 --- a/files/routes/chat.py +++ b/files/routes/chat.py @@ -58,9 +58,9 @@ def commit_and_close(): g.db.close() stdout.flush() -@app.post('/refresh_chat') -def refresh_chat(): - emit('refresh_chat', namespace='/', to=f'{SITE_FULL}/chat/1') +@app.post('/chat//refresh_chat') +def refresh_chat(chat_id): + emit('refresh_chat', namespace='/', to=f'{SITE_FULL}/chat/{chat_id}') return '' @socketio.on('speak') diff --git a/files/routes/chats.py b/files/routes/chats.py index cc28b2c3b..2a4527f4b 100644 --- a/files/routes/chats.py +++ b/files/routes/chats.py @@ -1,5 +1,6 @@ from files.classes.chats import * +from files.classes.orgy import * from files.routes.wrappers import * from files.helpers.config.const import * from files.helpers.get import * @@ -91,6 +92,11 @@ def chat(v, chat_id): sorted_memberships = [owner_membership] + sorted_memberships + 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() + return render_template("orgy.html", v=v, messages=displayed_messages, chat=chat, orgy=orgy, orgies=orgies) + return render_template("chat.html", v=v, messages=displayed_messages, chat=chat, sorted_memberships=sorted_memberships) @@ -141,3 +147,149 @@ def leave_chat(v, chat_id): g.db.add(chat_leave) return {"message": "Chat left successfully!"} + + +@app.get("/chat//orgies") +@auth_required +def orgy_control(v, chat_id): + chat = g.db.get(Chat, chat_id) + if not chat: + abort(404, "Chat not found!") + + if chat.id == 1: + if v.admin_level < PERMS["ORGIES"]: + abort(403, "Your admin-level is not sufficient enough for this action!") + elif v.id != chat.owner_id: + abort(403, "Only the chat owner can manage its orgies!") + + 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//schedule_orgy") +@auth_required +def schedule_orgy(v, chat_id): + chat = g.db.get(Chat, chat_id) + if not chat: + abort(404, "Chat not found!") + + if chat.id == 1: + if v.admin_level < PERMS["ORGIES"]: + abort(403, "Your admin-level is not sufficient enough for this action!") + elif v.id != chat.owner_id: + abort(403, "Only the chat owner can manage its orgies!") + + link = request.values.get("link", "").strip() + title = request.values.get("title", "").strip() + start_utc = request.values.get("start_utc", "").strip() + + if not link: + abort(400, "A link is required!") + + if not title: + abort(400, "A title is required!") + + normalized_link = normalize_url(link) + + if start_utc: + start_utc = int(start_utc) + else: + last_orgy = g.db.query(Orgy).order_by(Orgy.start_utc.desc()).first() + 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' + data, _ = get_youtube_id_and_t(normalized_link) + if YOUTUBE_KEY != DEFAULT_CONFIG_VALUE: + req = requests.get(f"https://www.googleapis.com/youtube/v3/videos?id={data}&key={YOUTUBE_KEY}&part=contentDetails", headers=HEADERS, timeout=5).json() + duration = req['items'][0]['contentDetails']['duration'] + if duration != 'P0D': + duration = isodate.parse_duration(duration).total_seconds() + end_utc = int(start_utc + duration) + orgy_type = 'file' + + ydl_opts = { + "quiet": True, + "simulate": True, + "forceurl": True, + 'format': 'b', + 'proxy': PROXY_URL + } + + with yt_dlp.YoutubeDL(ydl_opts) as ydl: + info = ydl.extract_info(f"https://www.youtube.com/watch?v={data}") + data = info["url"] + 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) + elif any((normalized_link.lower().endswith(f'.{x}') for x in VIDEO_FORMATS)): + orgy_type = 'file' + data = normalized_link + video_info = ffmpeg.probe(data, headers=f'referer:{SITE_FULL}/chat') + 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: + abort(400) + + 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'{title}', + ) + g.db.add(ma) + + return redirect(f"/chat/{chat_id}/orgies") + +@app.post("/chat//remove_orgy/") +@auth_required +def remove_orgy(v, created_utc, chat_id): + chat = g.db.get(Chat, chat_id) + if not chat: + abort(404, "Chat not found!") + + if chat.id == 1: + if v.admin_level < PERMS["ORGIES"]: + abort(403, "Your admin-level is not sufficient enough for this action!") + elif v.id != chat.owner_id: + abort(403, "Only the chat owner can manage its orgies!") + + orgy = g.db.query(Orgy).filter_by(created_utc=created_utc).one() + + if chat.id == 1: + ma = ModAction( + kind="remove_orgy", + user_id=v.id, + _note=f'{orgy.title}', + ) + 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}) + + return {"message": "Orgy stopped successfully!"} diff --git a/files/templates/admin/admin_home.html b/files/templates/admin/admin_home.html index 4e1a98655..57639da84 100644 --- a/files/templates/admin/admin_home.html +++ b/files/templates/admin/admin_home.html @@ -14,7 +14,7 @@ {% if v.admin_level >= PERMS['ORGIES'] %}

Orgies

{%- endif %} diff --git a/files/templates/chat.html b/files/templates/chat.html index f8f3f3d3f..6a63f2f71 100644 --- a/files/templates/chat.html +++ b/files/templates/chat.html @@ -26,6 +26,8 @@ + + {% else %} {% endif %} diff --git a/files/templates/default.html b/files/templates/default.html index 3388e5a9a..f7a2f4839 100644 --- a/files/templates/default.html +++ b/files/templates/default.html @@ -1,6 +1,7 @@ {%- extends 'root.html' -%} {% block body_attributes %}id="{% if request.path != '/comments' %}{% block pagetype %}frontpage{% endblock %}{% endif %}" class="has_header"{% endblock %} {% block body %} + {% set central_orgy = get_running_orgy(v, 1) %} {% block pulltorefresh %}
@@ -22,7 +23,7 @@ {% set src = hole.random_banner %} {% set alt = ['/h/', hole, ' banner']|join %} {% set class = 'site-banner-hole' %} - {% elif get_running_orgy(v) and os_path.exists(path ~ "/orgy_banners") %} + {% elif central_orgy and os_path.exists(path ~ "/orgy_banners") %} {% set src = macros.random_image("assets/images/" ~ SITE_NAME ~ "/orgy_banners") %} {% set href = "/chat/1" %} {% set expand = false %} diff --git a/files/templates/header.html b/files/templates/header.html index 62477da7f..16046ff4a 100644 --- a/files/templates/header.html +++ b/files/templates/header.html @@ -286,13 +286,13 @@ {%- if FEATURES['CHAT'] -%}
- + {% endblock %} diff --git a/migrations/20240408-add-orgies-for-private-chats.sql b/migrations/20240408-add-orgies-for-private-chats.sql new file mode 100644 index 000000000..848709436 --- /dev/null +++ b/migrations/20240408-add-orgies-for-private-chats.sql @@ -0,0 +1,2 @@ +alter table orgies add column chat_id integer not null; +alter table orgies add constraint chat_messages_chat_fkey foreign key (chat_id) references public.chats(id);