aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author苏向夜 <fu050409@163.com>2024-01-26 13:56:24 +0800
committer苏向夜 <fu050409@163.com>2024-01-26 13:56:24 +0800
commit0e8be9de58454de079d772dec6c0ef9c1774a775 (patch)
tree9e0d4863d87524c15c232bf5cbd555d98a90aa70
parent796455df18b87515a4f8a4933dd92d79bea58adf (diff)
downloadinfini-0e8be9de58454de079d772dec6c0ef9c1774a775.tar.gz
infini-0e8be9de58454de079d772dec6c0ef9c1774a775.zip
:recycle: refactor(infini): refacted infini as version 2.0.0-alpha.1
-rw-r--r--mkdocs.yml224
-rw-r--r--netlify.toml4
-rw-r--r--src/infini/__init__.py28
-rw-r--r--src/infini/const.py3
-rw-r--r--src/infini/core.py39
-rw-r--r--src/infini/event.py98
-rw-r--r--src/infini/exceptions.py43
-rw-r--r--src/infini/generator.py2
-rw-r--r--src/infini/handler.py62
-rw-r--r--src/infini/input.py21
-rw-r--r--src/infini/interceptor.py35
-rw-r--r--src/infini/internal.py18
-rw-r--r--src/infini/logging.py33
-rw-r--r--src/infini/matcher.py34
-rw-r--r--src/infini/output.py19
-rw-r--r--src/infini/queue.py47
-rw-r--r--src/infini/register.py163
-rw-r--r--src/infini/router.py21
-rw-r--r--src/infini/settings.py1
-rw-r--r--src/infini/typing.py28
-rw-r--r--src/infini/utils/cli.py28
-rw-r--r--src/infini/utils/gui.py5
-rw-r--r--tests/test_handlers.py84
-rw-r--r--tests/test_input.py16
-rw-r--r--tests/test_interceptor.py33
25 files changed, 381 insertions, 708 deletions
diff --git a/mkdocs.yml b/mkdocs.yml
deleted file mode 100644
index e7b7ba6f..00000000
--- a/mkdocs.yml
+++ /dev/null
@@ -1,224 +0,0 @@
-site_name: Infini 核心文档
-repo_url: https://github.com/HydroRoll-Team/infini
-repo_name: HydroRoll-Team/infini
-edit_uri: edit/master/docs/
-site_author: HsiangNianian
-site_description: >-
- 一个基于通用规则包标准的规则包加载器
-
-theme:
- name: material
- language: zh
- favicon: images/logo.png
- icon:
- logo: material/book-sync
- repo: fontawesome/brands/git-alt
- annotation: material/arrow-right-circle
- custom_dir: docs/src/overrides
- palette:
- - scheme: default
- primary: indigo
- accent: indigo
- toggle:
- icon: material/brightness-7
- name: Switch to dark mode
- - scheme: slate
- primary: black
- accent: indigo
- toggle:
- icon: material/brightness-4
- name: Switch to light mode
- features:
- - announce.dismiss
- - content.action.edit
- - content.action.view
- - content.tooltips
- - content.tabs.link
- - content.code.annotate
- - content.code.copy
- - content.code.select
- - navigation.footer
- - navigation.tabs
- # - navigation.tabs.sticky
- - navigation.sections
- # - navigation.expand
- - navigation.path
- - navigation.indexes
- - toc.follow
- - navigation.top
- - search.highlight
- - search.suggest
- - search.share
- - header.autohide
- - navigation.footer
- - navigation.instant
- - navigation.instant.prefetch
- - navigation.instant.progress
-
- font:
- text: Times New
- code: Times New
-extra:
- status:
- new: 最近添加
- deprecated: 已被废弃
- version:
- provider: mike
- homepage: https://grps.hydroroll.team # hydroroll-team.github.io
- consent:
- title: Cookie 偏好设置
- description: >-
- 我们使用Cookie来识别您的重复访问和偏好,以及衡量我们的文档的有效性和用户是否找到他们搜索的内容。在您的同意下,这将帮助我们改善我们的文档。
- actions:
- - accept
- - manage
- cookies:
- analytics:
- name: Google Analytics
- checked: true
- github:
- name: GitHub
- checked: true
-
- social:
- - icon: fontawesome/brands/github
- link: https://github.com/HydroRoll-Team
- - icon: fontawesome/brands/python
- link: https://pypi.org/project/infini/
-
-copyright: >
- Copyright &copy; 2023 - PRESENT. <a href="#__consent">(管理Cookies)</a>
-
-markdown_extensions:
- - attr_list
- - md_in_html
- - pymdownx.superfences
- - pymdownx.snippets:
- auto_append:
- - RulePackage/getStart/getDeeper/Module.md
- - abbr
- - admonition
- - def_list
- - footnotes
- - toc:
- permalink: ⚓
- - pymdownx.arithmatex:
- generic: true
- - pymdownx.betterem:
- smart_enable: all
- - pymdownx.caret
- - pymdownx.details
- - pymdownx.emoji:
- emoji_index: !!python/name:material.extensions.emoji.twemoji
- emoji_generator: !!python/name:material.extensions.emoji.to_svg
- - pymdownx.highlight:
- anchor_linenums: true
- line_spans: __span
- pygments_lang_class: true
- linenums: true
- - pymdownx.inlinehilite
- - pymdownx.keys
- - pymdownx.magiclink:
- normalize_issue_symbols: true
- repo_url_shorthand: true
- user: HydroRoll-Team
- repo: infini
- - pymdownx.mark
- - pymdownx.smartsymbols
- - pymdownx.superfences:
- custom_fences:
- - name: mermaid
- class: mermaid
- format: !!python/name:pymdownx.superfences.fence_code_format
- - pymdownx.tabbed:
- alternate_style: true
- combine_header_slug: true
- slugify: !!python/object/apply:pymdownx.slugs.slugify
- kwds:
- case: lower
- - pymdownx.tasklist:
- custom_checkbox: true
- - pymdownx.tilde
-
-plugins:
- - blog
- - search:
- separator: '[\s\u200b\-_,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])'
- - git-authors
- - mike:
- version_selector: true
- - mkdocstrings:
- handlers:
- python:
- options:
- docstring_style: google
- - git-revision-date-localized:
- enable_creation_date: true
- fallback_to_build_date: true
- timezone: Asia/Shanghai
- type: timeago
- custom_format: "%d. %B %Y"
- - git-committers:
- repository: HydroRoll-Team/infini
- - minify:
- minify_html: true
-nav:
- - 主页: index.md
- - 水系核心:
- - "核心概览": Core/index.md
- - 规则包入门:
- - 概述: RulePackage/intro.md
- - 开始:
- - "类": RulePackage/getStart/class.md
- - "方法": RulePackage/getStart/method.md
- - "属性": RulePackage/getStart/attribute.md
- - 深入:
- - "对象": RulePackage/getStart/getDeeper/object.md
- - "模块": RulePackage/getStart/getDeeper/Module.md
- - "包": RulePackage/getStart/getDeeper/Package.md
- - 实战:
- - "了解Python": RulePackage/Write/python.md
- - "准备规则书": RulePackage/Write/prepare.md
- - "编写规则包": RulePackage/Write/write.md
- - 技巧:
- - RulePackage/skill/index.md
- - "善用语法糖": RulePackage/skill/fstring.md
- - 通用规则包标准:
- - 什么是规则包?: Standard/what-is-rule-package.md
- - 为什么要做规则包?: Standard/why-rule-package.md
- - 什么是通用规则包标准?: Standard/what-is-standard.md
- - 术语表(草案):
- - setting 背景: Standard/Term/setting.md
- - scenario 剧本: Standard/Term/scenario.md
- - duration 时长:
- - Standard/Term/duration/index.md
- - sene 场: Standard/Term/duration/sene.md
- - module 模组: Standard/Term/duration/module.md
- - campaign 战役: Standard/Term/duration/campaign.md
- - DefaultDice 默认骰子:
- - Standard/Term/DefaultDice/index.md
- - _sides 骰子面数: Standard/Term/DefaultDice/_sides.md
- - _counts 骰子个数: Standard/Term/DefaultDice/_counts.md
- - check 检定:
- - Standard/Term/check/index.md
- - ability 检定: Standard/Term/check/ability.md
- - PlayerCard 人物卡:
- - Standard/Term/PlayerCard/index.md
- - CLI 参考:
- - "CLI Reference": CLI/index.md
- - API 参考:
- - "API Reference": API/index.md
- - 其他帮助:
- - "术语表一览": Help/TermList.md
- - "Python 技巧": Help/PythonSkill.md
- - "图标、图例、注释说明": Help/emoji.md
- - 博客:
- - blog/index.md
- - 日志:
- - changelog.md
-
-extra_css:
- - stylesheets/extra.css
-
-extra_javascript:
- - "https://samuelcolvin.github.io/mkdocs-run-code/run_code_main.js"
diff --git a/netlify.toml b/netlify.toml
deleted file mode 100644
index 37b79714..00000000
--- a/netlify.toml
+++ /dev/null
@@ -1,4 +0,0 @@
-# netlify.toml
-[build]
- command = "mkdocs build"
- publish = "site"
diff --git a/src/infini/__init__.py b/src/infini/__init__.py
index af9b8fa1..e69de29b 100644
--- a/src/infini/__init__.py
+++ b/src/infini/__init__.py
@@ -1,28 +0,0 @@
-"""Infini
-
-Core of HydroRoll, python version of GRPS-1.
-
-
-本模块从子模块导入了以下内容:
-- `Handler` => [`infini.handler`](./handler#Handler)
-- `MessageEvent` => [`infini.event.MessageEvent`](./event#MessageEvent)
-- `MatcherEvent` => [`infini.event.MatcherEvent`](./event#MatcherEvent)
-- `WorkflowEvent` => [`infini.event.WorkflowEvent`](./event#WorkflowEvent)
-- `Matcher` => [`infini.matcher.Matcher`](./matcher/#Matcher)
-- `Register` => [`infini.register.Register`](./register#Register)
-"""
-
-
-from infini.handler import Handler
-from infini.event import MessageEvent, MatcherEvent, WorkflowEvent
-from infini.matcher import Matcher
-from infini.register import Register
-
-__all__ = [
- "Handler",
- "MessageEvent",
- "Matcher",
- "Register",
- "MatcherEvent",
- "WorkflowEvent",
-]
diff --git a/src/infini/const.py b/src/infini/const.py
new file mode 100644
index 00000000..2ec42488
--- /dev/null
+++ b/src/infini/const.py
@@ -0,0 +1,3 @@
+from pathlib import Path
+
+SRC_HOME = Path.home() / ".ipm" / "src" \ No newline at end of file
diff --git a/src/infini/core.py b/src/infini/core.py
new file mode 100644
index 00000000..2bb3e7da
--- /dev/null
+++ b/src/infini/core.py
@@ -0,0 +1,39 @@
+from infini.input import Input
+from infini.interceptor import Interceptor
+from infini.generator import Generator
+from infini.handler import Handler
+from infini.output import Output
+
+
+class Core:
+ pre_interceptor: Interceptor
+ handler: Handler
+ generator: Generator
+ interceptor: Interceptor
+
+ def input(self, input: Input):
+ if isinstance(pre_intercepted_stream := self.pre_intercept(input), Output):
+ yield self.generate(pre_intercepted_stream)
+ return
+ for handled_stream in self.handle(pre_intercepted_stream):
+ if handled_stream.is_empty():
+ return # TODO 处理未找到 Handler
+ yield self.intercept(self.generate(handled_stream))
+
+ def pre_intercept(self, input: Input) -> Input | Output:
+ return self.pre_interceptor.input(input)
+
+ def handle(self, input: Input):
+ iterator = self.handler.input(input)
+ for output in iterator:
+ yield output
+
+ def generate(self, output: Output) -> str:
+ return output.name # TODO 生成器实现
+
+ def intercept(self, output: str) -> str:
+ return (
+ self.generate(callback)
+ if isinstance(callback := self.interceptor.output(output), Output)
+ else callback
+ )
diff --git a/src/infini/event.py b/src/infini/event.py
deleted file mode 100644
index 5ef9444e..00000000
--- a/src/infini/event.py
+++ /dev/null
@@ -1,98 +0,0 @@
-"""Infini 事件模块
-
-此模块定义了 Infini 的事件类,包括 InfiniEvent, MessageEvent, WorkflowEvent 和 MatcherEvent。
-这些事件类用于在 Infini 框架中处理各种事件。
-"""
-
-from abc import ABCMeta, abstractmethod
-from .typing import Dict, Any
-
-__all__ = [
- "InfiniEvent",
- "MessageEvent",
- "WorkflowEvent",
- "MatcherEvent",
-]
-
-
-class InfiniEvent(metaclass=ABCMeta):
- """Infini 事件基类"""
-
- name: str
- kwargs: Dict[str, Any]
-
- @abstractmethod
- def __repr__(self) -> str:
- raise NotImplementedError
-
- def get_event_name(self) -> str:
- return self.name
-
-
-class MessageEvent(InfiniEvent):
- """Message 事件"""
-
- name: str
- output: str
- kwargs: Dict[str, Any]
-
- def __init__(self, name: str, **kwargs) -> None:
- self.name = name
- self.kwargs = kwargs
-
- def __str__(self) -> str:
- return f"<MessageEvent [{self.name}]>"
-
- def __repr__(self) -> str:
- return self.__str__()
-
-
-class WorkflowEvent(InfiniEvent):
- """Workflow 事件"""
-
- name: str
- kwargs: Dict[str, Any]
-
- def __init__(self, name: str, **kwargs) -> None:
- self.name = name
- self.kwargs = kwargs
-
- def __repr__(self) -> str:
- return f"<WorkflowEvent [{self.name}]>"
-
- def __eq__(self, __value: object) -> bool:
- if __value is str:
- return self.name == __value
- if isinstance(__value, WorkflowEvent):
- return self.name == __value.name and self.kwargs == __value.kwargs
- return False
-
-
-class MatcherEvent(InfiniEvent):
- """Matcher 事件"""
-
- name: str
- prefix: str
- string: str
- kwargs: Dict[str, Any]
-
- def __init__(
- self,
- event_name: str,
- prefix: str | None = None,
- string: str | None = None,
- **kwargs,
- ):
- self.name = event_name
- self.prefix = prefix or ""
- self.string = string or ""
- self.kwargs = kwargs
-
- def __repr__(self) -> str:
- return f"<MatcherEvent [{self.name}]>"
-
- def get_prefix(self):
- return self.prefix
-
- def get_plain_text(self):
- return self.string
diff --git a/src/infini/exceptions.py b/src/infini/exceptions.py
index 2038efcd..cb0a9e41 100644
--- a/src/infini/exceptions.py
+++ b/src/infini/exceptions.py
@@ -1,45 +1,6 @@
-"""Infini 异常处理模块
-
-此模块定义了 Infini 项目中所有的自定义异常类。
-规则包作者后续实现的每个异常类都应该继承自 InfiniException。
-"""
-
-
class InfiniException(Exception):
"""Infini 异常基类"""
-class RulePackageException(InfiniException):
- """由规则包抛出的异常基类, 所有规则包抛出的异常都应该继承此类。"""
-
-
-class LoadError(InfiniException):
- """加载规则包错误, 找不到指定的规则包或事件声明有误导致运行时错误时抛出。"""
-
-
-class PackageNotFound(LoadError):
- """规则包不存在时错误"""
-
-
-class EventLoadError(LoadError, RuntimeError):
- """事件声明导入失败"""
-
-
-class HandlerLoadError(LoadError, RuntimeError):
- """业务函数导入失败"""
-
-
-class EventException(InfiniException):
- """事件异常基类"""
-
-
-class UnknownMatcherEvent(EventException):
- """未知的给入实现"""
-
-
-class UnknownMessageEvent(EventException):
- """未知的给出实现"""
-
-
-class UnsupportedError(EventException, RuntimeError):
- """方法未被支持"""
+class KeyError(InfiniException):
+ """键值错误"""
diff --git a/src/infini/generator.py b/src/infini/generator.py
new file mode 100644
index 00000000..a4d60e27
--- /dev/null
+++ b/src/infini/generator.py
@@ -0,0 +1,2 @@
+class Generator:
+ ...
diff --git a/src/infini/handler.py b/src/infini/handler.py
index 0f72eccf..ae3561f8 100644
--- a/src/infini/handler.py
+++ b/src/infini/handler.py
@@ -1,34 +1,28 @@
-"""Infini Handler
-
-此类是所有规则包业务类的基类。
-每个规则包业务类都需要定义一个名为 process 的抽象方法, 用于接收和处理传入的 MatcherEvent 事件,并返回一个 InfiniEvent 事件。
-此外,每个规则包业务类还可以定义一个名为 priority 的类属性,用于指定该业务类的优先级。优先级越高,该业务类处理事件的顺序越靠前。
-"""
-
-from abc import ABC, abstractmethod
-from enum import Enum
-from .typing import StateT, ClassVar, Generic
-from .event import MatcherEvent, InfiniEvent
-
-__all__ = ["Handler", "HandlerLoadType"]
-
-
-class HandlerLoadType(Enum):
- """规则包加载类型。"""
-
- DIR = "dir"
- NAME = "name"
- FILE = "file"
- CLASS = "class"
-
-
-class Handler(ABC, Generic[StateT]):
- """规则包业务基类"""
-
- name: str
- priority: ClassVar[int] = 0
- block: ClassVar[bool] = False
-
- @abstractmethod
- def process(self, event: MatcherEvent) -> InfiniEvent:
- raise NotImplementedError
+from infini.input import Input
+from infini.output import Output
+from infini.typing import List, RouterType, Callable
+from infini.queue import EventQueue
+
+
+class Handler:
+ handlers: List[RouterType]
+
+ def input(self, input: Input):
+ queue = self.match(input.get_plain_text())
+ if queue.is_empty():
+ yield Output.empty()
+ return
+ while not queue.is_empty():
+ output = queue.pop()(input)
+ yield output
+ if output.block:
+ return
+
+ def match(self, text: str) -> EventQueue[Callable[[Input], Output]]:
+ queue = EventQueue()
+
+ for handler in self.handlers:
+ if handler["router"].match(text):
+ queue.push(handler["priority"], handler["handler"])
+
+ return queue
diff --git a/src/infini/input.py b/src/infini/input.py
new file mode 100644
index 00000000..21c12a2f
--- /dev/null
+++ b/src/infini/input.py
@@ -0,0 +1,21 @@
+from infini.typing import Dict, Any, Generic, T
+
+
+class Input(Generic[T]):
+ plain_data: T
+ variables: Dict[str, Any]
+
+ def __init__(self, plain_data: Any, variables: Dict[str, Any] | None = None) -> None:
+ self.plain_data = plain_data
+ self.variables = variables or {}
+
+ def get_session_id(self) -> str:
+ if session_id := self.variables.get("session_id"):
+ return session_id
+
+ user_id = self.variables.get("user_id", "unknown")
+ group_id = self.variables.get("group_id", "unknown")
+ return f"session_{group_id}_{user_id}"
+
+ def get_plain_text(self) -> str:
+ return str(self.plain_data)
diff --git a/src/infini/interceptor.py b/src/infini/interceptor.py
new file mode 100644
index 00000000..54387281
--- /dev/null
+++ b/src/infini/interceptor.py
@@ -0,0 +1,35 @@
+from infini.input import Input
+from infini.output import Output
+from infini.typing import List, RouterType, Callable, Generic, T, overload
+from infini.queue import EventQueue
+
+
+class Interceptor:
+ interceptors: List[RouterType]
+
+ def input(self, input: Input) -> Input | Output:
+ queue = self.match(input.get_plain_text())
+ while not queue.is_empty():
+ if isinstance(intercepted := queue.pop()(input), Output):
+ return intercepted # TODO 允许拦截器产出文本
+ else:
+ input = intercepted
+ return input
+
+ def output(self, output_text: str) -> str | Output:
+ queue = self.match(output_text)
+ while not queue.is_empty():
+ if isinstance(intercepted := queue.pop()(input), Output):
+ return intercepted
+ else:
+ input = intercepted
+ return output_text
+
+ def match(self, text: str) -> EventQueue[Callable[[Input], Input | Output]]:
+ queue = EventQueue()
+
+ for interceptor in self.interceptors:
+ if interceptor["router"].match(text):
+ queue.push(interceptor["priority"], interceptor["handler"])
+
+ return queue
diff --git a/src/infini/internal.py b/src/infini/internal.py
new file mode 100644
index 00000000..4f800297
--- /dev/null
+++ b/src/infini/internal.py
@@ -0,0 +1,18 @@
+from infini.typing import List, ModuleType
+from infini.const import SRC_HOME
+
+import importlib
+import sys
+
+
+def require(name: str, paths: List | None = None) -> ModuleType:
+ paths = [
+ str(path)
+ for path in (
+ (list(paths) + [str(SRC_HOME / name)]) if paths else [str(SRC_HOME / name)]
+ )
+ ]
+ sys.path.extend(paths)
+ module = importlib.import_module(name)
+ (sys.path.remove(path) for path in paths)
+ return module
diff --git a/src/infini/logging.py b/src/infini/logging.py
deleted file mode 100644
index 979fee7d..00000000
--- a/src/infini/logging.py
+++ /dev/null
@@ -1,33 +0,0 @@
-"""Infini 日志。
-
-Infini 使用 [loguru](https://github.com/Delgan/loguru) 来记录日志信息。
-自定义 logger 请参考 [loguru](https://github.com/Delgan/loguru) 文档。
-"""
-from datetime import datetime
-from multilogging import multilogger
-from pathlib import Path
-from .settings import DEBUG
-
-
-__all__ = ["logger", "error_or_exception"]
-
-logger = multilogger(name="Infini", payload="Core",
- level="DEBUG" if DEBUG else "INFO")
-CURRENT_PATH = Path(__file__).resolve().parent
-DATA_PATH = Path.home() / ".infini"
-LOG_PATH = DATA_PATH / "logs"
-if not LOG_PATH.exists():
- LOG_PATH.mkdir(parents=True, exist_ok=True)
-logger.add(
- sink=LOG_PATH / (datetime.now().strftime("%Y-%m-%d") + ".log"),
- level="INFO",
- rotation="10 MB",
-) # 每个日志文件最大为 10MB
-
-
-def error_or_exception(message: str, exception: Exception, verbose: bool = True):
- if verbose:
- logger.exception(exception)
- logger.critical(message)
- else:
- logger.critical(f"{message} {exception!r}")
diff --git a/src/infini/matcher.py b/src/infini/matcher.py
deleted file mode 100644
index 8c11ff29..00000000
--- a/src/infini/matcher.py
+++ /dev/null
@@ -1,34 +0,0 @@
-"""Infini Matcher
-
-用于处理和匹配事件。
-"""
-
-from .register import Register, register
-from .event import MatcherEvent
-from .handler import Handler
-from .exceptions import UnknownMatcherEvent
-
-
-class Matcher:
- """事件处理单元"""
-
- register: Register
-
- def __init__(self, _register: Register | None = None) -> None:
- self.register = _register or register
-
- def match(self, name: str) -> Handler:
- if handler := self.register.handlers.match(name):
- return handler if isinstance(handler, Handler) else handler()
- else:
- raise UnknownMatcherEvent(f"未知的规则包: {name}")
-
- def run(self, event: MatcherEvent) -> str:
- callback_event = self.match(event.name).process(event)
- return self.register.events.process(
- callback_event.name,
- **callback_event.kwargs or callback_event.kwargs,
- )
-
-
-matcher = Matcher()
diff --git a/src/infini/output.py b/src/infini/output.py
new file mode 100644
index 00000000..ddb7f0a5
--- /dev/null
+++ b/src/infini/output.py
@@ -0,0 +1,19 @@
+from infini.typing import Literal
+
+
+class Output:
+ type: Literal["null", "text", "workflow"]
+ name: str
+ status: int
+ block: bool
+
+ @classmethod
+ def empty(cls) -> "Output":
+ output = cls()
+ output.type = "null"
+ output.status = 0
+ output.block = True
+ return output
+
+ def is_empty(self) -> bool:
+ return self.type == "null"
diff --git a/src/infini/queue.py b/src/infini/queue.py
new file mode 100644
index 00000000..9dd441ee
--- /dev/null
+++ b/src/infini/queue.py
@@ -0,0 +1,47 @@
+from infini.exceptions import KeyError
+from infini.typing import Generic, T
+from collections import deque
+
+import heapq
+
+
+class EventQueue(Generic[T]):
+ heap: list
+ entry_finder: dict
+ counter: int
+ order_queue: deque
+
+ def __init__(self) -> None:
+ self.heap = []
+ self.entry_finder = {}
+ self.counter = 0
+ self.order_queue = deque()
+
+ def push(self, priority: int, item: T) -> None:
+ if item in self.entry_finder:
+ self.remove(item)
+ entry = [priority, self.counter, item]
+ self.counter += 1
+ self.entry_finder[item] = entry
+ heapq.heappush(self.heap, entry)
+ self.order_queue.appendleft(item)
+
+ def pop(self) -> T:
+ while self.heap:
+ _, _, item = heapq.heappop(self.heap)
+ if self.entry_finder[item] is not None:
+ del self.entry_finder[item]
+ self.order_queue.remove(item)
+ return item
+ raise KeyError("事件队列为空.")
+
+ def remove(self, item) -> None:
+ entry = self.entry_finder.pop(item)
+ entry[-1] = None
+
+ def is_empty(self) -> bool:
+ return not bool(self.heap)
+
+ def generate(self):
+ while not self.is_empty():
+ yield self.pop()
diff --git a/src/infini/register.py b/src/infini/register.py
deleted file mode 100644
index 55677152..00000000
--- a/src/infini/register.py
+++ /dev/null
@@ -1,163 +0,0 @@
-"""Infini 注册模块
-
-包含了 Infini 事件和处理器的注册和加载功能。
-"""
-
-from pathlib import Path
-from .exceptions import UnknownMatcherEvent, UnsupportedError
-from .handler import Handler
-from .event import InfiniEvent, MessageEvent, WorkflowEvent
-from .typing import Dict, Type
-from .exceptions import LoadError, EventLoadError, HandlerLoadError
-
-import re
-import sys
-import importlib
-import inspect
-
-
-class Loader:
- """Infini 事件加载器
-
- Raises:
- LoadError: 规则包导入错误
- EventLoadError: 规则包事件导入错误
- HandlerLoadError: 规则包业务函数导入错误
- """
-
- name: str
- meta_path: Path
- events: dict
- handlers: dict
-
- def __init__(self, meta_path: Path | str) -> None:
- if isinstance(meta_path, str):
- self.meta_path = Path(meta_path).resolve()
- else:
- self.meta_path = meta_path.resolve()
- self.name = self.meta_path.name
- self.events = {}
- self.handlers = {}
-
- def load(self) -> None:
- sys.path.append(str(self.meta_path))
- try:
- importlib.import_module(f"{self.name}")
- except Exception as error:
- raise LoadError(f"规则包[{self.name}]导入失败: {error}") from error
-
- try:
- event_module = importlib.import_module(f"{self.name}.event")
- events = [
- cls[1]
- for cls in inspect.getmembers(event_module, inspect.isclass)
- if issubclass(cls[1], InfiniEvent)
- and not cls[1].__module__.startswith("infini")
- ]
- for event in events:
- self.events[f"{self.name}.{event.__dict__['name']}"] = event
- except Exception as error:
- raise EventLoadError(f"规则包[{self.name}]事件导入失败: {error}") from error
-
- try:
- handler_module = importlib.import_module(f"{self.name}.handler")
- handlers = [
- cls[1]
- for cls in inspect.getmembers(handler_module, inspect.isclass)
- if issubclass(cls[1], Handler)
- and not cls[1].__module__.startswith("infini")
- ]
- for handler in handlers:
- self.handlers[f"{self.name}.{handler.__dict__['name']}"] = handler
- except Exception as error:
- raise HandlerLoadError(f"规则包[{self.name}]业务函数导入失败: {error}") from error
-
- sys.path.remove(str(self.meta_path))
-
-
-class Events:
- """规则包事件集合
-
- Raises:
- UnsupportedError: 事件尚未被支持
- UnknownMatcherEvent: 事件不存在
-
- Returns:
- _type_: _description_
- """
-
- _events: Dict[str, Type[InfiniEvent]]
-
- def __init__(self) -> None:
- self._events = {}
-
- def register(self, name: str, event: Type[InfiniEvent]) -> None:
- self._events[name.lower()] = event
-
- def update(self, _events: dict) -> None:
- self._events.update(_events)
-
- def _process(self, event: Type[InfiniEvent], **kwargs) -> str:
- # sourcery skip: merge-duplicate-blocks
- if issubclass(event, MessageEvent):
- return self._format(event.__dict__["output"], **kwargs)
- elif issubclass(event, WorkflowEvent):
- # TODO: handle workflow events
- raise UnsupportedError
- else:
- raise UnsupportedError
-
- def process(self, name: str, **kwargs) -> str:
- if event := self._events.get(name.lower()):
- return self._process(event, **kwargs)
- raise UnknownMatcherEvent(f"事件[{name.lower()}]不存在!")
-
- def _format(self, string: str, **kwargs):
- pattern = r"{(.*?)}"
- values = re.findall(pattern, string)
- for value in values:
- kwarg = kwargs.get(value)
- value = kwarg or ""
- string = re.sub(pattern, value, string)
- return string
-
-
-class Handlers:
- """规则包业务集合
-
- Returns:
- _type_: _description_
- """
-
- _handlers: Dict[str, Handler] = {}
-
- def register(self, name: str, handler: Handler) -> None:
- self._handlers[name.lower()] = handler
-
- def update(self, _events: dict) -> None:
- self._handlers.update(_events)
-
- def match(self, name: str) -> Handler | None:
- return self._handlers.get(name.lower())
-
-
-class Register:
- """注册器"""
-
- events: Events
- handlers: Handlers
-
- def __init__(self) -> None:
- self.events = Events()
- self.handlers = Handlers()
-
- def register(
- self, meta_path: Path | str | None = None, loader: Type[Loader] | None = None
- ):
- _loader = Loader(meta_path or ".") if not loader else loader(meta_path or ".")
- _loader.load()
- self.events.update(_loader.events)
- self.handlers.update(_loader.handlers)
-
-
-register = Register()
diff --git a/src/infini/router.py b/src/infini/router.py
new file mode 100644
index 00000000..a520a456
--- /dev/null
+++ b/src/infini/router.py
@@ -0,0 +1,21 @@
+from infini.typing import overload
+
+
+class Router:
+ sign: str
+
+ def __init__(self, sign: str) -> None:
+ self.sign = sign
+
+ def match(self, input: str) -> bool:
+ return self.sign == input.strip()
+
+
+class StartswithRouter(Router):
+ def match(self, input: str) -> bool:
+ return input.strip().startswith(self.sign)
+
+
+class ContainsRouter(Router):
+ def match(self, input: str) -> bool:
+ return self.sign in input.strip()
diff --git a/src/infini/settings.py b/src/infini/settings.py
deleted file mode 100644
index 51c64dd2..00000000
--- a/src/infini/settings.py
+++ /dev/null
@@ -1 +0,0 @@
-DEBUG = True
diff --git a/src/infini/typing.py b/src/infini/typing.py
index 0da72385..0a0d3292 100644
--- a/src/infini/typing.py
+++ b/src/infini/typing.py
@@ -1,24 +1,22 @@
-"""Infini 类型提示支持。
-
-此模块定义了部分 Infini 使用的类型。
-"""
-
from typing import (
Dict as Dict,
+ List as List,
Any as Any,
- Type as Type,
- ClassVar as ClassVar,
Generic as Generic,
- TYPE_CHECKING as TYPE_CHECKING,
- TypeVar as TypeVar,
Callable as Callable,
- NoReturn as NoReturn,
- Awaitable as Awaitable,
+ Literal as Literal,
+ overload as overload,
+ TypeVar,
+ TypedDict,
+ Union,
)
+from types import ModuleType as ModuleType
+from . import router, input, output
-if TYPE_CHECKING:
- from typing import Any
+T = TypeVar("T")
-__all__ = ["StateT"]
-StateT = TypeVar("StateT")
+class RouterType(TypedDict):
+ priority: int
+ router: router.Router
+ handler: Callable[["input.Input"], Union["input.Input", "output.Output"]]
diff --git a/src/infini/utils/cli.py b/src/infini/utils/cli.py
deleted file mode 100644
index 3d30f839..00000000
--- a/src/infini/utils/cli.py
+++ /dev/null
@@ -1,28 +0,0 @@
-"""
-Infini 终端命令解析模块
-"""
-
-import argparse
-import sys
-
-
-def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
- # sourcery skip: extract-duplicate-method
- parser = argparse.ArgumentParser(prog="Infini CLI", description="Infini 命令行工具")
-
- parser.add_argument("--gui", action="store_true", help="启用 GUI 模式")
-
- subparsers = parser.add_subparsers(title="功能件", dest="operate")
-
- # 子命令 `new`
- new_parser = subparsers.add_parser("new", help="创建一个 Infini 规则包模板")
- new_parser.add_argument("path", help="目标位置")
- new_parser.add_argument("-v", "--verbose", action="store_true", help="异常追踪")
- new_parser.add_argument("-f", "--force", action="store_true", help="强制创建")
-
- # 子命令 `test`
- test_parser = subparsers.add_parser("test", help="测试 Infini 规则包")
- test_parser.add_argument("path", help="目标位置")
- test_parser.add_argument("-v", "--verbose", action="store_true", help="异常追踪")
-
- return parser.parse_args(sys.argv[1:] or argv or ["--help"])
diff --git a/src/infini/utils/gui.py b/src/infini/utils/gui.py
deleted file mode 100644
index 5e8c91c4..00000000
--- a/src/infini/utils/gui.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""
-Infini 图形用户界面模块
-"""
-
-# TODO
diff --git a/tests/test_handlers.py b/tests/test_handlers.py
new file mode 100644
index 00000000..8d2d2894
--- /dev/null
+++ b/tests/test_handlers.py
@@ -0,0 +1,84 @@
+from infini.handler import Handler
+from infini.input import Input
+from infini.output import Output
+from infini.router import StartswithRouter
+
+
+def test_handler():
+ input = Input(".add 1 2")
+
+ def add(input: Input) -> Output:
+ a, b = map(int, input.get_plain_text().lstrip(".add").split())
+ output = Output()
+ output.block = False
+ output.status = 0
+ output.type = "text"
+ output.name = str(a + b)
+ return output
+
+ def cmd(_: Input) -> Output:
+ output = Output()
+ output.block = False
+ output.status = 0
+ output.type = "text"
+ output.name = "cmd"
+ return output
+
+ handler = Handler()
+ handler.handlers = [
+ {
+ "priority": 2,
+ "router": StartswithRouter(".add"),
+ "handler": add,
+ },
+ {
+ "priority": 1,
+ "router": StartswithRouter("."),
+ "handler": cmd,
+ },
+ ]
+
+ names = []
+ for output in handler.input(input):
+ names.append(output.name)
+ assert names == ["cmd", "3"]
+
+
+def test_handler_block():
+ input = Input(".add 1 2")
+
+ def add(input: Input) -> Output:
+ a, b = map(int, input.get_plain_text().lstrip(".add").split())
+ output = Output()
+ output.block = False
+ output.status = 0
+ output.type = "text"
+ output.name = str(a + b)
+ return output
+
+ def cmd(_: Input) -> Output:
+ output = Output()
+ output.block = True
+ output.status = 0
+ output.type = "text"
+ output.name = "cmd"
+ return output
+
+ handler = Handler()
+ handler.handlers = [
+ {
+ "priority": 2,
+ "router": StartswithRouter(".add"),
+ "handler": add,
+ },
+ {
+ "priority": 1,
+ "router": StartswithRouter("."),
+ "handler": cmd,
+ },
+ ]
+
+ names = []
+ for output in handler.input(input):
+ names.append(output.name)
+ assert names == ["cmd"]
diff --git a/tests/test_input.py b/tests/test_input.py
new file mode 100644
index 00000000..5f44ca1e
--- /dev/null
+++ b/tests/test_input.py
@@ -0,0 +1,16 @@
+from infini.input import Input
+
+
+def test_new_input_without_vars():
+ assert Input("test plain_str").plain_data == "test plain_str"
+ assert Input("test plain_str").get_plain_text() == "test plain_str"
+
+
+def test_new_input_with_session_id():
+ input = Input("test plain_str", variables={"session_id": "test"})
+ assert input.get_session_id() == "test"
+
+
+def test_new_input_without_session_id():
+ input = Input("test plain_str", variables={"user_id": "test"})
+ assert input.get_session_id() == "session_unknown_test"
diff --git a/tests/test_interceptor.py b/tests/test_interceptor.py
new file mode 100644
index 00000000..df5b6750
--- /dev/null
+++ b/tests/test_interceptor.py
@@ -0,0 +1,33 @@
+from infini.input import Input
+from infini.interceptor import Interceptor
+from infini.output import Output
+from infini.router import ContainsRouter
+
+
+def test_interceptor():
+ input = Input("这个人叫简律纯.")
+ valid_input = Input("这个叫苏向夜.")
+
+ def intercept(_: Input) -> Input | Output:
+ output = Output()
+ output.block = True # TODO 拦截器阻塞标识
+ output.name = "block.jianlvchun"
+ output.status = 0
+ output.type = "text"
+ return output
+
+ interceptor = Interceptor()
+ interceptor.interceptors = [
+ {
+ "priority": 1,
+ "router": ContainsRouter("简律纯"),
+ "handler": intercept,
+ }
+ ]
+ output = interceptor.input(input)
+ assert isinstance(output, Output)
+ assert output.name == "block.jianlvchun"
+
+ valid_output = interceptor.input(valid_input)
+ assert isinstance(valid_output, Input)
+ assert valid_output.get_plain_text() == "这个叫苏向夜."