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()
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, )