Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e09fa13d0f | |||
| 990a622cf6 | |||
| 6144563d4d | |||
| a6413c9809 |
9
bot.py
9
bot.py
@ -7,6 +7,10 @@ from nonebot.adapters.discord import Adapter as DiscordAdapter
|
|||||||
from nonebot.adapters.minecraft import Adapter as MinecraftAdapter
|
from nonebot.adapters.minecraft import Adapter as MinecraftAdapter
|
||||||
from nonebot.adapters.onebot.v11 import Adapter as OnebotAdapter
|
from nonebot.adapters.onebot.v11 import Adapter as OnebotAdapter
|
||||||
|
|
||||||
|
from konabot.common.log import init_logger
|
||||||
|
from konabot.common.nb.exc import BotExceptionMessage
|
||||||
|
from konabot.common.path import LOG_PATH
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
dotenv.load_dotenv()
|
||||||
env = os.environ.get("ENVIRONMENT", "prod")
|
env = os.environ.get("ENVIRONMENT", "prod")
|
||||||
env_enable_console = os.environ.get("ENABLE_CONSOLE", "none")
|
env_enable_console = os.environ.get("ENABLE_CONSOLE", "none")
|
||||||
@ -14,7 +18,12 @@ env_enable_qq = os.environ.get("ENABLE_QQ", "none")
|
|||||||
env_enable_discord = os.environ.get("ENABLE_DISCORD", "none")
|
env_enable_discord = os.environ.get("ENABLE_DISCORD", "none")
|
||||||
env_enable_minecraft = os.environ.get("ENABLE_MINECRAFT", "none")
|
env_enable_minecraft = os.environ.get("ENABLE_MINECRAFT", "none")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
init_logger(LOG_PATH, [
|
||||||
|
BotExceptionMessage,
|
||||||
|
])
|
||||||
|
|
||||||
nonebot.init()
|
nonebot.init()
|
||||||
|
|
||||||
driver = nonebot.get_driver()
|
driver = nonebot.get_driver()
|
||||||
|
|||||||
79
konabot/common/log.py
Normal file
79
konabot/common/log.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TYPE_CHECKING, List, Type
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from loguru import Record
|
||||||
|
|
||||||
|
|
||||||
|
def file_exception_filter(
|
||||||
|
record: "Record",
|
||||||
|
ignored_exceptions: tuple[Type[Exception], ...]
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
一个自定义的 Loguru 过滤器函数。
|
||||||
|
如果日志记录包含异常信息,并且该异常的类型在 ignored_exceptions 中,则返回 False(忽略)。
|
||||||
|
否则,返回 True(允许记录)。
|
||||||
|
"""
|
||||||
|
exception_info = record.get("exception")
|
||||||
|
|
||||||
|
if exception_info:
|
||||||
|
exception_type = exception_info[0]
|
||||||
|
|
||||||
|
if exception_type and issubclass(exception_type, ignored_exceptions):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def init_logger(
|
||||||
|
log_dir: Path,
|
||||||
|
ignored_exceptions: List[Type[Exception]]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
配置全局 Loguru Logger。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
log_dir (Path): 存放日志文件的文件夹路径,会自动创建。
|
||||||
|
ignored_exceptions (List[Type[Exception]]): 在 WARNING 级别文件日志中需要忽略的异常类型列表。
|
||||||
|
"""
|
||||||
|
|
||||||
|
ignored_exceptions_tuple = tuple(ignored_exceptions)
|
||||||
|
logger.remove()
|
||||||
|
|
||||||
|
log_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
logger.add(
|
||||||
|
sys.stderr,
|
||||||
|
level="INFO",
|
||||||
|
colorize=True,
|
||||||
|
format="<green>{time:HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
|
||||||
|
)
|
||||||
|
|
||||||
|
info_log_path = log_dir / "log.log"
|
||||||
|
logger.add(
|
||||||
|
str(info_log_path),
|
||||||
|
level="INFO",
|
||||||
|
rotation="10 MB",
|
||||||
|
retention="7 days",
|
||||||
|
enqueue=True,
|
||||||
|
backtrace=False,
|
||||||
|
diagnose=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
warning_error_log_path = log_dir / "error.log"
|
||||||
|
logger.add(
|
||||||
|
str(warning_error_log_path),
|
||||||
|
level="WARNING",
|
||||||
|
rotation="10 MB",
|
||||||
|
compression="zip",
|
||||||
|
enqueue=True,
|
||||||
|
filter=lambda record: file_exception_filter(record, ignored_exceptions_tuple),
|
||||||
|
backtrace=True,
|
||||||
|
diagnose=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("Loguru Logger 初始化完成!")
|
||||||
|
logger.info(f"控制台日志级别: INFO")
|
||||||
@ -4,6 +4,8 @@ ASSETS_PATH = Path(__file__).resolve().parent.parent.parent / "assets"
|
|||||||
FONTS_PATH = ASSETS_PATH / "fonts"
|
FONTS_PATH = ASSETS_PATH / "fonts"
|
||||||
|
|
||||||
SRC_PATH = Path(__file__).resolve().parent.parent
|
SRC_PATH = Path(__file__).resolve().parent.parent
|
||||||
|
DATA_PATH = SRC_PATH.parent / "data"
|
||||||
|
LOG_PATH = DATA_PATH / "logs"
|
||||||
|
|
||||||
DOCS_PATH = SRC_PATH / "docs"
|
DOCS_PATH = SRC_PATH / "docs"
|
||||||
DOCS_PATH_MAN1 = DOCS_PATH / "user"
|
DOCS_PATH_MAN1 = DOCS_PATH / "user"
|
||||||
|
|||||||
@ -5,17 +5,17 @@
|
|||||||
giftool [图片] [选项]
|
giftool [图片] [选项]
|
||||||
|
|
||||||
示例
|
示例
|
||||||
回复一张 GIF 并发送:
|
回复一张 GIF 并发送:
|
||||||
`giftool --ss 1.5 -t 2.0`
|
`giftool --ss 1.5 -t 2.0`
|
||||||
从 1.5 秒处开始,截取 2 秒长度的片段。
|
从 1.5 秒处开始,截取 2 秒长度的片段。
|
||||||
|
|
||||||
`giftool [图片] --ss 0:10 -to 0:15`
|
`giftool [图片] --ss 0:10 -to 0:15`
|
||||||
截取从 10 秒到 15 秒之间的片段(支持 MM:SS 或 HH:MM:SS 格式)。
|
截取从 10 秒到 15 秒之间的片段(支持 MM:SS 或 HH:MM:SS 格式)。
|
||||||
|
|
||||||
`giftool [图片] --frames:v 10`
|
`giftool [图片] --frames:v 10`
|
||||||
将整张 GIF 均匀抽帧,最终保留 10 帧。
|
将整张 GIF 均匀抽帧,最终保留 10 帧。
|
||||||
|
|
||||||
`giftool [图片] --ss 2 --frames:v 5`
|
`giftool [图片] --ss 2 --frames:v 5`
|
||||||
从第 2 秒开始截取,并将结果抽帧为 5 帧。
|
从第 2 秒开始截取,并将结果抽帧为 5 帧。
|
||||||
|
|
||||||
参数说明
|
参数说明
|
||||||
@ -46,7 +46,7 @@
|
|||||||
- 若原始帧数 ≤ 指定帧数,则保留全部帧。
|
- 若原始帧数 ≤ 指定帧数,则保留全部帧。
|
||||||
|
|
||||||
--s <速度>(可选)
|
--s <速度>(可选)
|
||||||
- 调整 gif 图的速度
|
- 调整 gif 图的速度。若为负数,则代表倒放
|
||||||
|
|
||||||
使用方式
|
使用方式
|
||||||
1. 发送指令前,请确保:
|
1. 发送指令前,请确保:
|
||||||
|
|||||||
@ -71,8 +71,11 @@ async def _(
|
|||||||
raise BotExceptionMessage("错误:出点时间小于入点")
|
raise BotExceptionMessage("错误:出点时间小于入点")
|
||||||
if frame_count is not None and frame_count <= 0:
|
if frame_count is not None and frame_count <= 0:
|
||||||
raise BotExceptionMessage("错误:帧数量应该大于 0")
|
raise BotExceptionMessage("错误:帧数量应该大于 0")
|
||||||
if speed_factor <= 0:
|
if speed_factor == 0:
|
||||||
raise BotExceptionMessage("错误:--speed 必须大于 0")
|
raise BotExceptionMessage("错误:速度不能为 0")
|
||||||
|
|
||||||
|
is_rev = speed_factor < 0
|
||||||
|
speed_factor = abs(speed_factor)
|
||||||
|
|
||||||
if not getattr(image, "is_animated", False):
|
if not getattr(image, "is_animated", False):
|
||||||
raise BotExceptionMessage("错误:输入的不是动图(GIF)")
|
raise BotExceptionMessage("错误:输入的不是动图(GIF)")
|
||||||
@ -185,6 +188,10 @@ async def _(
|
|||||||
if transparency_flag:
|
if transparency_flag:
|
||||||
tf['transparency'] = 0
|
tf['transparency'] = 0
|
||||||
|
|
||||||
|
if is_rev:
|
||||||
|
rframes = rframes[::-1]
|
||||||
|
rdur = rdur[::-1]
|
||||||
|
|
||||||
if rframes:
|
if rframes:
|
||||||
rframes[0].save(
|
rframes[0].save(
|
||||||
output_img,
|
output_img,
|
||||||
|
|||||||
@ -107,11 +107,16 @@ async def notify_now(notify: Notify):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def create_notify_task(notify: Notify, fail2remove: bool = True):
|
def create_notify_task(notify: Notify, fail2remove: bool = True):
|
||||||
async def mission():
|
async def mission():
|
||||||
begin_time = datetime.datetime.now()
|
begin_time = datetime.datetime.now()
|
||||||
if begin_time < notify.notify_time:
|
if begin_time < notify.notify_time:
|
||||||
await asyncio.sleep((notify.notify_time - begin_time).total_seconds())
|
await asyncio.sleep((notify.notify_time - begin_time).total_seconds())
|
||||||
|
else:
|
||||||
|
logger.warning(
|
||||||
|
f"期望在 {notify.notify_time} 在平台 {notify.platform} {notify.target_env}"
|
||||||
|
f"{notify.target} 的代办通知 {notify.notify_msg} 已经超时,将会直接通知!"
|
||||||
|
)
|
||||||
res = await notify_now(notify)
|
res = await notify_now(notify)
|
||||||
if fail2remove or res:
|
if fail2remove or res:
|
||||||
await DATA_FILE_LOCK.acquire()
|
await DATA_FILE_LOCK.acquire()
|
||||||
@ -143,6 +148,7 @@ async def _(msg: UniMsg, mEvt: Event):
|
|||||||
# target_time = get_target_time(notify_time)
|
# target_time = get_target_time(notify_time)
|
||||||
try:
|
try:
|
||||||
target_time = ptimeparse.parse(notify_time)
|
target_time = ptimeparse.parse(notify_time)
|
||||||
|
logger.info(f"从 {notify_time} 解析出了时间:{target_time}")
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.info(f"无法从 {notify_time} 中解析出时间")
|
logger.info(f"无法从 {notify_time} 中解析出时间")
|
||||||
return
|
return
|
||||||
@ -154,7 +160,7 @@ async def _(msg: UniMsg, mEvt: Event):
|
|||||||
|
|
||||||
await DATA_FILE_LOCK.acquire()
|
await DATA_FILE_LOCK.acquire()
|
||||||
cfg = load_notify_config()
|
cfg = load_notify_config()
|
||||||
|
|
||||||
if isinstance(mEvt, ConsoleMessageEvent):
|
if isinstance(mEvt, ConsoleMessageEvent):
|
||||||
platform = "console"
|
platform = "console"
|
||||||
target = mEvt.get_user_id()
|
target = mEvt.get_user_id()
|
||||||
@ -181,7 +187,7 @@ async def _(msg: UniMsg, mEvt: Event):
|
|||||||
notify_time=target_time,
|
notify_time=target_time,
|
||||||
notify_msg=notify_text,
|
notify_msg=notify_text,
|
||||||
)
|
)
|
||||||
await create_notify_task(notify)
|
task = create_notify_task(notify)
|
||||||
|
|
||||||
cfg.notifies.append(notify)
|
cfg.notifies.append(notify)
|
||||||
save_notify_config(cfg)
|
save_notify_config(cfg)
|
||||||
@ -189,6 +195,7 @@ async def _(msg: UniMsg, mEvt: Event):
|
|||||||
|
|
||||||
await evt.send(await UniMessage().at(mEvt.get_user_id()).text(
|
await evt.send(await UniMessage().at(mEvt.get_user_id()).text(
|
||||||
f" 了解啦!将会在 {notify.notify_time} 提醒你哦~").export())
|
f" 了解啦!将会在 {notify.notify_time} 提醒你哦~").export())
|
||||||
|
logger.info(f"创建了一条于 {notify.notify_time} 的代办提醒")
|
||||||
|
|
||||||
|
|
||||||
driver = nonebot.get_driver()
|
driver = nonebot.get_driver()
|
||||||
@ -205,15 +212,26 @@ async def _():
|
|||||||
|
|
||||||
NOTIFIED_FLAG["task_added"] = True
|
NOTIFIED_FLAG["task_added"] = True
|
||||||
|
|
||||||
await asyncio.sleep(10)
|
DELTA = 2
|
||||||
|
logger.info(f"第一次探测到 Bot 连接,等待 {DELTA} 秒后开始通知")
|
||||||
|
await asyncio.sleep(DELTA)
|
||||||
|
|
||||||
await DATA_FILE_LOCK.acquire()
|
await DATA_FILE_LOCK.acquire()
|
||||||
tasks = []
|
|
||||||
|
tasks: set[asyncio.Task[Any]] = set()
|
||||||
cfg = load_notify_config()
|
cfg = load_notify_config()
|
||||||
if cfg.version == 1:
|
if cfg.version == 1:
|
||||||
|
logger.info("将配置文件的版本升级为 2")
|
||||||
cfg.version = 2
|
cfg.version = 2
|
||||||
else:
|
else:
|
||||||
for notify in cfg.notifies:
|
counter = 0
|
||||||
tasks.append(create_notify_task(notify, fail2remove=False))
|
for notify in [*cfg.notifies]:
|
||||||
|
task = create_notify_task(notify, fail2remove=False)
|
||||||
|
tasks.add(task)
|
||||||
|
task.add_done_callback(lambda self: tasks.remove(self))
|
||||||
|
counter += 1
|
||||||
|
logger.info(f"成功创建了 {counter} 条代办事项")
|
||||||
|
save_notify_config(cfg)
|
||||||
DATA_FILE_LOCK.release()
|
DATA_FILE_LOCK.release()
|
||||||
|
|
||||||
await asyncio.gather(*tasks)
|
await asyncio.gather(*tasks)
|
||||||
|
|||||||
Reference in New Issue
Block a user