Пример #1
0
    def msg_handler(ws: websocket.WebSocketApp, message: AnyStr):
        message = json.loads(message)

        print(f'student {i} received message: {message["command"]}')

        time.sleep(random.randint(0, 5))

        if 'command' in message and message['command'] == 'intro':
            time.sleep(random.randint(0, 5))

            ws.send(json.dumps({'command': 'next'}))
            print(f'student {i} hit next')

        if 'command' in message and message['command'] == 'in_progress':
            time.sleep(random.randint(0, 5))

            if not all_answered(message['result']['questions']):
                print(f'student {i} answering questions')

                answer_questions(i, ws, message['result']['questions'])
            else:
                print(f'student {i} done answering questions')

                ws.send(json.dumps({'command': 'next'}))
                print(f'student {i} hit next')

        if 'command' in message and message['command'] == 'complete':
            print(f'student {i} completed a text reading and quit.')
            print(f'student {i} results: {message["result"]}')
            ws.keep_running = False

        if 'command' in message and message['command'] == 'exception':
            print(f'student {i} got an exception: {message}')
            ws.keep_running = False
Пример #2
0
 def _send_ack(ws: WebSocketApp, msg):
     envelope_id = msg.get('envelope_id')
     if envelope_id:
         # Send ACK
         ws.send(json.dumps({
             'envelope_id': envelope_id,
         }))
Пример #3
0
class Publisher():
    
    def __init__(self):
        self._endpoint = None
        self._wsserver = None
        self._wsclient = None

    def bind(self, endpoint):
        self._endpoint = endpoint

        class WampApplication(WebSocketApplication):
            protocol_class = WampProtocol

            def on_open(self):
                wamp = self.protocol
                wamp.register_pubsub('http://topic1')


        ep = urlparse(self._endpoint)
        resource = Resource({'^%s$' % ep.path: WampApplication})
        self._wsserver = WebSocketServer(ep.netloc, resource, debug=True)
        gevent.spawn(self._wsserver.serve_forever)
        self._wsclient = WebSocketClient(self._endpoint)
        gevent.spawn(self._wsclient.run_forever)

    def publish(self, topic, message):
        data = json.dumps([WampProtocol.MSG_PUBLISH, topic, message])
        print data
        self._wsclient.send(data)
Пример #4
0
class GameSocket:
    def __init__(self, cookie):
        self.game_socket = None
        self.cookie = cookie
        pass

    @staticmethod
    def on_open(ws):
        def run(*args):
            while (True):
                pass

        thread.start_new_thread(run, ())

    def open_game(self):
        def on_recive(ws, message):
            message = json.loads(message)
            self.handle(message)

        pass
        self.game_socket = WebSocketApp("ws://localhost:8080/game",
                                        cookie=self.cookie,
                                        on_message=on_recive)
        self.game_socket.on_open = self.on_open
        self.game_socket.run_forever()

    def handle(self, message):
        print(message)
        tp = message["type"]
        if tp == "GetCardFromHand":
            self.game_socket.send(
                '{"type":"ChooseCardFromHand", "chosenCard":0}')
        if tp == "GetCardFromTable":
            self.game_socket.send(
                '{"type":"ChooseCardFromTable", "chosenCard":0}')
Пример #5
0
    def _ws_on_open(self, ws: websocket.WebSocketApp):
        """Callback for sending the initial authentication data

        This "payload" contains the required data to authenticate this websocket
        client as a suitable bot connection to the Discord websocket.

        Args:
            ws: websocket connection
        """
        payload = {
            'op': WebSocketEvent.IDENTIFY.value,
            'd': {
                'token': self.token,
                'properties': {
                    '$os': sys.platform,
                    '$browser': 'Pycord',
                    '$device': 'Pycord',
                    '$referrer': '',
                    '$referring_domain': ''
                },
                'compress': True,
                'large_threshold': 250
            }
        }
        self.logger.debug('Sending identify payload')
        ws.send(json.dumps(payload))
        self.connected = True
Пример #6
0
 def send(self, cmd):
     if isinstance(cmd, str):
         cmd = json.loads(cmd)
     if self.__client_state is not CMDClient.State.CONNECTED:
         raise SendCommandError("Client is not connected to command server")
     if not self.__validate_cmd(cmd):
         raise SendCommandError("Illegal command")
     cmd['type'] = str(cmd['type'])
     WebSocketApp.send(self, json.dumps(cmd))
Пример #7
0
def answer_question(i: int, ws: websocket.WebSocketApp, question: Dict):
    time.sleep(random.randint(0, 1))

    answers = question['answers']

    rand_ans = question['answers'][random.randint(0, len(answers) - 1)]

    ws.send(json.dumps({'command': 'answer', 'answer_id': rand_ans['id']}))

    print(
        f'student {i} answered question {question["id"]} with answer {rand_ans["id"]}'
    )
Пример #8
0
def _stream_data(buffer, bufferSize: int, wsConn: WebSocketApp, event: Event) -> bool:
    while True:
        data = buffer.read(bufferSize)
        if not data:
            break
        elif event.is_set():
            event.clear()
            return False
        
        wsConn.send(data, ABNF.OPCODE_BINARY)

    return True
Пример #9
0
 def on_open(self, ws: WebSocketApp):
     self.logger.info('WebSocket connected')
     ws.send(f'PASS {self.oauth_token}')
     ws.send(f'NICK {self.nick}')
     ws.send(f'JOIN #{self.channel}')
     ws.send(f'CAP REQ :twitch.tv/tags twitch.tv/commands twitch.tv/membership')
     self._callback(self.post_login)
Пример #10
0
class WebSocketClient(AbstractWebSocketClient):
    __instance: Optional['WebSocketClient'] = None

    def __init__(self, logger: AbstractLogger = Logger()):
        if WebSocketClient.__instance:
            raise Exception('Use get_instance() instead!')
        self.__ws = WebSocketApp(
            'wss://jqbx.fm/socket.io/?EIO=3&transport=websocket')
        self.__logger = logger
        WebSocketClient.__instance = self

    @staticmethod
    def get_instance() -> 'WebSocketClient':
        if WebSocketClient.__instance is None:
            WebSocketClient()
        return WebSocketClient.__instance

    def register(self, on_open: Callable[[], None],
                 on_message: Callable[[WebSocketMessage],
                                      None], on_error: Callable[[Any], None],
                 on_close: Callable[[], None]) -> None:
        self.__ws.on_open = lambda _: on_open()
        self.__ws.on_message = lambda _, raw_message: on_message(
            self.__parse(raw_message))

    def run(self) -> None:
        self.__ws.run_forever()

    def send(self, web_socket_message: WebSocketMessage) -> None:
        self.__logger.info('Outgoing Message', web_socket_message.as_dict())
        serialized = str(web_socket_message.code)
        array_part = [
            x for x in [web_socket_message.label, web_socket_message.payload]
            if x
        ]
        if array_part:
            serialized += json.dumps(array_part)
        self.__ws.send(serialized)

    @staticmethod
    def __parse(raw_message: str) -> WebSocketMessage:
        stripped = raw_message.strip()
        parts = stripped.split('[', 1)
        label = None
        payload = None
        if len(parts) > 1:
            json_array = json.loads('[%s' % parts[1])
            label = json_array[0]
            payload = None if len(json_array) == 1 else json_array[1]
        return WebSocketMessage(int(parts[0]), label, payload)
Пример #11
0
class UpbitReal:
    def __init__(self, request, callback=print):
        self.request = request
        self.callback = callback
        self.ws = WebSocketApp(
            url="wss://api.upbit.com/websocket/v1",
            on_message=lambda ws, msg: self.on_message(ws, msg),
            on_error=lambda ws, msg: self.on_error(ws, msg),
            on_close=lambda ws: self.on_close(ws),
            on_open=lambda ws: self.on_open(ws))
        self.running = False

    def on_message(self, ws, msg):
        msg = json.loads(msg.decode('utf-8'))
        # result = dict()
        now = datetime.datetime.now()
        # result['time'] = msg['tdt'] + msg['ttm']    -> UTC
        # result['time'] = str(now)
        # result['cur_price'] = msg['tp']
        # result['acc_volume'] = msg['atv']
        # result = json.dumps(result)
        # print(type(result))
        conn.hmset("bitData", {
            "time": str(now),
            "curPrice": msg['tp'],
            "accVolume": msg['atv']
        })
        # self.callback(msg)

    def on_error(self, ws, msg):
        self.callback(msg)

    def on_close(self, ws):
        self.callback("closed")
        self.running = False

    def on_open(self, ws):
        th = Thread(target=self.activate, daemon=True)
        th.start()

    def activate(self):
        self.ws.send(self.request)
        while self.running:
            time.sleep(1)
        self.ws.close()

    def start(self):
        self.running = True
        self.ws.run_forever()
Пример #12
0
class WebsocketClient:
    ''' Websocket Client ラッパークラス '''
    def __init__(self):
        self.client = None
        self.logger = create_logger()

    def open(self, server_host):
        ''' server_hostにクライアントを新規接続 '''
        self.close()
        self.client = create_connection(server_host)

    def open_server(self, server_host):
        ''' server_hostにクライアントを新規接続し、サーバーとして動作させる '''
        self.close()
        enableTrace(False)
        self.client = WebSocketApp(
            server_host,
            on_message=lambda ws, message: self.on_message_recieved(message),
            on_error=lambda ws, error: self.on_error(error),
            on_close=lambda ws: self.on_disconnect())
        self.client.on_open = lambda ws: self.on_connect()
        self.client.run_forever()

    def close(self):
        ''' clientをサーバーから切断する '''
        if self.client is not None:
            self.client.close()
            self.client = None

    def log(self, msg, *args, **kargs):
        ''' ログメッセージを出力 '''
        self.logger.info(msg, *args, **kargs)

    def send_message(self, message):
        ''' 接続先のサーバーにmessage送信 '''
        return self.client.send(message)

    def recieve_message(self):
        ''' 接続先サーバーから送られてきたメッセージを受信 '''
        return self.client.recv()

    # --- event methods (サーバーモードで動作する際に使用) --- #
    def on_connect(self):
        ''' Websocketサーバー接続時のコールバック '''
        self.log('Connect')

    def on_disconnect(self):
        ''' Websocketサーバーから切断時のコールバック '''
        self.log('Disconnect')

    def on_message_recieved(self, message):
        ''' Websocketサーバーからメッセージ受信時のコールバック '''
        self.log('Received:{}'.format(message))

    def on_error(self, error):
        ''' エラー発生時のコールバック '''
        self.log('Error:{}'.format(error))
Пример #13
0
class Firebase(Thread):

    def __init__(self):
        super(Firebase, self).__init__()
        self.sock = WebSocketApp(url='ws://%s.ws' % BASE_URL,
                                 on_message=self.msg_handler,
                                 on_open=self.connect_handler,
                                 on_error=self.err_handler)
        self.ready = Event()
        self._rn = 0

    def run(self):
        self.sock.run_forever()

    def connect_handler(self, sock):
        sock.send(u'version=1')

    def err_handler(self, exc):
        print exc

    def send(self, msg):
        self.sock.send(msg)

    @property
    def rn(self):
        self._rn = self._rn + 1
        return self._rn

    def listen_to_channel(self, channel):
        print 'listening to %s' % channel
        self.send('1')
        data = {
            "rn": "%i" % self.rn,
            "payload": {
                "action": "listen",
                "path": channel
            }
        }
        self.send(json.dumps(data))

    def close(self):
        self.sock.close()
Пример #14
0
class WsClient(object):
    def __init__(self, url: str, key: str):
        """

        :param url:
        :param key:
        """
        self._url = url
        self._key = key
        self._ws_client = WebSocketApp(self._url)

    def get_user_info(self):
        return self._ws_client.send(self._key)

    def send(self):
        """

        :return:
        """
        return self._ws_client.send(self._key)
Пример #15
0
class chatUser():
    messageList = None

    def __init__(self, info, eventList, sleepTime, waitTime, id):
        self.actionList = eventList
        self.sleepTime = sleepTime
        self.id = id
        self.messageList = []
        time.sleep(waitTime)
        print('\nweb socket-', id, ' setting: ', info, ' start_at: ',
              int(time.time() * 1000))
        self.ws = WebSocketApp(info,
                               on_message=self.on_message,
                               on_error=self.on_error,
                               on_close=self.on_close)
        self.ws.on_open = self.on_open
        self.ws.run_forever()

    def __del__(self):
        print('call del fun')
        self.messageList = None
        self.actionList = None

    def on_message(self, message):
        data = json.loads(message)
        pprint(data)
        self.messageList.insert(0, data)

    def on_error(self, error):
        print('get ', self.id, ' error: ', error)

    def on_close(self):
        print("webSocket-", self.id, " has been closed at ", int(time.time()))

    def on_open(self):
        for i in self.actionList:
            pprint(i)
            waitTime = i.pop('sleep')
            time.sleep(waitTime)
            self.ws.send(json.dumps(i))
        time.sleep(self.sleepTime)
class GameSocket:
    def __init__(self, cookie, testsystem):
        self.game_socket = None
        self.cookie = cookie
        self.testsystem = testsystem
        self.errorst = False
        self.error_next = False
        self.good_msg = ''
        pass
    @staticmethod
    def on_open(ws):
        def run(*args):
            while(True):
                pass
        thread.start_new_thread(run, ())

    def open_game(self):
        def on_recive(ws,message):
            message = json.loads(message)
            self.handle(message)
        pass
        self.game_socket = WebSocketApp("ws://localhost:8080/game",cookie=self.cookie, on_message=on_recive)
        self.game_socket.on_open = self.on_open
        self.game_socket.run_forever()

    def truehandl(self,message):
        tp = message["type"]
        if tp == "ErrorMsg":
            #print("error handled")
            self.game_socket.send(self.good_msg)
            self.error_next = False
            self.errorst = False
        if self.error_next:
            self.testsystem.fail("error was not")

    def fakehandl(self,message):
        tp = message["type"]
        if tp == "GetCardFromHand":
            self.game_socket.send('#####ERROR#####')
            self.good_msg = '{"type":"ChooseCardFromHand", "chosenCard":0}'
            #print("error send")
            self.errorst = True
            self.error_next = True
        if tp == "GetCardFromTable":
            self.game_socket.send('#####ERROR#####')
            self.good_msg = '{"type":"ChooseCardFromTable", "chosenCard":0}'
            #print("error send")
            self.errorst = True
            self.error_next = True

    def handle(self,message):
        #print(message)
        #print(self.errorst)
        if self.errorst:
            self.truehandl(message)
        else:
            self.fakehandl(message)
Пример #17
0
class UpbitWebSocket:
    def __init__(self, request, callback=print):
        self.request = request
        self.callback = callback
        self.ws = WebSocketApp(
            url="wss://api.upbit.com/websocket/v1",
            on_message=lambda ws, msg: self.on_message(ws, msg),
            on_error=lambda ws, msg: self.on_error(ws, msg),
            on_close=lambda ws:     self.on_close(ws),
            on_open=lambda ws:     self.on_open(ws))
        self.running = False
    
    def on_message(self, ws, msg):
        msg = json.loads(msg.decode('utf-8'))
        self.callback(msg)
    
    def on_error(self, ws, msg):
        self.callback(msg)
    
    def on_close(self, ws):
        self.callback("closed")
        self.running = False

    def on_open(self, ws):
        th = Thread(target=self.activate, daemon=True)
        th.start()
    
    def activate(self):
        self.ws.send(self.request)
        while self.running:
            time.sleep(1)
        self.ws.close()
    
    def start(self):
        self.running = True
        self.ws.run_forever()
Пример #18
0
class MidiHandler:
    # Initializes the handler class
    def __init__(self,
                 config_path=args.config,
                 ws_server=args.host,
                 ws_port=args.port):
        # Setting up logging first and foremost
        self.log = get_logger("midi_to_obs")

        # Internal service variables
        self._action_buffer = []
        self._action_counter = 2
        self._portobjects = []

        # Feedback blocking
        self.blockcount = 0
        self.block = False
        #load tinydb configuration database
        self.log.debug("Trying to load config file  from %s" % config_path)
        tiny_database = TinyDB(config_path, indent=4)
        tiny_db = tiny_database.table("keys", cache_size=20)
        tiny_devdb = tiny_database.table("devices", cache_size=20)

        #get all mappings and devices
        self._mappings = tiny_db.all()
        self._devices = tiny_devdb.all()

        #open dbj datebase for mapping and clear
        self.mappingdb = dbj("temp-mappingdb.json")
        self.mappingdb.clear()

        #convert database to dbj in-memory
        for _mapping in self._mappings:
            self.mappingdb.insert(_mapping)

        self.log.debug("Mapping database: `%s`" % str(self.mappingdb.getall()))

        if len(self.mappingdb.getall()) < 1:
            self.log.critical("Could not cache device mappings")
            # ENOENT (No such file or directory)
            exit(2)

        self.log.debug("Successfully imported mapping database")

        result = tiny_devdb.all()
        if not result:
            self.log.critical("Config file %s doesn't exist or is damaged" %
                              config_path)
            # ENOENT (No such file or directory)
            exit(2)

        self.log.info("Successfully parsed config file")

        self.log.debug("Retrieved MIDI port name(s) `%s`" % result)
        #create new class with handler and open from there, just create new instances
        for device in result:
            self._portobjects.append(
                (DeviceHandler(device, device.doc_id), device.doc_id))

        self.log.info("Successfully initialized midi port(s)")
        del result

        # close tinydb
        tiny_database.close()

        # setting up a Websocket client
        self.log.debug("Attempting to connect to OBS using websocket protocol")
        self.obs_socket = WebSocketApp("ws://%s:%d" % (ws_server, ws_port))
        self.obs_socket.on_message = lambda ws, message: self.handle_obs_message(
            ws, message)
        self.obs_socket.on_error = lambda ws, error: self.handle_obs_error(
            ws, error)
        self.obs_socket.on_close = lambda ws: self.handle_obs_close(ws)
        self.obs_socket.on_open = lambda ws: self.handle_obs_open(ws)

    def getPortObject(self, mapping):
        deviceID = mapping.get("out_deviceID", mapping["deviceID"])
        for portobject, _deviceID in self._portobjects:
            if _deviceID == deviceID:
                return portobject

    def handle_midi_input(self, message, deviceID, deviceName):
        self.log.debug("Received %s %s %s %s %s", str(message), "from device",
                       deviceID, "/", deviceName)

        if message.type == "note_on":
            return self.handle_midi_button(deviceID, message.channel,
                                           message.type, message.note)

        # `program_change` messages can be only used as regular buttons since
        # they have no extra value, unlike faders (`control_change`)
        if message.type == "program_change":
            return self.handle_midi_button(deviceID, message.channel,
                                           message.type, message.program)

        if message.type == "control_change":
            return self.handle_midi_fader(deviceID, message.channel,
                                          message.control, message.value)

    def handle_midi_button(self, deviceID, channel, type, note):
        results = self.mappingdb.getmany(
            self.mappingdb.find(
                'msg_channel == %s and msg_type == "%s" and msgNoC == %s and deviceID == %s'
                % (channel, type, note, deviceID)))

        if not results:
            self.log.debug("Cound not find action for note %s", note)
            return

        for result in results:
            if self.send_action(result):
                pass

    def handle_midi_fader(self, deviceID, channel, control, value):
        results = self.mappingdb.getmany(
            self.mappingdb.find(
                'msg_channel == %s and msg_type == "control_change" and msgNoC == %s and deviceID == %s'
                % (channel, control, deviceID)))

        if not results:
            self.log.debug("Cound not find action for fader %s", control)
            return
        if self.block == True:
            if self.blockcount <= 4:
                self.log.debug(
                    "Blocked incoming message due to sending message")
                self.block = False
                self.blockcount += 1
        else:
            self.blockcount = 0
            for result in results:
                input_type = result["input_type"]
                action = result["action"]

                if input_type == "button":
                    if value == 127 and not self.send_action(result):
                        continue

                if input_type == "fader":
                    command = result["cmd"]
                    scaled = map_scale(value, 0, 127, result["scale_low"],
                                       result["scale_high"])

                    if command == "SetSourceScale":
                        self.obs_socket.send(action.format(scaled))

                    # Super dirty hack but @AlexDash says that it works
                    # @TODO: find an explanation _why_ it works
                    if command == "SetVolume":
                        # Yes, this literally raises a float to a third degree
                        self.obs_socket.send(action % scaled**3)

                    if command in [
                            "SetGainFilter", "SetOpacity",
                            "SetColorCorrectionHueShift",
                            "Filter/Chroma Key - Contrast",
                            "Filter/Chroma Key - Brightness",
                            "Filter/Chroma Key - Gamma",
                            "Filter/Luma Key - Luma Max",
                            "Filter/Luma Key - Luma Max Smooth",
                            "Filter/Luma Key - Luma Min",
                            "Filter/Luma Key - Luma Min Smooth",
                            "Filter/Color Correction - Saturation",
                            "Filter/Color Correction - Contrast",
                            "Filter/Color Correction - Brightness",
                            "Filter/Color Correction - Gamma",
                            "Filter/Color Correction - Hue Shift",
                            "Filter/Color Key - Brightness",
                            "Filter/Color Key - Contrast",
                            "Filter/Color Key - Gamma",
                            "Filter/Sharpen - Sharpness"
                    ]:
                        self.obs_socket.send(action % scaled)

                    if command in [
                            "SetSourceRotation", "SetTransitionDuration",
                            "SetSyncOffset", "SetSourcePosition",
                            "Filter/Chroma Key - Opacity",
                            "Filter/Chroma Key - Spill Reduction",
                            "Filter/Chroma Key - Similarity",
                            "Filter/Color Key - Similarity",
                            "Filter/Color Key - Smoothness",
                            "Filter/Scroll - Horizontal Speed",
                            "Filter/Scroll - Vertical Speed"
                    ]:
                        self.obs_socket.send(action % int(scaled))

    def handle_obs_message(self, ws, message):
        self.log.debug("Received new message from OBS")
        payload = json.loads(message)

        self.log.debug("Successfully parsed new message from OBS: %s" %
                       message)

        if "error" in payload:
            self.log.error("OBS returned error: %s" % payload["error"])
            return

        if "message-id" in payload:
            message_id = payload["message-id"]

            self.log.debug("Looking for action with message id `%s`" %
                           message_id)
            for action in self._action_buffer:
                (buffered_id, template, kind) = action

                if buffered_id != int(payload["message-id"]):
                    continue

                del buffered_id
                self.log.info("Action `%s` was requested by OBS" % kind)

                if kind == "ToggleSourceVisibility":
                    # Dear lain, I so miss decent ternary operators...
                    invisible = "false" if payload["visible"] else "true"
                    self.obs_socket.send(template % invisible)
                elif kind == "ReloadBrowserSource":
                    source = payload["sourceSettings"]["url"]
                    target = source[0:-1] if source[-1] == '#' else source + '#'
                    self.obs_socket.send(template % target)
                elif kind == "ToggleSourceFilter":
                    invisible = "false" if payload["enabled"] else "true"
                    self.obs_socket.send(template % invisible)
                elif kind in ["SetCurrentScene", "SetPreviewScene"]:
                    self.sceneChanged(kind, payload["name"])

                self.log.debug(
                    "Removing action with message id %s from buffer" %
                    message_id)
                self._action_buffer.remove(action)
                break

            if message_id == "MIDItoOBSscreenshot":
                if payload["status"] == "ok":
                    with open(str(time()) + ".png", "wb") as fh:
                        fh.write(
                            base64.decodebytes(payload["img"][22:].encode()))

        elif "update-type" in payload:
            update_type = payload["update-type"]
            self.log.debug(update_type)
            request_types = {
                "PreviewSceneChanged": "SetPreviewScene",
                "SwitchScenes": "SetCurrentScene"
            }
            if update_type in request_types:
                scene_name = payload["scene-name"]
                self.sceneChanged(request_types[update_type], scene_name)
            elif update_type == "SourceVolumeChanged":
                self.volChanged(payload["sourceName"], payload["volume"])

    def volChanged(self, source_name, volume):
        self.log.info("Volume " + source_name + " changed to val: " +
                      str(volume))
        results = self.mappingdb.getmany(
            self.mappingdb.find(
                'input_type == "fader" and bidirectional == 1'))
        if not results:
            self.log.info("no fader results")
            return
        for result in results:

            j = result["action"] % "0"
            k = json.loads(j)["source"]
            self.log.info(k)
            if k == source_name:
                val = int(
                    map_scale(volume, result["scale_low"],
                              result["scale_high"], 0, 127))
                self.log.info(val)

                msgNoC = result.get("out_msgNoC", result["msgNoC"])
                self.log.info(msgNoC)
                portobject = self.getPortObject(result)
                if portobject and portobject._port_out:
                    self.block = True
                    portobject._port_out.send(
                        mido.Message('control_change',
                                     channel=0,
                                     control=int(result["msgNoC"]),
                                     value=val))

    def sceneChanged(self, event_type, scene_name):
        self.log.debug("Scene changed, event: %s, name: %s" %
                       (event_type, scene_name))
        # only buttons can change the scene, so we can limit our search to those
        results = self.mappingdb.getmany(
            self.mappingdb.find(
                'input_type == "button" and bidirectional == 1'))
        if not results:
            return
        for result in results:
            j = json.loads(result["action"])
            if j["request-type"] != event_type:
                continue
            msgNoC = result.get("out_msgNoC", result["msgNoC"])
            channel = result.get("out_channel", 0)
            portobject = self.getPortObject(result)
            if portobject and portobject._port_out:
                if result["msg_type"] == "control_change":
                    value = 127 if j["scene-name"] == scene_name else 0
                    portobject._port_out.send(
                        mido.Message(type="control_change",
                                     channel=channel,
                                     control=msgNoC,
                                     value=value))
                elif result["msg_type"] == "note_on":
                    velocity = 1 if j["scene-name"] == scene_name else 0
                    portobject._port_out.send(
                        mido.Message(type="note_on",
                                     channel=channel,
                                     note=msgNoC,
                                     velocity=velocity))

    def handle_obs_error(self, ws, error=None):
        # Protection against potential inconsistencies in `inspect.ismethod`
        if error is None and isinstance(ws, BaseException):
            error = ws

        if isinstance(error, (KeyboardInterrupt, SystemExit)):
            self.log.info("Keyboard interrupt received, gracefully exiting...")
        else:
            self.log.error("Websocket error: %" % str(error))

    def handle_obs_close(self, ws):
        self.log.error("OBS has disconnected, timed out or isn't running")
        self.log.error("Please reopen OBS and restart the script")
        self.close(teardown=True)

    def handle_obs_open(self, ws):
        self.log.info("Successfully connected to OBS")

        # initialize bidirectional controls
        self.send_action({
            "action": 'GetCurrentScene',
            "request": "SetCurrentScene",
            "target": ":-)"
        })
        self.send_action({
            "action": 'GetPreviewScene',
            "request": "SetPreviewScene",
            "target": ":-)"
        })

    def send_action(self, action_request):
        action = action_request.get("action")
        if not action:
            # @NOTE: this potentionally should never happen but you never know
            self.log.error("No action supplied in current request")
            return False

        request = action_request.get("request")
        if not request:
            self.log.debug("No request body for action %s, sending action" %
                           action)
            self.obs_socket.send(action)
            # Success, breaking the loop
            return True

        template = TEMPLATES.get(request)
        if not template:
            self.log.error("Missing template for request %s" % request)
            # Keep searching
            return False

        target = action_request.get("target")
        if not target:
            self.log.error("Missing target in %s request for %s action" %
                           (request, action))
            # Keep searching
            return False

        field2 = action_request.get("field2")
        if not field2:
            field2 = False

        self._action_buffer.append([self._action_counter, action, request])
        if field2:
            self.obs_socket.send(template %
                                 (self._action_counter, target, field2))
        else:
            self.obs_socket.send(template % (self._action_counter, target))
        self._action_counter += 1

        # Explicit return is necessary here to avoid extra searching
        return True

    def start(self):
        self.log.info("Connecting to OBS...")
        self.obs_socket.run_forever()

    def close(self, teardown=False):
        # set bidirectional controls to their 0 state (i.e., turn off LEDs)
        self.log.debug("Attempting to turn off bidirectional controls")
        result = self.mappingdb.getmany(
            self.mappingdb.find('bidirectional == 1'))
        if result:
            for row in result:
                msgNoC = row.get("out_msgNoC", row["msgNoC"])
                channel = row.get("out_channel", 0)
                portobject = self.getPortObject(row)
                if portobject and portobject._port_out:
                    if row["msg_type"] == "control_change":
                        portobject._port_out.send(
                            mido.Message(type="control_change",
                                         channel=channel,
                                         control=msgNoC,
                                         value=0))
                    elif row["msg_type"] == "note_on":
                        portobject._port_out.send(
                            mido.Message(type="note_on",
                                         channel=channel,
                                         note=msgNoC,
                                         velocity=0))

        self.log.debug("Attempting to close midi port(s)")
        for portobject, _ in self._portobjects:
            portobject.close()

        self.log.info("Midi connection has been closed successfully")

        # If close is requested during keyboard interrupt, let the websocket
        # client tear itself down and make a clean exit
        if not teardown:
            self.log.debug("Attempting to close OBS connection")
            self.obs_socket.close()

            self.log.info("OBS connection has been closed successfully")

        self.log.info("Config file has been successfully released")

    def __end__(self):
        self.log.info("Exiting script...")
        self.close()
Пример #19
0
class XSocketsClient(object):
	def __init__(self, url, onerror = None, onopen = None, onclose = None):
		super(XSocketsClient, self).__init__()
		self.onerror = onerror
		self.onopen = onopen
		self.subscriptions = Subscriptions()
		self.webSocket = None
		self.bind(XSockets.Events.open, self._open_event_handler, opts = {"skip": True})

		thread.start_new_thread(self.start, (url, onopen, onclose))

	def _open_event_handler(self, data):
		log.DEBUG(data)
		data[u"clientType"] = u"RFC6455"
		self.connection = data
		self.XSocketsClientStorageGuid = data["StorageGuid"]
		for sub in self.subscriptions.getAll():
			for callback in sub.callbacks:
				if sub.name and callback.state.get("options", {}).get("skip", False): continue
				self.trigger(Message(
					XSockets.Events.pubSub.subscribe,
					{"Event": sub.name, "Confim": False}))
		self.dispatchEvent(XSockets.Events.bindings.completed, self.subscriptions.getAll())
		
		if self.onopen: self.onopen()


	def start(self, url, onopen, onclose):
		self.webSocket = ConnectionManager(url,
			on_message = self._on_message,
			on_error = self.print_error,
			on_close = onclose)
		self.webSocket.run_forever()

	def print_error(self, *args, **kwargs):
	  print args, kwargs

	def __del__(self, *args):
		if self.webSocket is not None:
			self.webSocket.close()

	def _on_message(self, ws, message):
		cont = json.loads(message)
		log.DEBUG(cont)
		self.dispatchEvent(cont["event"], cont["data"])
		if cont["event"] == XSockets.Events.onError:
			if self.onerror: self.onerror(message)
			self.dispatchEvent(cont["event"], cont["data"])

	def bind(self, event, fn, opts = {}, callback = None):
		state = {
			"options": opts,
			"ready": self.webSocket is not None and self.webSocket.sock is not None,
			"confim": callback is not None
		}

		log.DEBUG("%s - %s" %(event, fn))
		if state["ready"]:
			self.trigger(Message(
				XSockets.Events.pubSub.subscribe,
				{"Event": event, "Confim": state["confim"]}))

		if isinstance(fn, list):
			for f in fn:
				self.subscriptions.add(event, f, state)
		else:
			self.subscriptions.add(event, fn, state)

		return self
	on = bind
	subscribe = bind

	def trigger(self, event, json = {}, callback = None):
		if isinstance(event, Message):
			message = event
		else:
			event = event.lower()
			message = Message(event, json)

		log.DEBUG(message.to_json())
		self.webSocket.send(message.to_json())
		if callback is not None: callback()
		return self

	publish = trigger
	emit = trigger

	def dispatchEvent(self, event, message):
		if self.subscriptions.get(event) is None: return
		self.subscriptions.fire(event, json.loads(message))
	
	def send(self, data, event):
		mes = Message(event, data)
		log.DEBUG(mes.to_json())
		self.webSocket.send(mes.to_json())
Пример #20
0
class SkyroomBot:
    def __init__(self, url: str, username: str, password: str, token: str, on_message: Callable[[str], None] = None):
        self.is_opened = False
        self.username = username
        self.password = password
        self.token = token

        self.url = url
        self.gather_data_from_url()

        self.logger = logging.getLogger(__class__.__name__)
        if on_message == None:
            on_message = self.on_message

        self.ws = WebSocketApp(self.websocket_addr, \
            on_open    = lambda ws: self.on_open(), \
            on_message = lambda ws, msg: on_message(msg),\
            on_close   = lambda ws: self.on_close()
        )

    def gather_data_from_url(self):
        if self.url[-1] == '/':
            self.url[: len(self.url) - 1]

        url_split = self.url.split("/")
        room = url_split[-1]
        customer = url_split[-2]

        data = '------WebKitFormBoundaryLYlCWZpm2cRdPd4B\r\nContent-Disposition: form-data; name="customer"\r\n\r\n%s\r\n------WebKitFormBoundaryLYlCWZpm2cRdPd4B\r\nContent-Disposition: form-data; name="room"\r\n\r\n%s\r\n------WebKitFormBoundaryLYlCWZpm2cRdPd4B\r\nContent-Disposition: form-data; name="gadget"\r\n\r\nSkyroom\r\n------WebKitFormBoundaryLYlCWZpm2cRdPd4B\r\nContent-Disposition: form-data; name="action"\r\n\r\nFetchRoom\r\n------WebKitFormBoundaryLYlCWZpm2cRdPd4B--\r\n' \
            % (customer, room)
        headers = {
            "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36",
            "content-type": "multipart/form-data; boundary=----WebKitFormBoundaryLYlCWZpm2cRdPd4B",
        }

        res = post("%s?%d" % (self.url, time()*1000), data=data, headers=headers)
        if res.status_code != 200:
            raise Exception("Invalid skyroom url")

        res = loads(res.content)["result"]
        self.customer_id = res["customer_id"]
        self.room_id = res["room_id"]
        self.websocket_addr = str(res["server"]).replace("https", "wss")

    def on_open(self):
        self.is_opened = True
        self.join_room()
        Thread(target=self.keep_alive).start()

    def on_message(self, msg: str):
        self.logger.info(msg)

    def send_chat(self, msg):
        msg_info = {
            "id": int(time()),
            "text": msg
        }
        self.ws.send(dumps(["s", "chat", "message-new", msg_info]))

    def on_close(self):
        self.is_opened = False

    def join_room(self):
        payload = [
            "s", "user", "join", {
                "origin": "www.skyroom.online",
                "app_id": "27642275",
                "room_id": self.room_id,
                "customer_id": self.customer_id,
                "username": self.username,
                "password": self.password,
                "token": self.token,
                "nickname": "",
                "platform": {
                    "version":"12.4.8",
                    "os": 1,
                    "browser": 0
                }
            }
        ]

        self.ws.send(dumps(payload))
        self.logger.info("Joined room")

    def keep_alive(self):
        alive_expire = time() + 5
        while self.is_opened:
            if alive_expire < time():
                self.ws.send("imalive")
                alive_expire = time() + 5
        
        self.logger.info("Connection closed!")
class Client:
    Ws = None
    IsOpen = False

    def __init__(self,
                 url,
                 onWsOpen=None,
                 onWsMsg=None,
                 onWsData=None,
                 onWsClose=None,
                 onWsError=None,
                 headerArray=None):
        def OnOpen(ws):
            self.IsOpen = True
            if onWsOpen:
                onWsOpen(self)

        def OnMsg(ws, msg):
            if onWsMsg:
                onWsMsg(self, msg)

        def onClosed(ws):
            if onWsClose:
                onWsClose(self)

        def OnError(ws, msg):
            if onWsError:
                onWsError(self, msg)

        def OnData(ws, buffer, type, continueFlag):
            if onWsData:
                onWsData(self, buffer, type == websocket.ABNF.OPCODE_BINARY)

        self.Ws = WebSocketApp(url,
                               on_message=OnMsg,
                               on_close=onClosed,
                               on_error=OnError,
                               on_data=OnData,
                               header=headerArray)
        self.Ws.on_open = OnOpen

    def RunUntilClosed(self):
        # Note we must set the ping_interval and ping_timeout or we won't get a multithread
        # safe socket... python. >.>
        self.Ws.run_forever(skip_utf8_validation=True,
                            ping_interval=600,
                            ping_timeout=120)

    def RunAsync(self):
        t = threading.Thread(target=self.RunUntilClosed, args=())
        t.daemon = True
        t.start()

    def Close(self):
        self.IsOpen = False
        if self.Ws:
            self.Ws.close()
            self.Ws = None

    def Send(self, msgBytes, isData):
        if self.Ws:
            if isData:
                self.Ws.send(msgBytes, opcode=websocket.ABNF.OPCODE_BINARY)
            else:
                self.Ws.send(msgBytes, opcode=websocket.ABNF.OPCODE_TEXT)
Пример #22
0
class Binary(mt_helper.MultiThreadWSHelper):
    '''
    Wrapper of Binary.com API
    Provides unathorized operations. Authorized scope is in binary_account module
    '''

    def __init__(self, ):

        super().__init__()
        self.url = bin_api.get_binary_url()
        self.app = WebSocketApp(url=self.url,
                                on_open = lambda ws: self.on_app_open(ws),
                                on_close = lambda ws: self.on_app_close(ws),
                                on_message = lambda ws, msg: self.on_app_msg(ws, msg),
                                on_error = lambda ws, err: self.on_app_error(ws, err),
                                on_ping = lambda ws: self.on_app_ping(ws))

        self.reconnect = True


    def send_request(self, request):
        '''
        Sends request considering sharing between threads.
        Ideally it should be placed into helper,
        but app (WebSocketApp) defined here
        '''

        with self.ws_lock:
            self.app.send(request)

    def open_app(self):
        Thread(target=self.app.run_forever).start()

    def close_app(self):
        del self.responses
        self.reconnect = False
        self.app.close()

    # Websockets methods:

    def on_app_open(self):
        '''
        We have nothing to do here
        :return:
        '''
        pass


    def on_app_msg(self, ws, msg):

        try:
            resp = json.loads(msg)
            self.add_response(resp)

            id_ = resp['req_id']
            if self.in_events(id_):
                self.set_event(id_)

        except:
            ex_type, ex_val, ex_tb = sys.exc_info()
            logging.error(str(ex_type)+'\n'+'\n'.join(traceback.format_tb(ex_tb)))

        ready = True # line for debug

    def on_app_close(self, ws):

        if self.reconnect:
            self.app = WebSocketApp(url=self.url,
                                on_open = lambda ws: self.on_app_open(ws),
                                on_close = lambda ws: self.on_app_close(ws),
                                on_message = lambda ws, msg: self.on_app_msg(ws, msg),
                                on_error = lambda ws, err: self.on_app_error(ws, err),
                                on_ping = lambda ws: self.on_app_ping(ws))
            Thread(target=self.app.run_forever).start()

    def on_app_error(self, ws, error):
        pass

    def on_app_ping(self, ws):
        pass

    def get_history(self, asset = 'frxEURUSD', granularity=3600,
                    count=50, subscribe=0, style='candles'):
        '''
        Returns list of candles as tuple:
        tuple = (date, open, high, low, close, subscription_id)
        :return: list
        '''

        curID = self.get_next_req_id()
        curEvent = Event()
        self.add_event(curID, curEvent)
        self.send_request(bin_api.get_tick_history_json(symbol=asset,
                                                        style=style,
                                                        granularity=granularity,
                                                        count=count,
                                                        subscribe=subscribe,
                                                        req_id=curID))

        # TODO: subscribe

        curEvent.wait()

        response = self.get_response(curID)
        response = response['candles']
        return [(r['epoch'], r['open'], r['high'], r['low'], r['close'])
                    for r in response
            ]

    def get_price_proposal(self, asset = 'frxEURUSD', amount = 1,
                          duration = 60, duration_unit = 'm', type='CALL'):
        '''
        Returns price proposal for buying an option as dict
        Keys:
            - date_start
            - proposal_id
            - description
            - payout

        :return: dict
        '''

        curID = self.get_next_req_id()
        curEvent = Event()
        self.add_event(curID, curEvent)
        self.send_request(bin_api.get_price_proposal_json(amount=amount,
                                                          type=type,
                                                          duration=duration,
                                                          duration_unit=duration_unit,
                                                          symbol=asset,
                                                          req_id=curID))

        curEvent.wait()

        response = self.get_response(curID)
        if 'error' in response:
            return {
                'date_start' : '',
                'proposal_id' : '',
                'description' : response['error']['message'],
                'payout' : 0,
                'error' : True,
                'err_msg' : response['error']['message']
            }

        return {
            'date_start' : response['proposal']['date_start'] if 'date_start' in response['proposal'] else '',
            'proposal_id' : response['proposal']['id'],
            'description' : response['proposal']['longcode'],
            'payout' : response['proposal']['payout'],
            'error' : False,
            'err_msg' : ''
        }


    def forget_subscription(self, subscription_id, event_to_remove = None):
        '''
        Makes request to stop receive events by subscription
        :return:
        '''

        curID = self.get_next_req_id()
        curEvent = Event()
        self.add_event(curID, curEvent)
        self.send_request(bin_api.get_forget_stream_json(subscription_id, req_id=curID))

        curEvent.wait()

        if not event_to_remove is None:
            self.remove_event(event_to_remove)
Пример #23
0
class Subscriber():

    def __init__(self):
        self._endpoint = None
        # we actually just use the constant definitions
        self._wamp = WampProtocol(None)
        self._ws = None
        self._session_id = None
        self._handlers_table = {'': set()}
        self._received = list()

    def connect(self, endpoint):
        self._endpoint = endpoint
        self._ws = WebSocketClient(self._endpoint, on_message=self._on_message)
        gevent.spawn(self._ws.run_forever, None, None, 10)

    def subscribe(self, topic, handler):
        #self._ws.send(json.dumps([self._wamp.MSG_PREFIX, topic.split(':')[0], topic]))
        self._ws.send(json.dumps([self._wamp.MSG_SUBSCRIBE, topic]))
        # TODO wait for SUBSCRIBED
        if handler:
            self._add_handler(topic, handler)

    def unsubscribe(self, topic):
        self._ws.send([self._wamp.MSG_UNSUBSCRIBE, topic])
        # TODO wait for UNSUBSCRIBED
        return self._handlers_table.pop(topic, set())

    def receive(self):
        return self._received.pop(0)

    def run(self):
        self._ws = WebSocketClient(self._endpoint, on_message=self._on_message)
        gevent.spawn(self._receiver)
        gevent.spawn(self._ws.run_forever, None, None, 10)

    def _receiver(self):
        while True:
            topic, message = self.receive()
            handlers = self._handlers_table.get(topic, set())
            for handler in handlers:
                handler(topic, message)

    def _on_message(self, sock, msg):
        data = json.loads(msg)
        if data[0] == self._wamp.MSG_WELCOME and len(data) > 1:
            self._session_id = data[1]
        elif data[0] == self._wamp.MSG_EVENT and len(data) >= 3:
            topic, message = data[1], data[2]
            # TODO list length limit
            self.received.append((topic, message))
        else:
            raise Exception('Unexpected message received')

    def _add_handler(self, topic, handler):
        def _add_topic_handler(topic, handler):
            handlers = self._handlers_table.get(topic, set())
            handlers |= set([handler])
            self._handlers_table[topic] = handlers

        if callable(handler):
            if topic == '':
                for tpc in self._handlers_table.keys():
                    _add_topic_handler(tpc, handler)
            else:
                _add_topic_handler(topic, handler)
        else:
            raise ValueError('invalid handler')
Пример #24
0
class WebSockets(object):
    def __init__(self, url, topic):
        self.url = url
        self.topic = topic
        self.connection = None

    def connect(self):
        logger.debug('connect()')
        self.connection = WebSocketApp(
            self.url,
            on_open=self.on_open,
            on_close=self.on_close,
            on_message=self.on_message,
            on_error=self.on_error,
        )
        sslopt = {
            'cert_reqs': CERT_NONE,
        }
        self.connection.run_forever(sslopt=sslopt)

    def disconnect(self):
        logger.debug('disconnect()')
        if self.connection:
            self.connection.close()

    def send(self, payload, *args, **kwargs):
        logger.debug(payload)
        self.connection.send(payload)

    def on_open(self, _):
        logger.debug('on_open()')
        pass

    def on_close(self, _):
        logger.debug('on_close()')
        pass

    def on_message(self, _, payload):
        logger.debug('on_message()')
        prefix, message = self.parse(payload)
        if prefix == '0':
            return
        if prefix == '3':
            return
        if prefix == '40':
            message = [
                'subscribe',
                {
                    'Topic': self.topic,
                    'ConditionsUpdates': 'true',
                    'LiveUpdates': 'true',
                    'OddsUpdates': 'false',
                    'VideoUpdates': 'false',
                },
            ]
            message = dumps(message)
            message = '{prefix:d}{message:s}'.format(prefix=42,
                                                     message=message)
            self.send(message)
            return
        if prefix == '42':
            message = loads(message)
            if 'ActiveMQMessage' in message[1]:
                message[1]['ActiveMQMessage'] = loads(
                    message[1]['ActiveMQMessage'])
                mlu = message[1]['ActiveMQMessage']['MLU']
                t = mlu.get('T', [])
                eid = mlu.get('EID', '?')
                en = mlu.get('EN', '?')
                logger.info((mlu['CPT'], mlu['CR'], mlu['PSID'], mlu['TSID'],
                             mlu['SCH'], mlu['SCA'], len(t), eid, en))
            message = '2'
            self.send(message)
            return

    def parse(self, payload):
        prefix = []
        message = payload
        while True:
            if not message:
                break
            character = message[0]
            if not character.isdigit():
                break
            prefix.append(character)
            message = message[1:]
        prefix = ''.join(prefix)
        return prefix, message

    def on_error(self, _, error):
        logger.debug('on_error()')
        logger.debug(error)
        pass
Пример #25
0
class WebsocketClient(Thread):
    _id: int = 0

    ws: WebSocketApp

    def __init__(self,
                 session: Streamlink,
                 url: str,
                 subprotocols: Optional[List[str]] = None,
                 header: Optional[Union[List, Dict]] = None,
                 cookie: Optional[str] = None,
                 sockopt: Optional[Tuple] = None,
                 sslopt: Optional[Dict] = None,
                 host: Optional[str] = None,
                 origin: Optional[str] = None,
                 suppress_origin: bool = False,
                 ping_interval: Union[int, float] = 0,
                 ping_timeout: Optional[Union[int, float]] = None,
                 ping_payload: str = ""):
        if rootlogger.level <= TRACE:
            enableTrace(True, log)

        if not header:
            header = []
        if not any(True for h in header if h.startswith("User-Agent: ")):
            header.append(f"User-Agent: {session.http.headers['User-Agent']}")

        proxy_options = {}
        http_proxy: Optional[str] = session.get_option("http-proxy")
        if http_proxy:
            p = urlparse(http_proxy)
            proxy_options["proxy_type"] = p.scheme
            proxy_options["http_proxy_host"] = p.hostname
            if p.port:  # pragma: no branch
                proxy_options["http_proxy_port"] = p.port
            if p.username:  # pragma: no branch
                proxy_options["http_proxy_auth"] = unquote_plus(
                    p.username), unquote_plus(p.password or "")

        self._reconnect = False
        self._reconnect_lock = RLock()

        self.session = session
        self._ws_init(url, subprotocols, header, cookie)
        self._ws_rundata = dict(sockopt=sockopt,
                                sslopt=sslopt,
                                host=host,
                                origin=origin,
                                suppress_origin=suppress_origin,
                                ping_interval=ping_interval,
                                ping_timeout=ping_timeout,
                                ping_payload=ping_payload,
                                **proxy_options)

        self._id += 1
        super().__init__(name=f"Thread-{self.__class__.__name__}-{self._id}",
                         daemon=True)

    def _ws_init(self, url, subprotocols, header, cookie):
        self.ws = WebSocketApp(url=url,
                               subprotocols=subprotocols,
                               header=header,
                               cookie=cookie,
                               on_open=self.on_open,
                               on_error=self.on_error,
                               on_close=self.on_close,
                               on_ping=self.on_ping,
                               on_pong=self.on_pong,
                               on_message=self.on_message,
                               on_cont_message=self.on_cont_message,
                               on_data=self.on_data)

    def run(self) -> None:
        while True:
            log.debug(f"Connecting to: {self.ws.url}")
            self.ws.run_forever(**self._ws_rundata)
            # check if closed via a reconnect() call
            with self._reconnect_lock:
                if not self._reconnect:
                    return
                self._reconnect = False

    # ----

    def reconnect(self,
                  url: str = None,
                  subprotocols: Optional[List[str]] = None,
                  header: Optional[Union[List, Dict]] = None,
                  cookie: Optional[str] = None,
                  closeopts: Optional[Dict] = None) -> None:
        with self._reconnect_lock:
            # ws connection is not active (anymore)
            if not self.ws.keep_running:
                return
            log.debug("Reconnecting...")
            self._reconnect = True
            self.ws.close(**(closeopts or {}))
            self._ws_init(url=self.ws.url if url is None else url,
                          subprotocols=self.ws.subprotocols
                          if subprotocols is None else subprotocols,
                          header=self.ws.header if header is None else header,
                          cookie=self.ws.cookie if cookie is None else cookie)

    def close(self,
              status: int = STATUS_NORMAL,
              reason: Union[str, bytes] = "",
              timeout: int = 3) -> None:
        self.ws.close(status=status,
                      reason=bytes(reason, encoding="utf-8"),
                      timeout=timeout)
        if self.is_alive():  # pragma: no branch
            self.join()

    def send(self,
             data: Union[str, bytes],
             opcode: int = ABNF.OPCODE_TEXT) -> None:
        return self.ws.send(data, opcode)

    def send_json(self, data: Any) -> None:
        return self.send(json.dumps(data, indent=None, separators=(",", ":")))

    # ----

    # noinspection PyMethodMayBeStatic
    def on_open(self, wsapp: WebSocketApp) -> None:
        log.debug(f"Connected: {wsapp.url}")  # pragma: no cover

    # noinspection PyMethodMayBeStatic
    # noinspection PyUnusedLocal
    def on_error(self, wsapp: WebSocketApp, error: Exception) -> None:
        log.error(error)  # pragma: no cover

    # noinspection PyMethodMayBeStatic
    # noinspection PyUnusedLocal
    def on_close(self, wsapp: WebSocketApp, status: int, message: str) -> None:
        log.debug(f"Closed: {wsapp.url}")  # pragma: no cover

    def on_ping(self, wsapp: WebSocketApp, data: str) -> None:
        pass  # pragma: no cover

    def on_pong(self, wsapp: WebSocketApp, data: str) -> None:
        pass  # pragma: no cover

    def on_message(self, wsapp: WebSocketApp, data: str) -> None:
        pass  # pragma: no cover

    def on_cont_message(self, wsapp: WebSocketApp, data: str,
                        cont: Any) -> None:
        pass  # pragma: no cover

    def on_data(self, wsapp: WebSocketApp, data: str, data_type: int,
                cont: Any) -> None:
        pass  # pragma: no cover
Пример #26
0
class Slack(Base):
    """Provide bot for Slack."""

    def __init__(self,
                 token: str = '',
                 plugins: Iterable[PluginConfig] = None,
                 max_workers: int = None) -> None:
        """Initializer.

        :param token: Access token provided by Slack.
        :param plugins: List of plugin modules.
        :param max_workers: Optional number of worker threads.
        :return: None
        """
        super().__init__(plugins=plugins, max_workers=max_workers)

        self.client = self.setup_client(token=token)
        self.message_id = 0
        self.ws = None  # type: WebSocketApp
        self.connect_attempt_count = 0

    def setup_client(self, token: str) -> SlackClient:
        """Setup ClackClient and return its instance.

        :param token: Slack access token.
        :return: SlackClient instance
        """
        return SlackClient(token=token)

    def connect(self) -> None:
        """Connect to Slack websocket server and start interaction.

        :return: None
        """
        while self.connect_attempt_count < 10:
            try:
                self.connect_attempt_count += 1
                self.try_connect()
            except KeyboardInterrupt:
                break
            except Exception as e:
                logging.error(e)

            time.sleep(self.connect_attempt_count)
        else:
            logging.error("Attempted 10 times, but all failed. Quitting.")

    def try_connect(self) -> None:
        """Try establish connection with Slack websocket server."""
        try:
            response = self.client.get('rtm.start')
            if 'url' not in response:
                raise Exception("url is not in the response. %s" % response)
        except Exception as e:
            raise SarahSlackException(
                "Slack request error on /rtm.start. %s" % e)
        else:
            self.ws = WebSocketApp(response['url'],
                                   on_message=self.message,
                                   on_error=self.on_error,
                                   on_open=self.on_open,
                                   on_close=self.on_close)
            self.ws.run_forever()

    def generate_schedule_job(self,
                              command: ScheduledCommand) \
            -> Optional[Callable[..., None]]:
        """Generate callback function to be registered to scheduler.

        This creates a function that execute given command and then handle the
        command response. If the response is SlackMessage instance, it make
        HTTP POST request to Slack web API endpoint. If string is returned,
        then it submit it to the message sending worker.

        :param command: ScheduledCommand object that holds job information
        :return: Optional callable object to be scheduled
        """
        channels = command.schedule_config.pop('channels', [])
        if not channels:
            logging.warning(
                'Missing channels configuration for schedule job. %s. '
                'Skipping.' % command.module_name)
            return None

        def job_function() -> None:
            ret = command()
            if isinstance(ret, SlackMessage):
                for channel in channels:
                    # TODO Error handling
                    data = {'channel': channel}
                    data.update(ret.to_request_params())
                    self.client.post('chat.postMessage', data=data)
            else:
                for channel in channels:
                    self.enqueue_sending_message(self.send_message,
                                                 channel,
                                                 str(ret))

        return job_function

    @concurrent
    def message(self, _: WebSocketApp, event: str) -> None:
        """Receive event from Slack and dispatch it to corresponding method.

        :param _: WebSocketApp instance. This is not to be used here.
        :param event: JSON string that contains event information.
        :return: None
        """
        decoded_event = json.loads(event)

        if 'ok' in decoded_event and 'reply_to' in decoded_event:
            # https://api.slack.com/rtm#sending_messages
            # Replies to messages sent by clients will always contain two
            # properties: a boolean ok indicating whether they succeeded and
            # an integer reply_to indicating which message they are in response
            # to.
            if decoded_event['ok'] is False:
                # Something went wrong with the previous message
                logging.error(
                    'Something went wrong with the previous message. '
                    'message_id: %s. error: %s' % (
                        decoded_event['reply_to'],
                        decoded_event.get('error', "")))
            return None

        # TODO organize
        type_map = {
            'hello': {
                'method': self.handle_hello,
                'description': "The client has successfully connected to the "
                               "server"},
            'message': {
                'method': self.handle_message,
                'description': "A message was sent to a channel"},
            'user_typing': {
                'description': "A channel member is typing a message"},
            'presence_change': {
                'description': "A team member's presence changed"},
            'team_migration_started': {
                'method': self.handle_team_migration,
                'description': "The team is being migrated between servers"}
        }  # type: EventTypeMap

        if 'type' not in decoded_event:
            # https://api.slack.com/rtm#events
            # Every event has a type property which describes the type of
            # event.
            logging.error("Given event doesn't have type property. %s" %
                          event)
            return None

        if decoded_event['type'] not in type_map:
            logging.error('Unknown type value is given. %s' % event)
            return None

        logging.debug(
            '%s: %s. %s' % (
                decoded_event['type'],
                type_map[decoded_event['type']].get('description',
                                                    'NO DESCRIPTION'),
                event))

        method = type_map[decoded_event['type']].get('method', None)
        if method:
            method(decoded_event)

        return None

    def handle_hello(self, _: Dict) -> None:
        """Handle hello event.

        This is called when connection is established.

        :param _: Dictionary that represent event. This is not used here.
        :return: None
        """
        self.connect_attempt_count = 0  # Reset retry count
        logging.info('Successfully connected to the server.')

    def handle_message(self, content: Dict) -> Optional[Future]:
        """Handle message event.

        :param content: Dictionary that represent event.
        :return: Optional Future instance that represent message sending
            result.
        """
        # content
        # {
        #     "type":"message",
        #     "channel":"C06TXXXX",
        #     "user":"******",
        #     "text":".bmw",
        #     "ts":"1438477080.000004",
        #     "team":"T06TXXXXX"
        # }
        required_props = ('type', 'channel', 'user', 'text', 'ts')
        missing_props = [p for p in required_props if p not in content]

        if missing_props:
            logging.error('Malformed event is given. Missing %s. %s' % (
                ', '.join(missing_props),
                content))
            return None

        ret = self.respond(content['user'], content['text'])
        if isinstance(ret, SlackMessage):
            # TODO Error handling
            data = {'channel': content["channel"]}
            data.update(ret.to_request_params())
            self.client.post('chat.postMessage', data=data)
            return None
        elif isinstance(ret, str):
            return self.enqueue_sending_message(self.send_message,
                                                content['channel'],
                                                ret)

    def handle_team_migration(self, _: Dict) -> None:
        """Handle team_migration_started event.

        https://api.slack.com/events/team_migration_started
        "When clients recieve this event they can immediately start a
        reconnection process by calling rtm.start again."

        :param _: Dictionary that represent event.
        :return: None
        """
        logging.info("Team migration started.")

    def on_error(self, _: WebSocketApp, error: Exception) -> None:
        """Callback method called by WebSocketApp when error occurred.

        :param _: WebSocketApp instance that is not currently used.
        :param error: Exception instance
        :return: None
        """
        logging.error("error %s", error)

    def on_open(self, _: WebSocketApp) -> None:
        """Callback method called by WebSocketApp on connection establishment.

        :param _: WebSocketApp instance that is not currently used.
        :return: None
        """
        logging.info('connected')

    def on_close(self, _: WebSocketApp, code: int, reason: str) -> None:
        """Callback method called by WebSocketApp when connection is closed.

        :param _: WebSocketApp instance that is not currently used.
        :param code: Closing code described in RFC6455.
            https://tools.ietf.org/html/rfc6455#section-7.4
        :param reason: Closing reason.
        :return: None
        """
        logging.info('connection closed. code: %d. reason: %s', code, reason)

    def send_message(self,
                     channel: str,
                     text: str,
                     message_type: str = 'message') -> None:
        """Send message to Slack via websocket connection.

        :param channel: Target channel to send message.
        :param text: Sending text.
        :param message_type: Message type. Default is "message."
        :return: None
        """
        params = {'channel': channel,
                  'text': text,
                  'type': message_type,
                  'id': self.next_message_id()}
        self.ws.send(json.dumps(params))

    def next_message_id(self) -> int:
        """Return unique ID for sending message.

        https://api.slack.com/rtm#sending_messages
        Every event should have a unique (for that connection) positive
        integer ID. All replies to that message will include this ID.

        :return: Unique ID as int
        """
        self.message_id += 1
        return self.message_id
Пример #27
0
class WifiClient:
    """
    Class to manage entire wifi client

    Usage:
        >>> client = WifiClient()  # Starts client
        >>> client.join()
    """
    def __init__(self, allow_timeout=True):
        self.allow_timeout = allow_timeout
        self.running = False

        self.last_lease_mod = self.get_last_lease_mod()
        self.wiface = pyw.winterfaces()[0]
        self.ap = AccessPoint(self.wiface)
        self.client = WebSocketApp(url=config.websocket['url'],
                                   on_message=self.on_message)
        Thread(target=self.client.run_forever).start()
        self.run_thread = Thread(target=self.run, daemon=True)

        self.server = None
        try:
            self.server = WebServer(self.ap.ip, 80)
            self.server.start()
        except RuntimeError:
            self.close()
            raise

        self.run_thread.start()

    def get_last_lease_mod(self):
        """When a new user connects, the lease modification time changes"""
        return os.path.getmtime('/var/lib/misc/dnsmasq.leases')

    def join(self):
        """Waits for wifi setup to complete"""
        try:
            self.run_thread.join()
        except:
            LOG.exception('Error in wifi thread:')
            self.close()

    def notify_server(self, name, data=None):
        """Send a message to javascript"""
        self.client.send(json.dumps({'type': name, 'data': data or {}}))

    def on_message(self, _, message: str):
        """Handle communication from javascript"""
        message = json.loads(message)

        def invalid_message(**_):
            pass

        # Javascript events
        {
            'wifi.cancel': self.cancel,
            'wifi.stop': self.close,
            'wifi.scan': self.scan,
            'wifi.connect': self.connect
        }.get(message['type'], invalid_message)(**message.get('data', {}))

    def run(self):
        """
        Fire up the MYCROFT access point for the user to connect to
        with a phone or computer.
        """
        try:
            self.monitor_connection()
        except:
            LOG.exception('Error in wifi client:')
            self.close()

    def monitor_connection(self):
        trigger_event('ap_up')
        has_connected = False
        num_failures = 0
        start_time = time.time()
        self.running = True

        while self.running:
            # do our monitoring...
            mod_time = self.get_last_lease_mod()
            if self.last_lease_mod != mod_time:
                # Something changed in the dnsmasq lease file -
                # presumably a (re)new lease
                if not has_connected:
                    trigger_event('ap_device_connected')
                has_connected = True
                num_failures = 0
                self.last_lease_mod = mod_time
                start_time = time.time()  # reset start time after connection

            if time.time() - start_time > 60 * 5 and self.allow_timeout:
                # After 5 minutes, shut down the access point (unless the
                # system has never been setup, in which case we stay up
                # indefinitely)
                LOG.info("Auto-shutdown of access point after 5 minutes")
                self.cancel()
                continue

            if has_connected:
                # Flush the ARP entries associated with our access point
                # This will require all network hardware to re-register
                # with the ARP tables if still present.
                if num_failures == 0:
                    cli_no_output('ip', '-s', '-s', 'neigh', 'flush',
                                  self.ap.subnet + '.0/24')

                # now look at the hardware that has responded, if no entry
                # shows up on our access point after 2*5=10 seconds, the user
                # has disconnected
                if not self.is_ARP_filled():
                    num_failures += 1
                    LOG.info('Lost connection: ' + str(num_failures))
                    if num_failures > 5:
                        trigger_event('ap_device_disconnected')
                        has_connected = False
                else:
                    num_failures = 0
            sleep(5)  # wait a bit to prevent thread from hogging CPU

    def is_ARP_filled(self):
        out = cli_no_output('/usr/sbin/arp', '-n')["stdout"]
        if not out:
            return False
        # Parse output, skipping header
        for o in out.split("\n")[1:]:
            if o.startswith(self.ap.subnet):
                if "(incomplete)" in o:
                    # ping the IP to get the ARP table entry reloaded
                    ip_disconnected = o.split(" ")[0]
                    cli_no_output('/bin/ping', '-c', '1', '-W', '3',
                                  ip_disconnected)
                else:
                    return True  # something on subnet is connected!
        return False

    def scan(self):
        trigger_event('ap_scan')
        LOG.info("Scanning wifi connections...")
        networks = {}
        status = self.get_connection_info()

        for cell in Cell.all(self.wiface):
            if "x00" in cell.ssid:
                continue  # ignore hidden networks

            # Fix UTF-8 characters
            ssid = literal_eval("b'" + cell.ssid + "'").decode('utf8')
            quality = self.get_quality(cell.quality)

            # If there are duplicate network IDs (e.g. repeaters) only
            # report the strongest signal
            update = True
            if ssid in networks:
                update = networks[ssid]["quality"] < quality
            if update and ssid:
                networks[ssid] = {
                    'quality': quality,
                    'encrypted': cell.encrypted,
                    'connected': self.is_connected(ssid, status),
                    'demo': False
                }
        LOG.info("Found wifi networks: %s" % networks)
        self.notify_server('wifi.scanned', {'networks': networks})

    @staticmethod
    def get_quality(quality):
        values = quality.split("/")
        return float(values[0]) / float(values[1])

    def connect(self, ssid, password=None):
        LOG.info('Connecting to ' + ssid + '...')
        connected = self.is_connected(ssid)

        if connected:
            LOG.warning("Device is already connected to %s" % ssid)
        else:
            self.disconnect()
            LOG.info("Connecting to: %s" % ssid)
            nid = wpa(self.wiface, 'add_network')
            wpa(self.wiface, 'set_network', nid, 'ssid', '"' + ssid + '"')

            if password:
                psk = '"' + password + '"'
                wpa(self.wiface, 'set_network', nid, 'psk', psk)
            else:
                wpa(self.wiface, 'set_network', nid, 'key_mgmt', 'NONE')

            wpa(self.wiface, 'enable', nid)
            connected = self.get_connected(ssid)
            if connected:
                wpa(self.wiface, 'save_config')

        trigger_event(
            'ap_connection_success' if connected else 'ap_connection_failed')
        self.notify_server('connection.status', {'connected': connected})
        LOG.info("Connection status for %s = %s" % (ssid, connected))

    def disconnect(self):
        """Disconnect from current SSID"""
        status = self.get_connection_info()
        nid = status.get("id")
        if nid:
            ssid = status.get("ssid")
            wpa(self.wiface, 'disable', nid)
            LOG.info("Disconnecting %s id: %s" % (ssid, nid))

    def get_connection_info(self):
        res = cli('wpa_cli', '-i', self.wiface, 'status')
        out = str(res["stdout"])
        if out:
            return dict(o.split("=") for o in out.split("\n")[:-1])
        return {}

    def get_connected(self, ssid, retry=5):
        connected = self.is_connected(ssid)
        while not connected and retry > 0:
            sleep(1)
            retry -= 1
            connected = self.is_connected(ssid)
        return connected

    def is_connected(self, ssid, status=None):
        status = status or self.get_connection_info()
        state = status.get("wpa_state")
        return status.get("ssid") == ssid and state == "COMPLETED"

    def cancel(self):
        trigger_event('ap_cancel')
        self.close()

    def close(self):
        trigger_event('ap_down')
        self.running = False
        LOG.info('Shutting down access point...')
        self.ap.close()
        LOG.info('Sending shutdown signal...')
        if self.server:
            self.server.shutdown()
        LOG.info('Closing websocket...')
        self.client.close()
        LOG.info("Wifi client stopped!")
class TracerApp(object):
   """
   Main tracer application.
   """
   def __init__(self, options):
      self.counter  = 0
      self.host     = options.host
      self.port     = options.port
      self.ios      = options.ios
      self.page_num = options.page_num

      self.timeStart = time.mktime(time.localtime())

      self.shouldStep  = True    # If we should try to single step the debugger
      self._isStepping = False   # If we are currently inside stepping

      self.ws_url = None
      self.ws     = None

      self._requestDetails = {}   # Map of details about a request


   def showConnectionList(self):
      """
      Print out list of possible debugger connections
      """
      # Mobile safari has a different listing page
      if self.ios:
         pages_url  = "http://%s:%s/listing.json" % (self.host, self.port)
      else:
         pages_url  = "http://%s:%s/json" % (self.host, self.port)

      pages      = urllib2.urlopen(pages_url)
      pages_data = json.loads(pages.read())
      for page in pages_data:
         print "----------------------------------------------------------"
         if self.ios:
            print "Page: ", page.get('pageTitle')
            print "    id: ", page.get('pageId')
            print "   url: ", page.get('pageURL')
            print "  debug url: ", page.get('debugURL')
         else:
            print "Page: ", page.get('title', '')
            print "   url: ", page.get('url', '')
            print "   ws_debug_url: ", page.get('webSocketDebuggerUrl', '')


   def start(self):
      self.ws_url = "ws://%s:%s/devtools/page/%s" % (self.host, self.port, self.page_num)

      self.ws = WebSocketApp(self.ws_url,
         on_open    = self.onOpen,
         on_message = self.onMessage,
         on_error   = self.onError,
         on_close   = self.onClose,
         ipv6 = self.ios)

      self.ws.run_forever()


   def send(self, method, params = None):
      self.counter += 1

      # separators is important, you'll get "Message should be in JSON format." otherwise
      msg_data = {"id": self.counter, "method": method}
      if params is not None:
         msg_data['params'] = params
      message = json.dumps(msg_data, separators=(',', ':'))
      print "> %s" % (message,)
      self.ws.send(message)


   def recv(self):
      result = self.ws.recv()
      print "< %s" % (result,)
      return result


   # ---- PRIMARY CALLBACKS ---- #
   def onOpen(self, ws):
      #self.send('Runtime.evaluate', {'expression': '1+1'})
      #self.send('Runtime.evaluate', {'expression': 'alert("hello from python")'})
      #self.send('Timeline.start', {'maxCallStackDepth': 5})
      #self.send('Network.enable')
      #self.send('Console.enable')
      #self.send('Timeline.start',  {'maxCallStackDepth': 5})
      self.send('Debugger.enable')


   def onMessage(self, ws, message):
      # Decode message into a bunch object to make easier to access attributes
      # but store original message so we can get to it
      msg = Bunch.loads(message)
      msg._rawMsg = json.loads(message)

      if hasattr(msg, 'result'):
         self.handleResultMsg(msg)
      elif hasattr(msg, 'method'):
         self.handleMethodMsg(msg)
      else:
         print "UNKNOWN MSG TYPE: "
         self.prettyPrintMsg(msg)

   def onError(self, ws, error):
      print "error: ", error

   def onClose(self, ws):
      print "CLOSED"


   # --- Helpers ---- #
   def prettyPrintMsg(self, msg):
      if type(msg) in types.StringTypes:
         msg = json.loads(msg)
      elif type(msg) == Bunch:
         msg = msg._rawMsg

      print json.dumps(msg, sort_keys=True, indent=3)


   # --- MSG Helpers --- #
   def handleResultMsg(self, msg):
      print "RESULT: [%s]" % msg.id
      print json.dumps(msg._rawMsg['result'], sort_keys=True, indent=3)


   def handleMethodMsg(self, msg):
      """
      Try to map method name to a local handler.
      get name as: 'handle' + method in camel case and no .'s
      """
      def replace(match):
         return match.group()[1].upper()

      method         = msg.method
      handler_name   = 'handle' + re.sub('\.\w', replace, method)
      handler_method = getattr(self, handler_name, self.handleNotificationDefault)
      handler_method(msg)


   def handleNotificationDefault(self, msg):
      """ By default just print the method name. """
      print self.getMethodHeader(msg)

   # --- Console Notification Processing --- #
   def handleConsoleMessageAdded(self, msg):
      print self.getMethodHeader(msg)
      cmsg = msg.params.message
      print "  msg: [%s] %s" % (cmsg.level, cmsg.text)


   # --- Timeline Notification Processing -- #
   def handleTimelineEventRecorded(self, msg):
      record = msg.params.record
      record_raw = msg._rawMsg.get('params').get('record')
      print "[%s] ==[ %s:%s ]=====" % ('', #self.getDeltaTsStr(record.endTime),
                                       msg.method, record.type)
      print "        mem: %.2f/%.2f MB"  % (record.usedHeapSize / (1024.0*1024.0),
                                        record.totalHeapSize / (1024.0*1024.0))
      if record.has_key('endTime'):
         print "   duration: ", (record.endTime - record.startTime)
      print "       data: ", json.dumps(record_raw.get('data'), indent = 2)
      #print "   children: ", json.dumps(record_raw.get('children'), indent = 2)


   # --- Network Notification Processing --- #
   def handleNetworkDataReceived(self, msg):
      print self.getMethodHeader(msg)
      print "  request: ", self.getRequestSummary(msg)
      print "  dataLen: ", msg.params.dataLength

   def handleNetworkLoadingFailed(self, msg):
      print self.getMethodHeader(msg)
      print "  request: ", self.getRequestSummary(msg)
      print "    error: ", msg.params.errorText

   def handleNetworkLoadingFinished(self, msg):
      print self.getMethodHeader(msg)
      print "  request: ", self.getRequestSummary(msg)

   def handleNetworkRequestServedFromCache(self, msg):
      print self.getMethodHeader(msg)
      print "  request: ", self.getRequestSummary(msg)

   def handleNetworkRequestServedFromMemoryCache(self, msg):
      print self.getMethodHeader(msg)
      print "  request: ", self.getRequestSummary(msg)
      print "      url: ", msg.params.documentURL

   def handleNetworkRequestWillBeSent(self, msg):
      request_id = msg.params.get_first('requestId', 'identifier')
      self._requestDetails[request_id] = Bunch(requestId = request_id,
         request     = msg.params.request,
         loaderId    = msg.params.loaderId,
         documentUrl = msg.params.documentURL,
         startTs     = msg.params.timestamp,
         initiator   = msg.params.get('initiator', None),
         stack       = msg.params.stackTrace)

      print self.getMethodHeader(msg)
      print "  request: ", self.getRequestSummary(msg)
      print "      url: ", msg.params.documentURL

   def handleNetworkResponseReceived(self, msg):
      resp = msg.params.response
      print self.getMethodHeader(msg)
      print "    request: ", self.getRequestSummary(msg)
      print "       type: ", msg.params.type
      print "     reused: ", resp.connectionReused
      print "  from disk: ", resp.fromDiskCache
      print "       mime: ", resp.mimeType
      print "     status: [%s] %s" % (resp.status, resp.statusText)


   def handleDebuggerDebuggerWasEnabled(self, msg):
      print self.getMethodHeader(msg)

      if self.shouldStep:
         self.send('Debugger.pause')
      #   self.send('Debugger.stepInto')
      #   self._isStepping = True


   def handleDebuggerPaused(self, msg):
      print self.getMethodHeader(msg)
      print "   reason: ", msg.params.reason

      params_raw = msg._rawMsg.get('params')
      print "       data: ", json.dumps(params_raw.get('data'), indent = 2)
      print "       frame: ", json.dumps(params_raw.get('callFrames'), indent = 2)

      if self.shouldStep:
         self.send('Debugger.stepInto')


   def handleDebuggerResumed(self, msg):
      print self.getMethodHeader(msg)

   def handleDebuggerScriptParsed(self, msg):
      print self.getMethodHeader(msg)
      print json.dumps(msg._rawMsg.get('params'), indent = 2)


   # ---- Helpers ---- #
   def getMethodHeader(self, msg):
      return "[%s] ==[ %s ]=====" % (self.getTS(msg), msg.method)

   def getRequestSummary(self, msg):
      request_id = msg.params.get_first('requestId', 'identifier')
      req_record = self._requestDetails.get(request_id, None)
      if req_record:
         return "[%s] %s" % (request_id, req_record.request.url)
      else:
         return "[%s] {unknown}" % request_id

   def getTS(self, msg):
      """ Returns a timestamp string to use as prefix """
      ts_value = None
      if hasattr(msg, 'params'):
         ts_value = msg.params.get('timestamp', None)

      if ts_value is None:
         return '<nots>'
      else:
         return self.getDeltaTsStr(ts_value)

   def getDeltaTsStr(self, ts):
      ts_delta = ts - self.timeStart
      return "%8.4f" % ts_delta
Пример #29
0
class Slack(Base):
    def __init__(self, token: str = "", plugins: Sequence[PluginConfig] = None, max_workers: int = None) -> None:

        super().__init__(plugins=plugins, max_workers=max_workers)

        self.client = self.setup_client(token=token)
        self.message_id = 0
        self.ws = None

    def setup_client(self, token: str) -> SlackClient:
        return SlackClient(token=token)

    def connect(self) -> None:
        try:
            response = self.client.get("rtm.start")
        except Exception as e:
            raise SarahSlackException("Slack request error on /rtm.start. %s" % e)
        else:
            if "url" not in response:
                raise SarahSlackException("Slack response did not contain connecting url. %s" % response)

            self.ws = WebSocketApp(
                response["url"],
                on_message=self.message,
                on_error=self.on_error,
                on_open=self.on_open,
                on_close=self.on_close,
            )
            self.ws.run_forever()

    def add_schedule_job(self, command: Command) -> None:
        if "channels" not in command.config:
            logging.warning("Missing channels configuration for schedule job. %s. " "Skipping." % command.module_name)
            return

        def job_function() -> None:
            ret = command.execute()
            if isinstance(ret, SlackMessage):
                for channel in command.config["channels"]:
                    # TODO Error handling
                    data = dict({"channel": channel})
                    data.update(ret.to_request_params())
                    self.client.post("chat.postMessage", data=data)
            else:
                for channel in command.config["channels"]:
                    self.enqueue_sending_message(self.send_message, channel, str(ret))

        job_id = "%s.%s" % (command.module_name, command.name)
        logging.info("Add schedule %s" % id)
        self.scheduler.add_job(job_function, "interval", id=job_id, minutes=command.config.get("interval", 5))

    @concurrent
    def message(self, _: WebSocketApp, event: str) -> None:
        decoded_event = json.loads(event)

        if "ok" in decoded_event and "reply_to" in decoded_event:
            # https://api.slack.com/rtm#sending_messages
            # Replies to messages sent by clients will always contain two
            # properties: a boolean ok indicating whether they succeeded and
            # an integer reply_to indicating which message they are in response
            # to.
            if decoded_event["ok"] is False:
                # Something went wrong with the previous message
                logging.error(
                    "Something went wrong with the previous message. "
                    "message_id: %d. error: %s" % (decoded_event["reply_to"], decoded_event["error"])
                )
            return

        # TODO organize
        type_map = {
            "hello": {
                "method": self.handle_hello,
                "description": "The client has successfully connected " "to the server",
            },
            "message": {"method": self.handle_message, "description": "A message was sent to a channel"},
            "user_typing": {"description": "A channel member is typing a " "message"},
        }

        if "type" not in decoded_event:
            # https://api.slack.com/rtm#events
            # Every event has a type property which describes the type of
            # event.
            logging.error("Given event doesn't have type property. %s" % event)
            return

        if decoded_event["type"] not in type_map:
            logging.error("Unknown type value is given. %s" % event)
            return

        logging.debug(
            "%s: %s. %s"
            % (decoded_event["type"], type_map[decoded_event["type"]].get("description", "NO DESCRIPTION"), event)
        )

        if "method" in type_map[decoded_event["type"]]:
            type_map[decoded_event["type"]]["method"](decoded_event)
            return

    def handle_hello(self, _: Dict) -> None:
        logging.info("Successfully connected to the server.")

    def handle_message(self, content: Dict) -> Optional[Future]:
        # content
        # {
        #     "type":"message",
        #     "channel":"C06TXXXX",
        #     "user":"******",
        #     "text":".bmw",
        #     "ts":"1438477080.000004",
        #     "team":"T06TXXXXX"
        # }
        required_props = ("type", "channel", "user", "text", "ts")
        missing_props = [p for p in required_props if p not in content]

        if missing_props:
            logging.error("Malformed event is given. Missing %s. %s" % (", ".join(missing_props), content))
            return

        ret = self.respond(content["user"], content["text"])
        if isinstance(ret, SlackMessage):
            # TODO Error handling
            data = dict({"channel": content["channel"]})
            data.update(ret.to_request_params())
            self.client.post("chat.postMessage", data=data)
        elif isinstance(ret, str):
            return self.enqueue_sending_message(self.send_message, content["channel"], ret)

    def on_error(self, _: WebSocketApp, error) -> None:
        logging.error(error)

    def on_open(self, _: WebSocketApp) -> None:
        logging.info("connected")

    def on_close(self, _: WebSocketApp) -> None:
        logging.info("closed")

    def send_message(self, channel: str, text: str, message_type: str = "message") -> None:
        params = {"channel": channel, "text": text, "type": message_type, "id": self.next_message_id()}
        self.ws.send(json.dumps(params))

    def next_message_id(self) -> int:
        # https://api.slack.com/rtm#sending_messages
        # Every event should have a unique (for that connection) positive
        # integer ID. All replies to that message will include this ID.
        self.message_id += 1
        return self.message_id

    def stop(self) -> None:
        super().stop()
        logging.info("STOP SLACK INTEGRATION")
        self.ws.close()
Пример #30
0
class DXJobLogStreamClient:
    def __init__(
        self, job_id, input_params=None, msg_output_format="{job} {level} {msg}",
        msg_callback=None, print_job_info=True
    ):
        self.job_id = job_id
        self.input_params = input_params
        self.msg_output_format = msg_output_format
        self.msg_callback = msg_callback
        self.print_job_info = print_job_info
        self.seen_jobs = {}
        self.error = False
        self.exception = None
        self.closed_code = None
        self.closed_reason = None
        self.url = "{protocol}://{host}:{port}/{job_id}/getLog/websocket".format(
            protocol='wss' if dxpy.APISERVER_PROTOCOL == 'https' else 'ws',
            host=dxpy.APISERVER_HOST,
            port=dxpy.APISERVER_PORT,
            job_id=job_id
        )
        self._app = None

    def connect(self):
        while True:
            self.error = False
            self.exception = None
            self.closed_code = None
            self.closed_reason = None

            try:
                self._app = WebSocketApp(
                    self.url,
                    on_open=self.opened,
                    on_close=self.closed,
                    on_error=self.errored,
                    on_message=self.received_message
                )
                self._app.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
            except:
                if not self.server_restarted():
                    raise
            finally:
                self._app = None

            if self.server_restarted():
                # Instead of trying to reconnect in a retry loop with backoff, run an
                # API call that will do the same and block while it retries.
                logger.warn("Server restart, reconnecting...")
                time.sleep(1)
                dxpy.describe(self.job_id)
            else:
                break

    def server_restarted(self):
        return (
            self.closed_code == 1001 and
            self.closed_reason == "Server restart, please reconnect later"
        )

    def opened(self):
        args = {
            "access_token": dxpy.SECURITY_CONTEXT['auth_token'],
            "token_type": dxpy.SECURITY_CONTEXT['auth_token_type']
        }
        if self.input_params:
            args.update(self.input_params)
        self._app.send(json.dumps(args))

    def errored(self, exception=None):
        self.error = True
        self.exception = exception

    def closed(self, code=None, reason=None):
        if code:
            self.closed_code = code
            self.closed_reason = reason
        elif not self.error:
            self.closed_code = 1000
            self.closed_reason = "Normal"
        elif self.exception and type(self.exception) in {KeyboardInterrupt, SystemExit}:
            self.closed_code = 1000
            self.closed_reason = "Connection terminated by client"
        else:
            self.closed_code = 1006
            self.closed_reason = str(self.exception) if self.exception else "Abnormal"

        if self.closed_code != 1000:
            try:
                error = json.loads(self.closed_reason)
                raise DXJobLogStreamingException(
                    "Error while streaming job logs: {type}: {message}\n".format(
                        **error
                    )
                )
            except (KeyError, ValueError):
                raise DXJobLogStreamingException(
                    "Error while streaming job logs: {code}: {reason}\n".format(
                        code=self.closed_code, reason=self.closed_reason
                    )
                )
        elif self.print_job_info:
            if self.job_id not in self.seen_jobs:
                self.seen_jobs[self.job_id] = {}
            for job_id in self.seen_jobs.keys():
                self.seen_jobs[job_id] = dxpy.describe(job_id)
                print(
                    get_find_executions_string(
                        self.seen_jobs[job_id],
                        has_children=False,
                        show_outputs=True
                    )
                )
        else:
            self.seen_jobs[self.job_id] = dxpy.describe(self.job_id)

        if self.seen_jobs[self.job_id].get('state') in {'failed', 'terminated'}:
            err_exit(code=3)

    def received_message(self, message):
        message_dict = json.loads(message)

        if (
            self.print_job_info and
            'job' in message_dict and
            message_dict['job'] not in self.seen_jobs
        ):
            self.seen_jobs[message_dict['job']] = dxpy.describe(message_dict['job'])
            print(
                get_find_executions_string(
                    self.seen_jobs[message_dict['job']],
                    has_children=False,
                    show_outputs=False
                )
            )

        if (
            message_dict.get('source') == 'SYSTEM' and
            message_dict.get('msg') == 'END_LOG'
        ):
            self._app.keep_running = False
        elif self.msg_callback:
            self.msg_callback(message_dict)
        else:
            print(self.msg_output_format.format(**message_dict))
Пример #31
0
class DXJobLogStreamClient:
    def __init__(
        self, job_id, input_params=None, msg_output_format="{job} {level} {msg}",
        msg_callback=None, print_job_info=True, exit_on_failed=True
    ):
        """Initialize job log client.

        :param job_id: dxid for a job (hash ID 'job-xxxx')
        :type job_id: str
        :param input_params: blob with connection parameters, should have keys
        ``numRecentMessages`` (int) (wich may not be more than 1024 * 256, otherwise no logs will be returned),
        ``recurseJobs`` (bool) - if True, attempts to traverse subtree
        ``tail`` (bool) - if True, keep watching job. Should also be set to True to get all logs
        from a completed job.
        :type input_params: dict
        :param msg_output_format: how messages should be printed to console. Ignored if
        ``msg_callback`` is specified
        :type msg_output_form: str
        :param print_job_info: if True, prints metadata about job
        :type print_job_info: bool
        :param msg_callback: single argument function that accepts a JSON blob with message
        details. Example:
        ``{"timestamp": 1575465039481, "source": "APP", "level": "STDOUT", "job": "job-123",
           "line":24, "msg": "success WfFragment"}``
        where ``timestamp`` is Unix epoch time in milliseconds and ``line`` is a sequence number.
        :type msg_callback: callable
        :param exit_on_failed: if True, will raise SystemExit with code of 3 if encountering a
        failed job (this is the default behavior)
        :type exit_on_failed: bool
        """
        # TODO: add unit tests; note it is a public class

        self.job_id = job_id
        self.input_params = input_params
        self.msg_output_format = msg_output_format
        self.msg_callback = msg_callback
        self.print_job_info = print_job_info
        self.seen_jobs = {}
        self.error = False
        self.exception = None
        self.closed_code = None
        self.closed_reason = None
        self.exit_on_failed = exit_on_failed
        self.url = "{protocol}://{host}:{port}/{job_id}/getLog/websocket".format(
            protocol='wss' if dxpy.APISERVER_PROTOCOL == 'https' else 'ws',
            host=dxpy.APISERVER_HOST,
            port=dxpy.APISERVER_PORT,
            job_id=job_id
        )
        self._app = None

    def connect(self):
        while True:
            self.error = False
            self.exception = None
            self.closed_code = None
            self.closed_reason = None

            try:
                self._app = WebSocketApp(
                    self.url,
                    on_open=self.opened,
                    on_close=self.closed,
                    on_error=self.errored,
                    on_message=self.received_message
                )
                self._app.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
            except:
                if not self.server_restarted():
                    raise
            finally:
                self._app = None

            if self.server_restarted():
                # Instead of trying to reconnect in a retry loop with backoff, run an
                # API call that will do the same and block while it retries.
                logger.warn("Server restart, reconnecting...")
                time.sleep(1)
                dxpy.describe(self.job_id)
            else:
                break

    def server_restarted(self):
        return (
            self.closed_code == 1001 and
            self.closed_reason == "Server restart, please reconnect later"
        )

    def opened(self):
        args = {
            "access_token": dxpy.SECURITY_CONTEXT['auth_token'],
            "token_type": dxpy.SECURITY_CONTEXT['auth_token_type']
        }
        if self.input_params:
            args.update(self.input_params)
        self._app.send(json.dumps(args))

    def errored(self, exception=None):
        self.error = True
        self.exception = exception

    def closed(self, code=None, reason=None):
        if code:
            self.closed_code = code
            self.closed_reason = reason
        elif not self.error:
            self.closed_code = 1000
            self.closed_reason = "Normal"
        elif self.exception and type(self.exception) in {KeyboardInterrupt, SystemExit}:
            self.closed_code = 1000
            self.closed_reason = "Connection terminated by client"
        else:
            self.closed_code = 1006
            self.closed_reason = str(self.exception) if self.exception else "Abnormal"

        if self.closed_code != 1000:
            try:
                error = json.loads(self.closed_reason)
                raise DXJobLogStreamingException(
                    "Error while streaming job logs: {type}: {message}\n".format(
                        **error
                    )
                )
            except (KeyError, ValueError):
                raise DXJobLogStreamingException(
                    "Error while streaming job logs: {code}: {reason}\n".format(
                        code=self.closed_code, reason=self.closed_reason
                    )
                )
        elif self.print_job_info:
            if self.job_id not in self.seen_jobs:
                self.seen_jobs[self.job_id] = {}
            for job_id in self.seen_jobs.keys():
                self.seen_jobs[job_id] = dxpy.describe(job_id)
                print(
                    get_find_executions_string(
                        self.seen_jobs[job_id],
                        has_children=False,
                        show_outputs=True
                    )
                )
        else:
            self.seen_jobs[self.job_id] = dxpy.describe(self.job_id)

        if (self.exit_on_failed
                and self.seen_jobs[self.job_id].get('state') in {'failed', 'terminated'}):
            err_exit(code=3)

    def received_message(self, message):
        message_dict = json.loads(message)

        if (
            self.print_job_info and
            'job' in message_dict and
            message_dict['job'] not in self.seen_jobs
        ):
            self.seen_jobs[message_dict['job']] = dxpy.describe(message_dict['job'])
            print(
                get_find_executions_string(
                    self.seen_jobs[message_dict['job']],
                    has_children=False,
                    show_outputs=False
                )
            )

        if (
            message_dict.get('source') == 'SYSTEM' and
            message_dict.get('msg') == 'END_LOG'
        ):
            self._app.keep_running = False
        elif self.msg_callback:
            self.msg_callback(message_dict)
        else:
            print(self.msg_output_format.format(**message_dict))
Пример #32
0
class ByBitWebsocketConnection:
    BTCUSD_KLINE_1M = 'kline' + '.BTCUSD.' + '1m'
    ETHUSD_KLINE_1M = 'kline' + '.ETHUSD.' + '1m'
    EOSUSD_KLINE_1M = 'kline' + '.EOSUSD.' + '1m'
    XRPUSD_KLINE_1M = 'kline' + '.XRPUSD.' + '1m'
    BTCUSD_ORDERBOOK = "orderBookL2_25." + "BTCUSD"
    ETHUSD_ORDERBOOK = "orderBookL2_25." + "ETHUSD"
    EOSUSD_ORDERBOOK = "orderBookL2_25." + "EOSUSD"
    XRPUSD_ORDERBOOK = "orderBookL2_25." + "XRPUSD"
    POSITION = 'position'
    EXECUTION = 'execution'
    ORDER = 'order'

    # noinspection PyUnresolvedReferences
    def __init__(self, api_key, secret, use_test_net):
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.INFO)
        self._api_key = api_key
        self._secret = secret
        self.ws_url_main = 'wss://stream.bybit.com/realtime'
        self.ws_url_test = 'wss://stream-testnet.bybit.com/realtime'
        self.__hb_count = 0

        if not use_test_net:
            self.ws_url = self.ws_url_main
        else:
            self.ws_url = self.ws_url_test

        self.ws: WebSocketApp

        self.ws_message_data = {
            self.BTCUSD_KLINE_1M: {},
            self.ETHUSD_KLINE_1M: {},
            self.EOSUSD_KLINE_1M: {},
            self.XRPUSD_KLINE_1M: {},
            self.POSITION: {},
            self.EXECUTION: {},
            self.ORDER: {},
            self.BTCUSD_ORDERBOOK: pd.DataFrame(),
            self.ETHUSD_ORDERBOOK: pd.DataFrame(),
            self.EOSUSD_ORDERBOOK: pd.DataFrame(),
            self.XRPUSD_ORDERBOOK: pd.DataFrame()
        }

        self.__lock = threading.RLock()

    def _connect_websocket(self):
        self.ws = WebSocketApp(url=self.ws_url,
                               on_open=self.__on_open,
                               on_message=self.__on_message,
                               on_close=self.__on_close)

        self.wst = threading.Thread(target=lambda: self.ws.run_forever())
        self.wst.daemon = True
        self.wst.start()
        self.wst.join()

        conn_timeout = 10
        while not self.ws.sock or not self.ws.sock.connected and conn_timeout:
            time.sleep(1)
            conn_timeout -= 1
        if not conn_timeout:
            raise websocket.WebSocketTimeoutException(
                "Couldn't connect to WS! Exiting. Please check your host settings. ")

    def __on_open(self, ws):
        expires = int(time.time() * 1000) + 10000
        param_str = 'GET/realtime' + str(expires)
        sign = hmac.new(self._secret.encode('utf-8'),
                        param_str.encode('utf-8'), hashlib.sha256).hexdigest()
        ws.send(json.dumps(
            {'op': 'auth', 'args': [self._api_key, expires, sign]}))

        self.ws.send(json.dumps(
            {'op': 'subscribe', 'args': [
                self.BTCUSD_KLINE_1M,
                self.ETHUSD_KLINE_1M,
                self.EOSUSD_KLINE_1M,
                self.XRPUSD_KLINE_1M,
                self.POSITION,
                self.EXECUTION,
                self.ORDER,
                self.BTCUSD_ORDERBOOK,
                self.ETHUSD_ORDERBOOK,
                self.EOSUSD_ORDERBOOK,
                self.XRPUSD_ORDERBOOK
            ]}))

    def __on_close(self, ws):
        raise ConnectionError("Connection Closed Unexpectedly.")

    # noinspection PyUnresolvedReferences
    def __save_orderbook(self, type, message):
        if message['type'] == 'snapshot':
            self.ws_message_data[type] = pd.io.json.json_normalize(message['data']).set_index('id').sort_index(
                ascending=False)
            self._save_orderbook_snapshot(self.ws_message_data[type])
        elif message['type'] == 'delta':
            if len(message['data']['delete']) != 0:
                for x in message['data']['delete']:
                    try:
                        self.ws_message_data[type].drop(index=x['id'])
                    except KeyError:
                        print(colored("%s:INFO: Duplicate orderbookl2_25 delete" % (__name__), "blue"))
                    self._save_orderbook_delta_delete(x)
            if len(message['data']['update']) != 0:
                update_list = pd.io.json.json_normalize(message['data']['update']).set_index('id')
                self.ws_message_data[type].update(update_list)
                self.ws_message_data[type] = self.ws_message_data[type].sort_index(ascending=False)
                for x in message['data']['update']:
                    self._save_orderbook_delta_update(x)
            if len(message['data']['insert']) != 0:
                insert_list = pd.io.json.json_normalize(message['data']['insert']).set_index('id')
                self.ws_message_data[type].update(insert_list)
                self.ws_message_data[type] = self.ws_message_data[type].sort_index(ascending=False)
                for x in message['data']['insert']:
                    self._save_orderbook_delta_insert(x)

    # noinspection PyUnresolvedReferences
    def __on_message(self, ws, message):
        message = json.loads(message)
        if "topic" not in message:
            if message.get("ret_msg") == "pong":
                print(colored("%s:INFO: Heart Beat Message Received at UTC time %s" % (
                    str(__name__), datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M:%S')),
                              "magenta"))
            return
        topic = message.get('topic')
        self.__hb_count = self.__hb_count + 1
        if self.__hb_count >= 5:
            ws.send(json.dumps({"op": "ping"}))
            self.__hb_count = 0
        else:
            """we don't need to send the heart beat so frequently. """

        print(colored("%s:INFO: New websocket message for topic %s received at UTC time %s" % (
            str(__name__),
            str(topic),
            datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M:%S')), "blue"))

        if topic == self.BTCUSD_KLINE_1M:
            self.ws_message_data[self.BTCUSD_KLINE_1M] = message
        elif topic == self.ETHUSD_KLINE_1M:
            self.ws_message_data[self.ETHUSD_KLINE_1M] = message
        elif topic == self.XRPUSD_KLINE_1M:
            self.ws_message_data[self.XRPUSD_KLINE_1M] = message
        elif topic == self.EOSUSD_KLINE_1M:
            self.ws_message_data[self.EOSUSD_KLINE_1M] = message
        elif topic == self.POSITION:
            self.ws_message_data[self.POSITION] = message
            self._save_position(self.ws_message_data[ByBitWebsocketConnection.POSITION])
        elif topic == self.EXECUTION:
            self.ws_message_data[self.EXECUTION] = message
            self._save_fill_history(self.ws_message_data[ByBitWebsocketConnection.EXECUTION])
        elif topic == self.ORDER:
            self.ws_message_data[self.ORDER] = message
            self._save_order(self.ws_message_data[ByBitWebsocketConnection.ORDER])
        elif topic == self.BTCUSD_ORDERBOOK:
            self.__save_orderbook(self.BTCUSD_ORDERBOOK, message)
        elif topic == self.ETHUSD_ORDERBOOK:
            self.__save_orderbook(self.ETHUSD_ORDERBOOK, message)
        elif topic == self.EOSUSD_ORDERBOOK:
            self.__save_orderbook(self.EOSUSD_ORDERBOOK, message)
        elif topic == self.XRPUSD_ORDERBOOK:
            self.__save_orderbook(self.XRPUSD_ORDERBOOK, message)
Пример #33
0
class BinaryAccount(mt_helper.MultiThreadWSHelper):
    '''
        Make all operations with binary.com account by given apiToken
        You can:
            - read portfolio
            - read statement
            - buy/sell contracts

        all methods can be called from different threads.

        Each request has unique ID for internal usage.
        And each response has the same ID.
    '''
    def __init__(self, apiToken):

        super().__init__()

        self.apiToken = apiToken
        self.url = bin_api.get_binary_url()
        self.app = WebSocketApp(
            url=self.url,
            on_open=lambda ws: self.on_app_open(ws),
            on_close=lambda ws: self.on_app_close(ws),
            on_message=lambda ws, msg: self.on_app_msg(ws, msg),
            on_error=lambda ws, err: self.on_app_error(ws, err),
            on_ping=lambda ws: self.on_app_ping(ws))

        self.authorized = False
        '''
            If connection closes by any reason, BinaryAccount will create new application.
            If the app is closed and reconnection is not required, it won't.
        '''
        self.reconnect = True

    def send_request(self, request):
        '''
        Sends request considering sharing between threads.
        Ideally it should be placed into helper,
        but app (WebSocketApp) defined here
        '''

        with self.ws_lock:
            self.app.send(request)

    def open_app(self):
        Thread(target=self.app.run_forever).start()
        while not self.authorized:
            pass

    def close_app(self):
        del self.responses
        self.reconnect = False
        self.app.close()

    # Websockets methods:

    def on_app_open(self, ws):

        # Authorize on open
        curID = self.get_next_req_id()
        curEvent = Event()
        self.add_event(curID, curEvent)
        self.send_request(
            bin_api.get_authorize_json(self.apiToken, req_id=curID))

    def on_app_msg(self, ws, msg):

        try:
            resp = json.loads(msg)
            self.add_response(resp)

            if 'authorize' in resp:
                self.authorized = True

            id_ = resp['req_id']
            if self.in_events(id_):
                self.set_event(id_)

        except:
            ex_type, ex_val, ex_tb = sys.exc_info()
            logging.error(
                str(ex_type) + '\n' + '\n'.join(traceback.format_tb(ex_tb)))

        ready = True  # line for debug

    def on_app_close(self, ws):

        if self.reconnect:
            self.app = WebSocketApp(
                url=self.url,
                on_open=lambda ws: self.on_app_open(ws),
                on_close=lambda ws: self.on_app_close(ws),
                on_message=lambda ws, msg: self.on_app_msg(ws, msg),
                on_error=lambda ws, err: self.on_app_error(ws, err),
                on_ping=lambda ws: self.on_app_ping(ws))
            Thread(target=self.app.run_forever).start()

    def on_app_error(self, ws, error):
        pass

    def on_app_ping(self, ws):
        pass

    # Account manipulation methods

    def get_balance(self):

        try:
            curID = self.get_next_req_id()
            curEvent = Event()
            self.add_event(curID, curEvent)
            self.send_request(bin_api.get_balance_json(req_id=curID))

            curEvent.wait()

            response = self.get_response(curID)

            return response['balance']['balance']

        except:
            ex_type, ex_val, ex_tb = sys.exc_info()
            logging.error(
                str(ex_type) + '\n' + '\n'.join(traceback.format_tb(ex_tb)))

    def get_portfolio(self):
        '''
        Returns current opened positions as list of dict
        Each dict has keys:
            - price
            - payout
            - contract_id
            - contract_type
            - date_start
            - date_end
            - description
            - symbol
        :return: list
        '''

        try:

            curID = self.get_next_req_id()
            curEvent = Event()
            self.add_event(curID, curEvent)
            self.send_request(bin_api.get_portfolio_json(req_id=curID))

            curEvent.wait()

            response = self.get_response(curID)

            return [{
                'price': r['buy_price'],
                'payout': r['payout'],
                'contract_id': r['contract_id'],
                'contract_type': r['contract_type'],
                'date_start': r['date_start'],
                'date_end': r['expiry_time'],
                'description': r['longcode'],
                'symbol': r['symbol']
            } for r in response['portfolio']['contracts']]

        except:
            ex_type, ex_val, ex_tb = sys.exc_info()
            logging.error(
                str(ex_type) + '\n' + '\n'.join(traceback.format_tb(ex_tb)))

    def get_login_history(self, limit=25):
        '''
        Returns list of string with description of login
        :param limit: max number of login notes
        :return: list
        '''

        try:

            curID = self.get_next_req_id()
            curEvent = Event()
            self.add_event(curID, curEvent)
            self.send_request(bin_api.get_login_history_json(req_id=curID))

            curEvent.wait()

            response = self.get_response(curID)

            return [r['environment'] for r in response['login_history']]

        except:
            ex_type, ex_val, ex_tb = sys.exc_info()
            logging.error(
                str(ex_type) + '\n' + '\n'.join(traceback.format_tb(ex_tb)))

    def get_profit_table(self, limit=10, date_from=None, date_to=None):
        '''
        Returns list of dict.
        Keys:
            - price
            - potential_payout
            - sell_price
            - contract_id
            - description
            - purchase_time
            - sell_time
        :param limit:
        :param date_from: int
        :param date_to: int
        :return:
        '''

        try:

            curID = self.get_next_req_id()
            curEvent = Event()
            self.add_event(curID, curEvent)

            self.send_request(
                bin_api.get_profit_table_json(limit=limit,
                                              date_from=date_from,
                                              date_to=date_to,
                                              req_id=curID))
            curEvent.wait()

            response = self.get_response(curID)

            return [{
                'price': r['buy_price'],
                'potential_payout': r['payout'],
                'sell_price': r['sell_price'],
                'contract_id': r['contract_id'],
                'purchase_time': r['purchase_time'],
                'sell_time': r['sell_time'],
                'description': r['longcode']
            } for r in response['profit_table']['transactions']]

        except:
            ex_type, ex_val, ex_tb = sys.exc_info()
            logging.error(
                str(ex_type) + '\n' + '\n'.join(traceback.format_tb(ex_tb)))

    def sell_contract(self, contract_id, price=0):
        '''
        Sells specified contract and shows the result as dict
        Keys:
            - balance_after
            - sold_for
            OR
            - error : err_msg
        :param contract_id:
        :param price:
        :return:
        '''

        try:

            curID = self.get_next_req_id()
            curEvent = Event()
            self.add_event(curID, curEvent)
            self.send_request(
                bin_api.get_sell_contract_json(contract_id,
                                               price,
                                               req_id=curID))

            curEvent.wait()

            response = self.get_response(curID)

            if not 'error' in response:
                return {
                    'balance_after': response['sell']['balance_after'],
                    'sold_for': response['sell']['sold_for']
                }
            else:
                return {'error': response['error']['message']}
        except:
            ex_type, ex_val, ex_tb = sys.exc_info()
            logging.error(
                str(ex_type) + '\n' + '\n'.join(traceback.format_tb(ex_tb)))

    def buy_contract(self,
                     proposal_id=None,
                     amount=1,
                     type='CALL',
                     duration=15,
                     duration_unit='m',
                     symbol='frxEURUSD'):
        '''
        Buys contract by proposal_id (the object where all parameters were already passed)
        or by given parameters.
        To use custom parameters proposal_id HAVE TO BE None

        Returns dict with details
        Keys:
            - balance_after
            - buy_price
            - contract_id
            - description
            - payout
            - start_time
        :return: dict
        '''

        try:
            curID = self.get_next_req_id()
            curEvent = Event()
            self.add_event(curID, curEvent)

            if not proposal_id is None:
                self.send_request(
                    bin_api.get_buy_contract_json(proposal_id=proposal_id,
                                                  price=amount,
                                                  proposal_parameters=None,
                                                  req_id=curID))
            else:
                proposal_parameters = bin_api.get_price_proposal_dict(
                    amount=amount,
                    type=type,
                    duration=duration,
                    duration_unit=duration_unit,
                    symbol=symbol)
                self.send_request(
                    bin_api.get_buy_contract_json(
                        proposal_id=1,
                        proposal_parameters=proposal_parameters,
                        price=amount,
                        req_id=curID))

            curEvent.wait()

            response = self.get_response(curID)

            if 'error' in response:
                return {'error': response['error']['message']}
            else:
                return {
                    'balance_after': response['buy']['balance_after'],
                    'buy_price': response['buy']['buy_price'],
                    'contract_id': response['buy']['contract_id'],
                    'description': response['buy']['longcode'],
                    'payout': response['buy']['payout'],
                    'start_time': response['buy']['start_time']
                }
        except:
            ex_type, ex_val, ex_tb = sys.exc_info()
            logging.error(
                str(ex_type) + '\n' + '\n'.join(traceback.format_tb(ex_tb)))
            print(ex_val)

    def get_price_proposal(self, contract_id, subscribe=0):
        '''
        Returns information about opened position as dict
        Keys:
            - buy_price
            - current_spot
            - entry_spot
            - contract_type
            - symbol
            - is_valid_to_sell
            - profit
            - profit_percentage
            - contract_id
            - description

            - subscription_id
            - req_id

        Last two keys are only available for subscriptions.
        This allows to forget subscription and then remove event
        :return: dict
        '''

        curID = self.get_next_req_id()
        curEvent = Event()
        self.add_event(curID, curEvent)

        self.send_request(
            bin_api.get_contract_proposal_json(contract_id=contract_id,
                                               subscribe=subscribe,
                                               req_id=curID))

        if subscribe == 1:

            saved_response = None

            while True:

                # if subscription had been removed
                if not self.in_events(curID):
                    return

                curEvent.wait(10)
                response = self.get_response(curID)
                resp = response['proposal_open_contract']

                curEvent.clear()

                if response != saved_response:
                    saved_response = response
                    yield {
                        'buy_price': resp['buy_price'],
                        'current_spot': resp['current_spot'],
                        'entry_spot': resp['entry_spot'],
                        'contract_type': resp['contract_type'],
                        'symbol': resp['display_name'],
                        'is_valid_to_sell': resp['is_valid_to_sell'],
                        'profit': resp['profit'],
                        'profit_percentage': resp['profit_percentage'],
                        'contract_id': resp['contract_id'],
                        'description': resp['longcode'],
                        'subscription_id': response['subscription']['id'],
                        'req_id': curID
                    }

        else:

            curEvent.wait()

            response = self.get_response(curID)
            resp = response['proposal_open_contract']

            return {
                'buy_price': resp['buy_price'],
                'current_spot': resp['current_spot'],
                'entry_spot': resp['entry_spot'],
                'contract_type': resp['contract_type'],
                'symbol': resp['display_name'],
                'is_valid_to_sell': resp['is_valid_to_sell'],
                'profit': resp['profit'],
                'profit_percentage': resp['profit_percentage'],
                'contract_id': resp['contract_id'],
                'description': resp['longcode']
            }

    def forget_subscription(self, subscription_id, event_to_remove=None):
        '''
        Makes request to stop receive events by subscription
        :return:
        '''

        curID = self.get_next_req_id()
        curEvent = Event()
        self.add_event(curID, curEvent)
        self.send_request(
            bin_api.get_forget_stream_json(subscription_id, req_id=curID))

        curEvent.wait()

        if not event_to_remove is None:
            self.remove_event(event_to_remove)
Пример #34
0
 def _send(self, ws: WebSocketApp, msg: dict):
     msg['reqid'] = self._req_id
     ws.send(json.dumps(msg))
     self._req_id += 1
Пример #35
0
class MidiHandler:
    # Initializes the handler class
    def __init__(self,
                 config_path="config.json",
                 ws_server="localhost",
                 ws_port=4444):
        # Setting up logging first and foremost
        self.log = get_logger("midi_to_obs")

        # Internal service variables
        self._action_buffer = []
        self._action_counter = 2

        self.log.debug("Trying to load config file  from %s" % config_path)
        self.db = TinyDB(config_path, indent=4)
        result = self.db.search(Query().type.exists())
        if not result:
            self.log.critical("Config file %s doesn't exist or is damaged" %
                              config_path)
            # ENOENT (No such file or directory)
            exit(2)

        self.log.info("Successfully parsed config file")
        port_name = str(result[0]["value"])

        self.log.debug("Retrieved MIDI port name `%s`" % port_name)
        del result

        try:
            self.log.debug("Attempting to open midi port")
            self.port = mido.open_input(name=port_name,
                                        callback=self.handle_midi_input)
        except:
            self.log.critical(
                "The midi device %s is not connected or has a different name" %
                port_name)
            self.log.critical(
                "Please plug the device in or run setup.py again and restart this script"
            )
            # EIO 5 (Input/output error)
            exit(5)

        self.log.info("Successfully initialized midi port `%s`" % port_name)
        del port_name

        # Properly setting up a Websocket client
        self.log.debug("Attempting to connect to OBS using websocket protocol")
        self.obs_socket = WebSocketApp("ws://%s:%d" % (ws_server, ws_port))
        self.obs_socket.on_message = self.handle_obs_message
        self.obs_socket.on_error = self.handle_obs_error
        self.obs_socket.on_close = self.handle_obs_close
        self.obs_socket.on_open = self.handle_obs_open

    def handle_midi_input(self, message):
        self.log.debug("Received %s message from midi: %s" %
                       (message.type, message))

        if message.type == "note_on":
            return self.handle_midi_button(message.type, message.note)

        # `program_change` messages can be only used as regular buttons since
        # they have no extra value, unlike faders (`control_change`)
        if message.type == "program_change":
            return self.handle_midi_button(message.type, message.program)

        if message.type == "control_change":
            return self.handle_midi_fader(message.control, message.value)

    def handle_midi_button(self, type, note):
        query = Query()
        results = self.db.search((query.msg_type == type)
                                 & (query.msgNoC == note))

        if not results:
            self.log.debug("Cound not find action for note %s", note)
            return

        for result in results:
            if self.send_action(result):
                break

    def handle_midi_fader(self, control, value):
        query = Query()
        results = self.db.search((query.msg_type == "control_change")
                                 & (query.msgNoC == control))

        if not results:
            self.log.debug("Cound not find action for fader %s", control)
            return

        for result in results:
            input_type = result["input_type"]
            action = result["action"]

            if input_type == "button":
                if value == 127 and not self.send_action(result):
                    continue
                break

            if input_type == "fader":
                command = result["cmd"]
                scaled = map_scale(value, 0, 127, result["scale_low"],
                                   result["scale_high"])

                if command == "SetSourcePosition" or command == "SetSourceScale":
                    self.obs_socket.send(action % scaled)
                    break

                # Super dirty hack but @AlexDash says that it works
                # @TODO: find an explanation _why_ it works
                if command == "SetVolume":
                    # Yes, this literally raises a float to a third degree
                    self.obs_socket.send(action % scaled**3)
                    break

                if command == "SetSourceRotation" or command == "SetTransitionDuration" or command == "SetSyncOffset":
                    self.obs_socket.send(action % int(scaled))
                    break

    def handle_obs_message(self, message):
        self.log.debug("Received new message from OBS")
        payload = json.loads(message)

        self.log.debug("Successfully parsed new message from OBS")

        if "error" in payload:
            self.log.error("OBS returned error: %s" % payload["error"])
            return

        message_id = payload["message-id"]

        self.log.debug("Looking for action with message id `%s`" % message_id)
        for action in self._action_buffer:
            (buffered_id, template, kind) = action

            if buffered_id != int(payload["message-id"]):
                continue

            del buffered_id
            self.log.info("Action `%s` was requested by OBS" % kind)

            if kind == "ToggleSourceVisibility":
                # Dear lain, I so miss decent ternary operators...
                invisible = "false" if payload["visible"] else "true"
                self.obs_socket.send(template % invisible)
            elif kind == "ReloadBrowserSource":
                source = payload["sourceSettings"]["url"]
                target = source[0:-1] if source[-1] == '#' else source + '#'
                self.obs_socket.send(template % target)

            self.log.debug("Removing action with message id %s from buffer" %
                           message_id)
            self._action_buffer.remove(action)
            break

    def handle_obs_error(self, ws, error=None):
        # Protection against potential inconsistencies in `inspect.ismethod`
        if error is None and isinstance(ws, BaseException):
            error = ws

        if isinstance(error, (KeyboardInterrupt, SystemExit)):
            self.log.info("Keyboard interrupt received, gracefully exiting...")
            self.close(teardown=True)
        else:
            self.log.error("Websocket error: %" % str(error))

    def handle_obs_close(self, ws):
        self.log.error("OBS has disconnected, timed out or isn't running")
        self.log.error("Please reopen OBS and restart the script")

    def handle_obs_open(self, ws):
        self.log.info("Successfully connected to OBS")

    def send_action(self, action_request):
        action = action_request.get("action")
        if not action:
            # @NOTE: this potentionally should never happen but you never know
            self.log.error("No action supplied in current request")
            return False

        request = action_request.get("request")
        if not request:
            self.log.debug("No request body for action %s, sending action" %
                           action)
            self.obs_socket.send(action)
            # Success, breaking the loop
            return True

        template = TEMPLATES.get(request)
        if not template:
            self.log.error("Missing template for request %s" % request)
            # Keep searching
            return False

        target = action_request.get("target")
        if not target:
            self.log.error("Missing target in %s request for %s action" %
                           (request, action))
            # Keep searching
            return False

        self._action_buffer.append([self._action_counter, action, request])
        self.obs_socket.send(template % (self._action_counter, target))
        self._action_counter += 1

        # Explicit return is necessary here to avoid extra searching
        return True

    def start(self):
        self.log.info("Connecting to OBS...")
        self.obs_socket.run_forever()

    def close(self, teardown=False):
        self.log.debug("Attempting to close midi port")
        self.port.close()

        self.log.info("Midi connection has been closed successfully")

        # If close is requested during keyboard interrupt, let the websocket
        # client tear itself down and make a clean exit
        if not teardown:
            self.log.debug("Attempting to close OBS connection")
            self.obs_socket.close()

            self.log.info("OBS connection has been closed successfully")

        self.log.debug("Attempting to close TinyDB instance on config file")
        self.db.close()

        self.log.info("Config file has been successfully released")

    def __end__(self):
        self.log.info("Exiting script...")
        self.close()
Пример #36
0
        def hndl(ws: WebSocketApp, msg):
            if not msg:
                # Reply back with an empty message when the server sends an empty message
                ws.send('')
                return

            # noinspection PyBroadException
            try:
                msg = json.loads(msg)
            except:
                self.logger.warning(
                    'Received invalid JSON message from Trello: {}'.format(
                        msg))
                return

            if 'error' in msg:
                self.logger.warning('Trello error: {}'.format(msg['error']))
                return

            if msg.get('reqid') == 0:
                self.logger.debug('Ping response received, subscribing boards')
                self._initialize_connection(ws)
                return

            notify = msg.get('notify')
            if not notify:
                return

            if notify['event'] != 'updateModels' or notify[
                    'typeName'] != 'Action':
                return

            for delta in notify['deltas']:
                args = {
                    'card_id':
                    delta['data']['card']['id'],
                    'card_name':
                    delta['data']['card']['name'],
                    'list_id':
                    (delta['data'].get('list')
                     or delta['data'].get('listAfter', {})).get('id'),
                    'list_name':
                    (delta['data'].get('list')
                     or delta['data'].get('listAfter', {})).get('name'),
                    'board_id':
                    delta['data']['board']['id'],
                    'board_name':
                    delta['data']['board']['name'],
                    'closed':
                    delta.get('closed'),
                    'member_id':
                    delta['memberCreator']['id'],
                    'member_username':
                    delta['memberCreator']['username'],
                    'member_fullname':
                    delta['memberCreator']['fullName'],
                    'date':
                    delta['date'],
                }

                if delta.get('type') == 'createCard':
                    self.bus.post(NewCardEvent(**args))
                elif delta.get('type') == 'updateCard':
                    if 'listBefore' in delta['data']:
                        args.update({
                            'old_list_id':
                            delta['data']['listBefore']['id'],
                            'old_list_name':
                            delta['data']['listBefore']['name'],
                        })

                        self.bus.post(MoveCardEvent(**args))
                    elif 'closed' in delta['data'].get('old', {}):
                        cls = UnarchivedCardEvent if delta['data']['old'][
                            'closed'] else ArchivedCardEvent
                        self.bus.post(cls(**args))
Пример #37
0
class WebsocketClient(Thread):
    _id = 0

    # type: int

    def __init__(
        self,
        session,
        # type: Streamlink
        url,
        # type: str
        subprotocols=None,
        # type: Optional[List[str]]
        header=None,
        # type: Optional[Union[List, Dict]]
        cookie=None,
        # type: Optional[str]
        sockopt=None,
        # type: Optional[Tuple]
        sslopt=None,
        # type: Optional[Dict]
        host=None,
        # type: Optional[str]
        origin=None,
        # type: Optional[str]
        suppress_origin=False,
        # type: bool
        ping_interval=0,
        # type: Union[int, float]
        ping_timeout=None,
        # type: Optional[Union[int, float]]
    ):
        if rootlogger.level <= TRACE:
            enableTrace(True, log)

        if not header:
            header = []
        if not any(True for h in header if h.startswith("User-Agent: ")):
            header.append("User-Agent: {0}".format(
                session.http.headers['User-Agent']))

        proxy_options = {}
        http_proxy = session.get_option("http-proxy")
        # type: Optional[str]
        if http_proxy:
            p = urlparse(http_proxy)
            proxy_options["proxy_type"] = p.scheme
            proxy_options["http_proxy_host"] = p.hostname
            if p.port:  # pragma: no branch
                proxy_options["http_proxy_port"] = p.port
            if p.username:  # pragma: no branch
                proxy_options["http_proxy_auth"] = unquote_plus(
                    p.username), unquote_plus(p.password or "")

        self._reconnect = False
        self._reconnect_lock = RLock()

        self.session = session
        self._ws_init(url, subprotocols, header, cookie)
        self._ws_rundata = dict(sockopt=sockopt,
                                sslopt=sslopt,
                                host=host,
                                origin=origin,
                                suppress_origin=suppress_origin,
                                ping_interval=ping_interval,
                                ping_timeout=ping_timeout,
                                **proxy_options)

        self._id += 1
        super(WebsocketClient, self).__init__(
            name="Thread-{0}-{1}".format(self.__class__.__name__, self._id))
        self.daemon = True

    def _ws_init(self, url, subprotocols, header, cookie):
        self.ws = WebSocketApp(url=url,
                               subprotocols=subprotocols,
                               header=header,
                               cookie=cookie,
                               on_open=self.on_open,
                               on_error=self.on_error,
                               on_close=self.on_close,
                               on_ping=self.on_ping,
                               on_pong=self.on_pong,
                               on_message=self.on_message,
                               on_cont_message=self.on_cont_message,
                               on_data=self.on_data)

    def run(self):
        # type: () -> None
        while True:
            log.debug("Connecting to: {0}".format(self.ws.url))
            self.ws.run_forever(**self._ws_rundata)
            # check if closed via a reconnect() call
            with self._reconnect_lock:
                if not self._reconnect:
                    return
                self._reconnect = False

    # ----

    def reconnect(self,
                  url=None,
                  subprotocols=None,
                  header=None,
                  cookie=None,
                  closeopts=None):
        # type: (str, Optional[List[str]], Optional[Union[List, Dict]], Optional[str], Optional[Dict]) -> None
        with self._reconnect_lock:
            # ws connection is not active (anymore)
            if not self.ws.keep_running:
                return
            log.debug("Reconnecting...")
            self._reconnect = True
            self.ws.close(**(closeopts or {}))
            self._ws_init(url=self.ws.url if url is None else url,
                          subprotocols=self.ws.subprotocols
                          if subprotocols is None else subprotocols,
                          header=self.ws.header if header is None else header,
                          cookie=self.ws.cookie if cookie is None else cookie)

    def close(self, status=STATUS_NORMAL, reason="", timeout=3):
        # type: (int, Union[str, bytes], int) -> None
        if is_py2:
            self.ws.close(status=status, reason=bytes(reason), timeout=timeout)
        else:
            self.ws.close(status=status,
                          reason=bytes(reason, encoding="utf-8"),
                          timeout=timeout)
        if self.is_alive():  # pragma: no branch
            self.join()

    def send(self, data, opcode=ABNF.OPCODE_TEXT):
        # type: (Union[str, bytes], int) -> None
        return self.ws.send(data, opcode)

    def send_json(self, data):
        # type: (Any) -> None
        return self.send(json.dumps(data, indent=None, separators=(",", ":")))

    # ----

    # noinspection PyMethodMayBeStatic
    def on_open(self, wsapp):
        # type: (WebSocketApp) -> None
        log.debug("Connected: {0}".format(wsapp.url))  # pragma: no cover

    # noinspection PyMethodMayBeStatic
    # noinspection PyUnusedLocal
    def on_error(self, wsapp, error):
        # type: (WebSocketApp, Exception) -> None
        log.error(error)  # pragma: no cover

    # noinspection PyMethodMayBeStatic
    # noinspection PyUnusedLocal
    def on_close(self, wsapp, status, message):
        # type: (WebSocketApp, int, str)
        log.debug("Closed: {0}".format(wsapp.url))  # pragma: no cover

    def on_ping(self, wsapp, data):
        # type: (WebSocketApp, str) -> None
        pass  # pragma: no cover

    def on_pong(self, wsapp, data):
        # type: (WebSocketApp, str) -> None
        pass  # pragma: no cover

    def on_message(self, wsapp, data):
        # type: (WebSocketApp, str) -> None
        pass  # pragma: no cover

    def on_cont_message(self, wsapp, data, cont):
        # type: (WebSocketApp, str, Any) -> None
        pass  # pragma: no cover

    def on_data(self, wsapp, data, data_type, cont):
        # type: (WebSocketApp, str, int, Any) -> None
        pass  # pragma: no cover
Пример #38
0
class WebSocket:
    def __init__(self,
                 url,
                 header=None,
                 keep_running=True,
                 get_mask_key=None,
                 cookie=None,
                 subprotocols=None):
        self.url = url
        self.ws = WebSocketApp(url,
                               header=header,
                               keep_running=keep_running,
                               get_mask_key=get_mask_key,
                               cookie=cookie,
                               subprotocols=subprotocols)

        # Create streams
        self.rx_on_close = Subject()
        self.rx_on_data = Subject()
        self.rx_on_error = Subject()
        self.rx_on_open = Subject()
        self.rx_on_message = Subject()
        self.rx_on_count_message = Subject()
        self.rx_on_ping = Subject()
        self.rx_on_pong = Subject()

        # Assign callbacks to streams
        self.ws.on_close = lambda x: self.rx_on_close.on_next(x)
        self.ws.on_data = lambda x, s, t, f: self.rx_on_data.on_next(
            (x, s, t, f))
        self.ws.on_error = lambda x, e: self.rx_on_error.on_next((x, e))
        self.ws.on_open = lambda x: self.rx_on_open.on_next(x)
        self.ws.on_message = lambda x, s: self.rx_on_message.on_next((x, s))
        self.ws.on_count_message = lambda x, s, f: self.rx_on_count_message.on_next(
            (x, s, f))
        self.ws.on_ping = lambda: self.rx_on_ping.on_next(None)
        self.ws.on_ping = lambda: self.rx_on_ping.on_next(None)

    def run_forever(self,
                    sockopt=None,
                    sslopt=None,
                    ping_interval=0,
                    ping_timeout=None,
                    http_proxy_host=None,
                    http_proxy_port=None,
                    http_no_proxy=None,
                    http_proxy_auth=None,
                    skip_utf8_validation=False,
                    host=None,
                    origin=None,
                    dispatcher=None,
                    suppress_origin=False,
                    proxy_type=None):
        self.ws.run_forever(sockopt=sockopt,
                            sslopt=sslopt,
                            ping_interval=ping_interval,
                            ping_timeout=ping_timeout,
                            http_proxy_host=http_proxy_host,
                            http_proxy_port=http_proxy_port,
                            http_no_proxy=http_no_proxy,
                            http_proxy_auth=http_proxy_auth,
                            skip_utf8_validation=skip_utf8_validation,
                            host=host,
                            origin=origin,
                            dispatcher=dispatcher,
                            suppress_origin=suppress_origin,
                            proxy_type=proxy_type)

    def close(self):
        self.ws.close()

    def send(self, data, opcode=ABNF.OPCODE_TEXT):
        self.ws.send(data, opcode)