import uuid from flask import Flask, render_template, request from flask_socketio import SocketIO, join_room, leave_room, emit app = Flask(__name__) socketio = SocketIO(app, cors_allowed_origins="*") # In-memory matchmaking state PROFILES = {} # sid -> profile dict WAITING = [] # list of sids waiting ROOMS = {} # room_id -> (sid1, sid2) @app.route("/", methods=["GET"]) def index(): return render_template("index.html") @app.route("/chat", methods=["GET"]) def chat(): return render_template("chat.html") @socketio.on("find") def handle_find(profile): sid = request.sid PROFILES[sid] = profile WAITING.append(sid) emit("status", {"msg": "Finding partner..."}, room=sid) try_match() @socketio.on("disconnect") def handle_disconnect(sid): # `sid` is passed in by Socket.IO on disconnect if sid in WAITING: WAITING.remove(sid) PROFILES.pop(sid, None) # Remove from any room for room, pair in list(ROOMS.items()): if sid in pair: other = pair[1] if pair[0] == sid else pair[0] emit("status", {"msg": "Partner left."}, room=other) leave_room(room, sid=other) ROOMS.pop(room) @socketio.on("subscribe") def handle_subscribe(data): room = data.get("room") sid = request.sid if room in ROOMS and sid in ROOMS[room]: join_room(room) emit("status", {"msg": f"Joined room {room}!"}, room=sid) else: emit("status", {"msg": "Invalid room or not in this room."}, room=sid) @socketio.on("chat") def handle_chat(data): room = data.get("room") msg = data.get("msg") sid = data.get("sid") or request.sid if room in ROOMS: emit("chat", {"msg": msg, "sid": sid}, room=room) @socketio.on("leave") def handle_leave(data): room = data.get("room") sid = request.sid emit("left", {}, room=sid) if room in ROOMS: other = next(s for s in ROOMS[room] if s != sid) emit("status", {"msg": "Partner left."}, room=other) leave_room(room, sid=other) ROOMS.pop(room) def try_match(): import random random.shuffle(WAITING) # To ensure fairness used = set() pairs = [] # === FIRST: Try matching by interest === for i, sid1 in enumerate(WAITING): if sid1 in used: continue p1 = PROFILES.get(sid1) if not p1: continue for j in range(i + 1, len(WAITING)): sid2 = WAITING[j] if sid2 in used: continue p2 = PROFILES.get(sid2) if not p2: continue # Gender filtering if p1['looking'] != 'any' and p2['gender'].lower() != p1['looking'].lower(): continue if p2['looking'] != 'any' and p1['gender'].lower() != p2['looking'].lower(): continue # Interests matching if set(p1['interests']) & set(p2['interests']): pairs.append((sid1, sid2)) used.update([sid1, sid2]) break # === SECOND: Fallback to random match (respect gender) === for i, sid1 in enumerate(WAITING): if sid1 in used: continue p1 = PROFILES.get(sid1) if not p1: continue for j in range(i + 1, len(WAITING)): sid2 = WAITING[j] if sid2 in used: continue p2 = PROFILES.get(sid2) if not p2: continue # Gender filtering only if p1['looking'] != 'any' and p2['gender'].lower() != p1['looking'].lower(): continue if p2['looking'] != 'any' and p1['gender'].lower() != p2['looking'].lower(): continue pairs.append((sid1, sid2)) used.update([sid1, sid2]) break # === Finalize pairs and assign rooms === for sid1, sid2 in pairs: for sid in (sid1, sid2): if sid in WAITING: WAITING.remove(sid) room = str(uuid.uuid4()) ROOMS[room] = (sid1, sid2) join_room(room, sid=sid1) join_room(room, sid=sid2) emit("matched", { "room": room, "my_id": PROFILES[sid1]["id"], "my_sid": sid1, "partner_id": PROFILES[sid2]["id"], "partner_sid": sid2 }, room=sid1) emit("matched", { "room": room, "my_id": PROFILES[sid2]["id"], "my_sid": sid2, "partner_id": PROFILES[sid1]["id"], "partner_sid": sid1 }, room=sid2) if __name__ == "__main__": socketio.run(app, host="0.0.0.0", port=7860)