Git Product home page Git Product logo

avilla's Issues

Onebot Resource Provider(s)

Required:

  • OnebotGroupFileResourceProvider - OnebotGroupFileResourceOperator
  • OnebotVoiceResourceProvider - OnebotVoiceResourceOperator
  • and more multi-media element

a huge change to service

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.

[AEP-C031] Enhanced Resource API

当你看到本段文本时, 本提案仍处于草案阶段

已有实现

摘要

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 表示这两点.

操作(Activity)

由此来看, 资源与其说是文件, 不如说是 "类文件( 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)
  • etc... 大概后面还会想到一点?

以下是本提案目前否决了的访问类型, 同样的也简述了相应的动机:

  • exists : 使用 stats 操作即可;
  • is_dir / is_file / typeof : 资源的访问标识符基于 Selector , 你可以通过 Selector.last 方法获取资源的类型(以 str 表示);
  • chmod : 本提案认为实际情况过于复杂, 不应该简单的使用 POSIX 的方式进行权限判定;

这一设计将使用 BehaviourSession 作为基盘, 为了简单的复用已有的设计.

由于 Avilla 中的多账户支持等设计的相关因素, 本提案约定以下条规:

  • 对于 Protocol:
    • 强烈建议开发者在构造 resource 选择器时, 加入 mainlineprotocol 键.
    • 建议开发者在构造访问标识符时, 使用字符串作为标识, 并且在其中加入类似 tencent-qq:// 这样的前缀(对于用户来说也是如此.);
  • 对于 Service/Interface 等内部接口开发:
    • 在处理 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 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(...) |
+-----------------------------+                +-------------------------------+

大概就到这里.

[Repo Manage] 项目管理相关: 添加 Issue/PR Template

如题, 这样能让问题解决的流程更完善.

  • 使用中遇到的问题
  • 发生了奇怪的异常
  • 协议实现行为不符合预期
  • 文档问题
  • (PR)新的 Protocol/Tools/etc. 要加进 Ecosystem 里面
  • 其他周边的问题

这个 issue 暂时得开着

[Core] 选择器(Selector) 相关

mainline = create_selector("mainline")

mainline.channel # -> SelectorKey("mainline.channel")
r = mainline.channel[123] # -> <mainline>.channel[123]
r.to_dict() # -> {"channel": 123}

Refactor: Selena

Relationship 上的交互式重构,基于最小功能单元(Minimal Function Unit)设计。

Issue about new abstraction "Subline" and "Notify"

        # 思考 -> 需要一个 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)的附加信息。

Metadata Core Support 的初步说明

已经完成.

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

[Refactoring] Landmark version `1.0.0-alpha.5`

概要

本次重构的 Landmark 版本被设定为 1.0.0-alpha.5.

本次主要对各种用名进行修改, 对 Relationship 模型进行重构, 以及分割 core 部件内的框架抽象到 avilla.core.abstract 处, 这将会不可避免的带来 API 的破坏性改动和用户体验的变化.

  • Rename Components
    • Context
    • Metadata
  • Context
    • #65
      • ContextClientSelector
      • ContextSceneSelector
      • ContextEndpointSelector
        • ContextRequestSelector
          • context.endpoint.expects_request() -> ContextRequestSelector
          • context.request (property)
    • #63
      • Context.wrap(bound: Selector) -> ContextSelector
      • Context.wrap(bound: MetadataOf) -> ContextWrappedMetadataOf
        • note: Generic omitted.
      • 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
    • #64
      • ContextSelector.pull(metadata: type[T extends Metadata] | MetadataRoute[..._L, T]) -> T
      • ContextWrappedMetadataOf[T].pull() -> T
        • note: not required for otherwise arguments.
    • #66
      • Design Completed
  • #67
    • bound(...)
    • Modifies on record impl and others.
  • Event
    • Core

用名修改

以下更改同时会影响到 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

API 变动概览

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 不再提供.
  • 鉴于 bound 参数的强制要求, 追加声明方法 ImplRecorder.pin, 实现签名类型 OrientFnDirectFn 将不再提供.
  • 鉴于 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()
)

contextvars / Ctx 交互改进

Avilla, Protocol, Relationship 中, 后者的上下文可以承担前者.

Avilla.current(), get_current_protocol(), Relationship.current() (Relationship 似乎不需要变)

Avilla Config - 配置文件规范

  • 只要能获取到 Avilla 实例就能用上
  • 使用 HOCON 作为配置文件格式, 不打算支持 yml / toml / json / ini.
  • 能够配合 pydantic.BaseSetting 生成样例文件
  • and more....

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.