Beispiel #1
0
async def init_event_stream (
        event_stream : AsyncIOEventEmitter,
        puppet       : PuppetStub,
) -> None:
    """doc"""
    async for response in puppet.event():
        # print(response)
        if response is not None:
            event_type = EventType(response.type).name
            payload = response.payload
            # print(event_type, payload)
            event_stream.emit(event_type, payload)
Beispiel #2
0
        def peerconn_oniceconnectionstatechange():
            state_change = AsyncIOEventEmitter()

            @state_change.once("failed")
            async def on_failed(error=None):
                log.warning(
                    "\n IceConnectionState failed with error %s "
                    "\n Closing connections to peer id %s", error, peerId)
                self.connection.emit(
                    ConnectionEventType.Error,
                    ConnectionError(
                        f"Negotiation of connection to {peerId} failed."))
                await self.connection.close()

            @state_change.once("closed")
            def on_closed():
                log.info("iceConnectionState is closed, "
                         f"closing connections to {peerId}")
                self.connection.emit(
                    ConnectionEventType.Error,
                    ConnectionError(f"Connection to {peerId} closed."))
                self.connection.close()

            @state_change.once("disconnected")
            def on_disconnected():
                log.info("iceConnectionState is disconnected, "
                         f"closing connections to {peerId}")
                self.connection.emit(
                    ConnectionEventType.Error,
                    ConnectionError(f"Connection to {peerId} disconnected."))
                self.connection.close()

            @state_change.once("completed")
            def on_completed():
                log.debug('iceConnectionState completed for peer id %s',
                          peerId)
                # this function needs to be implemented as in PeerJS
                # https://github.com/peers/peerjs/blob/5e36ba17be02a9af7c171399f7737d8ccb38fbbe/lib/negotiator.ts#L119
                # when the "icecandidate"
                # event handling issue above is resolved
                # peerConnection.remove_listener("icecandidate",
                #       peerconn_onicecandidate)
                pass

            log.debug('iceConnectionState event: %s',
                      peerConnection.iceConnectionState)
            # forward connection stage change event to local handlers
            state_change.emit(peerConnection.iceConnectionState)
            # notify higher level connection listeners
            self.connection.emit(ConnectionEventType.IceStateChanged,
                                 peerConnection.iceConnectionState)
Beispiel #3
0
async def test_asyncio_once_emit(event_loop):
    """Test that EventEmitters also wrap coroutines when using once
    """

    ee = AsyncIOEventEmitter(loop=event_loop)

    should_call = Future(loop=event_loop)

    @ee.once('event')
    async def event_handler():
        should_call.set_result(True)

    ee.emit('event')

    result = await wait_for(should_call, 0.1)

    assert result == True
Beispiel #4
0
async def test_asyncio_emit(event_loop):
    """Test that AsyncIOEventEmitter can handle wrapping
    coroutines
    """

    ee = AsyncIOEventEmitter(loop=event_loop)

    should_call = Future(loop=event_loop)

    @ee.on('event')
    async def event_handler():
        should_call.set_result(True)

    ee.emit('event')

    result = await wait_for(should_call, 0.1)

    assert result is True
Beispiel #5
0
async def test_sync_error(event_loop):
    """Test that regular functions have the same error handling as coroutines
    """
    ee = AsyncIOEventEmitter(loop=event_loop)

    should_call = Future(loop=event_loop)

    @ee.on('event')
    def sync_handler():
        raise PyeeTestError()

    @ee.on('error')
    def handle_error(exc):
        should_call.set_result(exc)

    ee.emit('event')

    result = await wait_for(should_call, 0.1)

    assert isinstance(result, PyeeTestError)
Beispiel #6
0
async def test_sync_error(event_loop):
    """Test that regular functions have the same error handling as coroutines
    """
    ee = AsyncIOEventEmitter(loop=event_loop)

    should_call = Future(loop=event_loop)

    @ee.on('event')
    def sync_handler():
        raise PyeeTestError()

    @ee.on('error')
    def handle_error(exc):
        should_call.set_result(exc)

    ee.emit('event')

    result = await wait_for(should_call, 0.1)

    assert isinstance(result, PyeeTestError)
Beispiel #7
0
class MockWebsocket():
    def __init__(self):
        self.events = AsyncIOEventEmitter()
        self.saved_items = []
        self.emitted_items = []

    def on(self, *args, **kwargs):
        self.events.on(*args, **kwargs)

    def once(self, *args, **kwargs):
        self.events.once(*args, **kwargs)

    def _emit(self, event, *args, **kwargs):
        # save published items for testing
        self.emitted_items += [{
            'time': int(round(time.time() * 1000)),
            'data': {
                'event': event,
                'args': args,
                'kwargs': kwargs
            }
        }]
        self.events.emit(event, *args, **kwargs)

    def remove_all_listeners(self, *args, **kwargs):
        self.events.remove_all_listeners(*args, **kwargs)

    def cancel_order(self, *args, **kawargs):
        pass

    def submit_order(self, *args, **kawargs):
        pass

    def get_emitted_items(self):
        return self.emitted_items

    def get_last_emitted_item(self):
        return self.emitted_items[-1:][0]

    def get_emitted_items_count(self):
        return len(self.emitted_items)
Beispiel #8
0
async def test_asyncio_error(event_loop):
    """Test that AsyncIOEventEmitter can handle errors when
    wrapping coroutines
    """
    ee = AsyncIOEventEmitter(loop=event_loop)

    should_call = Future(loop=event_loop)

    @ee.on('event')
    async def event_handler():
        raise PyeeTestError()

    @ee.on('error')
    def handle_error(exc):
        should_call.set_result(exc)

    ee.emit('event')

    result = await wait_for(should_call, 0.1)

    assert isinstance(result, PyeeTestError)
Beispiel #9
0
async def test_asyncio_cancellation(event_loop):
    """Test that AsyncIOEventEmitter can handle Future cancellations"""

    cancel_me = Future(loop=event_loop)
    should_not_call = Future(loop=event_loop)

    ee = AsyncIOEventEmitter(loop=event_loop)

    @ee.on('event')
    async def event_handler():
        cancel_me.cancel()

    @ee.on('error')
    def handle_error(exc):
        should_not_call.set_result(None)

    ee.emit('event')

    try:
        await wait_for(should_not_call, 0.1)
    except TimeoutError:
        pass
    else:
        raise PyeeTestError()
async def test_event(emitter: AsyncIOEventEmitter):
    async def stream_event(data: str):
        assert data == '1'

    emitter.on('stream', stream_event)
    emitter.emit('stream', '2')
Beispiel #11
0
class PuppetMock(Puppet):
    """mock for puppet"""
    def __init__(self, options: PuppetMockOptions, name: str = 'puppet-mock'):
        super().__init__(options, name)

        if not options.mocker:
            raise WechatyPuppetMockError('mocker in options is required')
        self.mocker: Mocker = options.mocker

        self.started: bool = False
        self.emitter = AsyncIOEventEmitter()

    async def message_image(self, message_id: str,
                            image_type: ImageType) -> FileBox:
        """get image from message"""

    async def ding(self, data: Optional[str] = None):
        pass

    def on(self, event_name: str, caller):
        """listen event"""
        self.emitter.on(event_name, caller)

    def listener_count(self, event_name: str) -> int:
        """get the event count of the specific event"""
        listeners = self.emitter.listeners(event=event_name)
        return len(listeners)

    async def start(self) -> None:
        """star the account"""
        self.started = True
        if not self.mocker:
            raise WechatyPuppetMockError(
                'PuppetMock should not start without mocker'
            )

        def _emit_events(response: MockerResponse):
            """emit the events from the mocker"""
            payload_data = json.loads(response.payload)

            if response.type == int(EventType.EVENT_TYPE_MESSAGE):
                log.debug('receive message info <%s>', payload_data)
                event_message_payload = EventMessagePayload(
                    message_id=payload_data['messageId'])
                self.emitter.emit('message', event_message_payload)

        self.mocker.on('stream', _emit_events)

    async def stop(self):
        """stop the account"""
        self.started = False

    async def contact_list(self) -> List[str]:
        """get all of the contact"""
        return self.mocker.get_contact_ids()

    async def tag_contact_delete(self, tag_id: str) -> None:
        pass

    async def tag_favorite_delete(self, tag_id: str) -> None:
        pass

    async def tag_contact_add(self, tag_id: str, contact_id: str):
        pass

    async def tag_favorite_add(self, tag_id: str, contact_id: str):
        pass

    async def tag_contact_remove(self, tag_id: str, contact_id: str):
        pass

    async def tag_contact_list(self,
                               contact_id: Optional[str] = None) -> List[str]:
        pass

    async def message_send_text(self, conversation_id: str, message: str,
                                mention_ids: List[str] = None) -> str:
        """send the text message to the specific contact/room"""

        conversation: Union[Room, Contact]
        if conversation_id.startswith('room-'):
            conversation = self.mocker.Room.load(conversation_id)
        else:
            conversation = self.mocker.Contact.load(conversation_id)
        message_id = self.mocker.send_message(
            talker=self.mocker.login_user,
            conversation=conversation,
            msg=message
        )
        return message_id

    async def message_send_contact(self, contact_id: str,
                                   conversation_id: str) -> str:
        pass

    async def message_send_file(self, conversation_id: str,
                                file: FileBox) -> str:
        pass

    async def message_send_url(self, conversation_id: str, url: str) -> str:
        pass

    async def message_send_mini_program(self,
                                        conversation_id: str,
                                        mini_program: MiniProgramPayload
                                        ) -> str:
        pass

    async def message_search(self, query: Optional[MessageQueryFilter] = None
                             ) -> List[str]:
        pass

    async def message_recall(self, message_id: str) -> bool:
        pass

    async def message_payload(self, message_id: str) -> MessagePayload:
        """get the message payload"""
        return self.mocker.environment.get_message_payload(
            message_id=message_id)

    async def message_forward(self, to_id: str, message_id: str):
        pass

    async def message_file(self, message_id: str) -> FileBox:
        """get the file-box from message instance

        save the file-box data in message_payload.text field to avoid creating a
        new structure to support this feature
        """
        message_payload = self.mocker.environment.get_message_payload(
            message_id=message_id
        )
        return FileBox.from_json(message_payload.text)

    async def message_contact(self, message_id: str) -> str:
        """get the message Contact id info

            text field save the message contact_id info
        """
        message_payload = self.mocker.environment.get_message_payload(
            message_id=message_id
        )
        return message_payload.text

    async def message_url(self, message_id: str) -> UrlLinkPayload:
        """get the url link """

    async def message_mini_program(self, message_id: str) -> MiniProgramPayload:
        pass

    async def contact_alias(self, contact_id: str,
                            alias: Optional[str] = None) -> str:
        """get/save the contact alias"""
        contact_payload = self.mocker.environment.\
            get_contact_payload(contact_id)
        if not alias:
            return contact_payload.alias
        contact_payload.alias = alias
        self.mocker.environment.update_contact_payload(contact_payload)
        return alias

    async def contact_payload_dirty(self, contact_id: str):
        pass

    async def contact_payload(self, contact_id: str) -> ContactPayload:
        """get the contact payload"""
        return self.mocker.environment.get_contact_payload(contact_id)

    async def contact_avatar(self, contact_id: str,
                             file_box: Optional[FileBox] = None) -> FileBox:
        """get the contact avatar"""
        contact_payload = self.mocker.environment.\
            get_contact_payload(contact_id)
        if not file_box:
            return FileBox.from_base64(
                contact_payload.avatar,
                name=f'{contact_payload.name}.png'
            )
        contact_payload.avatar = file_box.base64
        self.mocker.environment.update_contact_payload(contact_payload)

    async def contact_tag_ids(self, contact_id: str) -> List[str]:
        pass

    def self_id(self) -> str:
        return self.mocker.login_user.contact_id

    async def friendship_search(self, weixin: Optional[str] = None,
                                phone: Optional[str] = None) -> Optional[str]:
        pass

    async def friendship_add(self, contact_id: str, hello: str):
        pass

    async def friendship_payload(self, friendship_id: str,
                                 payload: Optional[FriendshipPayload] = None
                                 ) -> FriendshipPayload:
        pass

    async def friendship_accept(self, friendship_id: str):
        pass

    async def room_list(self) -> List[str]:
        """get the room id list"""
        rooms = self.mocker.environment.get_room_payloads()
        return [room.id for room in rooms]

    async def room_create(self, contact_ids: List[str],
                          topic: str = None) -> str:
        """create the room"""
        room_payload = self.mocker.environment.new_room_payload(
            member_ids=contact_ids,
            topic=topic
        )
        return room_payload.id

    async def room_search(self, query: RoomQueryFilter = None) -> List[str]:
        pass

    async def room_invitation_payload(self,
                                      room_invitation_id: str,
                                      payload: Optional[
                                          RoomInvitationPayload] = None
                                      ) -> RoomInvitationPayload:
        pass

    async def room_invitation_accept(self, room_invitation_id: str):
        pass

    async def contact_self_qr_code(self) -> str:
        pass

    async def contact_self_name(self, name: str):
        pass

    async def contact_signature(self, signature: str):
        pass

    async def room_payload(self, room_id: str) -> RoomPayload:
        """get the room payload"""
        return self.mocker.environment.get_room_payload(room_id)

    async def room_members(self, room_id: str) -> List[str]:
        """get the room member ids from environment

        Args:
            room_id (str): the union identification for room

        Returns:
            List[str]: room member ids
        """
        room_payload: RoomPayload = self.mocker.environment.get_room_payload(
            room_id)
        return room_payload.member_ids

    async def room_add(self, room_id: str, contact_id: str):
        """add a contact to a room"""
        self.mocker.add_contact_to_room(
            contact_ids=[contact_id],
            room_id=room_id
        )

    async def room_delete(self, room_id: str, contact_id: str):
        pass

    async def room_quit(self, room_id: str):
        pass

    async def room_topic(self, room_id: str, new_topic: str):
        pass

    async def room_announce(self, room_id: str,
                            announcement: str = None) -> str:
        pass

    async def room_qr_code(self, room_id: str) -> str:
        pass

    async def room_member_payload(self, room_id: str,
                                  contact_id: str) -> RoomMemberPayload:
        pass

    async def room_avatar(self, room_id: str) -> FileBox:
        pass

    async def logout(self):
        pass

    async def login(self, user_id: str):
        """login the user data"""
        self.mocker.login(user_id=user_id)
    async def _handleMessage(self, message: ServerMessage) -> None:
        """Handle messages from the server."""
        type = message.type
        peerId = message.src
        payload = message.payload

        log.debug(
            '\n Handling server message \n type %s, '
            '\n source peer/client id %s, \n message payload %s, '
            '\n full message %r', type, peerId, payload, message)

        server_messenger = AsyncIOEventEmitter()

        # The connection to the server is open.
        @server_messenger.once(ServerMessageType.Open)
        def _on_server_open():
            self._lastServerId = self.id
            self._open = True
            log.info('Signaling server connection open.')
            self.emit(PeerEventType.Open, self.id)

        # Server error.
        @server_messenger.once(ServerMessageType.Error)
        async def _on_server_error():
            await self._abort(PeerErrorType.ServerError, payload.msg)

        # The selected ID is taken.
        @server_messenger.once(ServerMessageType.IdTaken)
        async def _on_server_idtaken():
            await self._abort(PeerErrorType.UnavailableID,
                              f'ID "${self.id}" is taken')

        # The given API key cannot be found.
        @server_messenger.once(ServerMessageType.InvalidKey)
        async def _on_server_invalidkey():
            await self._abort(PeerErrorType.InvalidKey,
                              f'API KEY "${self._options.key}" is invalid')

        # Another peer has closed its connection to this peer.
        @server_messenger.once(ServerMessageType.Leave)
        async def _on_server_leave():
            log.debug(f'Received leave message from ${peerId}')
            await self._cleanupPeer(peerId)
            self._connections.delete(peerId)

        # The offer sent to a peer has expired without response.
        @server_messenger.once(ServerMessageType.Expire)
        def _on_server_expire():
            self.emitError(PeerErrorType.PeerUnavailable,
                           f'Could not connect to peer ${peerId}')

        # Server relaying offer for a direct connection from a remote peer
        @server_messenger.once(ServerMessageType.Offer)
        async def _on_server_offer():
            await self._handle_offer(peerId, payload)

        # Something went wrong during emit message handling
        # @server_messenger.once('error')
        # def on_error(error_message):
        #     log.error('Error on server message emit: %r', error_message)

        is_handled = server_messenger.emit(type)
        if not is_handled:
            if not payload:
                log.warn(f'You received a malformed message '
                         f'from ${peerId} of type ${type}')
                return
            connectionId = payload['connectionId']
            connection = self.getConnection(peerId, connectionId)
            if connection and connection.peerConnection:
                # Pass it on.
                await connection.handleMessage(message)
            elif connectionId:
                # Store for possible later use
                self._storeMessage(connectionId, message)
            else:
                log.warn("You received an unrecognized message:", message)
Beispiel #13
0
class WebSocket:
    """The Binance DEX WebSocket Manager."""
    def __init__(
        self,
        address: str = None,
        testnet: bool = False,
        keepalive: bool = True,
        loop: asyncio.AbstractEventLoop = None,
        url: str = None,
    ) -> None:
        if not url:
            self.url = TESTNET_URL if testnet else MAINNET_URL
        else:
            self.url = url
        self.address = address
        self._session = aiohttp.ClientSession()
        self._ws: Optional[aiohttp.ClientWebSocketResponse] = None
        self._loop = loop or asyncio.get_event_loop()
        self._events = AsyncIOEventEmitter(loop=self._loop)
        self._sub_queue: List[Tuple[str, dict]] = []
        self._keepalive = keepalive
        self._keepalive_task: Optional[asyncio.Future] = None
        self._open = False
        self._testnet = testnet

    def on(self, event: str, func: Optional[Callable] = None, **kwargs):
        """Register an event, and optional handler.

        This can be used as a decorator or as a normal method.
        See `examples/websockets_decorator.py` for usage.
        """
        # Queue up most events from startup-time decorators until after we are open
        if not self._open and event not in ("open", "error", "new_listener"):
            self._sub_queue.append((event, kwargs))
        if func:
            self._events.on(event, func)
            return None
        else:
            return self._events.on(event)

    def start(
        self,
        on_open: Optional[Callable[[], None]] = None,
        on_error: Optional[Callable[[dict], None]] = None,
        loop: asyncio.AbstractEventLoop = None,
    ) -> None:
        """The main blocking call to start the WebSocket connection."""
        loop = loop or asyncio.get_event_loop()
        return loop.run_until_complete(self.start_async(on_open, on_error))

    async def start_async(
        self,
        on_open: Optional[Callable[[], None]] = None,
        on_error: Optional[Callable[[dict], None]] = None,
    ) -> None:
        """Processes all websocket messages."""
        if self.address:  # address-specific socket
            url = f"{self.url}/{self.address}"
        else:
            url = self.url

        async with self._session.ws_connect(url) as ws:
            self._ws = ws
            self._events.emit("open")
            while self._sub_queue:
                event, kwargs = self._sub_queue.pop()
                self.subscribe(event, **kwargs)
            if on_open:
                on_open()

            # Schedule keepalive calls every 30 minutes
            if self._keepalive:
                self._keepalive_task = asyncio.ensure_future(
                    self._auto_keepalive())

            async for msg in ws:
                if msg.type == aiohttp.WSMsgType.TEXT:
                    try:
                        data = msg.json(loads=orjson.loads)
                    except Exception as e:
                        log.error(f"Unable to decode msg: {msg}")
                        continue
                    if not data:
                        log.error(f"Got empty msg: {msg}")
                        continue
                    if "error" in data:
                        self._events.emit("error", data)
                        if on_error:
                            on_error(data)
                        else:
                            log.error(f"Unhandled error msg: {data}")
                        continue
                    if "stream" not in data:
                        log.error(f"Got msg without stream: {data}")
                        continue
                    if "data" not in data:
                        log.error(f"Got msg without data: {data}")
                        continue

                    self._events.emit(data["stream"], data)

                elif msg.type == aiohttp.WSMsgType.ERROR:
                    log.error(msg)
                    self._events.emit("error", msg)
                    break

    async def send(self, data: dict) -> None:
        """Send data to the WebSocket"""
        if not self._ws:
            log.error("Error: Cannot send to uninitialized websocket")
            return
        await self._ws.send_bytes(orjson.dumps(data))

    def subscribe(
        self,
        stream: str,
        symbols: Optional[List[str]] = None,
        address: Optional[str] = None,
        callback: Optional[Callable[[dict], None]] = None,
    ):
        """Subscribe to a WebSocket stream.

        See the documentation for more details on the available streams
        https://docs.binance.org/api-reference/dex-api/ws-streams.html
        """
        payload: Dict[Any, Any] = {"method": "subscribe", "topic": stream}
        if symbols:
            payload["symbols"] = symbols
        if address:
            payload["address"] = address
        elif self.address:
            payload["address"] = self.address
        self._events.on(stream, callback)
        asyncio.ensure_future(self.send(payload))

    def unsubscribe(self, stream, symbols=None) -> None:
        payload = {"method": "unsubscribe", "topic": stream}
        if symbols:
            payload["symbols"] = symbols
        asyncio.ensure_future(self.send(payload))

    def subscribe_user_orders(self,
                              callback: Callable[[dict], None],
                              address: Optional[str] = None) -> None:
        """Subscribe to individual order updates."""
        self.subscribe("orders", address=address, callback=callback)

    def subscribe_user_accounts(self,
                                callback: Callable[[dict], None],
                                address: Optional[str] = None) -> None:
        """Subscribe to account updates."""
        self.subscribe("accounts", address=address, callback=callback)

    def subscribe_user_transfers(self,
                                 callback: Callable[[dict], None],
                                 address: Optional[str] = None) -> None:
        """
        Subscribe to transfer updates if `address` is involved (as sender or
        receiver) in a transfer. Multisend is also covered.
        """
        self.subscribe("transfers", address=address, callback=callback)

    def subscribe_trades(self, symbols: List[str],
                         callback: Callable[[dict], None]) -> None:
        """Subscribe to individual trade updates."""
        self.subscribe("trades", symbols=symbols, callback=callback)

    def subscribe_market_diff(self, symbols: List[str],
                              callback: Callable[[dict], None]) -> None:
        "Order book price and quantity depth updates used to locally keep an order book." ""
        self.subscribe("marketDiff", symbols=symbols, callback=callback)

    def subscribe_market_depth(self, symbols: List[str],
                               callback: Callable[[dict], None]) -> None:
        """Top 20 levels of bids and asks."""
        self.subscribe("marketDepth", symbols=symbols, callback=callback)

    def subscribe_kline(self, interval: str, symbols: List[str],
                        callback: Callable[[dict], None]) -> None:
        """
        The kline/candlestick stream pushes updates to the current
        klines/candlestick every second.

        Kline/Candlestick chart intervals:
            m -> minutes; h -> hours; d -> days; w -> weeks; M -> months
            1m 3m 5m 15m 30m 1h 2h 4h 6h 8h 12h 1d 3d 1w 1M
        """
        self.subscribe(f"kline_{interval}", symbols=symbols, callback=callback)

    def subscribe_ticker(self, symbols: List[str],
                         callback: Callable[[dict], None]) -> None:
        """24hr Ticker statistics for a single symbol are pushed every second."""
        self.subscribe("ticker", symbols=symbols, callback=callback)

    def subscribe_all_tickers(self, callback: Callable[[dict], None]) -> None:
        """24hr Ticker statistics for a all symbols are pushed every second."""
        self.subscribe("allTickers", symbols=["$all"], callback=callback)

    def subscribe_mini_ticker(self, symbols: List[str],
                              callback: Callable[[dict], None]) -> None:
        """A ticker for a single symbol is pushed every second."""
        self.subscribe("miniTicker", symbols=symbols, callback=callback)

    def subscribe_all_mini_tickers(self, callback: Callable[[dict],
                                                            None]) -> None:
        """Array of 24hr Mini Ticker statistics for a all symbols pushed every second."""
        self.subscribe("allMiniTickers", symbols=["$all"], callback=callback)

    def subscribe_blockheight(self, callback: Callable[[dict], None]) -> None:
        """Streams the latest block height."""
        self.subscribe("blockheight", symbols=["$all"], callback=callback)

    def keepalive(self) -> None:
        """Extend the connection time by another 30 minutes"""
        asyncio.ensure_future(self.send({"method": "keepAlive"}))

    async def _auto_keepalive(self):
        while True:
            await asyncio.sleep(30 * 60)
            self.keepalive()

    def close(self) -> None:
        """Close the websocket session"""
        asyncio.ensure_future(self.send({"method": "close"}))
        if self._session:
            asyncio.ensure_future(self._session.close())
        if self._keepalive_task:
            self._keepalive_task.cancel()