diff --git a/app.py b/app.py index e3928a9..45a1954 100644 --- a/app.py +++ b/app.py @@ -53,7 +53,7 @@ from flask import Flask, request, send_from_directory from flask_socketio import SocketIO, emit, join_room, disconnect from database import db, init_db -from models import User, Message, UserIgnore +from models import User, Message, UserIgnore, Ban, Mute from config import ( SECRET_KEY, ADMIN_PASSWORD, DATABASE_URL, CORS_ORIGINS, MAX_MSG_LEN, LOBBY, AI_FREE_LIMIT, AI_BOT_NAME, @@ -304,6 +304,15 @@ def create_app() -> Flask: init_db(app) _app_ref = app + # Load persisted bans and mutes from the database + with app.app_context(): + for ban in Ban.query.all(): + banned_usernames.add(ban.username.lower()) + if ban.ip: + banned_ips.add(ban.ip) + for mute in Mute.query.all(): + muted_users.add(mute.username.lower()) + msg_queue = ( os.environ.get("SOCKETIO_MESSAGE_QUEUE") or os.environ.get("REDIS_URL") @@ -659,13 +668,19 @@ def on_ban(data): target = str(data.get("target", "")).strip() lower = target.lower() banned_usernames.add(lower) + ip = None target_sid = username_to_sid.get(lower) if target_sid: info = connected_users.get(target_sid, {}) if info.get("ip"): banned_ips.add(info["ip"]) + ip = info["ip"] socketio.emit("kicked", {"msg": "You have been banned."}, to=target_sid) eventlet.spawn_after(0.5, _do_disconnect, target_sid) + # Persist to DB + if not Ban.query.filter_by(username=lower).first(): + db.session.add(Ban(username=lower, ip=ip)) + db.session.commit() socketio.emit("system", {"msg": f"🔨 **{target}** was banned.", "ts": _ts()}, to=LOBBY) @@ -675,9 +690,16 @@ def on_mute(data): target = str(data.get("target", "")).strip() lower = target.lower() if lower in muted_users: - muted_users.discard(lower); action = "unmuted" + muted_users.discard(lower) + Mute.query.filter_by(username=lower).delete() + db.session.commit() + action = "unmuted" else: - muted_users.add(lower); action = "muted" + muted_users.add(lower) + if not Mute.query.filter_by(username=lower).first(): + db.session.add(Mute(username=lower)) + db.session.commit() + action = "muted" emit("system", {"msg": f"🔇 **{target}** was {action}.", "ts": _ts()}, to=LOBBY) @@ -688,13 +710,19 @@ def on_kickban(data): lower = target.lower() # Ban banned_usernames.add(lower) + ip = None target_sid = username_to_sid.get(lower) if target_sid: info = connected_users.get(target_sid, {}) if info.get("ip"): banned_ips.add(info["ip"]) + ip = info["ip"] socketio.emit("kicked", {"msg": "You have been banned."}, to=target_sid) eventlet.spawn_after(0.5, _do_disconnect, target_sid) + # Persist to DB + if not Ban.query.filter_by(username=lower).first(): + db.session.add(Ban(username=lower, ip=ip)) + db.session.commit() # Announce socketio.emit("system", {"msg": f"💀 **{target}** was kickbanned.", "ts": _ts()}, to=LOBBY) diff --git a/models.py b/models.py index fa105d9..c151487 100644 --- a/models.py +++ b/models.py @@ -80,3 +80,23 @@ class Message(db.Model): def __repr__(self): return f"" + + +class Ban(db.Model): + """Persisted ban entry – survives server restarts.""" + __tablename__ = "bans" + + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(20), nullable=False, index=True) + ip = db.Column(db.String(45), nullable=True, index=True) + reason = db.Column(db.String(255), nullable=True) + created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) + + +class Mute(db.Model): + """Persisted mute entry – survives server restarts.""" + __tablename__ = "mutes" + + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(20), unique=True, nullable=False, index=True) + created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)