graiaproject / avilla Goto Github PK
View Code? Open in Web Editor NEWThe next-gen framework for IM development. Powered by Graia Project.
License: MIT License
The next-gen framework for IM development. Powered by Graia Project.
License: MIT License
如题, 在 readme 里放个 ecosystem.md, 然后单独放一个文件就好
使用一系列工具链(black flake8 etc.), 提升代码的质量和可读性
Relationship 上的交互式重构,基于最小功能单元(Minimal Function Unit)设计。
因为计划在下一个版本中加入设计中的 Platform API, 我觉得可以搞一个 PlatformDB 出来, 记录各种 Protocol 实现的相对于适配的对象的 Platform 信息.
本次重构的 Landmark 版本被设定为 1.0.0-alpha.5
.
本次主要对各种用名进行修改, 对 Relationship
模型进行重构, 以及分割 core 部件内的框架抽象到 avilla.core.abstract
处, 这将会不可避免的带来 API 的破坏性改动和用户体验的变化.
ContextClientSelector
ContextSceneSelector
ContextEndpointSelector
ContextRequestSelector
context.endpoint.expects_request() -> ContextRequestSelector
context.request (property)
Context.wrap(bound: Selector) -> ContextSelector
Context.wrap(bound: MetadataOf) -> ContextWrappedMetadataOf
Context.wrap(bound: Selector, trait: type[T extends Trait]) -> T
Context.wrap(bound: MetadataOf, trait: type[T extends Trait]) -> T
ContextSelector.wrap(trait: type[T extends Trait]) -> T
ContextWrappedMetadata.wrap(trait: type[T extends Trait]) -> T
ContextSelector.pull(metadata: type[T extends Metadata] | MetadataRoute[..._L, T]) -> T
ContextWrappedMetadataOf[T].pull() -> T
bound(...)
impl
and others.以下更改同时会影响到 Filter
等内置组件上的方法命名, 请尤其是该提案的实现编写者注意.
Relationship => Context
Relationship.ctx => Context.client & Context.endpoint
Relationship.mainline => Context.scene
Relationship.via => Context.mediums (type: list[ContextMedium])
Relationship.cast =>
| Context.wrap(bound: Selector, trait: type[T extends Trait]) -> T
| Context.wrap(bound: Selector) -> ContextSelector
Cell => Metadata
CellOf => MetadataRoute
ContextSelector
该 Selector 同时包装了一个 Context, 使得可以配合其完成更多的操作, 这也导致了一些代码风格上的变化.
Context.{client, endpoint, scene, mediums[number].selector}
的类型都为 ContextSelector
.
await context.scene.send_message(...)
await context.scene.pull(...) # equals to context.pull(scene, ...)
await context.scene.wrap(trait) # equals to context.wrap(scene, trait)
目前正在考虑实现例如 ContextClientSelector
, ContextSceneSelector
等派生类, 用于进一步的提供高效的 API 实现.
Selector
相关新加入 Selector.expects
方法, 用于对 Selector 的内容进行断言. 在语法上使用 follows-style, 同时 follows-style 作为优雅的模式表达方式将得到进一步的推广.
selector = Selector().group("123").member("456")
selector.expects("group.member") # pass
selector.expects("group(123).member") # pass
selector.expects("friend") # error with ValueError
Metadata.of
& MetadataRoute.of
需要注意的是, 这里的 MetadataOf
与先前版本中的 CellOf
完全不同.
MetadataOf
的声明:
@dataclass
class MetadataOf(Generic[T]): # T bounds type[Metadata] | MetadataRoute
target: Selector
describe: T
可以这样创建:
Summary.of(context.scene) # Metadata.of
(Privilege >> Summary).of(context.self) # MetadataRoute.of
Context.wrap
该 API 不仅用于替代 Relationship.cast
, 还用于简单的包装 Selector 为 ContextSelector
.
该方法会自动运行 Context.complete
, 且自动忽略 land
字段.
# 以下几种调用方法等价.
ctx.wrap(Selector().group(...)).wrap(MessageSend).send(...)
ctx.wrap(Selector().group(...), MessageSend).send(...)
对于 Metadata 上的 bound, 这里有一个简单的草案, 但未经验证:
x = ctx.wrap(bound=Metadata.of(selector)) # -> ContextWrappedMetadataOf
x.wrap(...) # -> Trait
Context.check
!> 该项仍然处于设计中(In-Design)阶段.
在 1.0.0rc4
中, Relationship.check
尚未被实现, 1.0.0rc5
将着手相关工程.
await ctx.check_existence(...) # 确认其存在, 因例如权限原因无法确认就抛出 CheckFailed (lint: 流程控制问题警告)
await ctx.check_accessible(...) # 确认其可操作性, 包括权限等
...
由于要确认的包括且不限于 Metadata, Selector, Trait 之类的交互形式, 所以该项仍然处于设计中阶段.
impl
相关鉴于 bound 在 Context.wrap -> Trait
中的强制要求, 协议实现中 impl
的方式亦有所改变.
with bound("group.member"):
@impl(MessageSend.send)
...
鉴于现在的自动从 AvillaEvent
生成 Context
/Relationship
和注入 Metadata
的方式实在是不堪入目,
从 1.0.0-alpha.5
开始, 事件的实例化强制要求 Context
作为第一个参数的传入.
这不会影响事件本身描述的完整性, 也就是说, MessageReceived.scene
等不会被删除.
Trait
, Fn
以及各式 recorder, AvillaEvent
事件基类, Resource
基类等部件将被移动至 avilla.core.abstract
处.
由 avilla-core
所提供的事件, 元信息类, 内建 Trait 等都被移动至 avilla.core.standards
下.
Relationship.send_message
等缩写形式不再受到支持, 其职权将由 ContextSelector
及其派生完成, 而不需要实现默认求值 (default_target
).default_target
不再提供.ImplRecorder.pin
, 实现签名类型 OrientFn
与 DirectFn
将不再提供.bound
辅助型上下文管理器的出现, prefix
被标记为弃用.MessageSend.send_message
等实现签名 Fn 的方法名现已被改为仅包含谓词的形式, 如 MessageSend.send
, 这将减少语义杂糅.Message
派生为多个消息类型, 如 ChatMessage
, NoticeMessage
, PlatformMessage
等.MetadataModified.modifies
的表述方式现已如下呈现, 因为引入 Context.endpoint
, 这一切都变得更简单了:Modify(
bound: Selector | MetadataOf,
field: str,
action: str,
past: Any,
present: Any # use generic ?
)
avilla.core.exceptions
, 现阶段其大部分设计都来自于 OneBot
的错误类型.BaseProtocol
使用单个 ProtocolManifest
实例表述, 并使用一套 Credential 机制或是 kayaku
描述账号及其鉴权信息.
login = (
MatrixLogin("elaina", "https://matrix.org")\
.auth_with_credential_file("session_credential.json")\
.auth_with_token(...)\
.auth_with_password(...)\
.auth_interactive()
)
因为 flake8 和 pylance 疯狂给 avilla.tools 下面的 kanata 和 literature 报错, 所以开个 issue, 打算什么时候清理掉
# 思考 -> 需要一个 MemberPoked 事件?(platform specific event)
# answer: yes, but a more generic name
# SpecialNotify?
# 在其他平台,比如 Telegram, 似乎并没有这种特殊的触发方式或是消息类型。
# 作为消息类型反而本末倒置:我删去了 user-space 的 reply/ source 就可以看出来这点。
# Discord 似乎也没有。。。
# 反倒是比如 Telegram 最近版本能给消息丢shit比心这样的。。这种又该怎么处理呢?
# ^^^^^^^^^^^^ MessageChanged?
# 啊,一般来说,我认为这种应该不属于 “一条由特定个体发出的消息” 的范畴。。。
# 虽然 Message 确实是 Mutable, 从 MessageEdit 就可以看出来。。。。
# answer(you): 拓展一下 Message, 将 Poke type element 与 Poke action 放到一起, 作为...... metadata?
# Message metadata 还可以存储些别的, 比如 reaction
# GitHub discussion 为什么不能当作 message flow 呢
# me: 或许将 Message 扩展一个 Notify, 不强调其 content 为 chain?
# 那那种我认为叫 subline 比较好(单纯是设计用词)
# well, the inputer of ubunti is shit, go to qq.
# 结论: 可动态获取的(方式可以是 Operator)的附加信息。
Avilla, Protocol, Relationship 中, 后者的上下文可以承担前者.
Avilla.current()
, get_current_protocol()
, Relationship.current()
(Relationship 似乎不需要变)
已经完成.
group.name
(mainline) -> str
group.description
(mainline) -> str
group.member.max_count
-> int
group.member.current_count
-> int
member.name
-> str
member.muted
-> bool
member.join_time
-> datetime
member.last_active
-> datetime
member.mute_period
-> timedelta
member.budget
-> str
member.nickname
-> str
self.name
-> str
self.nickname
-> str
self.budget
-> str
self.muted
-> bool
self.mute_period
-> timedelta
self.join_time
-> datetime
如题, 这样能让问题解决的流程更完善.
这个 issue 暂时得开着
已经完成.
=
: set
~
: reset
<-
: prev
->
: next
>]
: push
]>
: pop
+
: add
-
: remove
Forward 被用于描述 QQ (通常情况) 中的功能 "合并转发", 该功能被确认为平台特性.
应该为忽略而不是当做无事发生直接合进去
当你看到本段文本时, 本提案仍处于草案阶段
Avilla Resource 在写作该提案的当下, 已经实现了由 Protocol.fetch_resource
承担的, 并由 resource
选择器, ResourceAvailable
等事件的实现. 本提案提议了对于该部分的优化建议.
对于资源文件的管理(包括远端服务器上的), Avilla 仍然不具备充分有效的能力去处理,
开发者可能用 tempfile
等库对该逻辑进行一个并不完备的实现, 而由此导致一系列的不可迁移性, 部署困难和不必要的耦合性.
本提案提出了基于 Service
特性的 FilehostInterface
实现, 并于 Core 模块内提供 aiofile
( LocalfileProvider
), aiofile+tempfile
( TempfileProvider
) 的本地文件管理实现.
avilla.core.service.common.file
avilla.core.service.localfile
建议的包名:
avillax.service.amazon_s3
avillax.service.onedrive
所引用/新增的命名空间:
avillax
~ avillax.service
对于 资源(Resource)
, 我们的理解是: 具有一个在一定上下文中唯一的访问标识符; 以及通常都可以用 bytes
表示这两点.
由此来看, 资源与其说是文件, 不如说是 "类文件( file-like
)",
且不说 QQ
等 IM 中的典型资源类型: 图片, 就没办法单纯的使用 create
操作先在服务器上注册,
然后再使用 write
方法进行数据写入/修改, 而只能使用 put
操作直接传达数据并获取资源的访问标识符 -- 何况是 remove
操作呢?
而当然的, 对于真正的存储在硬盘/对象存储里的真正的文件 -- 即使是 Amazon S3
或者是 Onedrive
, 这些操作都是可用的, 我们需要一个较为通用的方案去处理这种情况.
本提案将对资源的操作, 先并称之为 "访问( access
)".
如果要像是进行 create
, write
这些操作, 我们得首先对一个资源或者是资源类型的上下文发起访问会话.
本提案目前所计划的访问方式:
create
: 创建资源, 此时资源应该会是一个空值或者是上下文中的默认值(类似 DefaultDict
);write
: 对资源进行写入, 默认不启用覆写;put
: 直接创建资源并写入, 如果成功将返回一个 resource
选择器实例;read
: 读取资源数据, 返回 Stream[bytes]
;remove
: 删除资源;stats
: 作为发起请求后的错误判断, 返回是 Status
对象.rename
: 更改资源的访问标识符;ls
: 对于类似 resource.dir[...]
的操作. 返回一个 List[resource_selector]
;meta
: 返回 MetaWrapper
的实例, 用于操作资源的元信息, 同样的也可以用 Generic
指定返回的是 CoreSupport
等(类似 Relationship)以下是本提案目前否决了的访问类型, 同样的也简述了相应的动机:
exists
: 使用 stats
操作即可;is_dir
/ is_file
/ typeof
: 资源的访问标识符基于 Selector
, 你可以通过 Selector.last
方法获取资源的类型(以 str
表示);chmod
: 本提案认为实际情况过于复杂, 不应该简单的使用 POSIX
的方式进行权限判定;这一设计将使用 BehaviourSession
作为基盘, 为了简单的复用已有的设计.
由于 Avilla 中的多账户支持等设计的相关因素, 本提案约定以下条规:
resource
选择器时, 加入 mainline
与 protocol
键.tencent-qq://
这样的前缀(对于用户来说也是如此.);resource
时, 无论什么时候都使用 Avilla.access_resource
或是其他的由 Avilla
实例提供的方法进行操作(即使有可能在获取上下文时发生 LookupError
);Protocol
负责的资源访问操作时, 先使用 get_status
方法初步确认能否进行, 避免不必要的代码健壮性问题.对于资源的元信息, 本提案提议使用 Avilla.get_resource_meta
或者是 meta
操作获取 MetaWrapper
的实例,
并使用 pyi
的形式模仿/复用已有的 CoreSupport
以提供优秀的 type hint
体验.
附录: 本小节中 Avilla
的相关方法的签名:
@asynccontextmanager
async def access_resource(self, res: resource_selector) -> AsyncGenerator[BehaviourSession, None]:
...
async def fetch_resource(self, res: resource_selector) -> Stream[bytes]:
...
async def get_resource_meta(self, res: resource_selector) -> MetaWrapper:
# 在实际中应该要用 typing.cast...
# 基于 meta
...
资源提供方( Resource Provider
) 是 Resource API 中使用到了 Service
的部分, 声明/扩展了以下参数:
supported_resource_types (Set[str])
: 支持的资源类型;id_prefix (str)
: 选择器中标识符的前缀.对于 Service.supported_description_types
,
这个字段由 ResourceProvider
进行了值预置, 以便于开发(虽然大部分时候你都得重载);
在 ResourceProvider
中同样也能使用 status
, 但这主要是为了在写 Protocol 时能把 ResourceProvider
直接混入进去.
当你要重载 supported_description_types
时, 记得把上面提到的所有的都给填上, 不管你到底有没有用到.
ResourceProvider
自然也是个抽象基类, 你需要实现 access_resource
方法, 签名和 Avilla.access_resource
大致一致,
或许你可以把 res
的 annotation 写成 Union[xxx]
?
这里如果出现 res.protocol
字段, 必定是将当前 ResourceProvider
作为 BaseProtocol.resource_provider (property)
的 Protocol
.
以下是一个简单的获取图片内容的示例:
class OnebotService(ResourceProvider, Service):
...
_avilla: Optional[Avilla] = None
@property
def avilla(self) -> "Avilla":
if self._avilla is None:
raise TypeError("service is not ready!")
return self._avilla
@asynccontextmanager
async def access_resource(self, res: resource_selector) -> AsyncGenerator[BehaviourSession, None]:
if res.last() == "image":
image_id = res.path['image'].partition("://")[2]
http_interface: HttpClient = self.avilla.get_interface(HttpClient)
async with http_interface.get(f"https://xxx.ooo.xxx/{image_id}") as session:
status = await session.execute(get_status)
content_stream = await session.execute(content_read)
yield BehaviourSession(...)
async def launch_prepare(self, avilla: Avilla):
self._avilla = avilla
@property
def launch_component(self) -> LaunchComponent:
return LaunchComponent(
"protocol.onebot.service",
{"http.universal_client"},
prepare=self.launch_prepare
)
关于详细的实现可以在代码库内找到.
以上就是一个 ResourceProvider
实现, 因为是 low level api 所以这么长, 但这也保证了其灵活性.
Avilla.access_resource
会自动做一个 map, 根据资源的类型和 protocol
中的值转接到相应的提供方.
具体的调用树就像这样:
+-----------------------------+ calls +-------------------------------+
| Avilla.access_resource(...) | -------------> | Protocol.access_resource(...) |
+-----------------------------+ +-------------------------------+
大概就到这里.
event
: 断言需要有事件(event is not None), 并以给出的参数为基类, 返回 Eventoptional_event
: 如果有事件才断言, 返回为 None.Required:
OnebotGroupFileResourceProvider
- OnebotGroupFileResourceOperator
OnebotVoiceResourceProvider
- OnebotVoiceResourceOperator
用于自动 locate_target 这些的封装,处理 Execution 本身或是触发一些其他的操作,比如 ContextVar 相关。
BehaviourSession
will be a param that bypass to a callback-like action(like websocket/http listen ), and if it's a multi-action, the "callback" can be a class.
and if it's a client-like but need to do some cleanning work, maybe we can use __del__
that push a callback to a task queue or positively call clean
method.
B.S. must remove async-with
, and to be a base class that can be expand some activities like WebsocketSession.
mainline = create_selector("mainline")
mainline.channel # -> SelectorKey("mainline.channel")
r = mainline.channel[123] # -> <mainline>.channel[123]
r.to_dict() # -> {"channel": 123}
class Relationship:
ctx: "Selector['rs.ctx']"
mainline: "Selector['mainline']"
metadata: Metadata
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.