예제 #1
0
    def __init__(
        self,
        *,
        qq: Union[int, List[int]] = None,
        use_plugins: bool = True,
        port: int = None,
        host: str = None,
        group_blacklist: List[int] = None,
        friend_blacklist: List[int] = None,
        blocked_users: List[int] = None,
        log: bool = True,
        log_file: bool = True,
    ):
        if qq is not None:
            if isinstance(qq, Sequence):
                self.qq = list(qq)
            else:
                self.qq = [qq]
        else:
            self.qq = None

        self.use_plugins = use_plugins
        self.config = Config(
            host, port, group_blacklist, friend_blacklist, blocked_users
        )

        # 作为程序是否应该退出的标志,以便后续用到
        self._exit = False

        if log:
            if log_file:
                enble_log_file()
        else:
            logger.disable(__name__)

        # 消息接收函数列表
        # 这里只储存主体文件中通过装饰器或函数添加的接收函数
        self._friend_msg_receivers = []
        self._group_msg_receivers = []
        self._event_receivers = []

        # 消息上下文对象中间件列表
        # 中间件以对应消息上下文为唯一参数,返回值与上下文类型一致则向下传递
        # 否则直接丢弃该次消息
        self._friend_context_middlewares = []
        self._group_context_middlewares = []
        self._event_context_middlewares = []

        # webhook
        if self.config.webhook:
            from . import webhook  # pylint: disable=C0415

            self._friend_msg_receivers.append(webhook.receive_friend_msg)
            self._group_msg_receivers.append(webhook.receive_group_msg)
            self._event_receivers.append(webhook.receive_events)

        # 插件管理
        # 管理插件提供的接收函数
        self.plugMgr = PluginManager()
        if use_plugins:
            self.plugMgr.load_plugins()
            print(self.plugin_status)

        # 当连接上或断开连接运行的函数
        # 如果通过装饰器注册了, 这两个字段设置成(func, every_time)
        # func 是需要执行的函数, every_time 表示是否每一次连接或断开都会执行
        self._when_connected_do: Tuple[Callable, bool] = None
        self._when_disconnected_do: Tuple[Callable, bool] = None

        # 线程池 TODO: 开放该参数
        thread_works = 50
        self.pool = WorkerPool(thread_works)

        # 依次各种初始化
        self._initialize_socketio()
        self._initialize_handlers()
예제 #2
0
class Botoy:
    """
    :param qq: 机器人QQ号(多Q就传qq号列表), 如果不传则会监听服务端传过来的所有机器人的
                所有信息,如果传了,则只会接收对应机器人的信息
    :param use_plugins: 是否开启插件功能, 默认``False``
    :param port: 运行端口, 默认为``8888``
    :param host: ip,默认为``http://127.0.0.1``
    :param group_blacklist: 群黑名单, 此名单中的群聊消息不会被处理,默认为``空``
    :param friend_blacklist: 好友黑名单,此名单中的好友消息不会被处理,默认为``空``
    :param blocked_users: 用户黑名单,即包括群消息和好友消息, 该用户的消息都不会处理, 默认为``空``
    :param log: 是否开启日志
    :param log_file: 是否输出日志文件
    """

    def __init__(
        self,
        *,
        qq: Union[int, List[int]] = None,
        use_plugins: bool = True,
        port: int = None,
        host: str = None,
        group_blacklist: List[int] = None,
        friend_blacklist: List[int] = None,
        blocked_users: List[int] = None,
        log: bool = True,
        log_file: bool = True,
    ):
        if qq is not None:
            if isinstance(qq, Sequence):
                self.qq = list(qq)
            else:
                self.qq = [qq]
        else:
            self.qq = None

        self.use_plugins = use_plugins
        self.config = Config(
            host, port, group_blacklist, friend_blacklist, blocked_users
        )

        # 作为程序是否应该退出的标志,以便后续用到
        self._exit = False

        if log:
            if log_file:
                enble_log_file()
        else:
            logger.disable(__name__)

        # 消息接收函数列表
        # 这里只储存主体文件中通过装饰器或函数添加的接收函数
        self._friend_msg_receivers = []
        self._group_msg_receivers = []
        self._event_receivers = []

        # 消息上下文对象中间件列表
        # 中间件以对应消息上下文为唯一参数,返回值与上下文类型一致则向下传递
        # 否则直接丢弃该次消息
        self._friend_context_middlewares = []
        self._group_context_middlewares = []
        self._event_context_middlewares = []

        # webhook
        if self.config.webhook:
            from . import webhook  # pylint: disable=C0415

            self._friend_msg_receivers.append(webhook.receive_friend_msg)
            self._group_msg_receivers.append(webhook.receive_group_msg)
            self._event_receivers.append(webhook.receive_events)

        # 插件管理
        # 管理插件提供的接收函数
        self.plugMgr = PluginManager()
        if use_plugins:
            self.plugMgr.load_plugins()
            print(self.plugin_status)

        # 当连接上或断开连接运行的函数
        # 如果通过装饰器注册了, 这两个字段设置成(func, every_time)
        # func 是需要执行的函数, every_time 表示是否每一次连接或断开都会执行
        self._when_connected_do: Tuple[Callable, bool] = None
        self._when_disconnected_do: Tuple[Callable, bool] = None

        # 线程池 TODO: 开放该参数
        thread_works = 50
        self.pool = WorkerPool(thread_works)

        # 依次各种初始化
        self._initialize_socketio()
        self._initialize_handlers()

    ########################################################################
    # Add context receivers
    ########################################################################
    def on_friend_msg(self, receiver: Callable[[FriendMsg], Any]):
        """添加好友消息接收函数"""
        self._friend_msg_receivers.append(receiver)
        return self  # 包括下面的六个方法是都不需要返回值的, 但返回本身也无妨,可以支持链式初始化

    def on_group_msg(self, receiver: Callable[[GroupMsg], Any]):
        """添加群消息接收函数"""
        self._group_msg_receivers.append(receiver)
        return self

    def on_event(self, receiver: Callable[[EventMsg], Any]):
        """添加事件消息接收函数"""
        self._event_receivers.append(receiver)
        return self

    ########################################################################
    # Add context middlewares
    ########################################################################
    def friend_context_use(
        self, middleware: Callable[[FriendMsg], Optional[FriendMsg]]
    ):
        """注册好友消息中间件"""
        self._friend_context_middlewares.append(middleware)
        return self

    def group_context_use(self, middleware: Callable[[GroupMsg], Optional[GroupMsg]]):
        """注册群消息中间件"""
        self._group_context_middlewares.append(middleware)
        return self

    def event_context_use(self, middleware: Callable[[EventMsg], Optional[EventMsg]]):
        """注册事件消息中间件"""
        self._event_context_middlewares.append(middleware)
        return self

    ########################################################################
    # shortcuts to call plugin manager methods
    ########################################################################
    # 只推荐使用这几个方法,其他的更细致的方法需要通过 plugMgr 对象访问
    # 原始plugMgr提供了自由度更高的方法
    def load_plugins(self):
        '''加载新插件'''
        return self.plugMgr.load_plugins()

    def reload_plugins(self):
        '''重载旧插件,加载新插件'''
        return self.plugMgr.reload_plugins()

    def reload_plugin(self, plugin_name: str):
        '''重载指定插件'''
        return self.plugMgr.reload_plugin(plugin_name)

    def remove_plugin(self, plugin_name: str):
        '''停用指定插件'''
        return self.plugMgr.remove_plugin(plugin_name)

    def recover_plugin(self, plugin_name: str):
        '''启用指定插件'''
        return self.plugMgr.recover_plugin(plugin_name)

    @property
    def plugin_status(self):
        '''插件启用状态'''
        return self.plugMgr.info_table

    @property
    def plugins(self):
        '''插件名列表'''
        return self.plugMgr.plugins

    @property
    def removed_plugins(self):
        '''已停用的插件名列表'''
        return self.plugMgr.removed_plugins

    ##########################################################################
    # decorators for registering hook function when connected or disconnected
    ##########################################################################
    def when_connected(self, func: Callable = None, *, every_time=False):
        if func is None:
            return functools.partial(self.when_connected, every_time=every_time)
        self._when_connected_do = (func, every_time)
        return None

    def when_disconnected(self, func: Callable = None, *, every_time=False):
        if func is None:
            return functools.partial(self.when_disconnected, every_time=every_time)
        self._when_disconnected_do = (func, every_time)
        return None

    ########################################################################
    # about socketio
    ########################################################################
    def connect(self):
        logger.success('Connected to the server successfully!')

        # 连接成功执行用户定义的函数,如果有
        if self._when_connected_do is not None:
            self._when_connected_do[0]()
            # 如果不需要每次运行,这里运行一次后就废弃设定的函数
            if not self._when_connected_do[1]:
                self._when_connected_do = None

    def disconnect(self):
        logger.warning('Disconnected to the server!')
        # 断开连接后执行用户定义的函数,如果有
        if self._when_disconnected_do is not None:
            self._when_disconnected_do[0]()
            if not self._when_disconnected_do[1]:
                self._when_disconnected_do = None

    def close(self, status=0):
        self.pool.shutdown(wait=False)
        self.socketio.disconnect()
        self._exit = True
        sys.exit(status)

    def run(self):
        logger.info('Connecting to the server...')
        try:
            self.socketio.connect(self.config.address, transports=['websocket'])
        except Exception:
            logger.error(traceback.format_exc())
            self.close(1)
        else:
            try:
                self.socketio.wait()
            except KeyboardInterrupt:
                pass
            finally:
                print('bye~')
                self.close(0)

    ########################################################################
    # context distributor
    ########################################################################
    def _friend_context_distributor(self, context: FriendMsg):
        for f_receiver in [
            *self._friend_msg_receivers,
            *self.plugMgr.friend_msg_receivers,
        ]:
            self.pool.submit(f_receiver, copy.deepcopy(context))

    def _group_context_distributor(self, context: GroupMsg):
        for g_receiver in [
            *self._group_msg_receivers,
            *self.plugMgr.group_msg_receivers,
        ]:
            self.pool.submit(g_receiver, copy.deepcopy(context))

    def _event_context_distributor(self, context: EventMsg):
        for e_receiver in [
            *self._event_receivers,
            *self.plugMgr.event_receivers,
        ]:
            self.pool.submit(e_receiver, copy.deepcopy(context))

    ########################################################################
    # message handler
    ########################################################################
    def _friend_msg_handler(self, msg):
        context = FriendMsg(msg)
        if self.qq and context.CurrentQQ not in self.qq:
            return
        logger.info(f'{context.__class__.__name__} ->  {context.data}')
        # 黑名单
        if context.FromUin in self.config.friend_blacklist:
            return
        # 屏蔽用户
        if context.FromUin in self.config.blocked_users:
            return
        # 中间件
        if self._friend_context_middlewares:
            for middleware in self._friend_context_middlewares:
                new_context = middleware(context)
                if isinstance(new_context, type(context)):
                    context = new_context
                else:
                    return
        # 传递几个数据供(插件中的)接收函数调用, 其他不再注释
        context._host = self.config.host
        context._port = self.config.port
        self.pool.submit(self._friend_context_distributor, context)

    def _group_msg_handler(self, msg):
        context = GroupMsg(msg)
        if self.qq and context.CurrentQQ not in self.qq:
            return
        logger.info(f'{context.__class__.__name__} ->  {context.data}')
        # 黑名单
        if context.FromGroupId in self.config.group_blacklist:
            return
        # 屏蔽用户
        if context.FromUserId in self.config.blocked_users:
            return
        # 中间件
        if self._group_context_middlewares:
            for middleware in self._group_context_middlewares:
                new_context = middleware(context)
                if isinstance(new_context, type(context)):
                    context = new_context
                else:
                    return
        context._host = self.config.host
        context._port = self.config.port
        self.pool.submit(self._group_context_distributor, context)

    def _event_handler(self, msg):
        context = EventMsg(msg)
        if self.qq and context.CurrentQQ not in self.qq:
            return
        logger.info(f'{context.__class__.__name__} ->  {context.data}')
        # 中间件
        if self._event_context_middlewares:
            for middleware in self._event_context_middlewares:
                new_context = middleware(context)
                if isinstance(new_context, type(context)):
                    context = new_context
                else:
                    return
        context._host = self.config.host
        context._port = self.config.port
        self.pool.submit(self._event_context_distributor, context)

    ########################################################################
    # initialize
    ########################################################################
    def _initialize_socketio(self):
        self.socketio = socketio.Client()
        self.socketio.event()(self.connect)
        self.socketio.event()(self.disconnect)

    def _initialize_handlers(self):
        self.socketio.on('OnGroupMsgs')(self._group_msg_handler)
        self.socketio.on('OnFriendMsgs')(self._friend_msg_handler)
        self.socketio.on('OnEvents')(self._event_handler)

    ########################################################################
    def __repr__(self):
        return 'Botoy <{}> <host-{}> <port-{}> <address-{}>'.format(
            " ".join([str(i) for i in self.qq]),
            self.config.host,
            self.config.port,
            self.config.address,
        )