Example #1
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 emitter() -> AsyncIOEventEmitter:
    return AsyncIOEventEmitter(asyncio.get_event_loop())
Example #3
0
class Room(Accessory[RoomPayload]):
    """
    All wechat rooms(groups) will be encapsulated as a Room.
    """
    _pool: Dict[str, 'Room'] = defaultdict()

    def __init__(self, room_id: str) -> None:
        """docs"""
        super().__init__()

        self.room_id = room_id

    _event_stream: AsyncIOEventEmitter = AsyncIOEventEmitter()

    def on(self, event_name: str, func):
        """
        listen event for contact
        event_name:
        """
        self._event_stream.on(event_name, func)

    def emit(self, event_name: str, *args, **kwargs):
        """
        emit event for a specific
        """
        self._event_stream.emit(event_name, *args, **kwargs)

    @classmethod
    async def create(cls, contacts: List[Contact], topic: str) -> Room:
        """
        create room instance
        """
        if not hasattr(contacts, '__len__'):
            raise WechatyOperationError('contacts should be list type')
        if len(contacts) < 2:
            raise WechatyOperationError(
                'contactList need at least 2 contact to create a new room')

        log.info('Room create <%s - %s>',
                 ','.join([contact.contact_id for contact in contacts]), topic)

        try:
            contact_ids = list(map(lambda x: x.contact_id, contacts))
            room_id = await cls.get_puppet(). \
                room_create(contact_ids=contact_ids, topic=topic)
            room = cls.load(room_id=room_id)
            await room.ready()
            return room
        except Exception as exception:
            message = 'Room create error <%s>' % str(exception.args)
            log.error(message)
            raise WechatyOperationError(message)

    @classmethod
    async def find_all(cls,
                       query: Union[str,
                                    RoomQueryFilter] = None) -> List[Room]:
        """
        find room by query filter

        TODO -> we should remove search_query from puppet-*
        """
        log.info('Room find_all <%s>', query)

        room_ids = await cls.get_puppet().room_search()
        rooms = [cls.load(room_id) for room_id in room_ids]

        # using async parallel pattern to load room payload
        await asyncio.gather(*[room.ready() for room in rooms])

        # search by any field contains query word
        if query is not None:
            if isinstance(query, str):
                rooms = list(
                    filter(
                        lambda x: False if not x.payload else
                        (x.payload.id.__contains__(query)) or
                        (x.payload.topic.__contains__(query)), rooms))

            if isinstance(query, RoomQueryFilter):
                newQuery: Dict = dataclasses.asdict(query)
                rooms = list(
                    filter(
                        lambda x: False if not x.payload else
                        (x.payload.id == newQuery.get('id') or not newQuery.
                         get('id')) and (x.payload.topic == newQuery.get(
                             'topic') or not newQuery.get('topic')), rooms))
        return rooms

    @classmethod
    async def find(
            cls,
            query: Union[str, RoomQueryFilter] = None) -> Union[None, Room]:
        """
        Try to find a room by filter: {topic: string | RegExp}. If get many,
        return the first one.
        """
        log.info('Room find <%s>', query)

        rooms = await cls.find_all(query)

        if rooms is None or len(rooms) < 1:
            return None

        if len(rooms) > 1:
            log.warning('Room find() got more than one(%d) result', len(rooms))

        for index, room in enumerate(rooms):
            # TODO -> room_valid function is not implemented in puppet
            # this code need to be changed later

            valid = cls.get_puppet() is not None

            if valid:
                log.warning(
                    'Room find() confirm room[#%d] with id=%s '
                    'is valid result, return it.', index, room.room_id)
                return room
            log.info(
                'Room find() confirm room[#%d] with id=%s '
                'is INVALID result, try next', index, room.room_id)
        log.info('Room find() got %d rooms but no one is valid.', len(rooms))
        return None

    @classmethod
    def load(cls, room_id: str) -> Room:
        """
        dynamic load room instance
        """
        room = cls._pool.get(room_id)
        if room is not None:
            return room

        room = cls(room_id)
        cls._pool[room_id] = room
        return room

    def __str__(self):
        """
        string format for room instance
        """
        if self.payload is None:
            return self.__class__.__name__

        if self.payload.topic is None:
            return 'loading ...'

        return 'Room <%s - %s>' % (self.room_id, self.payload.topic)

    async def ready(self, force_sync=False):
        """
        Please not to use `ready()` at the user land.
        """
        if self.is_ready():
            return

        if force_sync:
            self.payload = None

            # TODO -> *_dirty method is not implemented in puppet
            # await self.puppet.room_payload_dirty(self.room_id)
            # await self.puppet.room_member_payload_dirty(self.room_id)

        self.payload = await self.puppet.room_payload(self.room_id)

        if self.payload is None:
            raise WechatyPayloadError('Room Payload can"t be ready')

        member_ids = await self.puppet.room_members(self.room_id)

        contacts = [
            self.wechaty.Contact.load(member_id) for member_id in member_ids
        ]

        for contact in contacts:
            try:
                await contact.ready()
            # pylint:disable=W0703
            except Exception as exception:
                log.error('Room ready() member.ready() rejection: %s',
                          exception)

    async def say(
            self,
            some_thing: Union[str, Contact, FileBox, MiniProgram, UrlLink],
            mention_ids: Optional[List[str]] = None) -> Union[None, Message]:
        """
        Room Say(%s, %s)
        """
        log.info('Room say <%s, %s>', some_thing, mention_ids)

        if not some_thing:
            log.error('can"t say nothing')
            return None

        # we should import UrlLink type locally because of circular dependency

        from wechaty.user.url_link import UrlLink
        from wechaty.user.mini_program import MiniProgram
        from wechaty.user.contact import Contact
        if isinstance(some_thing, str):
            if mention_ids:
                mention_info = []
                for mention_id in mention_ids:
                    mention_contact: Contact = self.wechaty.Contact.load(
                        mention_id)
                    await mention_contact.ready()
                    alias = await mention_contact.alias()
                    name = mention_contact.name
                    mention_info.append('@' + (alias or name))

                mention_text = AT_SEPARATOR.join(mention_info)
                some_thing = mention_text + ' ' + some_thing

            msg_id = await self.puppet.message_send_text(
                conversation_id=self.room_id,
                message=some_thing,
                mention_ids=mention_ids)
        elif isinstance(some_thing, FileBox):
            msg_id = await self.puppet.message_send_file(
                conversation_id=self.room_id, file=some_thing)
        elif isinstance(some_thing, Contact):
            msg_id = await self.puppet.message_send_contact(
                conversation_id=self.room_id, contact_id=some_thing.contact_id)
        elif isinstance(some_thing, UrlLink):
            msg_id = await self.puppet.message_send_url(
                conversation_id=self.room_id,
                url=json.dumps(dataclasses.asdict(some_thing.payload)))
        elif isinstance(some_thing, MiniProgram):
            # TODO -> mini_program key is not clear
            assert some_thing.payload is not None
            msg_id = await self.puppet.message_send_mini_program(
                conversation_id=self.room_id, mini_program=some_thing.payload)
        else:
            raise WechatyOperationError('arg unsupported: ', some_thing)

        if msg_id is not None:
            msg = self.wechaty.Message.load(msg_id)
            await msg.ready()
            return msg
        return None

    # '''
    # TODO -> sayTemplateStringsArray
    # '''

    # '''
    # TODO -> Event emit : on
    # '''

    # async def on(self, event: str, listener: Callable):

    async def add(self, contact: Contact):
        """
        Add contact in a room
        """
        log.info('Room add <%s>', contact)

        await self.puppet.room_add(self.room_id, contact.contact_id)
        # reload the payload
        await self.ready(force_sync=True)

    async def delete(self, contact: Contact):
        """
        delete room
        """
        log.info('Room delete<%s>', contact)

        if contact is None or contact.contact_id is None:
            raise WechatyOperationError(
                'Contact is none or contact_id not found')
        await self.puppet.room_delete(self.room_id, contact.contact_id)
        # reload the payload
        await self.ready(force_sync=True)

    async def quit(self):
        """
        Add contact in a room
        """
        log.info('Room quit <%s>', self)

        await self.puppet.room_quit(self.room_id)

    async def topic(self, new_topic: str = None) -> Optional[str]:
        """
        get/set room topic
        """
        log.info('Room topic (%s)', new_topic)

        await self.ready()

        if new_topic is None:
            if self.payload is not None and self.payload.topic is not None:
                return self.payload.topic

            # 获取名称之间的结合
            room_member_ids = await \
                self.puppet.room_members(self.room_id)
            # filter member_ids
            member_ids = [
                member_id for member_id in room_member_ids
                if member_id != self.wechaty.contact_id
            ]

            members: List[Contact] = [
                self.wechaty.Contact.load(member_id)
                for member_id in member_ids
            ]

            for member in members:
                await member.ready()

            names = [member.name for member in members]
            return ','.join(names)

        try:
            await self.puppet.room_topic(self.room_id, new_topic)
            # reload the payload
            await self.ready(force_sync=True)
            return new_topic
        # pylint:disable=W0703
        except Exception as exception:
            log.warning('Room topic(newTopic=%s) exception: %s', new_topic,
                        exception)
        return None

    async def announce(self, announce_text: str = None) -> Optional[str]:
        """
        SET/GET announce from the room

        It only works when bot is the owner of the room.
        """

        log.info('Room announce (%s)', announce_text)

        if announce_text is None:
            announce = await self.puppet.room_announce(self.room_id)
            return announce
        await self.puppet.room_announce(self.room_id, announce_text)
        # reload the payload
        await self.ready(force_sync=True)
        return None

    async def qr_code(self) -> str:
        """
        TODO -> need to rewrite this function later
        Get QR Code Value of the Room from the room, which can be used as
        scan and join the room.

        'Wechaty Puppet Unsupported API Error. Learn More At
        https://github.com/wechaty/wechaty-puppet/wiki/Compatibility', None)>

        """
        log.info('qr_code()')
        qr_code_str = await self.puppet.room_qr_code(self.room_id)
        return qr_code_str

    async def alias(self, member: Contact) -> Optional[str]:
        """
        Return contact's roomAlias in the room

        TODO -> 'Wechaty Puppet Unsupported API Error. Learn More At
        https://github.com/wechaty/wechaty-puppet/wiki/Compatibility', None)>

        the result of the function will always return an empty string
        """
        if member is None:
            raise WechatyOperationError('member can"t be none')
        room_member_payload = await self.puppet.room_member_payload(
            room_id=self.room_id, contact_id=member.contact_id)

        if room_member_payload is not None \
            and room_member_payload.room_alias is not None:
            return room_member_payload.room_alias
        return None

    async def has(self, contact: Contact) -> bool:
        """
        Check if the room has member `contact`, the return is a Promise and
        must be `await`-ed
        """
        if self.payload is None:
            await self.ready()
            assert self.payload is not None
        return contact.contact_id in self.payload.member_ids

    async def member_list(
            self,
            query: Union[str, RoomMemberQueryFilter] = None) -> List[Contact]:
        """
        Get all room member from the room
        """
        log.info('Get room <%s> all members', self)

        member_ids = await self.puppet.room_members(self.room_id)
        members: List[Contact] = [
            self.wechaty.Contact.load(member_id) for member_id in member_ids
        ]
        await asyncio.gather(*[member.ready() for member in members])

        if query is not None:
            if isinstance(query, str):
                member_search_result = []
                for member in members:

                    if member.payload is not None:
                        if member.name.__contains__(query):
                            member_search_result.append(member)
                        elif member.payload.alias is not None and \
                            member.payload.alias.__contains__(query):
                            member_search_result.append(member)

                    # get room_alias but hostie-server not support
                return member_search_result

            if isinstance(query, RoomMemberQueryFilter):
                member_search_result = []
                for member in members:
                    if member.payload is not None:
                        if member.name.__contains__(query.name):
                            member_search_result.append(member)

                        elif member.payload.alias is not None and \
                            member.payload.alias.__contains__(
                                query.contact_alias):

                            member_search_result.append(member)

                    # get room_alias but hostie-server not support
                return member_search_result

        return members

    async def member(
            self,
            query: Union[str,
                         RoomMemberQueryFilter] = None) -> Optional[Contact]:
        """
        Find all contacts in a room, if get many, return the first one.

        # TODO -> need to refactoring this function

        """
        log.info('Room member search <%s>', query)

        members = await self.member_list(query)
        if members is None or len(members) < 1:
            return None
        return members[0]

    async def owner(self) -> Optional[Contact]:
        """
        get room owner
        """
        log.info('Room <%s> owner', self)
        await self.ready()

        if self.payload.owner_id is None or self.payload.owner_id == '':
            # raise Exception('Room <%s> payload or payload.owner_id not found')
            return None

        contact = self.wechaty.Contact.load(self.payload.owner_id)
        return contact

    async def avatar(self) -> FileBox:
        """
        get the avatar of the room
        """
        log.info('avatar() <%s>', self)

        avatar = await self.puppet.room_avatar(self.room_id)
        return avatar
def _start_event_worker():
    return AsyncIOEventEmitter()
    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)
Example #6
0
class Room(Accessory[RoomPayload]):
    """
    All wechat rooms(groups) will be encapsulated as a Room.
    """
    _pool: Dict[str, 'Room'] = defaultdict()

    def __init__(self, room_id: str) -> None:
        """docs"""
        super().__init__()

        self.room_id = room_id

    _event_stream: AsyncIOEventEmitter = AsyncIOEventEmitter()

    def on(self, event_name: str, func: Callable[..., Any]) -> None:
        """
        listen event for contact
        event_name:
        """
        self._event_stream.on(event_name, func)

    def emit(self, event_name: str, *args: Any, **kwargs: Any) -> None:
        """
        emit event for a specific
        """
        self._event_stream.emit(event_name, *args, **kwargs)

    @classmethod
    async def create(cls, contacts: List[Contact], topic: str) -> Room:
        """
        create room instance
        """
        if not hasattr(contacts, '__len__'):
            raise WechatyOperationError('contacts should be list type')
        if len(contacts) < 2:
            raise WechatyOperationError(
                'contactList need at least 2 contact to create a new room'
            )

        log.info(
            'Room create <%s - %s>',
            ','.join([contact.contact_id for contact in contacts]),
            topic
        )

        try:
            contact_ids = list(map(lambda x: x.contact_id, contacts))
            room_id = await cls.get_puppet(). \
                room_create(contact_ids=contact_ids, topic=topic)
            room = cls.load(room_id=room_id)
            await room.ready()
            return room
        except Exception as exception:
            message = 'Room create error <%s>' % str(exception.args)
            log.error(message)
            raise WechatyOperationError(message)

    @classmethod
    def _filter_rooms(cls,
                      rooms: List[Room], query: Union[str, RoomQueryFilter, Callable[[Room], bool]]
                      ) -> List[Room]:
        """
        filter rooms with query which can be string, RoomQueryFilter, or callable<filter> function

        Args:
            rooms: list of room
            query:  the query message
        Returns: the filtered contacts
        """
        func: Callable[[Room], bool]

        if isinstance(query, str):
            def filter_func(room: Room) -> bool:
                payload = room.payload
                if not payload:
                    return False
                if query == payload.id or (query.lower() in payload.topic.lower()): # type: ignore
                    return True
                return False
            func = filter_func
        elif isinstance(query, RoomQueryFilter):
            def filter_func(room: Room) -> bool:
                # to pass the type checking
                assert isinstance(query, RoomQueryFilter)
                payload = room.payload
                if not payload:
                    return False

                if query.id == payload.id or (query.topic.lower() in payload.topic.lower()): # noqa
                    return True
                return False
            func = filter_func
        elif isinstance(query, types.FunctionType):
            func = query
        else:
            raise WechatyOperationError(f'Query Argument<{query}> is not correct')

        assert not not func
        rooms = list(filter(func, rooms))
        return rooms

    @classmethod
    async def find_all(cls,
                       query: Optional[Union[str, RoomQueryFilter, Callable[[Contact], bool]]] = None) -> List[Room]:
        """
        find all rooms based on query, which can be string, RoomQueryFilter, or callable<filter> function

        Args:
            query: the query body to build filter

        Examples:
            >>> # 1. find rooms based query string
            >>> Room.find_all('your-room-id')   # find: <your-room-id> == room.room_id
            >>> Room.find_all('room-topic')     # find: <room-topic> in room.topic()

            >>> # 2. find rooms based RoomQueryFilter object
            >>> query = RoomQueryFilter(topic='room-topic')     # find: <room-topic> in room.topic()
            >>> query = RoomQueryFilter(id='your-room-id')      # find: <your-room-id> == room.room_id
            >>> query = RoomQueryFilter(topic='room-topic', id='your-room-id')      # find: <your-room-id> == room.room_id and <room-topic> in room.topic()
            >>> Room.find_all(query)

            >>> # 3. find rooms based on callable query function
            >>> def filter_rooms(room: Room) -> bool:
            >>>     if room.room_id == "your-room-id":
            >>>         return True
            >>>     if room.payload.topic == 'room-topic':
            >>>         return True
            >>>     return False

        Returns: the filtered rooms

        """
        log.info('Room find_all <%s>', query)

        # 1. load rooms with concurrent tasks
        room_ids = await cls.get_puppet().room_search()
        rooms: List[Room] = [cls.load(room_id) for room_id in room_ids]
        tasks: List[Task] = [asyncio.create_task(room.ready()) for room in rooms]
        await gather_with_concurrency(PARALLEL_TASK_NUM, tasks)

        # 2. filter the rooms
        if not query:
            return rooms

        rooms = cls._filter_rooms(rooms, query)
        return rooms

    @classmethod
    async def find(cls,
                   query: Union[str, RoomQueryFilter, Callable[[Room], bool]] = None) -> Optional[Room]:
        """
        Try to find a room by query, if there are many rooms, it will return only the first one

        Examples:
            >>> # 1. find rooms based query string
            >>> Room.find_all('your-room-id')   # find: <your-room-id> == room.room_id
            >>> Room.find_all('room-topic')     # find: <room-topic> in room.topic()

            >>> # 2. find rooms based RoomQueryFilter object
            >>> query = RoomQueryFilter(topic='room-topic')     # find: <room-topic> in room.topic()
            >>> query = RoomQueryFilter(id='your-room-id')      # find: <your-room-id> == room.room_id
            >>> query = RoomQueryFilter(topic='room-topic', id='your-room-id')      # find: <your-room-id> == room.room_id and <room-topic> in room.topic()
            >>> Room.find(query)

            >>> # 3. find rooms based on callable query function
            >>> def filter_rooms(room: Room) -> bool:
            >>>     if room.room_id == "your-room-id":
            >>>         return True
            >>>     if room.payload.topic == 'room-topic':
            >>>         return True
            >>>     return False
            >>> Room.find(filter_rooms)

        Returns:
            Optional[Room]: Room or None
        """
        log.info('Room find <%s>', query)
        rooms = await cls.find_all(query)
        if not rooms:
            return None
        return rooms[0]

    @classmethod
    def load(cls, room_id: str) -> Room:
        """
        dynamic load room instance
        """
        room = cls._pool.get(room_id)
        if room is not None:
            return room

        room = cls(room_id)
        cls._pool[room_id] = room
        return room

    def __str__(self) -> str:
        """
        string format for room instance
        """
        if self.payload is None:
            return self.__class__.__name__

        if self.payload.topic is None:
            return 'loading ...'

        return 'Room <%s - %s>' % (self.room_id, self.payload.topic)

    async def ready(self, force_sync: bool = False, load_members: bool = False) -> None:
        """
        Please not to use `ready()` at the user land.
        """
        if self.is_ready():
            return

        if force_sync:
            self.payload = None

            # TODO -> *_dirty method is not implemented in puppet
            # await self.puppet.room_payload_dirty(self.room_id)
            # await self.puppet.room_member_payload_dirty(self.room_id)

        self.payload = await self.puppet.room_payload(self.room_id)

        if self.payload is None:
            raise WechatyPayloadError('Room Payload can"t be ready')

        if not load_members:
            return

        member_ids = await self.puppet.room_members(self.room_id)

        contacts = [
            self.wechaty.Contact.load(member_id) for member_id in member_ids]

        for contact in contacts:
            try:
                await contact.ready()
            # pylint:disable=W0703
            except Exception as exception:
                log.error(
                    'Room ready() member.ready() rejection: %s', exception
                )

    async def say(self,
                  some_thing: Union[str, Contact,
                                    FileBox, MiniProgram, UrlLink],
                  mention_ids: Optional[List[str]] = None
                  ) -> Union[None, Message]:
        """
        Room Say(%s, %s)
        """
        log.info('Room say <%s, %s>', some_thing, mention_ids)

        if not some_thing:
            log.error('can"t say nothing')
            return None

        # we should import UrlLink type locally because of circular dependency

        from wechaty.user.url_link import UrlLink
        from wechaty.user.mini_program import MiniProgram
        from wechaty.user.contact import Contact
        if isinstance(some_thing, str):
            if mention_ids:
                mention_info = []
                for mention_id in mention_ids:
                    mention_contact: Contact = self.wechaty.Contact.load(mention_id)
                    await mention_contact.ready()
                    name = mention_contact.name
                    mention_info.append('@' + name)

                mention_text = AT_SEPARATOR.join(mention_info)
                some_thing = mention_text + ' ' + some_thing

            msg_id = await self.puppet.message_send_text(
                conversation_id=self.room_id, message=some_thing,
                mention_ids=mention_ids
            )
        elif isinstance(some_thing, FileBox):
            msg_id = await self.puppet.message_send_file(
                conversation_id=self.room_id,
                file=some_thing
            )
        elif isinstance(some_thing, Contact):
            msg_id = await self.puppet.message_send_contact(
                conversation_id=self.room_id,
                contact_id=some_thing.contact_id
            )
        elif isinstance(some_thing, UrlLink):
            msg_id = await self.puppet.message_send_url(
                conversation_id=self.room_id,
                url=json.dumps(dataclasses.asdict(some_thing.payload))
            )
        elif isinstance(some_thing, MiniProgram):
            # TODO -> mini_program key is not clear
            assert some_thing.payload is not None
            msg_id = await self.puppet.message_send_mini_program(
                conversation_id=self.room_id,
                mini_program=some_thing.payload
            )
        else:
            raise WechatyOperationError('arg unsupported: ', some_thing)

        if msg_id is not None:
            msg = self.wechaty.Message.load(msg_id)
            await msg.ready()
            return msg
        return None

    # '''
    # TODO -> sayTemplateStringsArray
    # '''

    # '''
    # TODO -> Event emit : on
    # '''

    # async def on(self, event: str, listener: Callable):

    async def add(self, contact: Contact) -> None:
        """
        Add contact in a room
        """
        log.info('Room add <%s>', contact)

        await self.puppet.room_add(self.room_id, contact.contact_id)
        # reload the payload
        await self.ready(force_sync=True)

    async def delete(self, contact: Contact) -> None:
        """
        Delete contact in a room
        """
        log.info('Room delete<%s>', contact)

        if contact is None or contact.contact_id is None:
            raise WechatyOperationError('Contact is none or contact_id not found')
        await self.puppet.room_delete(self.room_id, contact.contact_id)
        # reload the payload
        await self.ready(force_sync=True)

    async def quit(self) -> None:
        """
        Robot quit a room
        """
        log.info('Room quit <%s>', self)

        await self.puppet.room_quit(self.room_id)

    async def topic(self, new_topic: str = None) -> Optional[str]:
        """
        get/set room topic
        """
        log.info('Room topic (%s)', new_topic)

        await self.ready()
        login_user: ContactSelf = self.wechaty.user_self()

        if new_topic is None:
            if self.payload is not None and self.payload.topic is not None:
                return self.payload.topic

            # 获取名称之间的结合
            room_member_ids = await \
                self.puppet.room_members(self.room_id)
            # filter member_ids
            member_ids = [member_id for member_id in
                          room_member_ids
                          if member_id != login_user.contact_id]

            members: List[Contact] = [
                self.wechaty.Contact.load(member_id)
                for member_id in member_ids]

            for member in members:
                await member.ready()

            names = [member.name for member in members]
            return ','.join(names)

        try:
            await self.puppet.room_topic(self.room_id, new_topic)
            # reload the payload
            await self.ready(force_sync=True)
            return new_topic
        # pylint:disable=W0703
        except Exception as exception:
            log.warning(
                'Room topic(newTopic=%s) exception: %s',
                new_topic,
                exception
            )
        return None

    async def announce(self, announce_text: str = None) -> Optional[str]:
        """
        SET/GET announce from the room

        It only works when bot is the owner of the room.
        """

        log.info('Room announce (%s)', announce_text)

        if announce_text is None:
            announce = await self.puppet.room_announce(self.room_id)
            return announce
        await self.puppet.room_announce(self.room_id, announce_text)
        # reload the payload
        await self.ready(force_sync=True)
        return None

    async def qr_code(self) -> str:
        """
        TODO -> need to rewrite this function later
        Get QR Code Value of the Room from the room, which can be used as
        scan and join the room.

        'Wechaty Puppet Unsupported API Error. Learn More At
        https://github.com/wechaty/wechaty-puppet/wiki/Compatibility', None)>

        """
        log.info('qr_code()')
        qr_code_str = await self.puppet.room_qr_code(self.room_id)
        return qr_code_str

    async def alias(self, member: Contact) -> Optional[str]:
        """
        Return contact's roomAlias in the room

        TODO -> 'Wechaty Puppet Unsupported API Error. Learn More At
        https://github.com/wechaty/wechaty-puppet/wiki/Compatibility', None)>

        the result of the function will always return an empty string
        """
        if member is None:
            raise WechatyOperationError('member can"t be none')
        room_member_payload = await self.puppet.room_member_payload(
            room_id=self.room_id, contact_id=member.contact_id)

        if room_member_payload is not None \
            and room_member_payload.room_alias is not None:
            return room_member_payload.room_alias
        return None

    async def has(self, contact: Contact) -> bool:
        """
        Check if the room has member `contact`, the return is a Promise and
        must be `await`-ed
        """
        if self.payload is None:
            await self.ready()
            assert self.payload is not None
        return contact.contact_id in self.payload.member_ids

    async def member_list(self,
                          query: Union[str, RoomMemberQueryFilter] = None
                          ) -> List[Contact]:
        """
        Get all room member from the room
        """
        log.info('Get room <%s> all members', self)

        member_ids = await self.puppet.room_members(self.room_id)
        members: List[Contact] = [
            self.wechaty.Contact.load(member_id)
            for member_id in member_ids
        ]
        await asyncio.gather(*[member.ready() for member in members])

        if query is not None:
            if isinstance(query, str):
                member_search_result = []
                for member in members:

                    if member.payload is not None:
                        if member.name.__contains__(query):
                            member_search_result.append(member)
                        elif member.payload.alias is not None and \
                            member.payload.alias.__contains__(query):
                            member_search_result.append(member)

                    # get room_alias but hostie-server not support
                return member_search_result

            if isinstance(query, RoomMemberQueryFilter):
                member_search_result = []
                for member in members:
                    if member.payload is not None:
                        if member.name.__contains__(query.name):
                            member_search_result.append(member)

                        elif member.payload.alias is not None and \
                            member.payload.alias.__contains__(
                                query.contact_alias):

                            member_search_result.append(member)

                    # get room_alias but hostie-server not support
                return member_search_result

        return members

    async def member(self,
                     query: Union[str, RoomMemberQueryFilter] = None
                     ) -> Optional[Contact]:
        """
        Find all contacts in a room, if get many, return the first one.

        # TODO -> need to refactoring this function

        """
        log.info('Room member search <%s>', query)

        members = await self.member_list(query)
        if members is None or len(members) < 1:
            return None
        return members[0]

    async def owner(self) -> Optional[Contact]:
        """
        get room owner
        """
        log.info('Room <%s> owner', self)
        await self.ready()

        if self.payload.owner_id is None or self.payload.owner_id == '':
            # raise Exception('Room <%s> payload or payload.owner_id not found')
            return None

        contact = self.wechaty.Contact.load(self.payload.owner_id)
        return contact

    async def avatar(self) -> FileBox:
        """
        get the avatar of the room
        """
        log.info('avatar() <%s>', self)

        avatar = await self.puppet.room_avatar(self.room_id)
        return avatar
Example #7
0
 def __init__(self):
     self.events = AsyncIOEventEmitter()
     self.saved_items = []
     self.emitted_items = []
Example #8
0
    def __init__(self, options: InternalTransportOptions, loop=None):
        super(Transport, self).__init__(loop=loop)

        logging.debug(
            f'constructor() [id:{options.id}, direction:{options.direction}]')

        # Closed flag.
        self._closed: bool = False
        # Whether we can produce audio/video based on computed extended RTP
        # capabilities.
        self._canProduceByKind: Dict[str, bool]
        # App custom data.
        self._appData: Optional[dict]
        # Transport connection state.
        self._connectionState: ConnectionState = 'new'
        # Producers indexed by id
        self._producers: Dict[str, Producer] = {}
        # Consumers indexed by id.
        self._consumers: Dict[str, Consumer] = {}
        # DataProducers indexed by id
        self._dataProducers: Dict[str, DataProducer] = {}
        # DataConsumers indexed by id.
        self._dataConsumers: Dict[str, DataConsumer] = {}
        # Whether the Consumer for RTP probation has been created.
        self._probatorConsumerCreated: bool = False
        # Observer instance.
        self._observer: AsyncIOEventEmitter = AsyncIOEventEmitter()

        # Id.
        self._id: str = options.id
        # Direction.
        self._direction: Literal['send', 'recv'] = options.direction
        # Extended RTP capabilities.
        self._extendedRtpCapabilities: ExtendedRtpCapabilities = options.extendedRtpCapabilities
        self._canProduceByKind: Dict[str, bool] = options.canProduceByKind
        self._maxSctpMessageSize = options.sctpParameters.maxMessageSize if options.sctpParameters else None

        if options.additionalSettings:
            additionalSettings = options.additionalSettings.copy(deep=True)
            del additionalSettings['iceServers']
            del additionalSettings['iceTransportPolicy']
            del additionalSettings['bundlePolicy']
            del additionalSettings['rtcpMuxPolicy']
            del additionalSettings['sdpSemantics']
        else:
            additionalSettings = None

        self._handler: HandlerInterface = options.handlerFactory()

        self._handler.run(
            direction=options.direction,
            iceParameters=options.iceParameters,
            iceCandidates=options.iceCandidates,
            dtlsParameters=options.dtlsParameters,
            sctpParameters=options.sctpParameters,
            iceServers=options.iceServers,
            iceTransportPolicy=options.iceTransportPolicy,
            additionalSettings=additionalSettings,
            proprietaryConstraints=options.proprietaryConstraints,
            extendedRtpCapabilities=options.extendedRtpCapabilities)

        self._appData = options.appData

        self._handleHandler()
Example #9
0
    def read(self):
        with self.read_lock:
            frame = self.frame.copy()
            grabbed = self.grabbed
        return grabbed, frame

    def stop(self):
        self.started = False
        self.thread.join()

    def __exit__(self, exec_type, exc_value, traceback):
        self.cap.release()

vs = VideoCaptureAsync(0)
ee = AsyncIOEventEmitter(loop=asyncio.get_event_loop())


class VideoImageTrack(VideoStreamTrack):
    def __init__(self, config):
        super().__init__()
        self.config = config

    async def recv(self):
        if not self.config.active:
            return None
        pts, time_base = await self.next_timestamp()
        ret, frm = vs.read()
        frm = self.process_frame(self.config, frm)
        frame = VideoFrame.from_ndarray(frm, format="bgr24")
        frame.pts = pts
Example #10
0
    def read(self):
        with self.read_lock:
            frame = self.frame.copy()
            grabbed = self.grabbed
        return grabbed, frame

    def stop(self):
        self.started = False
        self.thread.join()

    def __exit__(self, exec_type, exc_value, traceback):
        self.cap.release()


vs = VideoCaptureAsync(0)
ee2 = AsyncIOEventEmitter(
    loop=asyncio.get_event_loop())  # async event emitter to send messages
ee = BaseEventEmitter()  # event emitter to receive messages


class VideoImageTrack:
    def __init__(self, config):
        super().__init__()
        self.config = config

    async def recv(self):
        if not self.config.active:
            return None
        ret, frm = vs.read()
        frm = self.process_frame(self.config, frm)
        return frm
Example #11
0
def jpg_bytes(to_jpg):
    _, jpg = cv2.imencode('.jpg', to_jpg)
    return jpg.tobytes()


vs = VideoCaptureAsync(0)
time.sleep(2.0)

someState = State()
pt = ProcessingThread(vs, someState)
wt = WebsocketThread(someState)
sc = ServoControl(someState)

ee = BaseEventEmitter()
sendee = AsyncIOEventEmitter(loop=wt.sendLoop)


@ee.on('command_received')
def command_received(cmd):
    someState.handle_command(cmd)
    sc.handle_command(cmd)


@ee.on('ball_found')
def ball_found():
    sc.update_ball_camera()


app = Flask(__name__)
 def __init__(self, emitter=None, flag: Optional[JobStepFlag] = None):
     self.handlers = Handlers()
     self._emitter = emitter or AsyncIOEventEmitter()
     self._flag: JobStepFlag = flag or JobStepFlag(JobStepFlag.pending)
Example #13
0
class Room(Accessory):
    """
    All wechat rooms(groups) will be encapsulated as a Room.
    """
    _pool: Dict[str, 'Room'] = defaultdict()

    def __init__(self, room_id: str) -> None:
        """docs"""
        self.room_id = room_id
        self.payload: Optional[RoomPayload] = None

        # if self.__class__ is Room:
        #     raise Exception('Room class can not be instanciated directly!')

        if self.puppet is None:
            raise Exception(
                'Room class can not be instanciated without a puppet!')

    _event_stream: AsyncIOEventEmitter = AsyncIOEventEmitter()

    def on(self, event_name: str, func):
        """
        listen event for contact
        event_name:
        """
        self._event_stream.on(event_name, func)

    def emit(self, event_name: str, *args, **kwargs):
        """
        emit event for a specific
        """
        self._event_stream.emit(event_name, *args, **kwargs)

    @classmethod
    async def create(cls, contacts: List[Contact], topic: str) -> Room:
        """
        create room instance
        """
        if not hasattr(contacts, '__len__'):
            raise Exception('contacts should be list type')
        if len(contacts) < 2:
            raise Exception(
                'contactList need at least 2 contact to create a new room')

        log.info('Room create <%s, %s>',
                 ','.join([contact.contact_id for contact in contacts]), topic)

        try:
            contact_ids = list(map(lambda x: x.contact_id, contacts))
            room_id = await cls.get_puppet().\
                room_create(contact_ids=contact_ids, topic=topic)
            return cls.load(room_id=room_id)
        except Exception as exception:
            log.error('Room create error <%s>', str(exception.args))
            raise Exception('Room create error')

    @classmethod
    async def find_all(cls,
                       room_id: Optional[str] = None,
                       topic: Optional[str] = None) -> List[Room]:
        """
        find room by query filter
        """
        log.info('Room find_all <%s, %s>', room_id, topic)
        query_filter = RoomQueryFilter(id=room_id, topic=topic)
        room_ids = await cls.get_puppet().room_search(query_filter)
        rooms = [cls.load(room_id) for room_id in room_ids]

        room_result = []
        # TODO -> chang to more efficient way
        # jointly run async ready method
        for room in rooms:
            try:
                await room.ready()
                room_result.append(room)
            # pylint:disable=W0703
            except Exception as exception:
                log.warning('Room findAll() room.ready() rejection: %s',
                            exception.args)
        return room_result

    @classmethod
    async def find(cls,
                   room_id: Optional[str] = None,
                   topic: Optional[str] = None) -> Union[None, Room]:
        """
        Try to find a room by filter: {topic: string | RegExp}. If get many,
        return the first one.
        """
        log.info('Room find <%s, %s>', room_id, topic)

        rooms = await cls.find_all(room_id, topic)

        if rooms is None or len(rooms) < 1:
            return None

        if len(rooms) > 1:
            log.warning('Room find() got more than one(%d) result', len(rooms))

        for index, room in enumerate(rooms):
            # TODO -> room_valid function is not implemented in puppet
            # this code need to be changed later
            valid = cls.get_puppet() is None

            if valid:
                log.warning(
                    'Room find() confirm room[#%d] with id=%d '
                    'is valid result, return it.', index, room.room_id)
                return room
            log.info(
                'Room find() confirm room[#%d] with id=%d '
                'is INVALID result, try next', index, room.room_id)
        log.info('Room find() got %d rooms but no one is valid.', len(rooms))
        return None

    @classmethod
    def load(cls, room_id: str) -> Room:
        """
        dynamic load room instance
        """
        if room_id in cls._pool:
            room = cls._pool.get(room_id)
            if room is None:
                raise Exception('room not found')
            return room

        new_room = cls(room_id)
        cls._pool[room_id] = new_room
        return new_room

    def __str__(self):
        """
        string format for room instance
        """
        if self.payload is None:
            return self.__class__.__name__

        if self.payload.topic is None:
            return 'loading ...'

        return 'Room <%s>' % self.payload.topic

    def is_ready(self) -> bool:
        """
        check if room's payload is ready
        """
        return self.payload is not None

    async def ready(self, force_sync=False):
        """
        Please not to use `ready()` at the user land.
        """
        if self.is_ready():
            return

        if force_sync:
            pass
            # TODO -> *_dirty method is not implemented in puppet
            # await self.puppet.room_payload_dirty(self.room_id)
            # await self.puppet.room_member_payload_dirty(self.room_id)

        self.payload = await self.puppet.room_payload(self.room_id)

        if self.payload is None:
            raise Exception('Room Payload can"t be ready')

        return

        member_ids = await self.puppet.room_members(self.room_id)

        contacts = [
            self.wechaty.Contact.load(member_id) for member_id in member_ids
        ]

        for contact in contacts:
            try:
                await contact.ready()
            # pylint:disable=W0703
            except Exception as exception:
                log.error('Room ready() member.ready() rejection: %s',
                          exception)

    async def say(
            self,
            some_thing: Union[str, Contact, FileBox, MiniProgram, UrlLink],
            mention_ids: Optional[List[str]] = None) -> Union[None, Message]:
        """
        Room Say(%s, %s)
        """
        log.info('Room say <%s, %s>', some_thing, mention_ids)

        if isinstance(some_thing, str):
            msg_id = await self.puppet.message_send_text(
                conversation_id=self.room_id,
                message=some_thing,
                mention_ids=mention_ids)
        elif isinstance(some_thing, FileBox):
            msg_id = await self.puppet.message_send_file(
                conversation_id=self.room_id, file=some_thing)
        elif isinstance(some_thing, Contact):
            msg_id = await self.puppet.message_send_contact(
                conversation_id=self.room_id, contact_id=some_thing.contact_id)
        elif isinstance(some_thing, UrlLink):
            msg_id = await self.puppet.message_send_url(
                conversation_id=self.room_id, url=some_thing.url)
        elif isinstance(some_thing, MiniProgram):
            # TODO -> mini_program key is not clear
            assert some_thing.payload is not None
            msg_id = await self.puppet.message_send_mini_program(
                conversation_id=self.room_id, mini_program=some_thing.payload)
        else:
            raise Exception('arg unsupported: ', some_thing)

        if msg_id is not None:
            msg = self.wechaty.Message.load(msg_id)
            await msg.ready()
            return msg
        return None

    # '''
    # TODO -> sayTemplateStringsArray
    # '''

    # '''
    # TODO -> Event emit : on
    # '''

    # async def on(self, event: str, listener: Callable):

    async def add(self, contact: Contact):
        """
        Add contact in a room
        """
        log.info('Room add <%s>', contact)

        await self.puppet.room_add(self.room_id, contact.contact_id)

    async def delete(self, contact: Contact):
        """
        delete room
        """
        log.info('Room delete<%s>', contact)

        if contact is None or contact.contact_id is None:
            raise Exception('Contact is none or contact_id not found')
        await self.puppet.room_delete(self.room_id, contact.contact_id)

    async def quit(self):
        """
        Add contact in a room
        """
        log.info('Room quit <%s>', self)

        await self.puppet.room_quit(self.room_id)

    async def topic(self, new_topic: str = None) -> Optional[str]:
        """
        get/set room topic
        """
        log.info('Room topic (%s)', new_topic)

        if not self.is_ready():
            log.warning('Room topic() room not ready')
            raise Exception('Room not ready')

        if new_topic is None:
            if self.payload is not None and self.payload.topic is not None:
                return self.payload.topic

            # 获取名称之间的结合
            room_member_ids = await \
                self.puppet.room_members(self.room_id)
            # filter member_ids
            member_ids = [
                member_id for member_id in room_member_ids
                if member_id != self.wechaty.contact_id
            ]

            members: List[Contact] = [
                self.wechaty.Contact.load(member_id)
                for member_id in member_ids
            ]

            for member in members:
                await member.ready()

            # members: List[Contact] = list(
            #     map(lambda x: self.wechaty.Contact.load(x), member_ids)
            # )
            names = [member.name for member in members]
            return ','.join(names)

        try:
            await self.puppet.room_topic(self.room_id, new_topic)
            return new_topic
        # pylint:disable=W0703
        except Exception as exception:
            log.warning('Room topic(newTopic=%s) exception: %s', new_topic,
                        exception)
        return None

    async def announce(self, announce_text: str = None) -> Optional[str]:
        """
        SET/GET announce from the room

        It only works when bot is the owner of the room.
        """

        log.info('Room announce (%s)', announce_text)

        if announce_text is None:
            announce = await self.puppet.room_announce(self.room_id)
            return announce
        await self.puppet.room_announce(self.room_id, announce_text)
        return None

    async def qr_code(self) -> str:
        """
        TODO -> need to rewrite this function later
        Get QR Code Value of the Room from the room, which can be used as
        scan and join the room.
        """
        log.info('qr_code()')
        qr_code_str = await self.puppet.room_qr_code(self.room_id)
        return qr_code_str

    async def alias(self, member: Contact) -> Optional[str]:
        """
        Return contact's roomAlias in the room
        """
        if member is None:
            raise Exception('member can"t be none')
        room_member_payload = await self.puppet.room_member_payload(
            room_id=self.room_id, contact_id=member.contact_id)

        if room_member_payload is not None \
                and room_member_payload.room_alias is not None:
            return room_member_payload.room_alias
        return None

    async def has(self, contact: Contact) -> bool:
        """
        Check if the room has member `contact`, the return is a Promise and
        must be `await`-ed
        """
        member_ids = await self.puppet.room_members(self.room_id)
        return contact.contact_id in member_ids

    async def member_all(self,
                         query: Union[str, RoomQueryFilter] = None
                         ) -> List[Contact]:
        """
        Find all contacts in a room

        # TODO -> need to refactoring this function

        """
        log.info('room member all (%s)', json.dumps(query))
        if query is None:
            members = await self.member_list()
            return members

        contact_ids = await self.puppet.room_members(self.room_id)
        contacts = [
            self.wechaty.Contact.load(contact_id) for contact_id in contact_ids
        ]
        return contacts

    async def member_list(self) -> List[Contact]:
        """
        Get all room member from the room
        """
        log.info('Get room <%s> all members', self)

        member_ids = await self.puppet.room_members(self.room_id)
        contacts = [
            self.wechaty.Contact.load(member_id) for member_id in member_ids
        ]

        return contacts

    async def member(
            self,
            query: Union[str, RoomQueryFilter] = None) -> Optional[Contact]:
        """
        Find all contacts in a room, if get many, return the first one.

        # TODO -> need to refactoring this function

        """
        log.info('Room member search <%s>', query)

        members = await self.member_all(query)
        if members is None or len(members) < 1:
            return None
        return members[0]

    async def owner(self) -> Optional[Contact]:
        """
        get room owner
        """
        log.info('Room <%s> owner', self)
        if self.payload is None or self.payload.owner_id is None:
            # raise Exception('Room <%s> payload or payload.owner_id not found')
            return None

        contact = self.wechaty.Contact.load(self.payload.owner_id)
        return contact

    async def avatar(self) -> FileBox:
        """
        get the avatar of the room
        """
        log.info('avatar() <%s>', self)

        avatar = await self.puppet.room_avatar(self.room_id)
        return avatar
Example #14
0
from pyrogram.types import Chat, User, Message, InlineKeyboardMarkup, InlineKeyboardButton, InlineQueryResultArticle, InputTextMessageContent
from pyrogram.parser import parser
from pyrogram.parser.html import HTML as pyrogram_html
from pyrogram.handlers import CallbackQueryHandler, InlineQueryHandler
from pyrogram.methods.chats.get_chat_members import Filters as ChatMemberFilters
from pyrogram.errors.exceptions.bad_request_400 import PeerIdInvalid, ChannelInvalid
from pyrogram.errors.exceptions.flood_420 import FloodWait

# Globals.
loop = asyncio.get_event_loop()
help_dict = dict()
apps = []
app_user_ids = dict()
log_peer_ids = []
statistics = {}
ee = AsyncIOEventEmitter(loop=loop)
DB_AVAILABLE = False
# awaited task load measurements
loads = {
    1: 0.0,  # 1 minute load
    5: 0.0,  # 5 minute load
    15: 0.0,  # 15 minute load
    30: 0.0  # 30 minute load
}


# this code here exists because i can't be f****d
class Parser(parser.Parser):
    async def parse(self, text, mode):
        if mode == 'through':
            return text
Example #15
0
def app(loop=None, db_conf: DBConfiguration = None):

    loop = loop or asyncio.get_event_loop()
    _app = web.Application(loop=loop)
    app = web.Application(loop=loop) if len(PATH_PREFIX) > 0 else _app

    async_db = AsyncPostgresDB('ui')
    loop.run_until_complete(async_db._init(db_conf=db_conf, create_triggers=DB_TRIGGER_CREATE))

    event_emitter = AsyncIOEventEmitter()

    async_db_cache = AsyncPostgresDB('ui:cache')
    loop.run_until_complete(async_db_cache._init(db_conf))
    cache_store = CacheStore(app=app, db=async_db_cache, event_emitter=event_emitter)

    if FEATURE_DB_LISTEN_ENABLE:
        async_db_notify = AsyncPostgresDB('ui:notify')
        loop.run_until_complete(async_db_notify._init(db_conf))
        ListenNotify(app, db=async_db_notify, event_emitter=event_emitter)

    if FEATURE_HEARTBEAT_ENABLE:
        async_db_heartbeat = AsyncPostgresDB('ui:heartbeat')
        loop.run_until_complete(async_db_heartbeat._init(db_conf))
        RunHeartbeatMonitor(event_emitter, db=async_db_heartbeat)
        TaskHeartbeatMonitor(event_emitter, db=async_db_heartbeat, cache=cache_store)

    if FEATURE_WS_ENABLE:
        async_db_ws = AsyncPostgresDB('ui:websocket')
        loop.run_until_complete(async_db_ws._init(db_conf))
        Websocket(app, db=async_db_ws, event_emitter=event_emitter, cache=cache_store)

    AutoCompleteApi(app, async_db)
    FlowApi(app, async_db)
    RunApi(app, async_db, cache_store)
    StepApi(app, async_db)
    TaskApi(app, async_db, cache_store)
    MetadataApi(app, async_db)
    ArtificatsApi(app, async_db, cache_store)
    TagApi(app, async_db)
    ArtifactSearchApi(app, async_db, cache_store)
    DagApi(app, async_db, cache_store)
    FeaturesApi(app)
    ConfigApi(app)
    PluginsApi(app)
    CardsApi(app, async_db, cache_store)

    LogApi(app, async_db, cache_store)
    AdminApi(app, cache_store)

    # Add Metadata Service as a sub application so that Metaflow Client
    # can use it as a service backend in case none provided via METAFLOW_SERVICE_URL
    #
    # Metadata service exposed through UI service is intended for read-only use only.
    # 'allow_get_requests_only' middleware will only accept GET requests.
    app.add_subapp("/metadata", metadata_service_app(
        loop=loop, db_conf=db_conf, middlewares=[allow_get_requests_only]))

    if os.environ.get("UI_ENABLED", "1") == "1":
        # Serve UI bundle only if enabled
        # This has to be placed last due to catch-all route
        Frontend(app)

    if len(PATH_PREFIX) > 0:
        _app.add_subapp(PATH_PREFIX, app)

    logging.info("Metadata service available at {}".format(DEFAULT_METADATA_SERVICE_URL))

    async def _init_plugins():
        with concurrent.futures.ThreadPoolExecutor() as pool:
            await loop.run_in_executor(pool, init_plugins)
    asyncio.run_coroutine_threadsafe(_init_plugins(), loop)

    return _app