from files.mail import * from files.__main__ import app, limiter, mail from files.helpers.alerts import * from files.helpers.const import * from files.classes.award import AWARDS from sqlalchemy import func from os import path import calendar import matplotlib.pyplot as plt from files.classes.mod_logs import ACTIONTYPES, ACTIONTYPES2 site = environ.get("DOMAIN").strip() site_name = environ.get("SITE_NAME").strip() @app.get("/privacy") @auth_desired def privacy(v): return render_template(f"privacy.html", v=v) @app.get("/emojis") @auth_desired def emojis(v): if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}emojis.html", v=v, emojis=allemojis) @app.get('/sidebar') @auth_desired def sidebar(v): if not path.exists(f'files/templates/sidebar_{site_name}.html'): abort(404) with open(f'files/templates/sidebar_{site_name}.html', 'r') as f: sidebar = f.read() return render_template('sidebar.html', sidebar=sidebar, v=v) @app.get("/stats") @auth_required def participation_stats(v): now = int(time.time()) day = now - 86400 data = {"valid_users": g.db.query(User.id).count(), "private_users": g.db.query(User.id).filter_by(is_private=True).count(), "banned_users": g.db.query(User.id).filter(User.is_banned > 0).count(), "verified_email_users": g.db.query(User.id).filter_by(is_activated=True).count(), "total_coins": g.db.query(func.sum(User.coins)).scalar(), "signups_last_24h": g.db.query(User.id).filter(User.created_utc > day).count(), "total_posts": g.db.query(Submission.id).count(), "posting_users": g.db.query(Submission.author_id).distinct().count(), "listed_posts": g.db.query(Submission.id).filter_by(is_banned=False).filter(Submission.deleted_utc == 0).count(), "removed_posts": g.db.query(Submission.id).filter_by(is_banned=True).count(), "deleted_posts": g.db.query(Submission.id).filter(Submission.deleted_utc > 0).count(), "posts_last_24h": g.db.query(Submission.id).filter(Submission.created_utc > day).count(), "total_comments": g.db.query(Comment.id).filter(Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count(), "commenting_users": g.db.query(Comment.author_id).distinct().count(), "removed_comments": g.db.query(Comment.id).filter_by(is_banned=True).count(), "deleted_comments": g.db.query(Comment.id).filter(Comment.deleted_utc>0).count(), "comments_last_24h": g.db.query(Comment.id).filter(Comment.created_utc > day, Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count(), "post_votes": g.db.query(Vote.id).count(), "post_voting_users": g.db.query(Vote.user_id).distinct().count(), "comment_votes": g.db.query(CommentVote.id).count(), "comment_voting_users": g.db.query(CommentVote.user_id).distinct().count(), "total_awards": g.db.query(AwardRelationship.id).count(), "awards_given": g.db.query(AwardRelationship.id).filter(or_(AwardRelationship.submission_id != None, AwardRelationship.comment_id != None)).count() } if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}admin/content_stats.html", v=v, title="Content Statistics", data=data) @app.get("/chart") @auth_required def chart(v): days = int(request.values.get("days", 0)) file = cached_chart(days) return send_file(file) @cache.memoize(timeout=86400) def cached_chart(days): now = time.gmtime() midnight_this_morning = time.struct_time((now.tm_year, now.tm_mon, now.tm_mday, 0, 0, 0, now.tm_wday, now.tm_yday, 0) ) today_cutoff = calendar.timegm(midnight_this_morning) if not days: firstsignup = g.db.query(User.created_utc).filter(User.created_utc != 0).order_by(User.created_utc).first()[0] - 86400 nowstamp = int(time.time()) days = int((nowstamp - firstsignup) / 86400) if days > 31: file = "/weekly_chart.png" day_cutoffs = [today_cutoff - 86400 * 7 * i for i in range(31)][1:] else: file = "/daily_chart.png" day_cutoffs = [today_cutoff - 86400 * i for i in range(31)][1:] day_cutoffs.insert(0, calendar.timegm(now)) daily_times = [time.strftime("%d/%m", time.gmtime(day_cutoffs[i + 1])) for i in range(len(day_cutoffs) - 1)][::-1] daily_signups = [g.db.query(User.id).filter(User.created_utc < day_cutoffs[i], User.created_utc > day_cutoffs[i + 1]).count() for i in range(len(day_cutoffs) - 1)][::-1] post_stats = [g.db.query(Submission.id).filter(Submission.created_utc < day_cutoffs[i], Submission.created_utc > day_cutoffs[i + 1], Submission.is_banned == False).count() for i in range(len(day_cutoffs) - 1)][::-1] comment_stats = [g.db.query(Comment.id).filter(Comment.created_utc < day_cutoffs[i], Comment.created_utc > day_cutoffs[i + 1],Comment.is_banned == False, Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count() for i in range(len(day_cutoffs) - 1)][::-1] plt.rcParams["figure.figsize"] = (20,20) signup_chart = plt.subplot2grid((20, 4), (0, 0), rowspan=5, colspan=4) posts_chart = plt.subplot2grid((20, 4), (7, 0), rowspan=5, colspan=4) comments_chart = plt.subplot2grid((20, 4), (14, 0), rowspan=5, colspan=4) signup_chart.grid(), posts_chart.grid(), comments_chart.grid() signup_chart.plot( daily_times, daily_signups, color='red') posts_chart.plot( daily_times, post_stats, color='green') comments_chart.plot( daily_times, comment_stats, color='gold') signup_chart.set_ylim(ymin=0) posts_chart.set_ylim(ymin=0) comments_chart.set_ylim(ymin=0) signup_chart.set_ylabel("Signups") posts_chart.set_ylabel("Posts") comments_chart.set_ylabel("Comments") comments_chart.set_xlabel("Time (UTC)") signup_chart.legend(loc='upper left', frameon=True) posts_chart.legend(loc='upper left', frameon=True) comments_chart.legend(loc='upper left', frameon=True) plt.savefig(file) plt.clf() return file @app.get("/patrons") @app.get("/paypigs") @admin_level_required(3) def patrons(v): users = g.db.query(User).filter(User.patron > 0).order_by(User.patron.desc(), User.id).all() if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}patrons.html", v=v, users=users) @app.get("/admins") @app.get("/badmins") @auth_desired def admins(v): if v and v.admin_level > 2: admins = g.db.query(User).filter(User.admin_level>1).order_by(User.truecoins.desc()).all() admins += g.db.query(User).filter(User.admin_level==1).order_by(User.truecoins.desc()).all() else: admins = g.db.query(User).filter(User.admin_level>0).order_by(User.truecoins.desc()).all() if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}admins.html", v=v, admins=admins) @app.get("/log") @app.get("/modlog") @auth_desired def log(v): page = int(request.values.get("page",1)) admin = request.values.get("admin") if admin: admin_id = get_id(admin) else: admin_id = 0 kind = request.values.get("kind") if v and v.admin_level > 1: types = ACTIONTYPES else: types = ACTIONTYPES2 if kind not in types: kind = None actions = g.db.query(ModAction) if not (v and v.admin_level > 1): actions = actions.filter(ModAction.kind.notin_(["shadowban","unshadowban"])) if admin_id: actions = actions.filter_by(user_id=admin_id) if kind: actions = actions.filter_by(kind=kind) actions = actions.order_by(ModAction.id.desc()).offset(25*(page-1)).limit(26).all() next_exists=len(actions)>25 actions=actions[:25] admins = [x[0] for x in g.db.query(User.username).filter(User.admin_level > 1).all()] if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}log.html", v=v, admins=admins, types=types, admin=admin, type=kind, actions=actions, next_exists=next_exists, page=page) @app.get("/log/") @auth_desired def log_item(id, v): try: id = int(id) except: try: id = int(id, 36) except: abort(404) action=g.db.query(ModAction).filter_by(id=id).one_or_none() if not action: abort(404) if request.path != action.permalink: return redirect(action.permalink) admins = [x[0] for x in g.db.query(User.username).filter(User.admin_level > 1).all()] if v and v.admin_level > 1: types = ACTIONTYPES else: types = ACTIONTYPES2 if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}log.html", v=v, actions=[action], next_exists=False, page=1, action=action, admins=admins, types=types) @app.get("/static/assets/favicon.ico") def favicon(): return send_file(f"./assets/images/{site_name}/icon.webp") @app.get("/api") @auth_desired def api(v): if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}api.html", v=v) @app.get("/contact") @app.get("/press") @app.get("/media") @auth_required def contact(v): if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}contact.html", v=v) @app.post("/contact") @limiter.limit("1/second") @auth_required @validate_formkey def submit_contact(v): message = f'This message has been sent automatically to all admins via [/contact](/contact), user email is "{v.email}"\n\nMessage:\n\n' + request.values.get("message", "") send_admin(v.id, message) g.db.commit() if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}contact.html", v=v, msg="Your message has been sent.") @app.get('/archives') def archivesindex(): return redirect("/archives/index.html") @app.get('/archives/') def archives(path): resp = make_response(send_from_directory('/archives', path)) if request.path.endswith('.css'): resp.headers.add("Content-Type", "text/css") return resp @app.get('/static/') @limiter.exempt def static_service2(path): resp = make_response(send_from_directory('./static', path)) if request.path.endswith('.webp') or request.path.endswith('.gif') or request.path.endswith('.ttf') or request.path.endswith('.woff') or request.path.endswith('.woff2'): resp.headers.remove("Cache-Control") resp.headers.add("Cache-Control", "public, max-age=2628000") if request.path.endswith('.webp'): resp.headers.remove("Content-Type") resp.headers.add("Content-Type", "image/webp") return resp @app.get('/assets/') @app.get('/static/assets/') @limiter.exempt def static_service(path): if request.path.startswith('/assets/'): return redirect(request.full_path.replace('/assets/', '/static/assets/')) resp = make_response(send_from_directory('assets', path)) if request.path.endswith('.webp') or request.path.endswith('.gif') or request.path.endswith('.ttf') or request.path.endswith('.woff') or request.path.endswith('.woff2'): resp.headers.remove("Cache-Control") resp.headers.add("Cache-Control", "public, max-age=2628000") if request.path.endswith('.webp'): resp.headers.remove("Content-Type") resp.headers.add("Content-Type", "image/webp") return resp @app.get('/images/') @app.get('/hostedimages/') @app.get("/static/images/") @limiter.exempt def images(path): if request.path.startswith('/images/') or request.path.lower().startswith('/hostedimages/'): return redirect(request.full_path.replace('/images/', '/static/images/').replace('/hostedimages/', '/static/images/')) resp = make_response(send_from_directory('/images', path.replace('.WEBP','.webp'))) resp.headers.remove("Cache-Control") resp.headers.add("Cache-Control", "public, max-age=2628000") if request.path.endswith('.webp'): resp.headers.remove("Content-Type") resp.headers.add("Content-Type", "image/webp") return resp @app.get("/robots.txt") def robots_txt(): return send_file("assets/robots.txt") @app.get("/settings") @auth_required def settings(v): return redirect("/settings/profile") @app.get("/settings/profile") @auth_required def settings_profile(v): if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}settings_profile.html", v=v) @app.get("/badges") @auth_desired def badges(v): if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}badges.html", v=v, badges=BADGES) @app.get("/blocks") @auth_desired def blocks(v): blocks=g.db.query(UserBlock).all() users = [] targets = [] for x in blocks: users.append(get_account(x.user_id)) targets.append(get_account(x.target_id)) if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}blocks.html", v=v, users=users, targets=targets) @app.get("/banned") @auth_desired def banned(v): users = [x for x in g.db.query(User).filter(User.is_banned > 0, User.unban_utc == 0).all()] if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}banned.html", v=v, users=users) @app.get("/formatting") @auth_desired def formatting(v): if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}formatting.html", v=v) @app.get("/service-worker.js") def serviceworker(): with open("files/assets/js/service-worker.js", "r") as f: return Response(f.read(), mimetype='application/javascript') @app.get("/settings/security") @auth_required def settings_security(v): if not v or v.oldsite: template = '' else: template = 'CHRISTMAS/' return render_template(f"{template}settings_security.html", v=v, mfa_secret=pyotp.random_base32() if not v.mfa_secret else None ) @app.post("/dismiss_mobile_tip") @limiter.limit("1/second") def dismiss_mobile_tip(): session["tooltip_last_dismissed"]=int(time.time()) session.modified=True return "", 204