def __init__(self, config: dict): if not self.__first_init: logger.info("Initializing") if any(parameter not in config for parameter in self.necessary_parameters): raise ValueError( f"Missing necessary parameters! (miraiHost, authKey, BotQQ)" ) self.__loop = asyncio.get_event_loop() self.__bcc = Broadcast(loop=self.__loop) self.__app = GraiaMiraiApplication(broadcast=self.__bcc, connect_info=Session( host=config["miraiHost"], authKey=config["authKey"], account=config["BotQQ"], websocket=True), enable_chat_log=False) self.__saya = Saya(self.__bcc) self.__saya.install_behaviours(BroadcastBehaviour(self.__bcc)) self.__app.debug = False self.__config = config AppCore.__first_init = True logger.info("Initialize end") else: raise AppCoreAlreadyInitialized()
def __init__(self, config: GlobalConfig): if not self.__first_init: logger.info("Initializing") self.__loop = asyncio.get_event_loop() self.__bcc = Broadcast(loop=self.__loop) self.__app = Ariadne(broadcast=self.__bcc, chat_log_config=False, connect_info=DefaultAdapter( broadcast=self.__bcc, ping=False, mirai_session=MiraiSession( host=config.mirai_host, verify_key=str(config.verify_key), account=config.bot_qq), )) self.__saya = Saya(self.__bcc) self.__saya.install_behaviours(BroadcastBehaviour(self.__bcc)) if _install_scheduler: self.__sche = GraiaScheduler(loop=self.__loop, broadcast=self.__bcc) self.__saya.install_behaviours( GraiaSchedulerBehaviour(self.__sche)) self.__app.debug = False self.__config = config AppCore.__first_init = True logger.info("Initialize end") else: raise AppCoreAlreadyInitialized()
def saya_init(): channels = Saya.current().channels core = AppCore.get_core_instance() bcc = core.get_bcc() for channel in channels.values(): cubes = channel.content logger.info(f"converting saya module: {channel.module}") for cube in cubes: if isinstance(cube.metaclass, ListenerSchema): bcc.removeListener(bcc.getListener(cube.content)) if all([ issubclass(i, GroupEvent) for i in cube.metaclass.listening_events ]): cube.metaclass.decorators.append(manageable( channel.module)) else: cube.metaclass.decorators.append( manageable(channel.module, False)) listener = cube.metaclass.build_listener(cube.content, bcc) if not listener.namespace: listener.namespace = bcc.getDefaultNamespace() bcc.listeners.append(listener) if not cube.metaclass.inline_dispatchers: logger.warning( f"插件{channel._name}未使用inline_dispatchers!默认notice为False!" ) saya_data.add_saya(channel.module, notice=False) else: saya_data.add_saya(channel.module)
from datetime import datetime from graia.saya import Saya, Channel from graia.ariadne.app import Ariadne from graia.ariadne.message.chain import MessageChain from graia.saya.builtins.broadcast.schema import ListenerSchema from graia.ariadne.event.message import Group, Member, GroupMessage from graia.ariadne.message.element import ForwardNode, Plain, Forward, At from sagiri_bot.message_sender.strategy import Normal from sagiri_bot.decorators import switch, blacklist from sagiri_bot.handler.handler import AbstractHandler from sagiri_bot.message_sender.message_item import MessageItem from sagiri_bot.message_sender.message_sender import MessageSender saya = Saya.current() channel = Channel.current() channel.name("FakeForward") channel.author("SAGIRI-kawaii") channel.description("一个生成转发消息的插件,发送 '/fake [@目标] [内容]' 即可") @channel.use(ListenerSchema(listening_events=[GroupMessage])) async def fake_forward(app: Ariadne, message: MessageChain, group: Group, member: Member): if result := await FakeForward.handle(app, message, group, member): await MessageSender(result.strategy).send(app, result.message, message, group, member)
import asyncio from graia.saya import Saya from graia.broadcast import Broadcast from graia.saya.builtins.broadcast import BroadcastBehaviour from graia.scheduler import GraiaScheduler from graia.scheduler.saya import GraiaSchedulerBehaviour from graia.application.entry import ( GraiaMiraiApplication, Session ) from utils import ModuleLoader, BotAttributes from utils import logger loop = asyncio.get_event_loop() broadcast = Broadcast(loop=loop) scheduler = GraiaScheduler(loop, broadcast) saya = Saya(broadcast) bot = BotAttributes("./config.json") saya.install_behaviours(BroadcastBehaviour(broadcast)) saya.install_behaviours(GraiaSchedulerBehaviour(scheduler)) ModuleLoader.load(saya) app = GraiaMiraiApplication( broadcast=broadcast, connect_info=Session( host=bot.host, authKey=bot.authKey, account=bot.account, websocket=True
class AppCore(object): """ 应用核心,用于管理所有相关组件 """ __instance = None __first_init: bool = False __app: Ariadne = None __loop: AbstractEventLoop = None __bcc = None __saya = None __thread_pool = None __config: GlobalConfig = None __launched: bool = False __exception_resender: ExceptionReSender = None __frequency_limit_instance: GlobalFrequencyLimitDict = None necessary_parameters = ["miraiHost", "verify_key", "BotQQ"] def __new__(cls, config: GlobalConfig): if not cls.__instance: cls.__instance = object.__new__(cls) return cls.__instance def __init__(self, config: GlobalConfig): if not self.__first_init: logger.info("Initializing") self.__loop = asyncio.get_event_loop() self.__bcc = Broadcast(loop=self.__loop) self.__app = Ariadne(broadcast=self.__bcc, chat_log_config=False, connect_info=DefaultAdapter( broadcast=self.__bcc, ping=False, mirai_session=MiraiSession( host=config.mirai_host, verify_key=str(config.verify_key), account=config.bot_qq), )) self.__saya = Saya(self.__bcc) self.__saya.install_behaviours(BroadcastBehaviour(self.__bcc)) if _install_scheduler: self.__sche = GraiaScheduler(loop=self.__loop, broadcast=self.__bcc) self.__saya.install_behaviours( GraiaSchedulerBehaviour(self.__sche)) self.__app.debug = False self.__config = config AppCore.__first_init = True logger.info("Initialize end") else: raise AppCoreAlreadyInitialized() @classmethod def get_core_instance(cls) -> "AppCore": """ 获取 AppCore 实例 """ if cls.__instance: return cls.__instance else: raise AppCoreNotInitialized() def get_bcc(self) -> Broadcast: """ 获取 Broadcast 实例 """ if self.__bcc: return self.__bcc else: raise AppCoreNotInitialized() def get_loop(self) -> AbstractEventLoop: """ 获取 loop """ if self.__loop: return self.__loop else: raise AppCoreNotInitialized() def get_app(self) -> Ariadne: """ 获取 Ariadne 实例 """ if self.__app: return self.__app else: raise AppCoreNotInitialized() def get_saya(self) -> Saya: """ 获取 Saya 实例 """ return self.__saya def get_config(self) -> GlobalConfig: """ 获取配置 """ return self.__config def launch(self) -> None: """ 启动 Ariadne 实例 """ if not self.__launched: self.__app.launch_blocking() self.__launched = True else: raise AriadneAlreadyLaunched() def get_frequency_limit_instance(self) -> GlobalFrequencyLimitDict: """ 返回频率限制模块实例 """ return self.__frequency_limit_instance def get_exception_resender(self) -> ExceptionReSender: """ 返回错误重发模块实例 """ return self.__exception_resender async def bot_launch_init(self) -> None: """ 机器人启动初始化 """ self.config_check() try: try: await orm.init_check() except (AttributeError, InternalError, ProgrammingError): await orm.create_all() if not os.path.exists(f"{os.getcwd()}/alembic"): logger.info("未检测到alembic目录,进行初始化") os.system("alembic init alembic") with open(f"{os.getcwd()}/statics/alembic_env_py_content.txt", "r") as r: alembic_env_py_content = r.read() with open(f"{os.getcwd()}/alembic/env.py", "w") as w: w.write(alembic_env_py_content) logger.warning( f"请前往更改 {os.getcwd()}/alembic.ini 文件,将其中的 sqlalchemy.url 替换为自己的数据库url(不需注明引擎)后重启机器人" ) exit(0) if not os.path.exists(f"{os.getcwd()}/alembic/versions"): os.mkdir(f"{os.getcwd()}/alembic/versions") os.system("alembic revision --autogenerate -m 'update'") os.system("alembic upgrade head") await orm.update(Setting, [], {"active": False}) group_list = await self.__app.getGroupList() frequency_limit_dict = {} for group in group_list: frequency_limit_dict[group.id] = 0 await orm.insert_or_update(Setting, [Setting.group_id == group.id], { "group_id": group.id, "group_name": group.name, "active": True }) results = await orm.fetchall( select(Setting.group_id, Setting.group_name).where(Setting.active == True)) self.load_required_saya_modules() logger.info("本次启动活动群组如下:") for result in results: logger.info( f"群ID: {str(result.group_id).ljust(14)}群名: {result.group_name}" ) for result in results: await orm.insert_or_update( UserPermission, [ UserPermission.member_id == self.__config.host_qq, UserPermission.group_id == result[0] ], { "member_id": self.__config.host_qq, "group_id": result[0], "level": 4 }) self.__frequency_limit_instance = GlobalFrequencyLimitDict( frequency_limit_dict) threading.Thread(target=frequency_limit, args=(self.__frequency_limit_instance, )).start() exception_resender_instance = ExceptionReSender(self.__app) listener = threading.Thread(target=exception_resender_listener, args=(self.__app, exception_resender_instance, self.__loop)) listener.start() await group_setting.data_init() except: logger.error(traceback.format_exc()) exit(0) @staticmethod def dict_check(dictionary: dict, indent: int = 4) -> None: for key in dictionary.keys(): if isinstance(dictionary[key], dict): logger.success(f"{' ' * indent}{key}:") AppCore.dict_check(dictionary[key], indent + 4) elif dictionary[key] == key: logger.warning( f"{' ' * indent}Unchanged initial value detected: {key} - {dictionary[key]}" ) else: logger.success(f"{' ' * indent}{key} - {dictionary[key]}") def config_check(self) -> None: """ 配置检查 """ required_key = ("bot_qq", "host_qq", "mirai_host", "verify_key") logger.info("Start checking configuration") father_properties = tuple(dir(BaseModel)) properties = [ _ for _ in dir(self.__config) if _ not in father_properties and not _.startswith("_") ] for key in properties: value = self.__config.__getattribute__(key) if key in required_key and key == value: logger.error( f"Required initial value not changed detected: {key} - {value}" ) exit(0) elif isinstance(value, dict): logger.success(f"{key}:") self.dict_check(value) elif key == value: logger.warning( f"Unchanged initial value detected: {key} - {value}") else: logger.success(f"{key} - {value}") logger.info("Configuration check completed") def load_saya_modules(self) -> None: """ 加载自定义 saya 模块 """ ignore = ["__init__.py", "__pycache__"] with self.__saya.module_context(): for module in os.listdir(f"modules"): if module in ignore: continue try: if os.path.isdir(module): self.__saya.require(f"modules.{module}") else: self.__saya.require(f"modules.{module.split('.')[0]}") except ModuleNotFoundError as e: logger.error(f"saya模块:{module} - {e}") def load_required_saya_modules(self) -> None: """ 加载必要 saya 模块 """ ignore = ["__init__.py", "__pycache__"] with self.__saya.module_context(): for module in os.listdir(f"sagiri_bot/handler/required_module"): if module in ignore: continue try: if os.path.isdir(module): self.__saya.require( f"sagiri_bot.handler.required_module.{module}") else: self.__saya.require( f"sagiri_bot.handler.required_module.{module.split('.')[0]}" ) except ModuleNotFoundError as e: logger.error(f"saya模块:{module} - {e}") def get_saya_channels(self) -> Dict[str, Channel]: """ 获取 saya channels """ return self.__saya.channels def load_schedulers(self): pass
import asyncio import sys from pathlib import Path import yaml from graia.application import GraiaMiraiApplication, Session from graia.broadcast import Broadcast from graia.saya import Saya from graia.saya.builtins.broadcast import BroadcastBehaviour from yaml.loader import SafeLoader from utils.utils import get_all_package_name loop = asyncio.get_event_loop() bcc = Broadcast(loop=loop) saya = Saya(bcc) config_path = Path("config.yaml") if not config_path.is_file(): config_path.write_text(Path("config.yaml.sample").read_text()) sys.exit(1) config = yaml.load(config_path.read_text(), Loader=SafeLoader) saya.install_behaviours(BroadcastBehaviour(bcc)) app = GraiaMiraiApplication( broadcast=bcc, connect_info=Session( host=config["mirai"]["host"], authKey=config["mirai"]["authKey"], account=config["mirai"]["account"], websocket=config["mirai"]["websocket"],
import asyncio from graia.saya import Saya from graia.broadcast import Broadcast from graia.saya.builtins.broadcast import BroadcastBehaviour from graia.application.entry import GraiaMiraiApplication from config import connection_config loop = asyncio.get_event_loop() broadcast = Broadcast(loop=loop) saya = Saya(broadcast) saya.install_behaviours(BroadcastBehaviour(broadcast)) app = GraiaMiraiApplication(broadcast=broadcast, connect_info=connection_config(), enable_chat_log=True, debug=True) with saya.module_context(): saya.require("modules.module_as_file") try: app.launch_blocking() except KeyboardInterrupt: exit()
class AppCore: __instance = None __first_init: bool = False __app: GraiaMiraiApplication = None __loop: AbstractEventLoop = None __bcc = None __saya = None __thread_pool = None __config: dict = None __launched: bool = False __group_handler_chain = {} __exception_resender: ExceptionReSender = None __frequency_limit_instance: GlobalFrequencyLimitDict = None necessary_parameters = ["miraiHost", "authKey", "BotQQ"] def __new__(cls, config: dict): if not cls.__instance: cls.__instance = object.__new__(cls) return cls.__instance def __init__(self, config: dict): if not self.__first_init: logger.info("Initializing") if any(parameter not in config for parameter in self.necessary_parameters): raise ValueError( f"Missing necessary parameters! (miraiHost, authKey, BotQQ)" ) self.__loop = asyncio.get_event_loop() self.__bcc = Broadcast(loop=self.__loop) self.__app = GraiaMiraiApplication(broadcast=self.__bcc, connect_info=Session( host=config["miraiHost"], authKey=config["authKey"], account=config["BotQQ"], websocket=True), enable_chat_log=False) self.__saya = Saya(self.__bcc) self.__saya.install_behaviours(BroadcastBehaviour(self.__bcc)) self.__app.debug = False self.__config = config AppCore.__first_init = True logger.info("Initialize end") else: raise AppCoreAlreadyInitialized() @classmethod def get_core_instance(cls): if cls.__instance: return cls.__instance else: raise AppCoreNotInitialized() def get_bcc(self) -> Broadcast: if self.__bcc: return self.__bcc else: raise AppCoreNotInitialized() def get_loop(self) -> AbstractEventLoop: if self.__loop: return self.__loop else: raise AppCoreNotInitialized() def get_app(self) -> GraiaMiraiApplication: if self.__app: return self.__app else: raise AppCoreNotInitialized() def get_config(self): return self.__config def launch(self): if not self.__launched: self.__app.launch_blocking() self.__launched = True else: raise GraiaMiraiApplicationAlreadyLaunched() def set_group_chain(self, chains: list): for chain in chains: self.__group_handler_chain[chain.__name__] = chain def get_group_chains(self): return self.__group_handler_chain def get_group_chain(self, chain_name: str): return self.__group_handler_chain[ chain_name] if chain_name in self.__group_handler_chain else None def get_frequency_limit_instance(self): return self.__frequency_limit_instance def get_exception_resender(self): return self.__exception_resender async def bot_launch_init(self): self.config_check() try: await orm.create_all() await orm.update(Setting, [], {"active": False}) group_list = await self.__app.groupList() frequency_limit_dict = {} for group in group_list: frequency_limit_dict[group.id] = 0 await orm.insert_or_update(Setting, [Setting.group_id == group.id], { "group_id": group.id, "group_name": group.name, "active": True }) results = await orm.fetchall( select(Setting.group_id, Setting.group_name).where(Setting.active == True)) logger.info("本次启动活动群组如下:") for result in results: logger.info( f"群ID: {str(result.group_id).ljust(14)}群名: {result.group_name}" ) for result in results: await orm.insert_or_update( UserPermission, [ UserPermission.member_id == self.__config["HostQQ"], UserPermission.group_id == result[0] ], { "member_id": self.__config["HostQQ"], "group_id": result[0], "level": 4 }) self.__frequency_limit_instance = GlobalFrequencyLimitDict( frequency_limit_dict) threading.Thread(target=frequency_limit, args=(self.__frequency_limit_instance, )).start() exception_resender_instance = ExceptionReSender(self.__app) listener = threading.Thread(target=exception_resender_listener, args=(self.__app, exception_resender_instance, self.__loop)) listener.start() except: logger.error(traceback.format_exc()) exit() def config_check(self): logger.info("checking config") pic_paths = [ "setuPath", "setu18Path", "realPath", "realHighqPath", "wallpaperPath", "sketchPath" ] if self.__config["HostQQ"] == 123: logger.warning(f"HostQQ无效,请检查配置!") for path in pic_paths: if not os.path.exists(self.__config[path]): logger.warning(f"{path}无效,请检查配置!") if self.__config["saucenaoApiKey"] == "balabala": logger.warning("saucenaoApiKey无效,请检查配置!") if self.__config["txAppId"] == "1234567890": logger.warning("txAppId无效,请检查配置!") if self.__config["txAppKey"] == "ABCDEFGHIJKLMN": logger.warning("txAppKey无效,请检查配置!") if self.__config["loliconApiKey"] == "loliconApiKey": logger.warning("loliconApiKey无效,请检查配置!") if self.__config["wolframAlphaKey"] == "wolframAlphaKey": logger.warning("wolframAlphaKey无效,请检查配置!") logger.info("check done") def load_saya_modules(self): ignore = ["__init__.py", "__pycache__"] with self.__saya.module_context(): for module in os.listdir(f"modules"): if module in ignore: continue try: if os.path.isdir(module): self.__saya.require(f"modules.{module}") else: self.__saya.require(f"modules.{module.split('.')[0]}") except ModuleNotFoundError as e: logger.error(f"saya模块:{module} - {e}") def get_saya_channels(self): return self.__saya.channels def get_saya(self): return self.__saya
from graia.saya.builtins.broadcast import BroadcastBehaviour from . import TestSchema class TestBehaviour(Behaviour): def allocate(self, content: Cube) -> Any: if isinstance(content.metaclass, TestSchema): print("cube detected", content, content.metaclass) return content def uninstall(self, cube: Cube) -> Any: return cube loop = asyncio.get_event_loop() broadcast = Broadcast(loop=loop) saya = Saya(broadcast=broadcast) saya.install_behaviours(TestBehaviour()) saya.install_behaviours(BroadcastBehaviour(broadcast)) with saya.module_context(): test_sub1_export = saya.require("test.test_sub1") print(broadcast.listeners) saya.uninstall_channel(test_sub1_export) async def do_nothing(): pass loop.run_until_complete(do_nothing())
import asyncio from graia.saya import Saya from graia.broadcast import Broadcast from graia.saya.builtins.broadcast import BroadcastBehaviour loop = asyncio.get_event_loop() broadcast = Broadcast(loop=loop) saya = Saya(broadcast) with saya.module_context(): saya.require("modules.module_as_file") saya.require("modules.module_as_dir") try: loop.run_forever() except KeyboardInterrupt: exit()
import asyncio from graia.saya import Saya from graia.broadcast import Broadcast from graia.saya.builtins.broadcast import BroadcastBehaviour from graia.application import GraiaMiraiApplication, Session from util import get_module_in_dir import yaml import logging logging.basicConfig(format="[%(asctime)s][%(levelname)s]: %(message)s", level=logging.INFO) loop = asyncio.get_event_loop() bcc = Broadcast(loop=loop) saya = Saya(bcc) saya.install_behaviours(BroadcastBehaviour(bcc)) with open('configs.yml', encoding='UTF-8') as f: configs = yaml.safe_load(f) app = GraiaMiraiApplication(broadcast=bcc, connect_info=Session(host=configs["miraiHost"], authKey=configs["authKey"], account=configs['account'], websocket=True)) load_module = [ module for folder in configs['load_folder'] for module in get_module_in_dir(folder) ]
def load(cls, saya: Saya): modules = "modules.{}" with saya.module_context(): for module_name in cls._get_module_str(): saya.require(modules.format(module_name))
import asyncio import os from graia.saya import Saya from graia.broadcast import Broadcast from graia.saya.builtins.broadcast import BroadcastBehaviour from graia.application import GraiaMiraiApplication, Session from utils import load_config loop = asyncio.get_event_loop() bcc = Broadcast(loop=loop) saya = Saya(bcc) saya.install_behaviours(BroadcastBehaviour(bcc)) configs = load_config() app = GraiaMiraiApplication(broadcast=bcc, connect_info=Session(host=configs["miraiHost"], authKey=configs["authKey"], account=configs["BotQQ"], websocket=True)) ignore = ["__init__.py", "__pycache__"] with saya.module_context(): for module in os.listdir("modules"): if module in ignore: continue try: if os.path.isdir(module):