diff --git a/.idea/Liteyuki.iml b/.idea/Liteyuki.iml
index d0876a78..8437fe66 100644
--- a/.idea/Liteyuki.iml
+++ b/.idea/Liteyuki.iml
@@ -2,7 +2,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 87885112..dc9ea490 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/r.txt b/r.txt
index e69de29b..132ebdd8 100644
--- a/r.txt
+++ b/r.txt
@@ -0,0 +1,2 @@
+nonebot-adapter-onebot==2.3.0
+nonebot2==2.1.1
\ No newline at end of file
diff --git a/readme.md b/readme.md
index 2bcbb646..6ea0874a 100644
--- a/readme.md
+++ b/readme.md
@@ -21,7 +21,7 @@
1.安装Git,使用命令`git clone https://gitee.com/snowykami/liteyuki-bot` 克隆项目至本地
-2.切换到轻雪目录,使用`pip install -r requirements.txt`
+2.切换到轻雪目录,使用`pip install -r r.txt`
3.`python main.py`!启动!
@@ -35,7 +35,7 @@ echo 正在克隆项目...
git clone https://gitee.com/snowykami/liteyuki-bot
cd liteyuki-bot
echo 正在安装依赖...
-pip install -r requirements.txt
+pip install -r r.txt
echo python main.py > run.bat
echo pause >> run.bat
echo 启动脚本"run.bat"已创建,点击即可启动
diff --git a/resources/vanilla_pack/functions/group_welcome.lyfunction b/resources/vanilla_pack/functions/group_welcome.lyfunction
new file mode 100644
index 00000000..568bc1a2
--- /dev/null
+++ b/resources/vanilla_pack/functions/group_welcome.lyfunction
@@ -0,0 +1,2 @@
+send_group_msg message=欢迎新成员[CQ:at,qq=USER_ID]
+send_group_msg message=[CQ:poke,qq=USER_ID]
\ No newline at end of file
diff --git a/resources/vanilla_pack/metadata.json b/resources/vanilla_pack/metadata.json
new file mode 100644
index 00000000..b224894b
--- /dev/null
+++ b/resources/vanilla_pack/metadata.json
@@ -0,0 +1,4 @@
+{
+ "name": "轻雪原版包",
+ "id": ""
+}
\ No newline at end of file
diff --git a/resources/vanilla_pack/texts/en_GB.lang b/resources/vanilla_pack/texts/en_GB.lang
new file mode 100644
index 00000000..d01ecfc0
--- /dev/null
+++ b/resources/vanilla_pack/texts/en_GB.lang
@@ -0,0 +1,2 @@
+name=English(United Kingdom)
+fallback=en_US
\ No newline at end of file
diff --git a/resources/vanilla_pack/texts/en_US.lang b/resources/vanilla_pack/texts/en_US.lang
new file mode 100644
index 00000000..1eb15381
--- /dev/null
+++ b/resources/vanilla_pack/texts/en_US.lang
@@ -0,0 +1 @@
+name=English(United States)
\ No newline at end of file
diff --git a/src/builtin/liteyuki_plugin_manager/__init__.py b/resources/vanilla_pack/texts/ja_JP.lang
similarity index 100%
rename from src/builtin/liteyuki_plugin_manager/__init__.py
rename to resources/vanilla_pack/texts/ja_JP.lang
diff --git a/resources/vanilla_pack/texts/zh_CN.json b/resources/vanilla_pack/texts/zh_CN.json
new file mode 100644
index 00000000..684bf0a3
--- /dev/null
+++ b/resources/vanilla_pack/texts/zh_CN.json
@@ -0,0 +1,5 @@
+{
+ "text.weather.usage": [
+ "天气插件命令"
+ ]
+}
\ No newline at end of file
diff --git a/resources/vanilla_pack/texts/zh_CN.lang b/resources/vanilla_pack/texts/zh_CN.lang
new file mode 100644
index 00000000..6f246c06
--- /dev/null
+++ b/resources/vanilla_pack/texts/zh_CN.lang
@@ -0,0 +1,17 @@
+lang.en_US.name=英语(美国)
+lang.en_GB.name=英语(英国)
+lang.ja_JP.name=日语(日本)
+lang.zh_CN.name=简体中文
+lang.zh_TW.name=繁体中文
+
+log.main.res_name_not_found=资源名称未定义
+log.main.suc_to_load_resource=成功加载资源包:{NAME}
+log.main.no_superusers=当前没有超级用户,请通过QQ发送 {CS}SU{AUTH_CODE} 以注册为超级用户
+log.main.suc_to_reg_su=超级用户 {USER_ID} 注册成功
+log.main.fail_to_reg_su=超级用户 {USER_ID} 注册失败,验证码错误
+
+msg.main.suc_to_reg_su=超级用户「{USER_ID}」注册成功
+msg.main.fail_to_reg_su=超级用户「{USER_ID}」注册失败,验证码错误
+
+text.weather.name=轻雪天气
+text.weather.description=轻雪内置的一个天气插件,很精准
\ No newline at end of file
diff --git a/resources/vanilla_pack/texts/zh_TW.lang b/resources/vanilla_pack/texts/zh_TW.lang
new file mode 100644
index 00000000..75e397a2
--- /dev/null
+++ b/resources/vanilla_pack/texts/zh_TW.lang
@@ -0,0 +1 @@
+quote=zh_CN
\ No newline at end of file
diff --git a/src/api/adapter.py b/src/api/adapter.py
new file mode 100644
index 00000000..9e8c4fcc
--- /dev/null
+++ b/src/api/adapter.py
@@ -0,0 +1,8 @@
+from nonebot.adapters import onebot
+
+Bot = onebot.V11Bot | onebot.V12Bot
+Message = onebot.V11Message | onebot.V12Message
+
+Event = onebot.V11Event.Event | onebot.V12Event.Event
+MessageEvent = onebot.V11Event.MessageEvent | onebot.V12Event.MessageEvent
+GroupMessageEvent = onebot.V11Event.GroupMessageEvent | onebot.V12Event.GroupMessageEvent
diff --git a/src/api/command.py b/src/api/command.py
new file mode 100644
index 00000000..84149a90
--- /dev/null
+++ b/src/api/command.py
@@ -0,0 +1,38 @@
+from typing import List, Any, Tuple, Dict
+
+from src.api.adapter import Bot
+
+
+async def run_function(bot: Bot, function_name: str) -> List[Any]:
+ await bot.call_api()
+
+
+async def run_command(bot: Bot, cmd: str) -> Any:
+ await bot.call_api()
+
+
+def message_unescape(message: str):
+ """把那堆乱七八糟的文本转回原始文本
+
+ :param message:
+ :return:
+ """
+ data = {
+ '&': '&',
+ '[': '[',
+ ']': ']',
+ ',': ',',
+ '%20': ' '
+ }
+ for old, new in data.items():
+ message = message.replace(old, new)
+ return message
+
+
+def format_command(command_str: str) -> Tuple[str, Tuple[str], Dict[str, str]]:
+ args, kwargs = [], {}
+ for element in command_str.split(' '):
+ if '=' in element:
+ kwargs[element.split('=')[0]] = kwargs[element.split('=')[1]]
+ else:
+ args.append(element)
\ No newline at end of file
diff --git a/src/api/data.py b/src/api/data.py
new file mode 100644
index 00000000..36080280
--- /dev/null
+++ b/src/api/data.py
@@ -0,0 +1,80 @@
+import os
+from typing import Any
+
+from keyvalue_sqlite import KeyValueSqlite
+from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent, PrivateMessageEvent
+
+DB_PATH = 'data/liteyuki'
+
+
+class Data:
+ def __init__(self, db_name: str = None, table_name: str = None, event: MessageEvent = None):
+ """
+
+ :param db_name: DB Name, such as users/groups/globals/plugins
+ :param table_name: Table Name, such as target_id, not start with '_'
+ """
+
+ if event is not None:
+ if isinstance(event, PrivateMessageEvent):
+ db_name = 'users'
+ table_name = str(event.user_id)
+ else:
+ event: GroupMessageEvent
+ db_name = 'groups'
+ table_name = str(event.group_id)
+
+ if table_name.isalnum():
+ table_name = '_' + table_name
+ self.db = KeyValueSqlite(db_path=os.path.join(DB_PATH, str(db_name) + ".db"), table_name=table_name)
+
+ def get(self, key: str, default: Any = None) -> Any:
+ """Get data from table
+ :param key: key
+ :param default: if there is no key, return this
+ :return:
+ """
+ return self.db.get(key, default)
+
+ def get_many(self, key_set: set) -> dict[str, Any]:
+ """Get many data from table by a key set
+
+ :param key_set:
+ :return: {key: val,}
+ """
+ return self.db.get_many(key_set)
+
+ def set(self, key: str, val: Any):
+ """Store data to table
+ :param key: key
+ :param val: value
+ :return:
+ """
+ return self.db.set(key, val)
+
+ def set_many(self, data: dict):
+ """Store many data to table
+ :param data: {key: val,}
+ :return:
+ """
+ for key, val in data.items():
+ self.set(key, val)
+
+ def remove(self, key: str):
+ """Remove data from table
+
+ :param key: key
+ :return:
+ """
+ return self.db.remove(key, ignore_missing_key=True)
+
+ def detect(self, key: str) -> bool:
+ """Detect if the key in the table
+
+ :param key: key
+ :return:
+ """
+ return self.db.has_key(key)
+
+
+config_db = Data("globals", "config")
diff --git a/src/api/message.py b/src/api/message.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/api/resource.py b/src/api/resource.py
new file mode 100644
index 00000000..0de91e3b
--- /dev/null
+++ b/src/api/resource.py
@@ -0,0 +1,153 @@
+import json
+import locale
+import os
+import traceback
+from typing import Dict, List, Any
+
+from nonebot import logger
+
+from src.api.data import Data
+
+# global variable
+language_data: Dict[str, Dict[str, str]] = dict()
+resource_data: Dict[str, List[str]] = dict()
+
+# 按照优先级排序
+loaded_resource_packs: List['ResourcePack'] = list()
+
+# system language
+system_lang = locale.getdefaultlocale()[0]
+if not isinstance(system_lang, str): system_lang = 'en_US'
+
+"""
+Language Data
+{
+ "zh_CN":{
+ "example.text": "Hello World"
+ }
+}
+"""
+
+
+class Language:
+ def __init__(self, lang: str = system_lang, quote: str = system_lang, fallback: str = 'en_US'):
+ """语言类
+
+ :param lang: 语言id,例如zh_CN,en_US
+ :param quote: 语言引用,例如中文(新加坡)可引用于中文(简体),避免重复造轮子
+ :param fallback: 语言回退,当目前语言和引用语言均没有时,回退到en_US(默认)
+ """
+
+ self.lang = lang
+
+ if self.lang not in language_data: language_data[self.lang] = dict()
+ self.language_data = language_data[self.lang]
+
+ self.quote = self.language_data.get('quote', quote)
+ self.fallback = fallback
+
+ def get(self, key: str, **kwargs) -> str | Any:
+ """获取本地化键名的值
+
+ :param key:
+ :return:
+ """
+
+ val = self.language_data.get(key, language_data.get(self.quote).get(key, language_data.get(self.fallback).get(key, key)))
+ if isinstance(val, str):
+ return val.format(**kwargs)
+ else:
+ return val
+
+ def add(self, key: str, value: Any):
+ """添加一个新词条
+
+ :param key: 本地化键名
+ :param value: 键值
+ :return:
+ """
+ self.language_data[key] = value
+
+ def add_data(self, data: dict):
+ """添加一组键值对词条
+
+ :param data:
+ :return:
+ """
+ self.language_data.update(data)
+
+ @staticmethod
+ def load_file(fp: str):
+
+ f = open(fp, 'r', encoding='utf-8')
+ lang = os.path.basename(fp).split('.')[0]
+ language = Language(lang)
+
+ if fp.endswith('lang'):
+ for line in f.read().splitlines():
+ language.add(line.split('=')[0], line.split('=')[-1])
+ elif fp.endswith('json'):
+ language.add_data(json.load(f))
+
+ @staticmethod
+ def get_user_language(user_id: int) -> 'Language':
+ db = Data('user', f'u{user_id}')
+ return Language(db.get('language', system_lang))
+
+
+class ResourcePack:
+
+ def __init__(self, path: str):
+ """
+
+ :param path: resource pack dir path(include metadata.json)
+ """
+ self.path = path
+ self.dir_name = os.path.basename(self.path)
+ try:
+ metadata = json.load(open(os.path.join(path, 'metadata.json'), 'r', encoding='utf-8'))
+ self.name = metadata.get('name', self.dir_name)
+ self.description = metadata.get('description', 'main.no_details')
+ except BaseException as e:
+ logger.error(Language().get('log.main.load_resource_error'))
+
+ def __str__(self):
+ return f''
+
+ def load(self):
+ self.load_language()
+ self.load_file_path()
+ loaded_resource_packs.insert(0, self)
+ logger.success(Language().get('log.main.suc_to_load_resource', NAME=self.name))
+
+ def load_language(self):
+ texts_path = os.path.join(self.path, 'texts')
+ if os.path.exists(texts_path):
+ for lang_file in os.listdir(texts_path):
+ Language.load_file(os.path.join(self.path, 'texts', lang_file))
+
+ def load_file_path(self):
+ for path, folders, files in os.walk(self.path):
+ for f in files:
+ file_index = os.path.join(path, f).replace(self.path + os.path.sep, '')
+ file_path = os.path.join(path, f)
+ file_index = file_index.replace('\\', '/')
+ if file_index in resource_data:
+ resource_data[file_index].insert(0, file_path)
+ else:
+ resource_data[file_index] = [file_path]
+
+
+def load_resource_from_index():
+ resource_index: list = Data('common', 'config').get('resource_index', [])
+ resource_index.insert(0, 'vanilla_pack')
+ for dir_name in resource_index:
+ resource_pack = ResourcePack(os.path.join('resources', dir_name))
+ try:
+ resource_pack.load()
+ except BaseException as e:
+ traceback.print_exc()
+
+
+def get_resource_path(resource_name: str) -> str:
+ pass
\ No newline at end of file
diff --git a/src/builtin/liteyuki_main/__init__.py b/src/builtin/liteyuki_main/__init__.py
deleted file mode 100644
index 8e719c91..00000000
--- a/src/builtin/liteyuki_main/__init__.py
+++ /dev/null
@@ -1,11 +0,0 @@
-print(
- '''\033[34m __ ______ ________ ________ __ __ __ __ __ __ ______
-/ | / |/ |/ |/ \ / |/ | / |/ | / |/ |
-$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
-$$ | $$ | $$ | $$ |__ $$ \/$$/ $$ | $$ |$$ |/$$/ $$ |
-$$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ |
-$$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \ $$ |
-$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
-$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
-$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/ \033[0m'''
-)
\ No newline at end of file
diff --git a/src/builtin/liteyuki_plugin_manager/plugin.py b/src/builtin/liteyuki_plugin_manager/plugin.py
deleted file mode 100644
index ce65073e..00000000
--- a/src/builtin/liteyuki_plugin_manager/plugin.py
+++ /dev/null
@@ -1 +0,0 @@
-from nonebot import on_command
\ No newline at end of file
diff --git a/src/builtin/liteyuki_plugin_pluginmanager/__init__.py b/src/builtin/liteyuki_plugin_pluginmanager/__init__.py
new file mode 100644
index 00000000..38be3645
--- /dev/null
+++ b/src/builtin/liteyuki_plugin_pluginmanager/__init__.py
@@ -0,0 +1 @@
+from .autorun import *
\ No newline at end of file
diff --git a/src/builtin/liteyuki_plugin_pluginmanager/autorun.py b/src/builtin/liteyuki_plugin_pluginmanager/autorun.py
new file mode 100644
index 00000000..24a72fe8
--- /dev/null
+++ b/src/builtin/liteyuki_plugin_pluginmanager/autorun.py
@@ -0,0 +1,6 @@
+import nonebot
+from nonebot import get_driver
+from nonebot.utils import run_sync
+driver = get_driver()
+
+
diff --git a/src/builtin/liteyuki_plugin_weather/__init__.py b/src/builtin/liteyuki_plugin_weather/__init__.py
new file mode 100644
index 00000000..bee3caa9
--- /dev/null
+++ b/src/builtin/liteyuki_plugin_weather/__init__.py
@@ -0,0 +1,11 @@
+from nonebot.plugin import PluginMetadata
+
+
+__plugin_meta__ = PluginMetadata(
+ name='text.weather.name',
+ description='text.weather.description',
+ usage='text.weather.usage',
+ extra={
+ 'liteyuki_plugin': True
+ }
+)
\ No newline at end of file
diff --git a/src/liteyuki_main/__init__.py b/src/liteyuki_main/__init__.py
new file mode 100644
index 00000000..74f32591
--- /dev/null
+++ b/src/liteyuki_main/__init__.py
@@ -0,0 +1,52 @@
+import json
+import os
+import random
+
+import nonebot
+from nonebot import get_driver, logger, on_command
+from nonebot.params import CommandArg
+from nonebot.permission import SUPERUSER
+
+from src.api.adapter import Bot, Message, MessageEvent
+from src.api.resource import Language, ResourcePack, load_resource_from_index, language_data, system_lang
+from src.api.data import Data
+
+# register superuser
+driver = get_driver()
+
+# load resource pack
+load_resource_from_index()
+
+
+@driver.on_bot_connect
+async def detect_superuser(bot: Bot):
+ db = Data('common', 'config')
+ db.remove('auth_code')
+ if not len(bot.config.superusers):
+ auth_code = str(random.randint(1000, 9999))
+ db.set('auth_code', auth_code)
+ nonebot.logger.opt(colors=True).warning(Language().get('log.main.no_superusers', CS=list(bot.config.command_start)[0], AUTH_CODE=auth_code))
+
+
+cmd_reg_su = on_command(cmd='SU')
+su_test = on_command(cmd='ST', permission=SUPERUSER)
+
+
+@cmd_reg_su.handle()
+async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg(), ):
+ db = Data('common', 'config')
+ ul = Language.get_user_language(event.user_id)
+ auth_code = db.get('auth_code', None)
+ if isinstance(auth_code, str):
+ if str(arg) == auth_code:
+ bot.config.superusers.add(event.user_id)
+ await cmd_reg_su.send(ul.get('msg.main.suc_to_reg_su', USER_ID=event.user_id))
+ logger.opt(colors=True).success(Language().get('log.main.suc_to_reg_su', USER_ID=event.user_id))
+ else:
+ await cmd_reg_su.send(ul.get('msg.main.fail_to_reg_su', USER_ID=event.user_id))
+ logger.opt(colors=True).warning(Language().get('log.main.fail_to_reg_su', USER_ID=event.user_id))
+
+
+@su_test.handle()
+async def _():
+ await su_test.send('suc')
diff --git a/src/metadata.json b/src/metadata.json
new file mode 100644
index 00000000..3f06640c
--- /dev/null
+++ b/src/metadata.json
@@ -0,0 +1,7 @@
+{
+ "version_name": "5.0.2",
+ "version_id": 50002,
+ "new_feature": [
+ "1.更新不了一点"
+ ]
+}
\ No newline at end of file
diff --git a/src/start.py b/src/start.py
index 3e67f947..17d542f7 100644
--- a/src/start.py
+++ b/src/start.py
@@ -2,13 +2,20 @@ import os.path
import threading
import nonebot
import yaml
+from src.api.data import Data
+from nonebot import get_driver
+
+from nonebot.adapters.onebot.v11 import Adapter as V11Adapter
+from nonebot.adapters.onebot.v12 import Adapter as V12Adapter
+
+adapters = [V11Adapter, V12Adapter]
if not os.path.exists('config.yml'):
f = open('config.yml', 'w', encoding='utf-8')
yaml.dump(
{
'liteyuki': {
-
+
},
'nonebot': {
@@ -22,27 +29,52 @@ config = yaml.safe_load(open('config.yml', encoding='utf-8'))
if not isinstance(config, dict):
config = dict()
+
class Liteyuki:
def __init__(self, params=None):
+ print(
+ '\033[34m' + r''' __ ______ ________ ________ __ __ __ __ __ __ ______
+/ | / |/ |/ |/ \ / |/ | / |/ | / |/ |
+$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
+$$ | $$ | $$ | $$ |__ $$ \/$$/ $$ | $$ |$$ |/$$/ $$ |
+$$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ |
+$$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \ $$ |
+$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
+$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
+$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/ ''' + '\033[0m'
+ )
if params is None:
params = dict()
params.update()
-
kwargs = {
'port': 11451,
'host': '127.0.0.1',
'nickname': ['Liteyuki'],
+ 'command_start': ['']
}
kwargs.update(config.get('nonebot', {}))
kwargs.update(params)
- self.liteyuki_main = threading.Thread(target=nonebot.run)
+
self.running_state = 1
nonebot.init(**kwargs)
+ self.driver = get_driver()
def start(self):
- self.liteyuki_main.start()
- nonebot.load_plugin('src.builtin.liteyuki_main')
+ for adapter in adapters:
+ self.driver.register_adapter(adapter)
+ nonebot.load_plugin('src.liteyuki_main')
+ nonebot.load_plugins('src/builtin')
+ nonebot.run()
+
+ def after_load():
+ nonebot.load_plugins('plugins')
+ installed_plugins = Data('common', 'system').get('installed_plugins', [])
+ for plugin_name in installed_plugins:
+ nonebot.load_plugin(plugin_name)
+
+ after_loader = threading.Thread(target=after_load)
+ after_loader.start()
def stop(self):
pass