예제 #1
0
async def create_bot_seek(request):
    user_agent = request.headers.get("User-Agent")
    username = user_agent[user_agent.find("user:"******"users"]
    seeks = request.app["seeks"]
    sockets = request.app["lobbysockets"]

    bot_player = users[username]

    log.info("+++ %s created %s seek", bot_player.username, data["variant"])

    # Try to create BOT vs BOT game to test TV
    test_TV = True
    matching_seek = None
    if test_TV:
        for seek in seeks.values():
            if (seek.variant == data["variant"] and seek.creator.bot
                    and seek.creator.online
                    and seek.creator.username != username and seek.level > 0):
                log.debug("MATCHING BOT SEEK %s FOUND!", seek.id)
                matching_seek = seek
                break

    if matching_seek is None:
        seek = None
        for existing_seek in seeks.values():
            if existing_seek.creator == bot_player and existing_seek.variant == data[
                    "variant"]:
                seek = existing_seek
                break
        if seek is None:
            seek = Seek(bot_player, data["variant"], player1=bot_player)
            seeks[seek.id] = seek
        bot_player.seeks[seek.id] = seek

        # inform others
        await lobby_broadcast(sockets, get_seeks(seeks))
    else:
        games = request.app["games"]
        response = await join_seek(request.app, bot_player, matching_seek.id)

        gameId = response["gameId"]
        game = games[gameId]

        chall = challenge(seek, gameId)

        await seek.creator.event_queue.put(chall)
        seek.creator.game_queues[gameId] = asyncio.Queue()

        await bot_player.event_queue.put(chall)
        bot_player.game_queues[gameId] = asyncio.Queue()

        await seek.creator.event_queue.put(game.game_start)
        await bot_player.event_queue.put(game.game_start)

    return web.json_response({"ok": True})
예제 #2
0
    async def clear_seeks(self, sockets, seeks):
        has_seek = len(self.seeks) > 0
        if has_seek:
            for seek in self.seeks:
                del seeks[seek]
            self.seeks.clear()

            await lobby_broadcast(sockets, get_seeks(seeks))
예제 #3
0
    async def clear_seeks(self, sockets, seeks):
        has_seek = len(self.seeks) > 0
        if has_seek and len(self.lobby_sockets) == 0:
            for seek in self.seeks:
                game_id = self.seeks[seek].game_id
                # preserve invites (seek with game_id)!
                if game_id is None:
                    del seeks[seek]
            self.seeks.clear()

            await lobby_broadcast(sockets, get_seeks(seeks))
예제 #4
0
    async def clear_seeks(self, force=False):
        has_seek = len(self.seeks) > 0
        if has_seek and (len(self.lobby_sockets) == 0 or force):
            seeks = self.app["seeks"]
            sockets = self.app["lobbysockets"]
            for seek in self.seeks:
                game_id = self.seeks[seek].game_id
                # preserve invites (seek with game_id)!
                if game_id is None:
                    del seeks[seek]
            self.seeks.clear()

            await lobby_broadcast(sockets, get_seeks(seeks))
예제 #5
0
async def fishnet_acquire(request):
    data = await request.json()

    fm = request.app["fishnet_monitor"]
    fv = request.app["fishnet_versions"]
    key = data["fishnet"]["apikey"]
    version = data["fishnet"]["version"]
    en = data["stockfish"]["name"]
    worker = FISHNET_KEYS[key]
    fv[worker] = "%s %s" % (version, en)

    if key not in FISHNET_KEYS:
        return web.Response(status=404)

    if key not in request.app["workers"]:
        request.app["workers"].add(key)
        fm[worker].append("%s %s %s" % (datetime.utcnow(), "-", "joined"))
        request.app["users"]["Fairy-Stockfish"].bot_online = True

        if not request.app["users"]["Fairy-Stockfish"].seeks:
            ai = request.app["users"]["Fairy-Stockfish"]
            seeks = request.app["seeks"]
            sockets = request.app["websockets"]
            for variant in VARIANTS:
                variant960 = variant.endswith("960")
                variant_name = variant[:-3] if variant960 else variant
                seek = Seek(ai,
                            variant_name,
                            color="r",
                            base=5,
                            inc=3,
                            level=6,
                            chess960=variant960)
                seeks[seek.id] = seek
                ai.seeks[seek.id] = seek
            await lobby_broadcast(sockets, get_seeks(seeks))

    response = await get_work(request, data)
    return response
예제 #6
0
async def lobby_socket_handler(request):

    users = request.app["users"]
    sockets = request.app["lobbysockets"]
    seeks = request.app["seeks"]

    ws = WebSocketResponse(heartbeat=3.0, receive_timeout=10.0)

    ws_ready = ws.can_prepare(request)
    if not ws_ready.ok:
        raise web.HTTPFound("/")

    await ws.prepare(request)

    session = await aiohttp_session.get_session(request)
    session_user = session.get("user_name")
    user = users[
        session_user] if session_user is not None and session_user in users else None

    if (user is not None) and (not user.enabled):
        await ws.close()
        session.invalidate()
        raise web.HTTPFound("/")

    log.debug("-------------------------- NEW lobby WEBSOCKET by %s" % user)

    try:
        async for msg in ws:
            if msg.type == aiohttp.WSMsgType.TEXT:
                if msg.data == "close":
                    log.debug("Got 'close' msg.")
                    break
                else:
                    data = json.loads(msg.data)
                    if not data["type"] == "pong":
                        log.debug("Websocket (%s) message: %s" % (id(ws), msg))

                    if data["type"] == "get_seeks":
                        response = get_seeks(seeks)
                        await ws.send_json(response)

                    elif data["type"] == "create_ai_challenge":
                        no = await is_playing(request, user, ws)
                        if no:
                            continue

                        variant = data["variant"]
                        engine = users.get("Fairy-Stockfish")

                        if engine is None or not engine.online():
                            # TODO: message that engine is offline, but capture BOT will play instead
                            engine = users.get("Random-Mover")

                        seek = Seek(user,
                                    variant,
                                    fen=data["fen"],
                                    color=data["color"],
                                    base=data["minutes"],
                                    inc=data["increment"],
                                    byoyomi_period=data["byoyomiPeriod"],
                                    level=data["level"],
                                    rated=data["rated"],
                                    chess960=data["chess960"],
                                    alternate_start=data["alternateStart"])
                        # print("SEEK", user, variant, data["fen"], data["color"], data["minutes"], data["increment"], data["level"], False, data["chess960"])
                        seeks[seek.id] = seek

                        response = await new_game(request.app, engine, seek.id)
                        await ws.send_json(response)

                        if response["type"] != "error":
                            gameId = response["gameId"]
                            engine.game_queues[gameId] = asyncio.Queue()
                            await engine.event_queue.put(
                                challenge(seek, response))

                    elif data["type"] == "create_seek":
                        no = await is_playing(request, user, ws)
                        if no:
                            continue

                        print("create_seek", data)
                        create_seek(seeks, user, data, ws)
                        await lobby_broadcast(sockets, get_seeks(seeks))

                        if data.get("target"):
                            queue = users[data["target"]].notify_queue
                            if queue is not None:
                                await queue.put(
                                    json.dumps({"notify": "new_challenge"}))

                    elif data["type"] == "delete_seek":
                        del seeks[data["seekID"]]
                        del user.seeks[data["seekID"]]

                        await lobby_broadcast(sockets, get_seeks(seeks))

                    elif data["type"] == "accept_seek":
                        no = await is_playing(request, user, ws)
                        if no:
                            continue

                        if data["seekID"] not in seeks:
                            continue

                        seek = seeks[data["seekID"]]
                        # print("accept_seek", seek.as_json)
                        response = await new_game(request.app, user,
                                                  data["seekID"])
                        await ws.send_json(response)

                        if seek.user.bot:
                            gameId = response["gameId"]
                            seek.user.game_queues[gameId] = asyncio.Queue()
                            await seek.user.event_queue.put(
                                challenge(seek, response))
                        else:
                            await seek.ws.send_json(response)

                        # Inform others, new_game() deleted accepted seek allready.
                        await lobby_broadcast(sockets, get_seeks(seeks))

                    elif data["type"] == "lobby_user_connected":
                        if session_user is not None:
                            if data["username"] and data[
                                    "username"] != session_user:
                                log.info(
                                    "+++ Existing lobby_user %s socket connected as %s."
                                    % (session_user, data["username"]))
                                session_user = data["username"]
                                if session_user in users:
                                    user = users[session_user]
                                else:
                                    user = User(
                                        request.app,
                                        username=data["username"],
                                        anon=data["username"].startswith(
                                            "Anon-"))
                                    users[user.username] = user
                                response = {
                                    "type": "lobbychat",
                                    "user": "",
                                    "message":
                                    "%s joined the lobby" % session_user
                                }
                            else:
                                if session_user in users:
                                    user = users[session_user]
                                else:
                                    user = User(
                                        request.app,
                                        username=data["username"],
                                        anon=data["username"].startswith(
                                            "Anon-"))
                                    users[user.username] = user
                                response = {
                                    "type": "lobbychat",
                                    "user": "",
                                    "message":
                                    "%s joined the lobby" % session_user
                                }
                        else:
                            log.info(
                                "+++ Existing lobby_user %s socket reconnected."
                                % data["username"])
                            session_user = data["username"]
                            if session_user in users:
                                user = users[session_user]
                            else:
                                user = User(
                                    request.app,
                                    username=data["username"],
                                    anon=data["username"].startswith("Anon-"))
                                users[user.username] = user
                            response = {
                                "type": "lobbychat",
                                "user": "",
                                "message":
                                "%s rejoined the lobby" % session_user
                            }

                        await lobby_broadcast(sockets, response)

                        # update websocket
                        user.lobby_sockets.add(ws)
                        sockets[user.username] = user.lobby_sockets

                        response = {
                            "type": "lobby_user_connected",
                            "username": user.username
                        }
                        await ws.send_json(response)

                        response = {
                            "type": "fullchat",
                            "lines": list(request.app["chat"])
                        }
                        await ws.send_json(response)

                        # send game count
                        response = {
                            "type": "g_cnt",
                            "cnt": request.app["g_cnt"]
                        }
                        await ws.send_json(response)

                        # send user count
                        response = {
                            "type": "u_cnt",
                            "cnt": online_count(users)
                        }
                        if len(user.game_sockets) == 0:
                            await lobby_broadcast(sockets, response)
                        else:
                            await ws.send_json(response)

                    elif data["type"] == "lobbychat":
                        response = {
                            "type": "lobbychat",
                            "user": user.username,
                            "message": data["message"]
                        }
                        await lobby_broadcast(sockets, response)
                        request.app["chat"].append(response)

                    elif data["type"] == "logout":
                        await ws.close()

                    elif data["type"] == "disconnect":
                        # Used only to test socket disconnection...
                        await ws.close(code=1009)

            elif msg.type == aiohttp.WSMsgType.CLOSED:
                log.debug(
                    "--- Lobby websocket %s msg.type == aiohttp.WSMsgType.CLOSED"
                    % id(ws))
                break

            elif msg.type == aiohttp.WSMsgType.ERROR:
                log.error(
                    "--- Lobby ws %s msg.type == aiohttp.WSMsgType.ERROR" %
                    id(ws))
                break

            else:
                log.debug("--- Lobby ws other msg.type %s %s" %
                          (msg.type, msg))

    except concurrent.futures._base.CancelledError:
        # client disconnected
        pass
    except Exception as e:
        log.error("!!! Lobby ws exception occured: %s" % type(e))

    finally:
        log.debug("---fianlly: await ws.close()")
        await ws.close()

    if user is not None:
        if ws in user.lobby_sockets:
            user.lobby_sockets.remove(ws)

        # online user counter will be updated in quit_lobby also!
        if len(user.lobby_sockets) == 0:
            if user.username in sockets:
                del sockets[user.username]

            # not connected to lobby socket and not connected to game socket
            if len(user.game_sockets) == 0:
                response = {"type": "u_cnt", "cnt": online_count(users)}
                await lobby_broadcast(sockets, response)

            response = {
                "type": "lobbychat",
                "user": "",
                "message": "%s left the lobby" % user.username
            }
            await lobby_broadcast(sockets, response)

            await user.clear_seeks(sockets, seeks)

    return ws
예제 #7
0
async def create_bot_seek(request):
    auth = request.headers.get("Authorization")
    if auth is None:
        return web.HTTPForbidden()

    token = auth[auth.find("Bearer") + 7:]
    if token not in BOT_TOKENS:
        log.error("BOT account auth with token %s failed", token)
        return web.HTTPForbidden()

    user_agent = request.headers.get("User-Agent")
    username = user_agent[user_agent.find("user:"******"users"]
    seeks = request.app["seeks"]
    sockets = request.app["lobbysockets"]

    bot_player = users[username]

    log.info("+++ %s created %s seek", bot_player.username, data["variant"])

    # Try to create BOT vs BOT game to test TV
    test_TV = True
    matching_seek = None
    if test_TV:
        for seek in seeks.values():
            if seek.variant == data[
                    "variant"] and seek.user.bot and seek.user.online and seek.user.username != username and seek.level > 0:
                log.debug("MATCHING BOT SEEK %s FOUND!", seek.id)
                matching_seek = seek
                break

    if matching_seek is None:
        seek = None
        for existing_seek in seeks.values():
            if existing_seek.user == bot_player and existing_seek.variant == data[
                    "variant"]:
                seek = existing_seek
                break
        if seek is None:
            seek = Seek(bot_player, data["variant"])
            seeks[seek.id] = seek
        bot_player.seeks[seek.id] = seek

        # inform others
        await lobby_broadcast(sockets, get_seeks(seeks))
    else:
        games = request.app["games"]
        response = await new_game(request.app, bot_player, matching_seek.id)

        gameId = response["gameId"]
        game = games[gameId]

        chall = challenge(seek, gameId)

        await seek.user.event_queue.put(chall)
        seek.user.game_queues[gameId] = asyncio.Queue()

        await bot_player.event_queue.put(chall)
        bot_player.game_queues[gameId] = asyncio.Queue()

        await seek.user.event_queue.put(game.game_start)
        await bot_player.event_queue.put(game.game_start)

    return web.json_response({"ok": True})
예제 #8
0
async def event_stream(request):
    auth = request.headers.get("Authorization")
    if auth is None:
        return web.HTTPForbidden()

    token = auth[auth.find("Bearer") + 7:]
    if token not in BOT_TOKENS:
        log.error("BOT account auth with token %s failed", token)
        return web.HTTPForbidden()

    user_agent = request.headers.get("User-Agent")
    username = user_agent[user_agent.find("user:"******"users"]
    seeks = request.app["seeks"]
    sockets = request.app["lobbysockets"]
    games = request.app["games"]
    db = request.app["db"]

    resp = web.StreamResponse()
    resp.content_type = "text/plain"
    await resp.prepare(request)

    if username in users:
        bot_player = users[username]
        # After BOT lost connection it may have ongoing games
        # We notify BOT and he can ask to create new game_streams
        # to continue those games
        for gameId in bot_player.game_queues:
            if gameId in games and games[gameId].status == STARTED:
                await bot_player.event_queue.put(games[gameId].game_start)
    else:
        bot_player = User(request.app, bot=True, username=username)
        users[bot_player.username] = bot_player

        doc = await db.user.find_one({"_id": username})
        if doc is None:
            result = await db.user.insert_one({
                "_id": username,
                "first_name": None,
                "last_name": None,
                "country": None,
                "title": "BOT",
            })
            print("db insert user result %s" % repr(result.inserted_id))

    bot_player.online = True

    log.info("+++ BOT %s connected", bot_player.username)

    loop = asyncio.get_event_loop()
    pinger_task = loop.create_task(
        bot_player.pinger(sockets, seeks, users, games))

    # inform others
    # TODO: do we need this at all?
    await lobby_broadcast(sockets, get_seeks(seeks))

    # send "challenge" and "gameStart" events from event_queue to the BOT
    while bot_player.online:
        answer = await bot_player.event_queue.get()
        try:
            bot_player.event_queue.task_done()
        except ValueError:
            log.error(
                "task_done() called more times than there were items placed in the queue in bot_api.py event_stream()"
            )
        try:
            if request.protocol.transport.is_closing():
                log.error(
                    "BOT %s request.protocol.transport.is_closing() == True ...",
                    username)
                break
            else:
                await resp.write(answer.encode("utf-8"))
                await resp.drain()
        except Exception:
            log.error("BOT %s event_stream is broken...", username)
            break

    pinger_task.cancel()
    await bot_player.clear_seeks(sockets, seeks)
    return resp
예제 #9
0
async def lobby_socket_handler(request):

    users = request.app["users"]
    sockets = request.app["websockets"]
    seeks = request.app["seeks"]
    games = request.app["games"]

    ws = MyWebSocketResponse()

    ws_ready = ws.can_prepare(request)
    if not ws_ready.ok:
        raise web.HTTPFound("/")

    await ws.prepare(request)

    session = await aiohttp_session.get_session(request)
    session_user = session.get("user_name")
    user = users[
        session_user] if session_user is not None and session_user in users else None

    lobby_ping_task = None

    log.debug("-------------------------- NEW lobby WEBSOCKET by %s" % user)

    async for msg in ws:
        if msg.type == aiohttp.WSMsgType.TEXT:
            if type(msg.data) == str:
                if msg.data == "close":
                    log.debug("Got 'close' msg.")
                    break
                else:
                    data = json.loads(msg.data)
                    if not data["type"] == "pong":
                        log.debug("Websocket (%s) message: %s" % (id(ws), msg))

                    if data["type"] == "pong":
                        user.ping_counter -= 1

                    elif data["type"] == "get_seeks":
                        response = get_seeks(seeks)
                        await ws.send_json(response)

                    elif data["type"] == "create_ai_challenge":
                        variant = data["variant"]
                        engine = users.get("Fairy-Stockfish")

                        if engine is None or not engine.online():
                            # TODO: message that engine is offline, but capture BOT will play instead
                            engine = users.get("Random-Mover")

                        seek = Seek(user, variant, data["fen"], data["color"],
                                    data["minutes"], data["increment"],
                                    data["level"], data["rated"],
                                    data["chess960"], data["handicap"])
                        # print("SEEK", user, variant, data["fen"], data["color"], data["minutes"], data["increment"], data["level"], False, data["chess960"])
                        seeks[seek.id] = seek

                        response = await new_game(request.app, engine, seek.id)
                        await ws.send_json(response)

                        gameId = response["gameId"]
                        engine.game_queues[gameId] = asyncio.Queue()
                        await engine.event_queue.put(challenge(seek, response))

                    elif data["type"] == "create_seek":
                        print("create_seek", data)
                        create_seek(seeks, user, data)
                        await lobby_broadcast(sockets, get_seeks(seeks))

                        if data.get("target"):
                            queue = users[data["target"]].notify_queue
                            if queue is not None:
                                await queue.put(
                                    json.dumps({"notify": "new_challenge"}))

                    elif data["type"] == "delete_seek":
                        del seeks[data["seekID"]]
                        del user.seeks[data["seekID"]]

                        await lobby_broadcast(sockets, get_seeks(seeks))

                    elif data["type"] == "accept_seek":
                        if data["seekID"] not in seeks:
                            continue

                        seek = seeks[data["seekID"]]
                        print("accept_seek", seek.as_json)
                        response = await new_game(request.app, user,
                                                  data["seekID"])
                        await ws.send_json(response)

                        if seek.user.lobby_ws is not None:
                            await seek.user.lobby_ws.send_json(response)

                        if seek.user.bot:
                            gameId = response["gameId"]
                            seek.user.game_queues[gameId] = asyncio.Queue()
                            await seek.user.event_queue.put(
                                challenge(seek, response))

                        # Inform others, new_game() deleted accepted seek allready.
                        await lobby_broadcast(sockets, get_seeks(seeks))

                    elif data["type"] == "lobby_user_connected":
                        if session_user is not None:
                            if data["username"] and data[
                                    "username"] != session_user:
                                log.info(
                                    "+++ Existing lobby_user %s socket connected as %s."
                                    % (session_user, data["username"]))
                                session_user = data["username"]
                                if session_user in users:
                                    user = users[session_user]
                                else:
                                    user = User(
                                        request.app,
                                        username=data["username"],
                                        anon=data["username"].startswith(
                                            "Anon-"))
                                    users[user.username] = user
                                response = {
                                    "type": "lobbychat",
                                    "user": "",
                                    "message":
                                    "%s joined the lobby" % session_user
                                }
                            else:
                                if session_user in users:
                                    user = users[session_user]
                                else:
                                    user = User(
                                        request.app,
                                        username=data["username"],
                                        anon=data["username"].startswith(
                                            "Anon-"))
                                    users[user.username] = user
                                response = {
                                    "type": "lobbychat",
                                    "user": "",
                                    "message":
                                    "%s joined the lobby" % session_user
                                }
                        else:
                            log.info(
                                "+++ Existing lobby_user %s socket reconnected."
                                % data["username"])
                            session_user = data["username"]
                            if session_user in users:
                                user = users[session_user]
                            else:
                                user = User(
                                    request.app,
                                    username=data["username"],
                                    anon=data["username"].startswith("Anon-"))
                                users[user.username] = user
                            response = {
                                "type": "lobbychat",
                                "user": "",
                                "message":
                                "%s rejoined the lobby" % session_user
                            }

                        user.ping_counter = 0
                        await lobby_broadcast(sockets, response)

                        # update websocket
                        sockets[user.username] = ws
                        user.lobby_ws = ws

                        response = {
                            "type": "lobby_user_connected",
                            "username": user.username
                        }
                        await ws.send_json(response)

                        response = {
                            "type": "fullchat",
                            "lines": list(request.app["chat"])
                        }
                        await ws.send_json(response)

                        loop = asyncio.get_event_loop()
                        lobby_ping_task = loop.create_task(
                            user.pinger(sockets, seeks, users, games))
                        request.app["tasks"].add(lobby_ping_task)

                        # send game count
                        response = {
                            "type": "g_cnt",
                            "cnt": request.app["g_cnt"]
                        }
                        await ws.send_json(response)

                        # send user count
                        if len(user.game_sockets) == 0:
                            # not connected to any game socket but connected to lobby socket
                            request.app["u_cnt"] += 1
                            response = {
                                "type": "u_cnt",
                                "cnt": request.app["u_cnt"]
                            }
                            await lobby_broadcast(sockets, response)
                        else:
                            response = {
                                "type": "u_cnt",
                                "cnt": request.app["u_cnt"]
                            }
                            await ws.send_json(response)

                    elif data["type"] == "lobbychat":
                        response = {
                            "type": "lobbychat",
                            "user": user.username,
                            "message": data["message"]
                        }
                        await lobby_broadcast(sockets, response)
                        request.app["chat"].append(response)

                    elif data["type"] == "logout":
                        await ws.close()

                    elif data["type"] == "disconnect":
                        # Used only to test socket disconnection...
                        await ws.close(code=1009)

            else:
                log.debug("type(msg.data) != str %s" % msg)
        elif msg.type == aiohttp.WSMsgType.ERROR:
            log.debug("!!! Lobby ws connection closed with exception %s" %
                      ws.exception())
        else:
            log.debug("other msg.type %s %s" % (msg.type, msg))

    log.info("--- Lobby Websocket %s closed" % id(ws))

    if lobby_ping_task is not None:
        lobby_ping_task.cancel()
        if user is not None:
            await user.clear_seeks(sockets, seeks)
            # online user counter will be updated in quit_lobby also!
            await user.quit_lobby(sockets, disconnect=False)

    return ws
예제 #10
0
async def lobby_socket_handler(request):

    users = request.app["users"]
    sockets = request.app["lobbysockets"]
    seeks = request.app["seeks"]
    db = request.app["db"]
    invites = request.app["invites"]
    twitch = request.app["twitch"]
    youtube = request.app["youtube"]
    lobbychat = request.app["lobbychat"]

    ws = MyWebSocketResponse(heartbeat=3.0, receive_timeout=10.0)

    ws_ready = ws.can_prepare(request)
    if not ws_ready.ok:
        return web.HTTPFound("/")

    await ws.prepare(request)

    session = await aiohttp_session.get_session(request)
    session_user = session.get("user_name")
    user = users[
        session_user] if session_user is not None and session_user in users else None

    if (user is not None) and (not user.enabled):
        await ws.close()
        session.invalidate()
        return web.HTTPFound("/")

    log.debug("-------------------------- NEW lobby WEBSOCKET by %s", user)

    try:
        async for msg in ws:
            if msg.type == aiohttp.WSMsgType.TEXT:
                if msg.data == "close":
                    log.debug("Got 'close' msg.")
                    break
                else:
                    data = json.loads(msg.data)
                    if not data["type"] == "pong":
                        log.debug("Websocket (%s) message: %s", id(ws), msg)

                    if data["type"] == "get_seeks":
                        response = get_seeks(seeks)
                        await ws.send_json(response)

                    elif data["type"] == "create_ai_challenge":
                        no = await is_playing(request, user, ws)
                        if no:
                            continue

                        variant = data["variant"]
                        engine = users.get("Fairy-Stockfish")

                        if data["rm"] or (engine is
                                          None) or (not engine.online):
                            # TODO: message that engine is offline, but Random-Mover BOT will play instead
                            engine = users.get("Random-Mover")

                        seek = Seek(
                            user,
                            variant,
                            fen=data["fen"],
                            color=data["color"],
                            base=data["minutes"],
                            inc=data["increment"],
                            byoyomi_period=data["byoyomiPeriod"],
                            level=0 if data["rm"] else data["level"],
                            player1=user,
                            rated=False,
                            chess960=data["chess960"],
                            alternate_start=data["alternateStart"],
                        )
                        # print("SEEK", user, variant, data["fen"], data["color"], data["minutes"], data["increment"], data["level"], False, data["chess960"])
                        seeks[seek.id] = seek

                        response = await join_seek(request.app, engine,
                                                   seek.id)
                        await ws.send_json(response)

                        if response["type"] != "error":
                            gameId = response["gameId"]
                            engine.game_queues[gameId] = asyncio.Queue()
                            await engine.event_queue.put(
                                challenge(seek, response))

                    elif data["type"] == "create_seek":
                        no = await is_playing(request, user, ws)
                        if no:
                            continue

                        print("create_seek", data)
                        seek = await create_seek(db, invites, seeks, user,
                                                 data, ws)
                        await lobby_broadcast(sockets, get_seeks(seeks))
                        await discord_message(request.app, "create_seek",
                                              seek.discord_msg)

                    elif data["type"] == "create_invite":
                        no = await is_playing(request, user, ws)
                        if no:
                            continue

                        print("create_invite", data)
                        seek = await create_seek(db, invites, seeks, user,
                                                 data, ws)

                        response = {
                            "type": "invite_created",
                            "gameId": seek.game_id
                        }
                        await ws.send_json(response)

                    elif data["type"] == "create_host":
                        no = user.username not in TOURNAMENT_DIRECTORS
                        if no:
                            continue

                        print("create_host", data)
                        seek = await create_seek(db, invites, seeks, user,
                                                 data, ws, True)

                        response = {
                            "type": "host_created",
                            "gameId": seek.game_id
                        }
                        await ws.send_json(response)

                    elif data["type"] == "delete_seek":
                        try:
                            seek = seeks[data["seekID"]]
                            if seek.game_id is not None:
                                # delete game invite
                                del invites[seek.game_id]
                            del seeks[data["seekID"]]
                            del user.seeks[data["seekID"]]
                        except KeyError:
                            # Seek was already deleted
                            pass
                        await lobby_broadcast(sockets, get_seeks(seeks))

                    elif data["type"] == "accept_seek":
                        no = await is_playing(request, user, ws)
                        if no:
                            continue

                        if data["seekID"] not in seeks:
                            continue

                        seek = seeks[data["seekID"]]
                        # print("accept_seek", seek.as_json)
                        response = await join_seek(request.app, user,
                                                   data["seekID"])
                        await ws.send_json(response)

                        if seek.creator.bot:
                            gameId = response["gameId"]
                            seek.creator.game_queues[gameId] = asyncio.Queue()
                            await seek.creator.event_queue.put(
                                challenge(seek, response))
                        else:
                            if seek.ws is None:
                                remove_seek(seeks, seek)
                                await lobby_broadcast(sockets,
                                                      get_seeks(seeks))
                            else:
                                await seek.ws.send_json(response)

                        # Inform others, new_game() deleted accepted seek allready.
                        await lobby_broadcast(sockets, get_seeks(seeks))

                    elif data["type"] == "lobby_user_connected":
                        if session_user is not None:
                            if data["username"] and data[
                                    "username"] != session_user:
                                log.info(
                                    "+++ Existing lobby_user %s socket connected as %s.",
                                    session_user,
                                    data["username"],
                                )
                                session_user = data["username"]
                                if session_user in users:
                                    user = users[session_user]
                                else:
                                    user = User(
                                        request.app,
                                        username=data["username"],
                                        anon=data["username"].startswith(
                                            "Anon-"),
                                    )
                                    users[user.username] = user
                            else:
                                if session_user in users:
                                    user = users[session_user]
                                else:
                                    user = User(
                                        request.app,
                                        username=data["username"],
                                        anon=data["username"].startswith(
                                            "Anon-"),
                                    )
                                    users[user.username] = user
                        else:
                            log.info(
                                "+++ Existing lobby_user %s socket reconnected.",
                                data["username"],
                            )
                            session_user = data["username"]
                            if session_user in users:
                                user = users[session_user]
                            else:
                                user = User(
                                    request.app,
                                    username=data["username"],
                                    anon=data["username"].startswith("Anon-"),
                                )
                                users[user.username] = user

                        # update websocket
                        user.lobby_sockets.add(ws)
                        user.update_online()
                        sockets[user.username] = user.lobby_sockets

                        response = {
                            "type": "lobby_user_connected",
                            "username": user.username,
                        }
                        await ws.send_json(response)

                        response = {
                            "type": "fullchat",
                            "lines": list(lobbychat)
                        }
                        await ws.send_json(response)

                        # send game count
                        response = {
                            "type": "g_cnt",
                            "cnt": request.app["g_cnt"][0]
                        }
                        await ws.send_json(response)

                        # send user count
                        response = {
                            "type": "u_cnt",
                            "cnt": online_count(users)
                        }
                        if len(user.game_sockets) == 0:
                            await lobby_broadcast(sockets, response)
                        else:
                            await ws.send_json(response)

                        spotlights = tournament_spotlights(
                            request.app["tournaments"])
                        if len(spotlights) > 0:
                            await ws.send_json({
                                "type": "spotlights",
                                "items": spotlights
                            })

                        streams = twitch.live_streams + youtube.live_streams
                        if len(streams) > 0:
                            await ws.send_json({
                                "type": "streams",
                                "items": streams
                            })

                    elif data["type"] == "lobbychat":
                        if user.username.startswith("Anon-"):
                            continue

                        message = data["message"]
                        response = None

                        if user.username in ADMINS:
                            if message.startswith("/silence"):
                                response = silence(message, lobbychat, users)
                                # silence message was already added to lobbychat in silence()

                            elif message.startswith("/stream"):
                                parts = message.split()
                                if len(parts) >= 3:
                                    if parts[1] == "add":
                                        if len(parts) >= 5:
                                            youtube.add(
                                                parts[2], parts[3], parts[4])
                                        elif len(parts) >= 4:
                                            youtube.add(parts[2], parts[3])
                                        else:
                                            youtube.add(parts[2])
                                    elif parts[1] == "remove":
                                        youtube.remove(parts[2])
                                    await broadcast_streams(request.app)

                            elif message == "/state":
                                server_state(request.app)

                            else:
                                response = chat_response(
                                    "lobbychat", user.username,
                                    data["message"])
                                lobbychat.append(response)

                        elif user.anon and user.username != "Discord-Relay":
                            pass

                        else:
                            if user.silence == 0:
                                response = chat_response(
                                    "lobbychat", user.username,
                                    data["message"])
                                lobbychat.append(response)

                        if response is not None:
                            await lobby_broadcast(sockets, response)

                    elif data["type"] == "logout":
                        await ws.close()

                    elif data["type"] == "disconnect":
                        # Used only to test socket disconnection...
                        await ws.close(code=1009)

            elif msg.type == aiohttp.WSMsgType.CLOSED:
                log.debug(
                    "--- Lobby websocket %s msg.type == aiohttp.WSMsgType.CLOSED",
                    id(ws),
                )
                break

            elif msg.type == aiohttp.WSMsgType.ERROR:
                log.error(
                    "--- Lobby ws %s msg.type == aiohttp.WSMsgType.ERROR",
                    id(ws))
                break

            else:
                log.debug("--- Lobby ws other msg.type %s %s", msg.type, msg)

    except OSError:
        # disconnected
        pass

    except Exception:
        log.exception(
            "ERROR: Exception in lobby_socket_handler() owned by %s ",
            session_user)

    finally:
        log.debug("--- wsl.py fianlly: await ws.close() %s", session_user)
        await ws.close()

        if user is not None:
            if ws in user.lobby_sockets:
                user.lobby_sockets.remove(ws)
                user.update_online()

            # online user counter will be updated in quit_lobby also!
            if len(user.lobby_sockets) == 0:
                if user.username in sockets:
                    del sockets[user.username]

                # not connected to lobby socket and not connected to game socket
                if len(user.game_sockets) == 0:
                    response = {"type": "u_cnt", "cnt": online_count(users)}
                    await lobby_broadcast(sockets, response)

                # response = {"type": "lobbychat", "user": "", "message": "%s left the lobby" % user.username}
                # await lobby_broadcast(sockets, response)

                await user.clear_seeks()

    return ws
예제 #11
0
async def lobby_socket_handler(request):

    users = request.app["users"]
    sockets = request.app["lobbysockets"]
    seeks = request.app["seeks"]
    db = request.app["db"]
    invites = request.app["invites"]

    ws = MyWebSocketResponse(heartbeat=3.0, receive_timeout=10.0)

    ws_ready = ws.can_prepare(request)
    if not ws_ready.ok:
        return web.HTTPFound("/")

    await ws.prepare(request)

    session = await aiohttp_session.get_session(request)
    session_user = session.get("user_name")
    user = users[
        session_user] if session_user is not None and session_user in users else None

    if (user is not None) and (not user.enabled):
        await ws.close()
        session.invalidate()
        return web.HTTPFound("/")

    log.debug("-------------------------- NEW lobby WEBSOCKET by %s", user)

    try:
        async for msg in ws:
            if msg.type == aiohttp.WSMsgType.TEXT:
                if msg.data == "close":
                    log.debug("Got 'close' msg.")
                    break
                else:
                    data = json.loads(msg.data)
                    if not data["type"] == "pong":
                        log.debug("Websocket (%s) message: %s", id(ws), msg)

                    if data["type"] == "get_seeks":
                        response = get_seeks(seeks)
                        await ws.send_json(response)

                    elif data["type"] == "create_ai_challenge":
                        no = await is_playing(request, user, ws)
                        if no:
                            continue

                        variant = data["variant"]
                        engine = users.get("Fairy-Stockfish")

                        if engine is None or not engine.online:
                            # TODO: message that engine is offline, but capture BOT will play instead
                            engine = users.get("Random-Mover")

                        seek = Seek(user,
                                    variant,
                                    fen=data["fen"],
                                    color=data["color"],
                                    base=data["minutes"],
                                    inc=data["increment"],
                                    byoyomi_period=data["byoyomiPeriod"],
                                    level=data["level"],
                                    rated=data["rated"],
                                    chess960=data["chess960"],
                                    alternate_start=data["alternateStart"])
                        # print("SEEK", user, variant, data["fen"], data["color"], data["minutes"], data["increment"], data["level"], False, data["chess960"])
                        seeks[seek.id] = seek

                        response = await new_game(request.app, engine, seek.id)
                        await ws.send_json(response)

                        if response["type"] != "error":
                            gameId = response["gameId"]
                            engine.game_queues[gameId] = asyncio.Queue()
                            await engine.event_queue.put(
                                challenge(seek, response))

                    elif data["type"] == "create_seek":
                        no = await is_playing(request, user, ws)
                        if no:
                            continue

                        print("create_seek", data)
                        seek = await create_seek(db, invites, seeks, user,
                                                 data, ws)
                        await lobby_broadcast(sockets, get_seeks(seeks))

                        if data.get("target"):
                            queue = users[data["target"]].notify_queue
                            if queue is not None:
                                await queue.put(
                                    json.dumps({"notify": "new_challenge"}))

                        # Send msg to discord-relay BOT
                        try:
                            for dr_ws in sockets["Discord-Relay"]:
                                await dr_ws.send_json({
                                    "type":
                                    "create_seek",
                                    "message":
                                    seek.discord_msg
                                })
                                break
                        except (KeyError, ConnectionResetError):
                            # BOT disconnected
                            log.error("--- Discord-Relay disconnected!")

                    elif data["type"] == "create_invite":
                        no = await is_playing(request, user, ws)
                        if no:
                            continue

                        print("create_invite", data)
                        seek = await create_seek(db, invites, seeks, user,
                                                 data, ws)

                        response = get_seeks(seeks)
                        await ws.send_json(response)

                        response = {
                            "type": "invite_created",
                            "gameId": seek.game_id
                        }
                        await ws.send_json(response)

                    elif data["type"] == "delete_seek":
                        try:
                            seek = seeks[data["seekID"]]
                            if seek.game_id is not None:
                                # delete game invite
                                del invites[seek.game_id]
                            del seeks[data["seekID"]]
                            del user.seeks[data["seekID"]]
                        except KeyError:
                            # Seek was already deleted
                            pass
                        await lobby_broadcast(sockets, get_seeks(seeks))

                    elif data["type"] == "accept_seek":
                        no = await is_playing(request, user, ws)
                        if no:
                            continue

                        if data["seekID"] not in seeks:
                            continue

                        seek = seeks[data["seekID"]]
                        # print("accept_seek", seek.as_json)
                        response = await new_game(request.app, user,
                                                  data["seekID"])
                        await ws.send_json(response)

                        if seek.user.bot:
                            gameId = response["gameId"]
                            seek.user.game_queues[gameId] = asyncio.Queue()
                            await seek.user.event_queue.put(
                                challenge(seek, response))
                        else:
                            await seek.ws.send_json(response)

                        # Inform others, new_game() deleted accepted seek allready.
                        await lobby_broadcast(sockets, get_seeks(seeks))

                    elif data["type"] == "lobby_user_connected":
                        if session_user is not None:
                            if data["username"] and data[
                                    "username"] != session_user:
                                log.info(
                                    "+++ Existing lobby_user %s socket connected as %s.",
                                    session_user, data["username"])
                                session_user = data["username"]
                                if session_user in users:
                                    user = users[session_user]
                                else:
                                    user = User(
                                        request.app,
                                        username=data["username"],
                                        anon=data["username"].startswith(
                                            "Anon-"))
                                    users[user.username] = user
                                # response = {"type": "lobbychat", "user": "", "message": "%s joined the lobby" % session_user}
                            else:
                                if session_user in users:
                                    user = users[session_user]
                                else:
                                    user = User(
                                        request.app,
                                        username=data["username"],
                                        anon=data["username"].startswith(
                                            "Anon-"))
                                    users[user.username] = user
                                # response = {"type": "lobbychat", "user": "", "message": "%s joined the lobby" % session_user}
                        else:
                            log.info(
                                "+++ Existing lobby_user %s socket reconnected.",
                                data["username"])
                            session_user = data["username"]
                            if session_user in users:
                                user = users[session_user]
                            else:
                                user = User(
                                    request.app,
                                    username=data["username"],
                                    anon=data["username"].startswith("Anon-"))
                                users[user.username] = user
                            # response = {"type": "lobbychat", "user": "", "message": "%s rejoined the lobby" % session_user}

                        # await lobby_broadcast(sockets, response)

                        # update websocket
                        user.lobby_sockets.add(ws)
                        user.update_online()
                        sockets[user.username] = user.lobby_sockets

                        response = {
                            "type": "lobby_user_connected",
                            "username": user.username
                        }
                        await ws.send_json(response)

                        response = {
                            "type": "fullchat",
                            "lines": list(request.app["chat"])
                        }
                        await ws.send_json(response)

                        # send game count
                        response = {
                            "type": "g_cnt",
                            "cnt": request.app["g_cnt"]
                        }
                        await ws.send_json(response)

                        # send user count
                        response = {
                            "type": "u_cnt",
                            "cnt": online_count(users)
                        }
                        if len(user.game_sockets) == 0:
                            await lobby_broadcast(sockets, response)
                        else:
                            await ws.send_json(response)

                    elif data["type"] == "lobbychat":
                        message = data["message"]
                        response = None

                        if user.username in ADMINS:
                            if message.startswith("/silence"):
                                spammer = data["message"].split()[-1]
                                if spammer in users:
                                    users[spammer].set_silence()
                                    response = {
                                        "type":
                                        "lobbychat",
                                        "user":
                                        "",
                                        "message":
                                        "%s was timed out 10 minutes for spamming the chat."
                                        % spammer
                                    }
                            elif message == "/growth":
                                server_growth()
                            elif message == "/state":
                                server_state(request.app)
                            else:
                                response = {
                                    "type": "lobbychat",
                                    "user": user.username,
                                    "message": data["message"]
                                }
                        elif user.anon and user.username != "Discord-Relay":
                            pass
                        else:
                            if user.silence == 0:
                                response = {
                                    "type": "lobbychat",
                                    "user": user.username,
                                    "message": data["message"]
                                }

                        if response is not None:
                            await lobby_broadcast(sockets, response)
                            request.app["chat"].append(response)

                    elif data["type"] == "logout":
                        await ws.close()

                    elif data["type"] == "disconnect":
                        # Used only to test socket disconnection...
                        await ws.close(code=1009)

            elif msg.type == aiohttp.WSMsgType.CLOSED:
                log.debug(
                    "--- Lobby websocket %s msg.type == aiohttp.WSMsgType.CLOSED",
                    id(ws))
                break

            elif msg.type == aiohttp.WSMsgType.ERROR:
                log.error(
                    "--- Lobby ws %s msg.type == aiohttp.WSMsgType.ERROR",
                    id(ws))
                break

            else:
                log.debug("--- Lobby ws other msg.type %s %s", msg.type, msg)

    except OSError:
        # disconnected
        pass

    except Exception:
        log.exception(
            "ERROR: Exception in lobby_socket_handler() owned by %s ",
            session_user)

    finally:
        log.debug("--- wsl.py fianlly: await ws.close() %s", session_user)
        await ws.close()

        if user is not None:
            if ws in user.lobby_sockets:
                user.lobby_sockets.remove(ws)
                user.update_online()

            # online user counter will be updated in quit_lobby also!
            if len(user.lobby_sockets) == 0:
                if user.username in sockets:
                    del sockets[user.username]

                # not connected to lobby socket and not connected to game socket
                if len(user.game_sockets) == 0:
                    response = {"type": "u_cnt", "cnt": online_count(users)}
                    await lobby_broadcast(sockets, response)

                # response = {"type": "lobbychat", "user": "", "message": "%s left the lobby" % user.username}
                # await lobby_broadcast(sockets, response)

                await user.clear_seeks(sockets, seeks)

    return ws