<commit_before>"""
Webclient based on websockets.

This implements a webclient with WebSockets (http://en.wikipedia.org/wiki/WebSocket)
by use of the txws implementation (https://github.com/MostAwesomeDude/txWS). It is
used together with evennia/web/media/javascript/evennia_websocket_webclient.js.

Thanks to <NAME> whose Evennia plugin inspired this module.

Communication over the websocket interface is done with normal text
communication. A special case is OOB-style communication; to do this
the client must send data on the following form:

    OOB{"func1":[args], "func2":[args], ...}

where the dict is JSON encoded. The initial OOB-prefix is used to
identify this type of communication, all other data is considered
plain text (command input).

Example of call from a javascript client:

    var websocket = new WebSocket("ws://localhost:8021");
    var msg1 = "WebSocket Test";
    websocket.send(msg1);
    var msg2 = JSON.stringify({ testfunc: [[1, 2, 3], { kwarg: "val" }] });
    websocket.send("OOB" + msg2);
    websocket.close();

"""
import re
import json
from twisted.internet.protocol import Protocol
from django.conf import settings
from evennia.server.session import Session
from evennia.utils.utils import to_str, mod_import
from evennia.utils.ansi import parse_ansi
from evennia.utils.text2html import parse_html

_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
_CLIENT_SESSIONS = mod_import(settings.SESSION_ENGINE).SessionStore


class WebSocketClient(Protocol, Session):
    """
    Implements the server-side of the Websocket connection.
    """

    def connectionMade(self):
        """
        This is called when the connection is first established.

        """
        self.transport.validationMade = self.validationMade
        client_address = self.transport.client
        client_address = client_address[0] if client_address else None
        self.init_session("websocket", client_address, self.factory.sessionhandler)

    def validationMade(self):
        """
        This is called from the (modified) txws websocket library when
        the ws handshake and validation has completed fully.

        """

        self.csessid = self.transport.location.split("?", 1)[1]
        csession = _CLIENT_SESSIONS(session_key=self.csessid)
        uid = csession and csession.get("logged_in", False)
        if uid:
            # the client session is already logged in.
            self.uid = uid
            self.logged_in = True

        # watch for dead links
        self.transport.setTcpKeepAlive(1)
        # actually do the connection
        self.sessionhandler.connect(self)

    def disconnect(self, reason=None):
        """
        Generic hook for the engine to call in order to
        disconnect this protocol.

        Args:<commit_msg>Don't traceback if webclient don't pass the browser session with the wsurl, to make it easier on custom client implementations.<commit_after>"""
Webclient based on websockets.

This implements a webclient with WebSockets (http://en.wikipedia.org/wiki/WebSocket)
by use of the txws implementation (https://github.com/MostAwesomeDude/txWS). It is
used together with evennia/web/media/javascript/evennia_websocket_webclient.js.

Thanks to <NAME> whose Evennia plugin inspired this module.

Communication over the websocket interface is done with normal text
communication. A special case is OOB-style communication; to do this
the client must send data on the following form:

    OOB{"func1":[args], "func2":[args], ...}

where the dict is JSON encoded. The initial OOB-prefix is used to
identify this type of communication, all other data is considered
plain text (command input).

Example of call from a javascript client:

    var websocket = new WebSocket("ws://localhost:8021");
    var msg1 = "WebSocket Test";
    websocket.send(msg1);
    var msg2 = JSON.stringify({ testfunc: [[1, 2, 3], { kwarg: "val" }] });
    websocket.send("OOB" + msg2);
    websocket.close();

"""
import re
import json
from twisted.internet.protocol import Protocol
from django.conf import settings
from evennia.server.session import Session
from evennia.utils.utils import to_str, mod_import
from evennia.utils.ansi import parse_ansi
from evennia.utils.text2html import parse_html

_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
_CLIENT_SESSIONS = mod_import(settings.SESSION_ENGINE).SessionStore


class WebSocketClient(Protocol, Session):
    """
    Implements the server-side of the Websocket connection.
    """

    def connectionMade(self):
        """
        This is called when the connection is first established.

        """
        self.transport.validationMade = self.validationMade
        client_address = self.transport.client
        client_address = client_address[0] if client_address else None
        self.init_session("websocket", client_address, self.factory.sessionhandler)

    def validationMade(self):
        """
        This is called from the (modified) txws websocket library when
        the ws handshake and validation has completed fully.

        """

        try:
            self.csessid = self.transport.location.split("?", 1)[1]
        except IndexError:
            # this may happen for custom webclients not caring for the
            # browser session.
            self.csessid = None
        if self.csessid:
            csession = _CLIENT_SESSIONS(session_key=self.csessid)
            uid = csession and csession.get("logged_in", False)
            if uid:
                # the client session is already logged in.
                self.uid = uid
                self.logged_in = True

        # watch for dead links
        self.transport.setTcpKeepAlive(1)
        # actually do the connection
        self.sessionhandler.connect(self)

    def disconnect(self, reason=None):
        """
        Generic hook for the engine to call in order to
        disconnect this protocol.

        Args:
{'fraction_non_alphanumeric': 0.08478513356562137, 'fraction_numerical': 0.005973120955699353, 'mean_word_length': 3.1629834254143647, 'pattern_counts': {'":': 4, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6795626', 'n_tokens_mistral': 1720, 'n_tokens_neox': 1660, 'n_words': 555}