nonebot-plugin-access-control-api


Namenonebot-plugin-access-control-api JSON
Version 1.2.0 PyPI version JSON
download
home_pagehttps://github.com/bot-ssttkkl/nonebot-plugin-access-control
SummaryNone
upload_time2024-05-04 08:36:28
maintainerNone
docs_urlNone
authorssttkkl
requires_python<4.0,>=3.8
licenseMIT
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <!-- markdownlint-disable MD033 MD036 MD041 -->

<p align="center">
  <a href="https://v2.nonebot.dev/"><img src="https://v2.nonebot.dev/logo.png" width="200" height="200" alt="nonebot"></a>
</p>

<div align="center">

nonebot-plugin-access-control-api
============

_✨ Nonebot 权限控制插件(插件适配API) ✨_

</div>


<p align="center">
  <a href="https://raw.githubusercontent.com/ssttkkl/nonebot-plugin-access-control-api/master/LICENSE">
    <img src="https://img.shields.io/github/license/ssttkkl/nonebot-plugin-access-control-api.svg" alt="license">
  </a>
  <a href="https://pypi.python.org/pypi/nonebot-plugin-access-control-api">
    <img src="https://img.shields.io/pypi/v/nonebot-plugin-access-control-api.svg" alt="pypi">
  </a>
  <img src="https://img.shields.io/badge/python-3.9+-blue.svg" alt="python">
</p>

为插件开发者提供适配[nonebot-plugin-access-control](https://github.com/bot-ssttkkl/nonebot-plugin-access-control)的API,插件开发者可以在不引入本体实现的情况下适配access-control,避免污染用户环境。

## 插件适配

完整代码:[src/nonebot_plugin_ac_demo/matcher_demo.py](src/nonebot_plugin_ac_demo/matcher_demo.py)

1. 创建一个名为nonebot_plugin_ac_demo的插件

2. 通过create_plugin_service函数创建一个PluginService实例(注意参数必须为插件包名)

```python
from nonebot import require

require("nonebot_plugin_access_control_api")

from nonebot_plugin_access_control_api.service import create_plugin_service

plugin_service = create_plugin_service("nonebot_plugin_ac_demo")
```

3. 通过PluginService.create_subservice创建SubService实例。调用`Service.patch_matcher()`
   应用至Matcher,或在事件处理函数上应用装饰器`Service.patch_handle()`。(二选一)

```python
group1 = plugin_service.create_subservice("group1")

a_matcher = on_command('a')
a_service = group1.create_subservice('a')
a_service.patch_matcher(a_matcher)


@a_matcher.handle()
async def _(matcher: Matcher):
    await matcher.send("a")


b_matcher = on_command('b')
b_service = group1.create_subservice('b')
b_service.patch_matcher(b_matcher)


@b_matcher.handle()
async def _(matcher: Matcher):
    await matcher.send("b")


c_matcher = on_command('c')
c_service = plugin_service.create_subservice('c')


@c_matcher.handle()
@c_service.patch_handler()  # 必须在 @c_matcher.handle() 之下
async def _(matcher: Matcher):
    ok = do_your_logic()

    if not ok:
        # 功能执行失败时,收回限流消耗的次数
        await current_rate_limit_token.get().retire()
        return 

    await matcher.send("c")

```

插件服务的结构如下所示:

![](docs/img/2.svg)

4. 通过指令配置服务权限

执行下面的指令后,所有用户将无法调用指令`/a`与`/b`

```
/ac permission deny --sbj all --srv nonebot_plugin_ac_demo.group1
```

执行下面的指令后,QQ用户12345678将无法调用指令`/a`

```
/ac permission deny --sbj qq:12345678 --srv nonebot_plugin_ac_demo.group1.a
```

执行下面的指令后,QQ群组87654321的所有用户将无法调用除`/c`以外的任何指令

```
/ac permission deny --sbj qq:g87654321 --srv nonebot_plugin_ac_demo
/ac permission allow --sbj qq:g87654321 --srv nonebot_plugin_ac_demo.c
```

5. 手动鉴权

对于非Matcher的功能入口(如APScheduler的定时任务等),需要开发者手动进行鉴权。当然,对于Matcher,如果自动鉴权不能满足你的需求,你也可以使用手动鉴权。

- 方法一:调用`service.check(Bot, Event)`方法,传入Bot及Event实例,返回bool值表示该用户是否具有权限。
调用`service.acquire_token_for_rate_limit(Bot, Event)`方法,传入Bot及Event实例,返回token。
若token不为None表示限流次数扣除成功,后续可通过`token.retire()`收回限流消耗的次数。

- 方法二:调用`service.check_by_subject(*str)`方法,传入主体字符串,返回bool值表示该用户是否具有权限。
调用`service.acquire_token_for_rate_limit_by_subject(*str)`方法,传入主体字符串,返回token。
若token不为None表示限流次数扣除成功,后续可通过`token.retire()`收回限流消耗的次数。

APScheduler示例:[src/nonebot_plugin_ac_demo/apscheduler_demo.py](src/nonebot_plugin_ac_demo/apscheduler_demo.py)

Matcher手动鉴权示例:

```python
d_matcher = on_command('d')
d_service = plugin_service.create_subservice('d')


@d_matcher.handle()
async def _(bot: Bot, event: Event, matcher: Matcher):
    if not await d_service.check(bot, event, acquire_rate_limit_token=False):
        # 没有权限
        await matcher.finish()

    token = await d_service.acquire_token_for_rate_limit(bot, event)
    if token is None:
        # 已达到限流次数上线
        await matcher.finish()

    ok = do_your_logic()

    if not ok:
        # 功能执行失败时,收回限流消耗的次数
        await token.retire()

    await matcher.send("c")
```

6. 事件订阅

通过`service.on_set_permission`、`service.on_remove_permission`、`service.on_change_permission`方法可以订阅事件,具体如下表:

| 装饰器                                 | 事件类型                                  | 事件接收函数的参数                   |
|-------------------------------------|---------------------------------------|-----------------------------|
| `service.on_set_permission`         | 服务设置主体权限                              | service:服务<br>permission:权限 |
| `service.on_remove_permission`      | 服务删除主体权限                              | service:服务<br>subject:主体    |
| `service.on_change_permission`      | 服务变更主体权限(包括该服务及其所有祖先服务设置、删除权限导致的权限变更) | service:服务<br>permission:权限 |
| `service.on_add_rate_limit_rule`    | 服务添加限流规则(该服务及其所有祖先服务添加限流规则时都会触发)      | service:服务<br>rule:限流规则     |
| `service.on_remove_rate_limit_rule` | 服务删除限流规则(该服务及其所有祖先服务删除限流规则时都会触发)      | service:服务<br>rule:限流规则     |

**调用事件接收函数时通过具名参数传参,因此事件接收函数的参数必须严格遵循参数名。**

事件订阅示例:[src/nonebot_plugin_ac_demo/event_demo.py](src/nonebot_plugin_ac_demo/event_demo.py)

## 拓展定义新的主体

主体提取器(Subject Extractor)用于提取事件发出用户所具有的主体。开发者可以在自己的插件中注册自定义的主体提取器,从而实现拓展定义新的主体。

主体提取器为一个函数(或可调用对象),其应该具有如下的函数签名,并通过`add_subject_extractor`装饰器注入到插件中:

```python
@add_subject_extractor
def your_custom_subject_extractor(bot: Bot, event: Event, manager: SubjectManager):
    ...
```

传入参数`bot`与`event`其义自明,而`manager`参数提供方法用于添加主体。

主体的模型定义如下:

```python
class SubjectModel(NamedTuple):
    content: str
    offer_by: str
    tag: Optional[str]
```

其中content为主体字符串,offer_by用于标识提取出该主体的主体提取器(通常为插件名),tag用于标识主体的种类。通常我们约定,拥有同一个tag的主体包含的信息量完全一致。

在主体提取流程,插件会**按顺序依次**调用已注册的主体提取器,请注意维护主体提取器注册的顺序。(tips:如果你的主体提取器依赖其他插件中实现的主体提取器,可以依赖`nonebot`
提供的`require`机制控制主体提取器注册的顺序)

例如,下面这个主体提取器用于为OneBot V11协议中群管理/群主提取相应的主体:

```python
OFFER_BY = "nonebot_plugin_access_control"


def extract_onebot_v11_group_role(bot: Bot, event: Event, manager: SubjectManager):
    if bot.type != "OneBot V11":
        return

    group_id = getattr(event, "group_id", None)
    sender: Optional["Sender"] = getattr(event, "sender", None)

    if group_id is not None and sender is not None:
        li = []

        if sender.role == "owner":
            li.append(
                SubjectModel(
                    f"qq:g{group_id}.group_owner", OFFER_BY, "qq:group.group_owner"
                )
            )
            li.append(SubjectModel("qq:group_owner", OFFER_BY, "qq:group_owner"))

        if sender.role == "owner" or sender.role == "admin":
            li.append(
                SubjectModel(
                    f"qq:g{group_id}.group_admin", OFFER_BY, "qq:group.group_admin"
                )
            )
            li.append(SubjectModel("qq:group_admin", OFFER_BY, "qq:group_admin"))

        # 添加在platform:group之前
        manager.insert_before("platform:group", *li)
```

## 在线乞讨

<details><summary>点击请我打两把maimai</summary>

![](https://github.com/ssttkkl/ssttkkl/blob/main/afdian-ssttkkl.jfif)

</details>

## LICENSE

> MIT License
>
> Copyright (c) 2022 ssttkkl
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
> 


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/bot-ssttkkl/nonebot-plugin-access-control",
    "name": "nonebot-plugin-access-control-api",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.8",
    "maintainer_email": null,
    "keywords": null,
    "author": "ssttkkl",
    "author_email": "huang.wen.long@hotmail.com",
    "download_url": "https://files.pythonhosted.org/packages/51/51/283fe05d46d3ab6c260ffdeab182e16fb0d86a08bb7f37229ffe6e38bdaa/nonebot_plugin_access_control_api-1.2.0.tar.gz",
    "platform": null,
    "description": "<!-- markdownlint-disable MD033 MD036 MD041 -->\n\n<p align=\"center\">\n  <a href=\"https://v2.nonebot.dev/\"><img src=\"https://v2.nonebot.dev/logo.png\" width=\"200\" height=\"200\" alt=\"nonebot\"></a>\n</p>\n\n<div align=\"center\">\n\nnonebot-plugin-access-control-api\n============\n\n_\u2728 Nonebot \u6743\u9650\u63a7\u5236\u63d2\u4ef6\uff08\u63d2\u4ef6\u9002\u914dAPI\uff09 \u2728_\n\n</div>\n\n\n<p align=\"center\">\n  <a href=\"https://raw.githubusercontent.com/ssttkkl/nonebot-plugin-access-control-api/master/LICENSE\">\n    <img src=\"https://img.shields.io/github/license/ssttkkl/nonebot-plugin-access-control-api.svg\" alt=\"license\">\n  </a>\n  <a href=\"https://pypi.python.org/pypi/nonebot-plugin-access-control-api\">\n    <img src=\"https://img.shields.io/pypi/v/nonebot-plugin-access-control-api.svg\" alt=\"pypi\">\n  </a>\n  <img src=\"https://img.shields.io/badge/python-3.9+-blue.svg\" alt=\"python\">\n</p>\n\n\u4e3a\u63d2\u4ef6\u5f00\u53d1\u8005\u63d0\u4f9b\u9002\u914d[nonebot-plugin-access-control](https://github.com/bot-ssttkkl/nonebot-plugin-access-control)\u7684API\uff0c\u63d2\u4ef6\u5f00\u53d1\u8005\u53ef\u4ee5\u5728\u4e0d\u5f15\u5165\u672c\u4f53\u5b9e\u73b0\u7684\u60c5\u51b5\u4e0b\u9002\u914daccess-control\uff0c\u907f\u514d\u6c61\u67d3\u7528\u6237\u73af\u5883\u3002\n\n## \u63d2\u4ef6\u9002\u914d\n\n\u5b8c\u6574\u4ee3\u7801\uff1a[src/nonebot_plugin_ac_demo/matcher_demo.py](src/nonebot_plugin_ac_demo/matcher_demo.py)\n\n1. \u521b\u5efa\u4e00\u4e2a\u540d\u4e3anonebot_plugin_ac_demo\u7684\u63d2\u4ef6\n\n2. \u901a\u8fc7create_plugin_service\u51fd\u6570\u521b\u5efa\u4e00\u4e2aPluginService\u5b9e\u4f8b\uff08\u6ce8\u610f\u53c2\u6570\u5fc5\u987b\u4e3a\u63d2\u4ef6\u5305\u540d\uff09\n\n```python\nfrom nonebot import require\n\nrequire(\"nonebot_plugin_access_control_api\")\n\nfrom nonebot_plugin_access_control_api.service import create_plugin_service\n\nplugin_service = create_plugin_service(\"nonebot_plugin_ac_demo\")\n```\n\n3. \u901a\u8fc7PluginService.create_subservice\u521b\u5efaSubService\u5b9e\u4f8b\u3002\u8c03\u7528`Service.patch_matcher()`\n   \u5e94\u7528\u81f3Matcher\uff0c\u6216\u5728\u4e8b\u4ef6\u5904\u7406\u51fd\u6570\u4e0a\u5e94\u7528\u88c5\u9970\u5668`Service.patch_handle()`\u3002\uff08\u4e8c\u9009\u4e00\uff09\n\n```python\ngroup1 = plugin_service.create_subservice(\"group1\")\n\na_matcher = on_command('a')\na_service = group1.create_subservice('a')\na_service.patch_matcher(a_matcher)\n\n\n@a_matcher.handle()\nasync def _(matcher: Matcher):\n    await matcher.send(\"a\")\n\n\nb_matcher = on_command('b')\nb_service = group1.create_subservice('b')\nb_service.patch_matcher(b_matcher)\n\n\n@b_matcher.handle()\nasync def _(matcher: Matcher):\n    await matcher.send(\"b\")\n\n\nc_matcher = on_command('c')\nc_service = plugin_service.create_subservice('c')\n\n\n@c_matcher.handle()\n@c_service.patch_handler()  # \u5fc5\u987b\u5728 @c_matcher.handle() \u4e4b\u4e0b\nasync def _(matcher: Matcher):\n    ok = do_your_logic()\n\n    if not ok:\n        # \u529f\u80fd\u6267\u884c\u5931\u8d25\u65f6\uff0c\u6536\u56de\u9650\u6d41\u6d88\u8017\u7684\u6b21\u6570\n        await current_rate_limit_token.get().retire()\n        return \n\n    await matcher.send(\"c\")\n\n```\n\n\u63d2\u4ef6\u670d\u52a1\u7684\u7ed3\u6784\u5982\u4e0b\u6240\u793a\uff1a\n\n![](docs/img/2.svg)\n\n4. \u901a\u8fc7\u6307\u4ee4\u914d\u7f6e\u670d\u52a1\u6743\u9650\n\n\u6267\u884c\u4e0b\u9762\u7684\u6307\u4ee4\u540e\uff0c\u6240\u6709\u7528\u6237\u5c06\u65e0\u6cd5\u8c03\u7528\u6307\u4ee4`/a`\u4e0e`/b`\n\n```\n/ac permission deny --sbj all --srv nonebot_plugin_ac_demo.group1\n```\n\n\u6267\u884c\u4e0b\u9762\u7684\u6307\u4ee4\u540e\uff0cQQ\u7528\u623712345678\u5c06\u65e0\u6cd5\u8c03\u7528\u6307\u4ee4`/a`\n\n```\n/ac permission deny --sbj qq:12345678 --srv nonebot_plugin_ac_demo.group1.a\n```\n\n\u6267\u884c\u4e0b\u9762\u7684\u6307\u4ee4\u540e\uff0cQQ\u7fa4\u7ec487654321\u7684\u6240\u6709\u7528\u6237\u5c06\u65e0\u6cd5\u8c03\u7528\u9664`/c`\u4ee5\u5916\u7684\u4efb\u4f55\u6307\u4ee4\n\n```\n/ac permission deny --sbj qq:g87654321 --srv nonebot_plugin_ac_demo\n/ac permission allow --sbj qq:g87654321 --srv nonebot_plugin_ac_demo.c\n```\n\n5. \u624b\u52a8\u9274\u6743\n\n\u5bf9\u4e8e\u975eMatcher\u7684\u529f\u80fd\u5165\u53e3\uff08\u5982APScheduler\u7684\u5b9a\u65f6\u4efb\u52a1\u7b49\uff09\uff0c\u9700\u8981\u5f00\u53d1\u8005\u624b\u52a8\u8fdb\u884c\u9274\u6743\u3002\u5f53\u7136\uff0c\u5bf9\u4e8eMatcher\uff0c\u5982\u679c\u81ea\u52a8\u9274\u6743\u4e0d\u80fd\u6ee1\u8db3\u4f60\u7684\u9700\u6c42\uff0c\u4f60\u4e5f\u53ef\u4ee5\u4f7f\u7528\u624b\u52a8\u9274\u6743\u3002\n\n- \u65b9\u6cd5\u4e00\uff1a\u8c03\u7528`service.check(Bot, Event)`\u65b9\u6cd5\uff0c\u4f20\u5165Bot\u53caEvent\u5b9e\u4f8b\uff0c\u8fd4\u56debool\u503c\u8868\u793a\u8be5\u7528\u6237\u662f\u5426\u5177\u6709\u6743\u9650\u3002\n\u8c03\u7528`service.acquire_token_for_rate_limit(Bot, Event)`\u65b9\u6cd5\uff0c\u4f20\u5165Bot\u53caEvent\u5b9e\u4f8b\uff0c\u8fd4\u56detoken\u3002\n\u82e5token\u4e0d\u4e3aNone\u8868\u793a\u9650\u6d41\u6b21\u6570\u6263\u9664\u6210\u529f\uff0c\u540e\u7eed\u53ef\u901a\u8fc7`token.retire()`\u6536\u56de\u9650\u6d41\u6d88\u8017\u7684\u6b21\u6570\u3002\n\n- \u65b9\u6cd5\u4e8c\uff1a\u8c03\u7528`service.check_by_subject(*str)`\u65b9\u6cd5\uff0c\u4f20\u5165\u4e3b\u4f53\u5b57\u7b26\u4e32\uff0c\u8fd4\u56debool\u503c\u8868\u793a\u8be5\u7528\u6237\u662f\u5426\u5177\u6709\u6743\u9650\u3002\n\u8c03\u7528`service.acquire_token_for_rate_limit_by_subject(*str)`\u65b9\u6cd5\uff0c\u4f20\u5165\u4e3b\u4f53\u5b57\u7b26\u4e32\uff0c\u8fd4\u56detoken\u3002\n\u82e5token\u4e0d\u4e3aNone\u8868\u793a\u9650\u6d41\u6b21\u6570\u6263\u9664\u6210\u529f\uff0c\u540e\u7eed\u53ef\u901a\u8fc7`token.retire()`\u6536\u56de\u9650\u6d41\u6d88\u8017\u7684\u6b21\u6570\u3002\n\nAPScheduler\u793a\u4f8b\uff1a[src/nonebot_plugin_ac_demo/apscheduler_demo.py](src/nonebot_plugin_ac_demo/apscheduler_demo.py)\n\nMatcher\u624b\u52a8\u9274\u6743\u793a\u4f8b\uff1a\n\n```python\nd_matcher = on_command('d')\nd_service = plugin_service.create_subservice('d')\n\n\n@d_matcher.handle()\nasync def _(bot: Bot, event: Event, matcher: Matcher):\n    if not await d_service.check(bot, event, acquire_rate_limit_token=False):\n        # \u6ca1\u6709\u6743\u9650\n        await matcher.finish()\n\n    token = await d_service.acquire_token_for_rate_limit(bot, event)\n    if token is None:\n        # \u5df2\u8fbe\u5230\u9650\u6d41\u6b21\u6570\u4e0a\u7ebf\n        await matcher.finish()\n\n    ok = do_your_logic()\n\n    if not ok:\n        # \u529f\u80fd\u6267\u884c\u5931\u8d25\u65f6\uff0c\u6536\u56de\u9650\u6d41\u6d88\u8017\u7684\u6b21\u6570\n        await token.retire()\n\n    await matcher.send(\"c\")\n```\n\n6. \u4e8b\u4ef6\u8ba2\u9605\n\n\u901a\u8fc7`service.on_set_permission`\u3001`service.on_remove_permission`\u3001`service.on_change_permission`\u65b9\u6cd5\u53ef\u4ee5\u8ba2\u9605\u4e8b\u4ef6\uff0c\u5177\u4f53\u5982\u4e0b\u8868\uff1a\n\n| \u88c5\u9970\u5668                                 | \u4e8b\u4ef6\u7c7b\u578b                                  | \u4e8b\u4ef6\u63a5\u6536\u51fd\u6570\u7684\u53c2\u6570                   |\n|-------------------------------------|---------------------------------------|-----------------------------|\n| `service.on_set_permission`         | \u670d\u52a1\u8bbe\u7f6e\u4e3b\u4f53\u6743\u9650                              | service\uff1a\u670d\u52a1<br>permission\uff1a\u6743\u9650 |\n| `service.on_remove_permission`      | \u670d\u52a1\u5220\u9664\u4e3b\u4f53\u6743\u9650                              | service\uff1a\u670d\u52a1<br>subject\uff1a\u4e3b\u4f53    |\n| `service.on_change_permission`      | \u670d\u52a1\u53d8\u66f4\u4e3b\u4f53\u6743\u9650\uff08\u5305\u62ec\u8be5\u670d\u52a1\u53ca\u5176\u6240\u6709\u7956\u5148\u670d\u52a1\u8bbe\u7f6e\u3001\u5220\u9664\u6743\u9650\u5bfc\u81f4\u7684\u6743\u9650\u53d8\u66f4\uff09 | service\uff1a\u670d\u52a1<br>permission\uff1a\u6743\u9650 |\n| `service.on_add_rate_limit_rule`    | \u670d\u52a1\u6dfb\u52a0\u9650\u6d41\u89c4\u5219\uff08\u8be5\u670d\u52a1\u53ca\u5176\u6240\u6709\u7956\u5148\u670d\u52a1\u6dfb\u52a0\u9650\u6d41\u89c4\u5219\u65f6\u90fd\u4f1a\u89e6\u53d1\uff09      | service\uff1a\u670d\u52a1<br>rule\uff1a\u9650\u6d41\u89c4\u5219     |\n| `service.on_remove_rate_limit_rule` | \u670d\u52a1\u5220\u9664\u9650\u6d41\u89c4\u5219\uff08\u8be5\u670d\u52a1\u53ca\u5176\u6240\u6709\u7956\u5148\u670d\u52a1\u5220\u9664\u9650\u6d41\u89c4\u5219\u65f6\u90fd\u4f1a\u89e6\u53d1\uff09      | service\uff1a\u670d\u52a1<br>rule\uff1a\u9650\u6d41\u89c4\u5219     |\n\n**\u8c03\u7528\u4e8b\u4ef6\u63a5\u6536\u51fd\u6570\u65f6\u901a\u8fc7\u5177\u540d\u53c2\u6570\u4f20\u53c2\uff0c\u56e0\u6b64\u4e8b\u4ef6\u63a5\u6536\u51fd\u6570\u7684\u53c2\u6570\u5fc5\u987b\u4e25\u683c\u9075\u5faa\u53c2\u6570\u540d\u3002**\n\n\u4e8b\u4ef6\u8ba2\u9605\u793a\u4f8b\uff1a[src/nonebot_plugin_ac_demo/event_demo.py](src/nonebot_plugin_ac_demo/event_demo.py)\n\n## \u62d3\u5c55\u5b9a\u4e49\u65b0\u7684\u4e3b\u4f53\n\n\u4e3b\u4f53\u63d0\u53d6\u5668\uff08Subject Extractor\uff09\u7528\u4e8e\u63d0\u53d6\u4e8b\u4ef6\u53d1\u51fa\u7528\u6237\u6240\u5177\u6709\u7684\u4e3b\u4f53\u3002\u5f00\u53d1\u8005\u53ef\u4ee5\u5728\u81ea\u5df1\u7684\u63d2\u4ef6\u4e2d\u6ce8\u518c\u81ea\u5b9a\u4e49\u7684\u4e3b\u4f53\u63d0\u53d6\u5668\uff0c\u4ece\u800c\u5b9e\u73b0\u62d3\u5c55\u5b9a\u4e49\u65b0\u7684\u4e3b\u4f53\u3002\n\n\u4e3b\u4f53\u63d0\u53d6\u5668\u4e3a\u4e00\u4e2a\u51fd\u6570\uff08\u6216\u53ef\u8c03\u7528\u5bf9\u8c61\uff09\uff0c\u5176\u5e94\u8be5\u5177\u6709\u5982\u4e0b\u7684\u51fd\u6570\u7b7e\u540d\uff0c\u5e76\u901a\u8fc7`add_subject_extractor`\u88c5\u9970\u5668\u6ce8\u5165\u5230\u63d2\u4ef6\u4e2d\uff1a\n\n```python\n@add_subject_extractor\ndef your_custom_subject_extractor(bot: Bot, event: Event, manager: SubjectManager):\n    ...\n```\n\n\u4f20\u5165\u53c2\u6570`bot`\u4e0e`event`\u5176\u4e49\u81ea\u660e\uff0c\u800c`manager`\u53c2\u6570\u63d0\u4f9b\u65b9\u6cd5\u7528\u4e8e\u6dfb\u52a0\u4e3b\u4f53\u3002\n\n\u4e3b\u4f53\u7684\u6a21\u578b\u5b9a\u4e49\u5982\u4e0b\uff1a\n\n```python\nclass SubjectModel(NamedTuple):\n    content: str\n    offer_by: str\n    tag: Optional[str]\n```\n\n\u5176\u4e2dcontent\u4e3a\u4e3b\u4f53\u5b57\u7b26\u4e32\uff0coffer_by\u7528\u4e8e\u6807\u8bc6\u63d0\u53d6\u51fa\u8be5\u4e3b\u4f53\u7684\u4e3b\u4f53\u63d0\u53d6\u5668\uff08\u901a\u5e38\u4e3a\u63d2\u4ef6\u540d\uff09\uff0ctag\u7528\u4e8e\u6807\u8bc6\u4e3b\u4f53\u7684\u79cd\u7c7b\u3002\u901a\u5e38\u6211\u4eec\u7ea6\u5b9a\uff0c\u62e5\u6709\u540c\u4e00\u4e2atag\u7684\u4e3b\u4f53\u5305\u542b\u7684\u4fe1\u606f\u91cf\u5b8c\u5168\u4e00\u81f4\u3002\n\n\u5728\u4e3b\u4f53\u63d0\u53d6\u6d41\u7a0b\uff0c\u63d2\u4ef6\u4f1a**\u6309\u987a\u5e8f\u4f9d\u6b21**\u8c03\u7528\u5df2\u6ce8\u518c\u7684\u4e3b\u4f53\u63d0\u53d6\u5668\uff0c\u8bf7\u6ce8\u610f\u7ef4\u62a4\u4e3b\u4f53\u63d0\u53d6\u5668\u6ce8\u518c\u7684\u987a\u5e8f\u3002\uff08tips\uff1a\u5982\u679c\u4f60\u7684\u4e3b\u4f53\u63d0\u53d6\u5668\u4f9d\u8d56\u5176\u4ed6\u63d2\u4ef6\u4e2d\u5b9e\u73b0\u7684\u4e3b\u4f53\u63d0\u53d6\u5668\uff0c\u53ef\u4ee5\u4f9d\u8d56`nonebot`\n\u63d0\u4f9b\u7684`require`\u673a\u5236\u63a7\u5236\u4e3b\u4f53\u63d0\u53d6\u5668\u6ce8\u518c\u7684\u987a\u5e8f\uff09\n\n\u4f8b\u5982\uff0c\u4e0b\u9762\u8fd9\u4e2a\u4e3b\u4f53\u63d0\u53d6\u5668\u7528\u4e8e\u4e3aOneBot V11\u534f\u8bae\u4e2d\u7fa4\u7ba1\u7406/\u7fa4\u4e3b\u63d0\u53d6\u76f8\u5e94\u7684\u4e3b\u4f53\uff1a\n\n```python\nOFFER_BY = \"nonebot_plugin_access_control\"\n\n\ndef extract_onebot_v11_group_role(bot: Bot, event: Event, manager: SubjectManager):\n    if bot.type != \"OneBot V11\":\n        return\n\n    group_id = getattr(event, \"group_id\", None)\n    sender: Optional[\"Sender\"] = getattr(event, \"sender\", None)\n\n    if group_id is not None and sender is not None:\n        li = []\n\n        if sender.role == \"owner\":\n            li.append(\n                SubjectModel(\n                    f\"qq:g{group_id}.group_owner\", OFFER_BY, \"qq:group.group_owner\"\n                )\n            )\n            li.append(SubjectModel(\"qq:group_owner\", OFFER_BY, \"qq:group_owner\"))\n\n        if sender.role == \"owner\" or sender.role == \"admin\":\n            li.append(\n                SubjectModel(\n                    f\"qq:g{group_id}.group_admin\", OFFER_BY, \"qq:group.group_admin\"\n                )\n            )\n            li.append(SubjectModel(\"qq:group_admin\", OFFER_BY, \"qq:group_admin\"))\n\n        # \u6dfb\u52a0\u5728platform:group\u4e4b\u524d\n        manager.insert_before(\"platform:group\", *li)\n```\n\n## \u5728\u7ebf\u4e5e\u8ba8\n\n<details><summary>\u70b9\u51fb\u8bf7\u6211\u6253\u4e24\u628amaimai</summary>\n\n![](https://github.com/ssttkkl/ssttkkl/blob/main/afdian-ssttkkl.jfif)\n\n</details>\n\n## LICENSE\n\n> MIT License\n>\n> Copyright (c) 2022 ssttkkl\n>\n> Permission is hereby granted, free of charge, to any person obtaining a copy\n> of this software and associated documentation files (the \"Software\"), to deal\n> in the Software without restriction, including without limitation the rights\n> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n> copies of the Software, and to permit persons to whom the Software is\n> furnished to do so, subject to the following conditions:\n>\n> The above copyright notice and this permission notice shall be included in all\n> copies or substantial portions of the Software.\n>\n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n> SOFTWARE.\n> \n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": null,
    "version": "1.2.0",
    "project_urls": {
        "Homepage": "https://github.com/bot-ssttkkl/nonebot-plugin-access-control",
        "Repository": "https://github.com/bot-ssttkkl/nonebot-plugin-access-control"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "083fa8c6cd1cd7a0a93009b5ba543eb2c82b579664e6d168dd442c8d8d36ad59",
                "md5": "4ab060be24d0a341e1b768b7b9c04adf",
                "sha256": "bb534cebb07a02c51fdbbc9e222979e143cc891f7b3464b46fcf79b6a0d2629e"
            },
            "downloads": -1,
            "filename": "nonebot_plugin_access_control_api-1.2.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "4ab060be24d0a341e1b768b7b9c04adf",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.8",
            "size": 29233,
            "upload_time": "2024-05-04T08:36:26",
            "upload_time_iso_8601": "2024-05-04T08:36:26.946676Z",
            "url": "https://files.pythonhosted.org/packages/08/3f/a8c6cd1cd7a0a93009b5ba543eb2c82b579664e6d168dd442c8d8d36ad59/nonebot_plugin_access_control_api-1.2.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5151283fe05d46d3ab6c260ffdeab182e16fb0d86a08bb7f37229ffe6e38bdaa",
                "md5": "96e7a5b1c2cb0c2579d89ac1a2d317aa",
                "sha256": "e546fc3390ddbcb0e496297dcd50fb38830cf133112467bc2d12b6a9440dc781"
            },
            "downloads": -1,
            "filename": "nonebot_plugin_access_control_api-1.2.0.tar.gz",
            "has_sig": false,
            "md5_digest": "96e7a5b1c2cb0c2579d89ac1a2d317aa",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.8",
            "size": 18321,
            "upload_time": "2024-05-04T08:36:28",
            "upload_time_iso_8601": "2024-05-04T08:36:28.647334Z",
            "url": "https://files.pythonhosted.org/packages/51/51/283fe05d46d3ab6c260ffdeab182e16fb0d86a08bb7f37229ffe6e38bdaa/nonebot_plugin_access_control_api-1.2.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-05-04 08:36:28",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "bot-ssttkkl",
    "github_project": "nonebot-plugin-access-control",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "nonebot-plugin-access-control-api"
}
        
Elapsed time: 0.23923s