From 8c316975274feb55cef92d20f7e74f0ebb09c08e Mon Sep 17 00:00:00 2001 From: Azalea <22280294+hykilpikonna@users.noreply.github.com> Date: Thu, 12 Mar 2026 05:47:42 -0400 Subject: [PATCH] [+] Admin page, hide channels --- src/bot.py | 35 +++++++++++++++++++-- src/db.py | 30 ++++++++++++++++++ src/gentree.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 143 insertions(+), 6 deletions(-) diff --git a/src/bot.py b/src/bot.py index d76c58c..06e42f9 100644 --- a/src/bot.py +++ b/src/bot.py @@ -6,10 +6,10 @@ import urllib.parse from pathlib import Path import requests -from fastapi import FastAPI +from fastapi import FastAPI, Request, HTTPException, Body, Header from hypy_utils import ensure_dir from hypy_utils.logging_utils import setup_logger -from starlette.responses import HTMLResponse +from starlette.responses import HTMLResponse, JSONResponse from starlette.staticfiles import StaticFiles from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram.ext import Application, CallbackQueryHandler, CommandHandler, ContextTypes, MessageHandler, filters @@ -545,6 +545,37 @@ def api_tree(): return tree_to_dict("azaneko") +@app.get("/api/admin/channels") +def api_admin_channels(x_admin_password: str = Header(None)): + if x_admin_password not in CONFIG.get("admin-passwords", [CONFIG.get("init-password")]): + raise HTTPException(status_code=403, detail="Invalid admin password") + + channels = db.get_all_channels() + return [{"username": c.username, "name": c.name, "parent": c.parent_id, "hidden": c.hidden} for c in channels] + + +@app.post("/api/admin/channels/{username}/hide") +def api_hide_channel(username: str, hidden: bool = Body(..., embed=True), x_admin_password: str = Header(None)): + if x_admin_password not in CONFIG.get("admin-passwords", [CONFIG.get("init-password")]): + raise HTTPException(status_code=403, detail="Invalid admin password") + + if db.set_hidden(username, hidden): + return {"success": True} + else: + raise HTTPException(status_code=404, detail="Channel not found") + + +@app.delete("/api/admin/channels/{username}") +def api_delete_channel(username: str, x_admin_password: str = Header(None)): + if x_admin_password not in CONFIG.get("admin-passwords", [CONFIG.get("init-password")]): + raise HTTPException(status_code=403, detail="Invalid admin password") + + if db.remove_channel(username): + return {"success": True} + else: + raise HTTPException(status_code=404, detail="Channel not found") + + @app.get("/c/{channel}", response_class=HTMLResponse) def channel_info(channel: str): info = db.channel_info(channel) diff --git a/src/db.py b/src/db.py index 1295e62..88ca209 100644 --- a/src/db.py +++ b/src/db.py @@ -18,6 +18,7 @@ class Channel(BaseModel): parent = ForeignKeyField('self', null=True, backref='children', on_delete='CASCADE', field='username') height = IntegerField(default=0) # Tree height (depth) owner_id = BigIntegerField(null=True) # Telegram user ID of the channel owner + hidden = BooleanField(default=False) # Whether the channel is hidden from the tree class Vote(BaseModel): @@ -60,6 +61,35 @@ def channel_info(username: str) -> Channel | None: return None +def remove_channel(username: str) -> bool: + """Remove a channel and its descendants (via CASCADE).""" + try: + ch = Channel.get(Channel.username == username) + ch.delete_instance(recursive=True) + return True + except Channel.DoesNotExist: + return False + + +def set_hidden(username: str, hidden: bool) -> bool: + """Toggle the hidden status of a channel.""" + try: + ch = Channel.get(Channel.username == username) + ch.hidden = hidden + ch.save() + return True + except Channel.DoesNotExist: + return False + + +def get_all_channels(include_hidden: bool = True) -> list[Channel]: + """Get all registered channels.""" + query = Channel.select() + if not include_hidden: + query = query.where(Channel.hidden == False) + return list(query.order_by(Channel.height, Channel.username)) + + def register(username: str, name: str, parent_username: str = None, owner_id: int = None): """Register a channel using its username and assign the correct height.""" if parent_username: diff --git a/src/gentree.py b/src/gentree.py index ba69948..b535d74 100644 --- a/src/gentree.py +++ b/src/gentree.py @@ -14,26 +14,102 @@ def indent(string: str, level: int): return "\n".join(" " * level + line for line in string.split("\n")) -def dfs(channel: str): +def dfs(channel: str, admin: bool = False): info = db.channel_info(channel) + if not info or (info.hidden and not admin): + return "" + votes = db.get_votes(channel) water = f' 💧{votes}' if votes else '' - out = f"""@{channel} - {info.name}{water}\n""" + + # In admin mode, show hidden status and a hide button + hide_icon = "👁️" if info.hidden else "🚫" + admin_info = f' [HIDDEN]' if info.hidden else '' + hide_btn = f' ' if admin else '' + del_btn = f' ' if admin else '' + + out = (f"""@{channel} - {info.name}{water}{admin_info}{hide_btn}{del_btn}\n""") + if not info.children: return out out += f"""