mirror of
https://github.com/suyiiyii/nonebot-bison.git
synced 2025-06-05 19:36:43 +08:00
Merge branch 'main' into next
This commit is contained in:
commit
1f074b1045
25
.github/actions/setup-python/action.yml
vendored
25
.github/actions/setup-python/action.yml
vendored
@ -18,23 +18,14 @@ runs:
|
||||
- name: Install poetry
|
||||
uses: Gr1N/setup-poetry@v7
|
||||
|
||||
- name: Cache Windows dependencies
|
||||
uses: actions/cache@v2
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
with:
|
||||
path: ~/AppData/Local/pypoetry/Cache/virtualenvs
|
||||
key: ${{ runner.os }}-poetry-${{ inputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
|
||||
- id: poetry-cache
|
||||
run: echo "::set-output name=dir::$(poetry config virtualenvs.path)"
|
||||
shell: bash
|
||||
|
||||
- name: Cache Linux dependencies
|
||||
uses: actions/cache@v2
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pypoetry/virtualenvs
|
||||
key: ${{ runner.os }}-poetry-${{ inputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
|
||||
path: ${{ steps.poetry-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-poetry-${{ steps.python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
|
||||
|
||||
- name: Cache macOS dependencies
|
||||
uses: actions/cache@v2
|
||||
if: ${{ runner.os == 'macOS' }}
|
||||
with:
|
||||
path: ~/Library/Caches/pypoetry/virtualenvs
|
||||
key: ${{ runner.os }}-poetry-${{ inputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
|
||||
- run: poetry install
|
||||
shell: bash
|
||||
|
69
.github/workflows/main.yml
vendored
69
.github/workflows/main.yml
vendored
@ -1,10 +1,24 @@
|
||||
name: CI
|
||||
name: test-build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- admin-frontend/**
|
||||
- docker/**
|
||||
- src/**
|
||||
- tests/**
|
||||
- pyproject.toml
|
||||
- poetry.lock
|
||||
pull_request:
|
||||
paths:
|
||||
- admin-frontend/**
|
||||
- docker/**
|
||||
- src/**
|
||||
- tests/**
|
||||
- pyproject.toml
|
||||
- poetry.lock
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@ -63,18 +77,10 @@ jobs:
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
env_vars: OS,PYTHON_VERSION
|
||||
docker:
|
||||
name: Docker
|
||||
docker-main:
|
||||
name: Docker main
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-frontend, test]
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- file: ./docker/Dockerfile_with_frontend
|
||||
tags: felinae98/nonebot-bison:main
|
||||
- file: ./docker/Dockerfile_with_frontend_sentry
|
||||
tags: felinae98/nonebot-bison:main-sentry
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@ -101,8 +107,45 @@ jobs:
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ${{ matrix.file }}
|
||||
file: ./docker/Dockerfile_with_frontend
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ matrix.tags }}
|
||||
tags: felinae98/nonebot-bison:main
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
docker-main-sentry:
|
||||
name: Docker main sentry
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-frontend, test]
|
||||
if: github.event_name != 'pull_request'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Download frontend files
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: frontend
|
||||
path: ./src/plugins/nonebot_bison/admin_page/dist
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile_with_frontend_sentry
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: felinae98/nonebot-bison:main-sentry
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
4
.github/workflows/website-preview.yml
vendored
4
.github/workflows/website-preview.yml
vendored
@ -2,6 +2,10 @@ name: Site Deploy(Preview)
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- docs/**
|
||||
- package.json
|
||||
- yarn.lock
|
||||
|
||||
jobs:
|
||||
preview:
|
||||
|
@ -20,3 +20,4 @@ repos:
|
||||
hooks:
|
||||
- id: prettier
|
||||
types_or: [markdown, ts, tsx]
|
||||
exclude: 'CHANGELOG.md'
|
||||
|
16
CHANGELOG.md
16
CHANGELOG.md
@ -2,7 +2,21 @@
|
||||
|
||||
## 最近更新
|
||||
|
||||
- No changes
|
||||
### 新功能
|
||||
|
||||
- 添加bilibili开播提醒 [@Sichongzou](https://github.com/Sichongzou) ([#60](https://github.com/felinae98/nonebot-bison/pull/60))
|
||||
- 添加User-Agent配置 [@felinae98](https://github.com/felinae98) ([#78](https://github.com/felinae98/nonebot-bison/pull/78))
|
||||
- 增加代理设置 [@felinae98](https://github.com/felinae98) ([#71](https://github.com/felinae98/nonebot-bison/pull/71))
|
||||
- 增加Parse Target功能 [@felinae98](https://github.com/felinae98) ([#72](https://github.com/felinae98/nonebot-bison/pull/72))
|
||||
|
||||
### Bug 修复
|
||||
|
||||
- 捕获 JSONDecodeError [@felinae98](https://github.com/felinae98) ([#82](https://github.com/felinae98/nonebot-bison/pull/82))
|
||||
- 捕获SSL异常 [@felinae98](https://github.com/felinae98) ([#75](https://github.com/felinae98/nonebot-bison/pull/75))
|
||||
|
||||
### 文档
|
||||
|
||||
- 完善开发文档 [@AzideCupric](https://github.com/AzideCupric) ([#80](https://github.com/felinae98/nonebot-bison/pull/80))
|
||||
|
||||
## v0.5.3
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
[](https://pypi.org/project/nonebot-bison/)
|
||||
[](https://github.com/felinae98/nonebot-bison/blob/main/LICENSE)
|
||||
[](https://circleci.com/gh/felinae98/nonebot-bison)
|
||||
[](https://github.com/felinae98/nonebot-bison/actions/workflows/main.yml)
|
||||
[](https://hub.docker.com/r/felinae98/nonebot-bison)
|
||||
[](https://codecov.io/gh/felinae98/nonebot-bison)
|
||||
[](https://qm.qq.com/cgi-bin/qm/qr?k=pXYMGB_e8b6so3QTqgeV6lkKDtEeYE4f&jump_from=webapi)
|
||||
@ -38,6 +38,8 @@
|
||||
- 专栏
|
||||
- 转发
|
||||
- 纯文字
|
||||
- Bilibili 直播
|
||||
- 开播提醒
|
||||
- RSS
|
||||
- 富文本转换为纯文本
|
||||
- 提取出所有图片
|
||||
|
@ -31,6 +31,11 @@ sidebar: auto
|
||||
|
||||
本插件需要你的帮助!只需要会写简单的爬虫,就能给本插件适配新的网站。
|
||||
|
||||
::: danger
|
||||
Nonebot 项目使用了全异步的处理方式,所以你需要对异步,Python asyncio 的机制有一定了解,当然,
|
||||
依葫芦画瓢也是足够的
|
||||
:::
|
||||
|
||||
## 基本概念
|
||||
|
||||
- `nonebot_bison.post.Post`: 可以理解为推送内容,其中包含需要发送的文字,图片,链接,平台信息等
|
||||
@ -53,7 +58,7 @@ sidebar: auto
|
||||
例如:微博,Bilibili
|
||||
- `nonebot_bison.platform.platform.StatusChange` 每次爬虫获取一个状态,在状态改变时发布推送
|
||||
例如:游戏开服提醒,主播上播提醒
|
||||
- `nonebot_bison.platform.platform.SimplePost` 与`NewMessage`相似,但是不过滤新的消息
|
||||
- `nonebot_bison.platform.platform.SimplePost` 与`NewMessage`相似,但是不过滤之前发过的
|
||||
,每次发送全部消息
|
||||
例如:每日榜单定时发送
|
||||
|
||||
@ -64,10 +69,61 @@ sidebar: auto
|
||||
- 没有账号的概念
|
||||
例如:游戏公告,教务处公告
|
||||
|
||||
## 实现方法
|
||||
|
||||
现在你需要在`src/plugins/nonebot_bison/platform`下新建一个 py 文件,
|
||||
在里面新建一个类,继承推送类型的基类,重载一些关键的函数,然后……就完成了,不需要修改别的东西了。
|
||||
|
||||
任何一种订阅类型需要实现的方法/字段如下:
|
||||
### 不同类型 Platform 的实现适配以及逻辑
|
||||
|
||||
- `nonebot_bison.platform.platform.NewMessage`
|
||||
需要实现:
|
||||
|
||||
- `async get_sub_list(Target) -> list[RawPost]`
|
||||
- `get_id(RawPost)`
|
||||
- `get_date(RawPost)` (可选)
|
||||
|
||||
::: details 大致流程
|
||||
|
||||
1. 调用`get_sub_list`拿到 RawPost 列表
|
||||
2. 调用`get_id`判断是否重复,如果没有重复就说明是新的 RawPost
|
||||
3. 如果有`get_category`和`get_date`,则调用判断 RawPost 是否满足条件
|
||||
4. 调用`parse`生成正式推文
|
||||
:::
|
||||
|
||||
参考[nonebot_bison.platform.Weibo](https://github.com/felinae98/nonebot-bison/blob/v0.5.3/src/plugins/nonebot_bison/platform/weibo.py)
|
||||
|
||||
- `nonebot_bison.platform.platform.StatusChange`
|
||||
需要实现:
|
||||
|
||||
- `async get_status(Target) -> Any`
|
||||
- `compare_status(Target, old_status, new_status) -> list[RawPost]`
|
||||
|
||||
:::details 大致流程
|
||||
|
||||
1. `get_status`获取当前状态
|
||||
2. 传入`compare_status`比较前状态
|
||||
3. 通过则进入`parser`生成 Post
|
||||
:::
|
||||
|
||||
参考[nonenot_bison.platform.AkVersion](https://github.com/felinae98/nonebot-bison/blob/v0.5.3/src/plugins/nonebot_bison/platform/arknights.py#L86)
|
||||
|
||||
- `nonebot_bison.platform.platform.SimplePost`
|
||||
需要实现:
|
||||
|
||||
- `async get_sub_list(Target) -> list[RawPost]`
|
||||
- `get_date(RawPost)` (可选)
|
||||
|
||||
::: details 大致流程
|
||||
|
||||
1. 调用`get_sub_list`拿到 RawPost 列表
|
||||
2. 如果有`get_category`和`get_date`,则调用判断 RawPost 是否满足条件
|
||||
3. 调用`parse`生成正式推文
|
||||
:::
|
||||
|
||||
### 公共方法/成员
|
||||
|
||||
任何一种订阅类型需要实现的方法/成员如下:
|
||||
|
||||
- `schedule_type`, `schedule_kw` 调度的参数,本质是使用 apscheduler 的[trigger 参数](https://apscheduler.readthedocs.io/en/3.x/userguide.html?highlight=trigger#choosing-the-right-scheduler-job-store-s-executor-s-and-trigger-s),`schedule_type`可以是`date`,`interval`和`cron`,
|
||||
`schedule_kw`是对应的参数,一个常见的配置是`schedule_type=interval`, `schedule_kw={'seconds':30}`
|
||||
@ -80,11 +136,40 @@ sidebar: auto
|
||||
- `enable_tag` 平台发布内容是否带 Tag,例如微博
|
||||
- `platform_name` 唯一的,英文的识别标识,比如`weibo`
|
||||
- `async get_target_name(Target) -> Optional[str]` 通常用于获取帐号的名称,如果平台没有帐号概念,可以直接返回平台的`name`
|
||||
- `async parse(RawPost) -> Post`将获取到的 RawPost 处理成 Post
|
||||
- `get_tags(RawPost) -> Optional[Collection[Tag]]` (可选) 从 RawPost 中提取 Tag
|
||||
- `get_category(RawPos) -> Optional[Category]` (可选)从 RawPost 中提取 Category
|
||||
- `async parse(RawPost) -> Post` 将获取到的 RawPost 处理成 Post
|
||||
- `async parse_target(str) -> Target` (可选)定制化处理传入用户输入的 Target 字符串,返回 Target(一般是把用户的主页链接解析为 Target),如果输入本身就是 Target,则直接返回 Target
|
||||
- `parse_target_promot` (可选)在要求用户输入 Target 的时候显示的提示文字
|
||||
|
||||
例如要适配微博,我希望 bot 搬运新的消息,所以微博的类应该这样定义:
|
||||
### 特有的方法/成员
|
||||
|
||||
- `async get_sub_list(Target) -> list[RawPost]` 输入一个`Target`,输出一个`RawPost`的 list
|
||||
- 对于`nonebot_bison.platform.platform.NewMessage`
|
||||
`get_sub_list(Target) -> list[RawPost]` 用于获取对应 Target 的 RawPost 列表,与上一次`get_sub_list`获取的列表比较,过滤出新的 RawPost
|
||||
- 对于`nonebot_bison.platform.platform.SimplePost`
|
||||
`get_sub_list` 用于获取对应 Target 的 RawPost 列表,但不会与上次获取的结果进行比较,而是直接进行发送
|
||||
- `get_id(RawPost) -> Any` 输入一个`RawPost`,从`RawPost`中获取一个唯一的 ID,这个 ID 会用来判断这条`RawPost`是不是之前收到过
|
||||
- `get_date(RawPost) -> Optional[int]` 输入一个`RawPost`,如果可以从`RawPost`中提取出发文的时间,返回发文时间的 timestamp,否则返回`None`
|
||||
- `async get_status(Target) -> Any`
|
||||
- 对于`nonebot_bison.platform.platform.StatusChange`
|
||||
`get_status`用于获取对应 Target 当前的状态,随后将获取的状态作为参数`new_status`传入`compare_status`中
|
||||
- `compare_status(self, target: Target, old_status, new_status) -> list[RawPost]`
|
||||
- 对于`nonebot_bison.platform.platform.StatusChange`
|
||||
`compare_status` 用于比较储存的`old_status`与新传入的`new_status`,并返回发生变更的 RawPost 列表
|
||||
|
||||
### 单元测试
|
||||
|
||||
当然我们非常希望你对自己适配的平台写一些单元测试
|
||||
|
||||
你可以参照`tests/platforms/test_*.py`中的内容对单元测试进行编写。
|
||||
|
||||
为保证多次运行测试的一致性,可以 mock http 的响应,测试的内容应包括[获取 RawPost](https://github.com/felinae98/nonebot-bison/blob/v0.5.3/tests/platforms/test_weibo.py#L59),处理成 Post
|
||||
,测试分类以及提取 tag 等,当然最好和 rsshub 做一个交叉验证。
|
||||
|
||||
## 一些例子
|
||||
|
||||
例如要适配微博,我希望 bot 搬运新的消息,所以微博的类应该这样实现:
|
||||
|
||||
```python
|
||||
class Weibo(NewMessage):
|
||||
@ -103,17 +188,26 @@ class Weibo(NewMessage):
|
||||
schedule_type = "interval"
|
||||
schedule_kw = {"seconds": 3}
|
||||
has_target = True
|
||||
|
||||
async def get_target_name(self, target: Target) -> Optional[str]:
|
||||
#获取Target对应的用户名
|
||||
...
|
||||
async def get_sub_list(self, target: Target) -> list[RawPost]:
|
||||
#获取对应Target的RawPost列表,会与上一次get_sub_list获取的列表比较,过滤出新的RawPost
|
||||
...
|
||||
def get_id(self, post: RawPost) -> Any:
|
||||
#获取可以标识每个Rawpost的,不与之前RawPost重复的id,用于过滤出新的RawPost
|
||||
...
|
||||
def get_date(self, raw_post: RawPost) -> float:
|
||||
#获取RawPost的发布时间,若bot过滤出的新RawPost发布时间与当前时间差超过2小时,该RawPost将被忽略,可以返回None
|
||||
...
|
||||
def get_tags(self, raw_post: RawPost) -> Optional[list[Tag]]:
|
||||
#获取RawPost中包含的微博话题(#xxx#中的内容)
|
||||
...
|
||||
def get_category(self, raw_post: RawPost) -> Category:
|
||||
#获取该RawPost在该类定义categories的具体分类(转发?视频?图文?...?)
|
||||
...
|
||||
async def parse(self, raw_post: RawPost) -> Post:
|
||||
#将需要bot推送的RawPost处理成正式推送的Post
|
||||
...
|
||||
```
|
||||
|
||||
当然我们非常希望你对自己适配的平台写一些单元测试,你可以模仿`tests/platforms/test_*.py`中的内容写
|
||||
一些单元测试。为保证多次运行测试的一致性,可以 mock http 的响应,测试的内容包括获取 RawPost,处理成 Post
|
||||
,测试分类以及提取 tag 等,当然最好和 rsshub 做一个交叉验证。
|
||||
|
||||
::: danger
|
||||
Nonebot 项目使用了全异步的处理方式,所以你需要对异步,Python asyncio 的机制有一定了解,当然,
|
||||
依葫芦画瓢也是足够的
|
||||
:::
|
||||
|
||||
## 类的方法与成员变量
|
||||
|
||||
## 方法与变量的定义
|
||||
|
@ -94,23 +94,27 @@ sidebar: auto
|
||||
|
||||
## 配置
|
||||
|
||||
可参考[源文件](https://github.com/felinae98/nonebot-bison/blob/main/src/plugins/nonebot_bison/plugin_config.py)
|
||||
::: tip INFO
|
||||
|
||||
- 所有配置项可参考[源文件](https://github.com/felinae98/nonebot-bison/blob/main/src/plugins/nonebot_bison/plugin_config.py)
|
||||
- **配置项的配置方法** 请参考[NoneBot 配置方式](https://v2.nonebot.dev/docs/tutorial/configuration#%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F),在`.env`/`.env.*`文件中写入希望配置的 Bison 配置项
|
||||
:::
|
||||
|
||||
- `BISON_CONFIG_PATH`: 插件存放配置文件的位置,如果不设定默认为项目目录下的`data`目录
|
||||
- `BISON_USE_PIC`: 将文字渲染成图片后进行发送,多用于规避风控
|
||||
- `BISON_BROWSER`: 本插件使用 Chrome 来渲染图片
|
||||
- 使用 browserless 提供的 Chrome 管理服务,设置为`ws://xxxxxxxx`,值为 Chrome Endpoint(推荐)
|
||||
- 使用 cdp 连接相关服务,设置为`wsc://xxxxxxxxx`
|
||||
- 使用本地安装的 Chrome,设置为`local:<chrome path>`,例如`local:/usr/bin/google-chrome-stable`
|
||||
- 如果不进行配置,那么会在启动时候自动进行安装,在官方的 docker 镜像中已经安装了浏览器
|
||||
- 使用本地安装的 Chrome,设置为`local:<chrome path>`,例如`local:/usr/bin/google-chrome-stable`
|
||||
- 使用 cdp 连接相关服务,设置为`wsc://xxxxxxxxx`
|
||||
- 使用 browserless 提供的 Chrome 管理服务,设置为`ws://xxxxxxxx`,值为 Chrome Endpoint
|
||||
::: warning
|
||||
截止发布时,本项目尚不能完全与 browserless 兼容,目前建议使用镜像内自带的浏览器,即
|
||||
不要配置这个变量
|
||||
:::
|
||||
- `BISON_SKIP_BROWSER_CHECK`: 是否在启动时自动下载浏览器,如果选择`False`会在用到浏览器时自动下载,
|
||||
默认`True`
|
||||
- `BISON_OUTER_URL`: 从外部访问服务器的地址,默认为`http://localhost:8080/bison`,如果你的插件部署
|
||||
在服务器上,建议配置为`http://<你的服务器ip>:8080/bison`
|
||||
- `BISON_OUTER_URL`: 从外部访问服务器的地址,默认为`http://localhost:8080/bison/`,如果你的插件部署
|
||||
在服务器上,建议配置为`http://<你的服务器ip>:8080/bison/`
|
||||
::: warning
|
||||
如果需要从外网或者 Docker 容器外访问后台页面,请确保`HOST=0.0.0.0`
|
||||
:::
|
||||
@ -124,7 +128,7 @@ sidebar: auto
|
||||
- `1`: 首条消息单独发送,剩余图片合并转发
|
||||
- `2`: 所有消息全部合并转发
|
||||
|
||||
::: details 配置项示例
|
||||
::: details BISON_USE_PIC_MERGE 配置项示例
|
||||
|
||||
- 当`BISON_USE_PIC_MERGE=1`时:
|
||||

|
||||
@ -137,6 +141,7 @@ sidebar: auto
|
||||
:::
|
||||
|
||||
- `BISON_PROXY`: 使用的代理连接,形如`http://<ip>:<port>`(可选)
|
||||
- `BISON_UA`: 使用的 User-Agent,默认为 Chrome
|
||||
|
||||
## 使用
|
||||
|
||||
@ -154,12 +159,16 @@ sidebar: auto
|
||||
所有命令都需要@bot 触发
|
||||
|
||||
- 添加订阅(仅管理员和群主和 SUPERUSER):`添加订阅`
|
||||
::: tip 关于中止订阅
|
||||
对于[**v0.5.1**](https://github.com/felinae98/nonebot-bison/releases/tag/v0.5.1)及以上的版本中,已经为`添加订阅`命令添加了中止订阅的功能。
|
||||
在添加订阅命令的~~几乎~~各个阶段,都可以向 Bot 发送`取消`消息来中止订阅过程(需要订阅发起者本人发送)
|
||||
::: details 关于中止添加订阅
|
||||
对于[**v0.5.1**](https://github.com/felinae98/nonebot-bison/releases/tag/v0.5.1)及以上的版本中,已经为`添加订阅`命令添加了中止添加功能。
|
||||
在`添加订阅`命令的~~几乎~~各个阶段,都可以向 Bot 发送`取消`消息来中止订阅过程(需要发起者本人发送)
|
||||
:::
|
||||
- 查询订阅:`查询订阅`
|
||||
- 删除订阅(仅管理员和群主和 SUPERUSER):`删除订阅`
|
||||
::: details 关于中止删除订阅
|
||||
对于[**v0.5.3**](https://github.com/felinae98/nonebot-bison/releases/tag/v0.5.3)及以上的版本中,已经为`删除订阅`命令添加了中止删除功能。
|
||||
在`删除订阅`命令的~~几乎~~各个阶段,都可以向 Bot 发送`取消`消息来中止订阅过程(需要发起者本人发送)
|
||||
:::
|
||||
|
||||
#### 私聊机器人获取后台地址
|
||||
|
||||
@ -178,8 +187,8 @@ sidebar: auto
|
||||
#### 私聊机器人进行配置(需要 SUPERUER 权限)
|
||||
|
||||
请私聊 bot`群管理`
|
||||
::: tip 关于中止订阅
|
||||
与普通的[`添加订阅`](#在本群中进行配置)命令一样,在`群管理`命令中使用的`添加订阅`命令也可以使用`取消`来中止订阅过程
|
||||
::: details 关于中止订阅
|
||||
与普通的[`添加订阅`/`删除订阅`](#在本群中进行配置)命令一样,在`群管理`命令中使用的`添加订阅`/`删除订阅`命令也可以使用`取消`来中止订阅过程
|
||||
:::
|
||||
|
||||
### 所支持平台的 uid
|
||||
|
109
poetry.lock
generated
109
poetry.lock
generated
@ -229,7 +229,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
name = "charset-normalizer"
|
||||
version = "2.0.12"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5.0"
|
||||
|
||||
@ -275,14 +275,14 @@ six = ">=1.10"
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "6.3.3"
|
||||
version = "6.4"
|
||||
description = "Code coverage measurement for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
tomli = {version = "*", optional = true, markers = "extra == \"toml\""}
|
||||
tomli = {version = "*", optional = true, markers = "python_version < \"3.11\" and extra == \"toml\""}
|
||||
|
||||
[package.extras]
|
||||
toml = ["tomli"]
|
||||
@ -392,11 +392,11 @@ python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "httpcore"
|
||||
version = "0.14.7"
|
||||
version = "0.15.0"
|
||||
description = "A minimal low-level HTTP client."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
anyio = ">=3.0.0,<4.0.0"
|
||||
@ -421,22 +421,21 @@ test = ["Cython (>=0.29.24,<0.30.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.22.0"
|
||||
version = "0.23.0"
|
||||
description = "The next generation HTTP client."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
certifi = "*"
|
||||
charset-normalizer = "*"
|
||||
httpcore = ">=0.14.5,<0.15.0"
|
||||
httpcore = ">=0.15.0,<0.16.0"
|
||||
rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
|
||||
sniffio = "*"
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotlicffi", "brotli"]
|
||||
cli = ["click (>=8.0.0,<9.0.0)", "rich (>=10.0.0,<11.0.0)", "pygments (>=2.0.0,<3.0.0)"]
|
||||
cli = ["click (>=8.0.0,<9.0.0)", "rich (>=10,<13)", "pygments (>=2.0.0,<3.0.0)"]
|
||||
http2 = ["h2 (>=3,<5)"]
|
||||
socks = ["socksio (>=1.0.0,<2.0.0)"]
|
||||
|
||||
@ -1556,47 +1555,47 @@ cookiecutter = [
|
||||
{file = "cookiecutter-1.7.3.tar.gz", hash = "sha256:6b9a4d72882e243be077a7397d0f1f76fe66cf3df91f3115dbb5330e214fa457"},
|
||||
]
|
||||
coverage = [
|
||||
{file = "coverage-6.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df32ee0f4935a101e4b9a5f07b617d884a531ed5666671ff6ac66d2e8e8246d8"},
|
||||
{file = "coverage-6.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75b5dbffc334e0beb4f6c503fb95e6d422770fd2d1b40a64898ea26d6c02742d"},
|
||||
{file = "coverage-6.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:114944e6061b68a801c5da5427b9173a0dd9d32cd5fcc18a13de90352843737d"},
|
||||
{file = "coverage-6.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab88a01cd180b5640ccc9c47232e31924d5f9967ab7edd7e5c91c68eee47a69"},
|
||||
{file = "coverage-6.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad8f9068f5972a46d50fe5f32c09d6ee11da69c560fcb1b4c3baea246ca4109b"},
|
||||
{file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4cd696aa712e6cd16898d63cf66139dc70d998f8121ab558f0e1936396dbc579"},
|
||||
{file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c1a9942e282cc9d3ed522cd3e3cab081149b27ea3bda72d6f61f84eaf88c1a63"},
|
||||
{file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c06455121a089252b5943ea682187a4e0a5cf0a3fb980eb8e7ce394b144430a9"},
|
||||
{file = "coverage-6.3.3-cp310-cp310-win32.whl", hash = "sha256:cb5311d6ccbd22578c80028c5e292a7ab9adb91bd62c1982087fad75abe2e63d"},
|
||||
{file = "coverage-6.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:6d4a6f30f611e657495cc81a07ff7aa8cd949144e7667c5d3e680d73ba7a70e4"},
|
||||
{file = "coverage-6.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:79bf405432428e989cad7b8bc60581963238f7645ae8a404f5dce90236cc0293"},
|
||||
{file = "coverage-6.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:338c417613f15596af9eb7a39353b60abec9d8ce1080aedba5ecee6a5d85f8d3"},
|
||||
{file = "coverage-6.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db094a6a4ae6329ed322a8973f83630b12715654c197dd392410400a5bfa1a73"},
|
||||
{file = "coverage-6.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1414e8b124611bf4df8d77215bd32cba6e3425da8ce9c1f1046149615e3a9a31"},
|
||||
{file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:93b16b08f94c92cab88073ffd185070cdcb29f1b98df8b28e6649145b7f2c90d"},
|
||||
{file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fbc86ae8cc129c801e7baaafe3addf3c8d49c9c1597c44bdf2d78139707c3c62"},
|
||||
{file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b5ba058610e8289a07db2a57bce45a1793ec0d3d11db28c047aae2aa1a832572"},
|
||||
{file = "coverage-6.3.3-cp37-cp37m-win32.whl", hash = "sha256:8329635c0781927a2c6ae068461e19674c564e05b86736ab8eb29c420ee7dc20"},
|
||||
{file = "coverage-6.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:e5af1feee71099ae2e3b086ec04f57f9950e1be9ecf6c420696fea7977b84738"},
|
||||
{file = "coverage-6.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e814a4a5a1d95223b08cdb0f4f57029e8eab22ffdbae2f97107aeef28554517e"},
|
||||
{file = "coverage-6.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:61f4fbf3633cb0713437291b8848634ea97f89c7e849c2be17a665611e433f53"},
|
||||
{file = "coverage-6.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3401b0d2ed9f726fadbfa35102e00d1b3547b73772a1de5508ef3bdbcb36afe7"},
|
||||
{file = "coverage-6.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8586b177b4407f988731eb7f41967415b2197f35e2a6ee1a9b9b561f6323c8e9"},
|
||||
{file = "coverage-6.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:892e7fe32191960da559a14536768a62e83e87bbb867e1b9c643e7e0fbce2579"},
|
||||
{file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:afb03f981fadb5aed1ac6e3dd34f0488e1a0875623d557b6fad09b97a942b38a"},
|
||||
{file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cbe91bc84be4e5ef0b1480d15c7b18e29c73bdfa33e07d3725da7d18e1b0aff2"},
|
||||
{file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:91502bf27cbd5c83c95cfea291ef387469f2387508645602e1ca0fd8a4ba7548"},
|
||||
{file = "coverage-6.3.3-cp38-cp38-win32.whl", hash = "sha256:c488db059848702aff30aa1d90ef87928d4e72e4f00717343800546fdbff0a94"},
|
||||
{file = "coverage-6.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6534fcdfb5c503affb6b1130db7b5bfc8a0f77fa34880146f7a5c117987d0"},
|
||||
{file = "coverage-6.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc692c9ee18f0dd3214843779ba6b275ee4bb9b9a5745ba64265bce911aefd1a"},
|
||||
{file = "coverage-6.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:462105283de203df8de58a68c1bb4ba2a8a164097c2379f664fa81d6baf94b81"},
|
||||
{file = "coverage-6.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc972d829ad5ef4d4c5fcabd2bbe2add84ce8236f64ba1c0c72185da3a273130"},
|
||||
{file = "coverage-6.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06f54765cdbce99901871d50fe9f41d58213f18e98b170a30ca34f47de7dd5e8"},
|
||||
{file = "coverage-6.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7835f76a081787f0ca62a53504361b3869840a1620049b56d803a8cb3a9eeea3"},
|
||||
{file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6f5fee77ec3384b934797f1873758f796dfb4f167e1296dc00f8b2e023ce6ee9"},
|
||||
{file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:baa8be8aba3dd1e976e68677be68a960a633a6d44c325757aefaa4d66175050f"},
|
||||
{file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4d06380e777dd6b35ee936f333d55b53dc4a8271036ff884c909cf6e94be8b6c"},
|
||||
{file = "coverage-6.3.3-cp39-cp39-win32.whl", hash = "sha256:f8cabc5fd0091976ab7b020f5708335033e422de25e20ddf9416bdce2b7e07d8"},
|
||||
{file = "coverage-6.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c9441d57b0963cf8340268ad62fc83de61f1613034b79c2b1053046af0c5284"},
|
||||
{file = "coverage-6.3.3-pp36.pp37.pp38-none-any.whl", hash = "sha256:d522f1dc49127eab0bfbba4e90fa068ecff0899bbf61bf4065c790ddd6c177fe"},
|
||||
{file = "coverage-6.3.3.tar.gz", hash = "sha256:2781c43bffbbec2b8867376d4d61916f5e9c4cc168232528562a61d1b4b01879"},
|
||||
{file = "coverage-6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:50ed480b798febce113709846b11f5d5ed1e529c88d8ae92f707806c50297abf"},
|
||||
{file = "coverage-6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:26f8f92699756cb7af2b30720de0c5bb8d028e923a95b6d0c891088025a1ac8f"},
|
||||
{file = "coverage-6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60c2147921da7f4d2d04f570e1838db32b95c5509d248f3fe6417e91437eaf41"},
|
||||
{file = "coverage-6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:750e13834b597eeb8ae6e72aa58d1d831b96beec5ad1d04479ae3772373a8088"},
|
||||
{file = "coverage-6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af5b9ee0fc146e907aa0f5fb858c3b3da9199d78b7bb2c9973d95550bd40f701"},
|
||||
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a022394996419142b33a0cf7274cb444c01d2bb123727c4bb0b9acabcb515dea"},
|
||||
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5a78cf2c43b13aa6b56003707c5203f28585944c277c1f3f109c7b041b16bd39"},
|
||||
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9229d074e097f21dfe0643d9d0140ee7433814b3f0fc3706b4abffd1e3038632"},
|
||||
{file = "coverage-6.4-cp310-cp310-win32.whl", hash = "sha256:fb45fe08e1abc64eb836d187b20a59172053999823f7f6ef4f18a819c44ba16f"},
|
||||
{file = "coverage-6.4-cp310-cp310-win_amd64.whl", hash = "sha256:3cfd07c5889ddb96a401449109a8b97a165be9d67077df6802f59708bfb07720"},
|
||||
{file = "coverage-6.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:03014a74023abaf5a591eeeaf1ac66a73d54eba178ff4cb1fa0c0a44aae70383"},
|
||||
{file = "coverage-6.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c82f2cd69c71698152e943f4a5a6b83a3ab1db73b88f6e769fabc86074c3b08"},
|
||||
{file = "coverage-6.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b546cf2b1974ddc2cb222a109b37c6ed1778b9be7e6b0c0bc0cf0438d9e45a6"},
|
||||
{file = "coverage-6.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc173f1ce9ffb16b299f51c9ce53f66a62f4d975abe5640e976904066f3c835d"},
|
||||
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c53ad261dfc8695062fc8811ac7c162bd6096a05a19f26097f411bdf5747aee7"},
|
||||
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:eef5292b60b6de753d6e7f2d128d5841c7915fb1e3321c3a1fe6acfe76c38052"},
|
||||
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:543e172ce4c0de533fa892034cce260467b213c0ea8e39da2f65f9a477425211"},
|
||||
{file = "coverage-6.4-cp37-cp37m-win32.whl", hash = "sha256:00c8544510f3c98476bbd58201ac2b150ffbcce46a8c3e4fb89ebf01998f806a"},
|
||||
{file = "coverage-6.4-cp37-cp37m-win_amd64.whl", hash = "sha256:b84ab65444dcc68d761e95d4d70f3cfd347ceca5a029f2ffec37d4f124f61311"},
|
||||
{file = "coverage-6.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d548edacbf16a8276af13063a2b0669d58bbcfca7c55a255f84aac2870786a61"},
|
||||
{file = "coverage-6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:033ebec282793bd9eb988d0271c211e58442c31077976c19c442e24d827d356f"},
|
||||
{file = "coverage-6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:742fb8b43835078dd7496c3c25a1ec8d15351df49fb0037bffb4754291ef30ce"},
|
||||
{file = "coverage-6.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55fae115ef9f67934e9f1103c9ba826b4c690e4c5bcf94482b8b2398311bf9c"},
|
||||
{file = "coverage-6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd698341626f3c77784858427bad0cdd54a713115b423d22ac83a28303d1d95"},
|
||||
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:62d382f7d77eeeaff14b30516b17bcbe80f645f5cf02bb755baac376591c653c"},
|
||||
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:016d7f5cf1c8c84f533a3c1f8f36126fbe00b2ec0ccca47cc5731c3723d327c6"},
|
||||
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:69432946f154c6add0e9ede03cc43b96e2ef2733110a77444823c053b1ff5166"},
|
||||
{file = "coverage-6.4-cp38-cp38-win32.whl", hash = "sha256:83bd142cdec5e4a5c4ca1d4ff6fa807d28460f9db919f9f6a31babaaa8b88426"},
|
||||
{file = "coverage-6.4-cp38-cp38-win_amd64.whl", hash = "sha256:4002f9e8c1f286e986fe96ec58742b93484195defc01d5cc7809b8f7acb5ece3"},
|
||||
{file = "coverage-6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e4f52c272fdc82e7c65ff3f17a7179bc5f710ebc8ce8a5cadac81215e8326740"},
|
||||
{file = "coverage-6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b5578efe4038be02d76c344007b13119b2b20acd009a88dde8adec2de4f630b5"},
|
||||
{file = "coverage-6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8099ea680201c2221f8468c372198ceba9338a5fec0e940111962b03b3f716a"},
|
||||
{file = "coverage-6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a00441f5ea4504f5abbc047589d09e0dc33eb447dc45a1a527c8b74bfdd32c65"},
|
||||
{file = "coverage-6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e76bd16f0e31bc2b07e0fb1379551fcd40daf8cdf7e24f31a29e442878a827c"},
|
||||
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8d2e80dd3438e93b19e1223a9850fa65425e77f2607a364b6fd134fcd52dc9df"},
|
||||
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:341e9c2008c481c5c72d0e0dbf64980a4b2238631a7f9780b0fe2e95755fb018"},
|
||||
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:21e6686a95025927775ac501e74f5940cdf6fe052292f3a3f7349b0abae6d00f"},
|
||||
{file = "coverage-6.4-cp39-cp39-win32.whl", hash = "sha256:968ed5407f9460bd5a591cefd1388cc00a8f5099de9e76234655ae48cfdbe2c3"},
|
||||
{file = "coverage-6.4-cp39-cp39-win_amd64.whl", hash = "sha256:e35217031e4b534b09f9b9a5841b9344a30a6357627761d4218818b865d45055"},
|
||||
{file = "coverage-6.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:e637ae0b7b481905358624ef2e81d7fb0b1af55f5ff99f9ba05442a444b11e45"},
|
||||
{file = "coverage-6.4.tar.gz", hash = "sha256:727dafd7f67a6e1cad808dc884bd9c5a2f6ef1f8f6d2f22b37b96cb0080d4f49"},
|
||||
]
|
||||
decorator = [
|
||||
{file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
|
||||
@ -1691,8 +1690,8 @@ h11 = [
|
||||
{file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
|
||||
]
|
||||
httpcore = [
|
||||
{file = "httpcore-0.14.7-py3-none-any.whl", hash = "sha256:47d772f754359e56dd9d892d9593b6f9870a37aeb8ba51e9a88b09b3d68cfade"},
|
||||
{file = "httpcore-0.14.7.tar.gz", hash = "sha256:7503ec1c0f559066e7e39bc4003fd2ce023d01cf51793e3c173b864eb456ead1"},
|
||||
{file = "httpcore-0.15.0-py3-none-any.whl", hash = "sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6"},
|
||||
{file = "httpcore-0.15.0.tar.gz", hash = "sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b"},
|
||||
]
|
||||
httptools = [
|
||||
{file = "httptools-0.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcddfe70553be717d9745990dfdb194e22ee0f60eb8f48c0794e7bfeda30d2d5"},
|
||||
@ -1731,8 +1730,8 @@ httptools = [
|
||||
{file = "httptools-0.4.0.tar.gz", hash = "sha256:2c9a930c378b3d15d6b695fb95ebcff81a7395b4f9775c4f10a076beb0b2c1ff"},
|
||||
]
|
||||
httpx = [
|
||||
{file = "httpx-0.22.0-py3-none-any.whl", hash = "sha256:e35e83d1d2b9b2a609ef367cc4c1e66fd80b750348b20cc9e19d1952fc2ca3f6"},
|
||||
{file = "httpx-0.22.0.tar.gz", hash = "sha256:d8e778f76d9bbd46af49e7f062467e3157a5a3d2ae4876a4bbfd8a51ed9c9cb4"},
|
||||
{file = "httpx-0.23.0-py3-none-any.whl", hash = "sha256:42974f577483e1e932c3cdc3cd2303e883cbfba17fe228b0f63589764d7b9c4b"},
|
||||
{file = "httpx-0.23.0.tar.gz", hash = "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef"},
|
||||
]
|
||||
identify = [
|
||||
{file = "identify-2.5.0-py2.py3-none-any.whl", hash = "sha256:3acfe15a96e4272b4ec5662ee3e231ceba976ef63fd9980ed2ce9cc415df393f"},
|
||||
|
@ -17,7 +17,7 @@ from nonebot.rule import to_me
|
||||
from nonebot.typing import T_State
|
||||
|
||||
from .config import Config
|
||||
from .platform import check_sub_target, platform_manager
|
||||
from .platform import Platform, check_sub_target, platform_manager
|
||||
from .plugin_config import plugin_config
|
||||
from .types import Category, Target, User
|
||||
from .utils import parse_text
|
||||
@ -108,8 +108,13 @@ def do_add_sub(add_sub: Type[Matcher]):
|
||||
"platform", _gen_prompt_template("{_prompt}"), [Depends(parse_platform)]
|
||||
)
|
||||
async def init_id(state: T_State):
|
||||
if platform_manager[state["platform"]].has_target:
|
||||
state["_prompt"] = "请输入订阅用户的id:\n查询id获取方法请回复:“查询”"
|
||||
cur_platform = platform_manager[state["platform"]]
|
||||
if cur_platform.has_target:
|
||||
state["_prompt"] = (
|
||||
("1." + cur_platform.parse_target_promot + "\n2.")
|
||||
if cur_platform.parse_target_promot
|
||||
else ""
|
||||
) + "请输入订阅用户的id\n查询id获取方法请回复:“查询”"
|
||||
else:
|
||||
state["id"] = "default"
|
||||
state["name"] = await platform_manager[state["platform"]].get_target_name(
|
||||
@ -125,6 +130,8 @@ def do_add_sub(add_sub: Type[Matcher]):
|
||||
raise LookupError
|
||||
if target == "取消":
|
||||
raise KeyboardInterrupt
|
||||
platform = platform_manager[state["platform"]]
|
||||
target = await platform.parse_target(target)
|
||||
name = await check_sub_target(state["platform"], target)
|
||||
if not name:
|
||||
raise ValueError
|
||||
@ -141,6 +148,8 @@ def do_add_sub(add_sub: Type[Matcher]):
|
||||
await add_sub.finish("已中止订阅")
|
||||
except (ValueError):
|
||||
await add_sub.reject("id输入错误")
|
||||
except (Platform.ParseTargetException):
|
||||
await add_sub.reject("不能从你的输入中提取出id,请检查你输入的内容是否符合预期")
|
||||
else:
|
||||
await add_sub.send(
|
||||
"即将订阅的用户为:{} {} {}\n如有错误请输入“取消”重新订阅".format(
|
||||
|
@ -1,10 +1,11 @@
|
||||
import json
|
||||
import re
|
||||
from typing import Any, Optional
|
||||
|
||||
from ..post import Post
|
||||
from ..types import Category, RawPost, Tag, Target
|
||||
from ..utils import http_client
|
||||
from .platform import CategoryNotSupport, NewMessage
|
||||
from .platform import CategoryNotSupport, NewMessage, StatusChange
|
||||
|
||||
|
||||
class Bilibili(NewMessage):
|
||||
@ -25,6 +26,7 @@ class Bilibili(NewMessage):
|
||||
schedule_kw = {"seconds": 10}
|
||||
name = "B站"
|
||||
has_target = True
|
||||
parse_target_promot = "请输入用户主页的链接"
|
||||
|
||||
async def get_target_name(self, target: Target) -> Optional[str]:
|
||||
async with http_client() as client:
|
||||
@ -36,6 +38,16 @@ class Bilibili(NewMessage):
|
||||
return None
|
||||
return res_data["data"]["name"]
|
||||
|
||||
async def parse_target(self, target_text: str) -> Target:
|
||||
if re.match(r"\d+", target_text):
|
||||
return Target(target_text)
|
||||
elif match := re.match(
|
||||
r"(?:https?://)?space\.bilibili\.com/(\d+)", target_text
|
||||
):
|
||||
return Target(match.group(1))
|
||||
else:
|
||||
raise self.ParseTargetException()
|
||||
|
||||
async def get_sub_list(self, target: Target) -> list[RawPost]:
|
||||
async with http_client() as client:
|
||||
params = {"host_uid": target, "offset": 0, "need_top": 0}
|
||||
@ -143,3 +155,73 @@ class Bilibili(NewMessage):
|
||||
else:
|
||||
raise CategoryNotSupport(post_type)
|
||||
return Post("bilibili", text=text, url=url, pics=pic, target_name=target_name)
|
||||
|
||||
|
||||
class Bilibililive(StatusChange):
|
||||
# Author : Sichongzou
|
||||
# Date : 2022-5-18 8:54
|
||||
# Description : bilibili开播提醒
|
||||
# E-mail : 1557157806@qq.com
|
||||
categories = {}
|
||||
platform_name = "bilibili-live"
|
||||
enable_tag = True
|
||||
enabled = True
|
||||
is_common = True
|
||||
schedule_type = "interval"
|
||||
schedule_kw = {"seconds": 10}
|
||||
name = "Bilibili直播"
|
||||
has_target = True
|
||||
|
||||
async def get_target_name(self, target: Target) -> Optional[str]:
|
||||
async with http_client() as client:
|
||||
res = await client.get(
|
||||
"https://api.bilibili.com/x/space/acc/info", params={"mid": target}
|
||||
)
|
||||
res_data = json.loads(res.text)
|
||||
if res_data["code"]:
|
||||
return None
|
||||
return res_data["data"]["name"]
|
||||
|
||||
async def get_status(self, target: Target):
|
||||
async with http_client() as client:
|
||||
params = {"mid": target}
|
||||
res = await client.get(
|
||||
"https://api.bilibili.com/x/space/acc/info",
|
||||
params=params,
|
||||
timeout=4.0,
|
||||
)
|
||||
res_dict = json.loads(res.text)
|
||||
if res_dict["code"] == 0:
|
||||
info = {}
|
||||
info["uid"] = res_dict["data"]["mid"]
|
||||
info["uname"] = res_dict["data"]["name"]
|
||||
info["live_state"] = res_dict["data"]["live_room"]["liveStatus"]
|
||||
info["room_id"] = res_dict["data"]["live_room"]["roomid"]
|
||||
info["title"] = res_dict["data"]["live_room"]["title"]
|
||||
info["cover"] = res_dict["data"]["live_room"]["cover"]
|
||||
return info
|
||||
else:
|
||||
return []
|
||||
|
||||
def compare_status(self, target: Target, old_status, new_status) -> list[RawPost]:
|
||||
if (
|
||||
new_status["live_state"] != old_status["live_state"]
|
||||
and new_status["live_state"] == 1
|
||||
):
|
||||
return [new_status]
|
||||
else:
|
||||
return []
|
||||
|
||||
async def parse(self, raw_post: RawPost) -> Post:
|
||||
url = "https://live.bilibili.com/{}".format(raw_post["room_id"])
|
||||
pic = [raw_post["cover"]]
|
||||
target_name = raw_post["uname"]
|
||||
title = raw_post["title"]
|
||||
return Post(
|
||||
self.name,
|
||||
text=title,
|
||||
url=url,
|
||||
pics=pic,
|
||||
target_name=target_name,
|
||||
compress=True,
|
||||
)
|
||||
|
@ -1,3 +1,4 @@
|
||||
import re
|
||||
from typing import Any, Optional
|
||||
|
||||
from ..post import Post
|
||||
@ -17,6 +18,7 @@ class NcmArtist(NewMessage):
|
||||
schedule_kw = {"minutes": 1}
|
||||
name = "网易云-歌手"
|
||||
has_target = True
|
||||
parse_target_promot = "请输入歌手主页(包含数字ID)的链接"
|
||||
|
||||
async def get_target_name(self, target: Target) -> Optional[str]:
|
||||
async with http_client() as client:
|
||||
@ -29,6 +31,16 @@ class NcmArtist(NewMessage):
|
||||
return
|
||||
return res_data["artist"]["name"]
|
||||
|
||||
async def parse_target(self, target_text: str) -> Target:
|
||||
if re.match(r"^\d+$", target_text):
|
||||
return Target(target_text)
|
||||
elif match := re.match(
|
||||
r"(?:https?://)?music\.163\.com/#/artist\?id=(\d+)", target_text
|
||||
):
|
||||
return Target(match.group(1))
|
||||
else:
|
||||
raise self.ParseTargetException()
|
||||
|
||||
async def get_sub_list(self, target: Target) -> list[RawPost]:
|
||||
async with http_client() as client:
|
||||
res = await client.get(
|
||||
|
@ -1,3 +1,4 @@
|
||||
import re
|
||||
from typing import Any, Optional
|
||||
|
||||
from ..post import Post
|
||||
@ -17,6 +18,7 @@ class NcmRadio(NewMessage):
|
||||
schedule_kw = {"minutes": 10}
|
||||
name = "网易云-电台"
|
||||
has_target = True
|
||||
parse_target_promot = "请输入主播电台主页(包含数字ID)的链接"
|
||||
|
||||
async def get_target_name(self, target: Target) -> Optional[str]:
|
||||
async with http_client() as client:
|
||||
@ -30,6 +32,16 @@ class NcmRadio(NewMessage):
|
||||
return
|
||||
return res_data["programs"][0]["radio"]["name"]
|
||||
|
||||
async def parse_target(self, target_text: str) -> Target:
|
||||
if re.match(r"^\d+$", target_text):
|
||||
return Target(target_text)
|
||||
elif match := re.match(
|
||||
r"(?:https?://)?music\.163\.com/#/djradio\?id=(\d+)", target_text
|
||||
):
|
||||
return Target(match.group(1))
|
||||
else:
|
||||
raise self.ParseTargetException()
|
||||
|
||||
async def get_sub_list(self, target: Target) -> list[RawPost]:
|
||||
async with http_client() as client:
|
||||
res = await client.post(
|
||||
|
@ -1,3 +1,5 @@
|
||||
import json
|
||||
import ssl
|
||||
import time
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import defaultdict
|
||||
@ -47,6 +49,7 @@ class Platform(metaclass=RegistryABCMeta, base=True):
|
||||
enable_tag: bool
|
||||
store: dict[Target, Any]
|
||||
platform_name: str
|
||||
parse_target_promot: Optional[str] = None
|
||||
|
||||
@abstractmethod
|
||||
async def get_target_name(self, target: Target) -> Optional[str]:
|
||||
@ -58,6 +61,25 @@ class Platform(metaclass=RegistryABCMeta, base=True):
|
||||
) -> list[tuple[User, list[Post]]]:
|
||||
...
|
||||
|
||||
async def do_fetch_new_post(
|
||||
self, target: Target, users: list[UserSubInfo]
|
||||
) -> list[tuple[User, list[Post]]]:
|
||||
try:
|
||||
return await self.fetch_new_post(target, users)
|
||||
except httpx.RequestError as err:
|
||||
logger.warning(
|
||||
"network connection error: {}, url: {}".format(
|
||||
type(err), err.request.url
|
||||
)
|
||||
)
|
||||
return []
|
||||
except ssl.SSLError as err:
|
||||
logger.warning(f"ssl error: {err}")
|
||||
return []
|
||||
except json.JSONDecodeError as err:
|
||||
logger.warning(f"json error, parsing: {err.doc}")
|
||||
return []
|
||||
|
||||
@abstractmethod
|
||||
async def parse(self, raw_post: RawPost) -> Post:
|
||||
...
|
||||
@ -73,6 +95,12 @@ class Platform(metaclass=RegistryABCMeta, base=True):
|
||||
self.reverse_category[val] = key
|
||||
self.store = dict()
|
||||
|
||||
class ParseTargetException(Exception):
|
||||
pass
|
||||
|
||||
async def parse_target(self, target_string: str) -> Target:
|
||||
return Target(target_string)
|
||||
|
||||
@abstractmethod
|
||||
def get_tags(self, raw_post: RawPost) -> Optional[Collection[Tag]]:
|
||||
"Return Tag list of given RawPost"
|
||||
@ -219,30 +247,22 @@ class NewMessage(MessageProcess, abstract=True):
|
||||
async def fetch_new_post(
|
||||
self, target: Target, users: list[UserSubInfo]
|
||||
) -> list[tuple[User, list[Post]]]:
|
||||
try:
|
||||
post_list = await self.get_sub_list(target)
|
||||
new_posts = await self.filter_common_with_diff(target, post_list)
|
||||
if not new_posts:
|
||||
return []
|
||||
else:
|
||||
for post in new_posts:
|
||||
logger.info(
|
||||
"fetch new post from {} {}: {}".format(
|
||||
self.platform_name,
|
||||
target if self.has_target else "-",
|
||||
self.get_id(post),
|
||||
)
|
||||
)
|
||||
res = await self.dispatch_user_post(target, new_posts, users)
|
||||
self.parse_cache = {}
|
||||
return res
|
||||
except httpx.RequestError as err:
|
||||
logger.warning(
|
||||
"network connection error: {}, url: {}".format(
|
||||
type(err), err.request.url
|
||||
)
|
||||
)
|
||||
post_list = await self.get_sub_list(target)
|
||||
new_posts = await self.filter_common_with_diff(target, post_list)
|
||||
if not new_posts:
|
||||
return []
|
||||
else:
|
||||
for post in new_posts:
|
||||
logger.info(
|
||||
"fetch new post from {} {}: {}".format(
|
||||
self.platform_name,
|
||||
target if self.has_target else "-",
|
||||
self.get_id(post),
|
||||
)
|
||||
)
|
||||
res = await self.dispatch_user_post(target, new_posts, users)
|
||||
self.parse_cache = {}
|
||||
return res
|
||||
|
||||
|
||||
class StatusChange(Platform, abstract=True):
|
||||
@ -263,30 +283,22 @@ class StatusChange(Platform, abstract=True):
|
||||
async def fetch_new_post(
|
||||
self, target: Target, users: list[UserSubInfo]
|
||||
) -> list[tuple[User, list[Post]]]:
|
||||
try:
|
||||
new_status = await self.get_status(target)
|
||||
res = []
|
||||
if old_status := self.get_stored_data(target):
|
||||
diff = self.compare_status(target, old_status, new_status)
|
||||
if diff:
|
||||
logger.info(
|
||||
"status changes {} {}: {} -> {}".format(
|
||||
self.platform_name,
|
||||
target if self.has_target else "-",
|
||||
old_status,
|
||||
new_status,
|
||||
)
|
||||
new_status = await self.get_status(target)
|
||||
res = []
|
||||
if old_status := self.get_stored_data(target):
|
||||
diff = self.compare_status(target, old_status, new_status)
|
||||
if diff:
|
||||
logger.info(
|
||||
"status changes {} {}: {} -> {}".format(
|
||||
self.platform_name,
|
||||
target if self.has_target else "-",
|
||||
old_status,
|
||||
new_status,
|
||||
)
|
||||
res = await self.dispatch_user_post(target, diff, users)
|
||||
self.set_stored_data(target, new_status)
|
||||
return res
|
||||
except httpx.RequestError as err:
|
||||
logger.warning(
|
||||
"network connection error: {}, url: {}".format(
|
||||
type(err), err.request.url
|
||||
)
|
||||
)
|
||||
return []
|
||||
res = await self.dispatch_user_post(target, diff, users)
|
||||
self.set_stored_data(target, new_status)
|
||||
return res
|
||||
|
||||
|
||||
class SimplePost(MessageProcess, abstract=True):
|
||||
@ -295,29 +307,21 @@ class SimplePost(MessageProcess, abstract=True):
|
||||
async def fetch_new_post(
|
||||
self, target: Target, users: list[UserSubInfo]
|
||||
) -> list[tuple[User, list[Post]]]:
|
||||
try:
|
||||
new_posts = await self.get_sub_list(target)
|
||||
if not new_posts:
|
||||
return []
|
||||
else:
|
||||
for post in new_posts:
|
||||
logger.info(
|
||||
"fetch new post from {} {}: {}".format(
|
||||
self.platform_name,
|
||||
target if self.has_target else "-",
|
||||
self.get_id(post),
|
||||
)
|
||||
)
|
||||
res = await self.dispatch_user_post(target, new_posts, users)
|
||||
self.parse_cache = {}
|
||||
return res
|
||||
except httpx.RequestError as err:
|
||||
logger.warning(
|
||||
"network connection error: {}, url: {}".format(
|
||||
type(err), err.request.url
|
||||
)
|
||||
)
|
||||
new_posts = await self.get_sub_list(target)
|
||||
if not new_posts:
|
||||
return []
|
||||
else:
|
||||
for post in new_posts:
|
||||
logger.info(
|
||||
"fetch new post from {} {}: {}".format(
|
||||
self.platform_name,
|
||||
target if self.has_target else "-",
|
||||
self.get_id(post),
|
||||
)
|
||||
)
|
||||
res = await self.dispatch_user_post(target, new_posts, users)
|
||||
self.parse_cache = {}
|
||||
return res
|
||||
|
||||
|
||||
class NoTargetGroup(Platform, abstract=True):
|
||||
|
@ -28,6 +28,7 @@ class Weibo(NewMessage):
|
||||
schedule_type = "interval"
|
||||
schedule_kw = {"seconds": 3}
|
||||
has_target = True
|
||||
parse_target_promot = "请输入用户主页(包含数字UID)的链接"
|
||||
|
||||
async def get_target_name(self, target: Target) -> Optional[str]:
|
||||
async with http_client() as client:
|
||||
@ -41,6 +42,15 @@ class Weibo(NewMessage):
|
||||
else:
|
||||
return None
|
||||
|
||||
async def parse_target(self, target_text: str) -> Target:
|
||||
if re.match(r"\d+", target_text):
|
||||
return Target(target_text)
|
||||
elif match := re.match(r"(?:https?://)?weibo\.com/u/(\d+)", target_text):
|
||||
# 都2202年了应该不会有http了吧,不过还是防一手
|
||||
return Target(match.group(1))
|
||||
else:
|
||||
raise self.ParseTargetException()
|
||||
|
||||
async def get_sub_list(self, target: Target) -> list[RawPost]:
|
||||
async with http_client() as client:
|
||||
params = {"containerid": "107603" + target}
|
||||
@ -133,9 +143,9 @@ class Weibo(NewMessage):
|
||||
"https://m.weibo.cn/detail/{}".format(info["mid"]), headers=header
|
||||
)
|
||||
try:
|
||||
full_json_text = re.search(
|
||||
r'"status": ([\s\S]+),\s+"call"', res.text
|
||||
).group(1)
|
||||
match = re.search(r'"status": ([\s\S]+),\s+"call"', res.text)
|
||||
assert match
|
||||
full_json_text = match.group(1)
|
||||
info = json.loads(full_json_text)
|
||||
except:
|
||||
logger.info(
|
||||
|
@ -14,10 +14,11 @@ class PlugConfig(BaseSettings):
|
||||
bison_filter_log: bool = False
|
||||
bison_to_me: bool = True
|
||||
bison_skip_browser_check: bool = False
|
||||
bison_use_pic_merge: int = 0 # 多图片时启用图片合并转发(仅限群),当bison_use_queue为False时该配置不会生效
|
||||
bison_use_pic_merge: int = 0 # 多图片时启用图片合并转发(仅限群)
|
||||
# 0:不启用;1:首条消息单独发送,剩余照片合并转发;2以及以上:所有消息全部合并转发
|
||||
bison_resend_times: int = 0
|
||||
bison_proxy: Optional[str]
|
||||
bison_ua: str = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"
|
||||
|
||||
class Config:
|
||||
extra = "ignore"
|
||||
|
@ -59,7 +59,7 @@ async def fetch_and_send(target_type: str):
|
||||
send_user_list,
|
||||
)
|
||||
)
|
||||
to_send = await platform_manager[target_type].fetch_new_post(
|
||||
to_send = await platform_manager[target_type].do_fetch_new_post(
|
||||
target, send_userinfo_list
|
||||
)
|
||||
if not to_send:
|
||||
|
@ -4,9 +4,8 @@ import httpx
|
||||
|
||||
from ..plugin_config import plugin_config
|
||||
|
||||
if plugin_config.bison_proxy:
|
||||
http_client = functools.partial(
|
||||
httpx.AsyncClient, proxies=plugin_config.bison_proxy
|
||||
)
|
||||
else:
|
||||
http_client = httpx.AsyncClient
|
||||
http_client = functools.partial(
|
||||
httpx.AsyncClient,
|
||||
proxies=plugin_config.bison_proxy or None,
|
||||
headers={"user-agent": plugin_config.bison_ua},
|
||||
)
|
||||
|
114
tests/platforms/static/bili_live_status.json
Normal file
114
tests/platforms/static/bili_live_status.json
Normal file
@ -0,0 +1,114 @@
|
||||
{
|
||||
"code": 0,
|
||||
"message": "0",
|
||||
"ttl": 1,
|
||||
"data": {
|
||||
"mid": 13164144,
|
||||
"name": "魔法Zc目录",
|
||||
"sex": "男",
|
||||
"face": "http://i0.hdslb.com/bfs/face/a84fa10f90f7060d0336384954ee1cde7a8e9bc6.jpg",
|
||||
"face_nft": 0,
|
||||
"sign": "每日18:00~22:00欢乐直播!请勿在任何乌有相关内容中刷Zc,尊重角色;商务合作qq271374252",
|
||||
"rank": 10000,
|
||||
"level": 6,
|
||||
"jointime": 0,
|
||||
"moral": 0,
|
||||
"silence": 0,
|
||||
"coins": 0,
|
||||
"fans_badge": true,
|
||||
"fans_medal": {
|
||||
"show": false,
|
||||
"wear": false,
|
||||
"medal": null
|
||||
},
|
||||
"official": {
|
||||
"role": 1,
|
||||
"title": "bilibili 2021百大UP主、知名游戏UP主、直播高能主播",
|
||||
"desc": "",
|
||||
"type": 0
|
||||
},
|
||||
"vip": {
|
||||
"type": 2,
|
||||
"status": 1,
|
||||
"due_date": 1702051200000,
|
||||
"vip_pay_type": 0,
|
||||
"theme_type": 0,
|
||||
"label": {
|
||||
"path": "",
|
||||
"text": "年度大会员",
|
||||
"label_theme": "annual_vip",
|
||||
"text_color": "#FFFFFF",
|
||||
"bg_style": 1,
|
||||
"bg_color": "#FB7299",
|
||||
"border_color": ""
|
||||
},
|
||||
"avatar_subscript": 1,
|
||||
"nickname_color": "#FB7299",
|
||||
"role": 3,
|
||||
"avatar_subscript_url": "http://i0.hdslb.com/bfs/vip/icon_Certification_big_member_22_3x.png"
|
||||
},
|
||||
"pendant": {
|
||||
"pid": 3399,
|
||||
"name": "2233幻星集",
|
||||
"image": "http://i0.hdslb.com/bfs/garb/item/20c07ded13498a5b12db99660c766ddd92ecfe31.png",
|
||||
"expire": 0,
|
||||
"image_enhance": "http://i0.hdslb.com/bfs/garb/item/20c07ded13498a5b12db99660c766ddd92ecfe31.png",
|
||||
"image_enhance_frame": ""
|
||||
},
|
||||
"nameplate": {
|
||||
"nid": 1,
|
||||
"name": "黄金殿堂",
|
||||
"image": "http://i2.hdslb.com/bfs/face/82896ff40fcb4e7c7259cb98056975830cb55695.png",
|
||||
"image_small": "http://i0.hdslb.com/bfs/face/627e342851dfda6fe7380c2fa0cbd7fae2e61533.png",
|
||||
"level": "稀有勋章",
|
||||
"condition": "单个自制视频总播放数\u003e=100万"
|
||||
},
|
||||
"user_honour_info": {
|
||||
"mid": 0,
|
||||
"colour": null,
|
||||
"tags": []
|
||||
},
|
||||
"is_followed": true,
|
||||
"top_photo": "http://i2.hdslb.com/bfs/space/853fea2728651588a2cdef0a1e586bcefff8e3d8.png",
|
||||
"theme": {},
|
||||
"sys_notice": {},
|
||||
"live_room": {
|
||||
"roomStatus": 1,
|
||||
"liveStatus": 0,
|
||||
"url": "https://live.bilibili.com/3044248?broadcast_type=0\u0026is_room_feed=1",
|
||||
"title": "【Zc】早朝危机合约!",
|
||||
"cover": "http://i0.hdslb.com/bfs/live/new_room_cover/cf7d4d3b2f336c6dba299644c3af952c5db82612.jpg",
|
||||
"roomid": 3044248,
|
||||
"roundStatus": 1,
|
||||
"broadcast_type": 0,
|
||||
"watched_show": {
|
||||
"switch": true,
|
||||
"num": 13753,
|
||||
"text_small": "1.3万",
|
||||
"text_large": "1.3万人看过",
|
||||
"icon": "https://i0.hdslb.com/bfs/live/a725a9e61242ef44d764ac911691a7ce07f36c1d.png",
|
||||
"icon_location": "",
|
||||
"icon_web": "https://i0.hdslb.com/bfs/live/8d9d0f33ef8bf6f308742752d13dd0df731df19c.png"
|
||||
}
|
||||
},
|
||||
"birthday": "07-21",
|
||||
"school": {
|
||||
"name": ""
|
||||
},
|
||||
"profession": {
|
||||
"name": "",
|
||||
"department": "",
|
||||
"title": "",
|
||||
"is_show": 0
|
||||
},
|
||||
"tags": [
|
||||
"评论区UP主",
|
||||
"目标是星辰大海"
|
||||
],
|
||||
"series": {
|
||||
"user_upgrade_status": 3,
|
||||
"show_upgrade_window": false
|
||||
},
|
||||
"is_senior_member": 1
|
||||
}
|
||||
}
|
1
tests/platforms/static/bilibili_arknights_profile.json
Normal file
1
tests/platforms/static/bilibili_arknights_profile.json
Normal file
@ -0,0 +1 @@
|
||||
{"code":0,"message":"0","ttl":1,"data":{"mid":161775300,"name":"明日方舟","sex":"保密","face":"http://i0.hdslb.com/bfs/face/89154378c06a5ed332c40c2ca56f50cd641c0c90.jpg","face_nft":0,"sign":"重铸未来 方舟启航","rank":10000,"level":6,"jointime":0,"moral":0,"silence":0,"coins":0,"fans_badge":true,"fans_medal":{"show":false,"wear":false,"medal":null},"official":{"role":3,"title":"明日方舟官方账号","desc":"","type":1},"vip":{"type":2,"status":1,"due_date":1648828800000,"vip_pay_type":0,"theme_type":0,"label":{"path":"","text":"年度大会员","label_theme":"annual_vip","text_color":"#FFFFFF","bg_style":1,"bg_color":"#FB7299","border_color":""},"avatar_subscript":1,"nickname_color":"#FB7299","role":3,"avatar_subscript_url":"http://i0.hdslb.com/bfs/vip/icon_Certification_big_member_22_3x.png"},"pendant":{"pid":5305,"name":"明日方舟音律系列","image":"http://i0.hdslb.com/bfs/garb/item/615a1653281141ddf64cbb98c792ddaee78f7f40.png","expire":0,"image_enhance":"http://i0.hdslb.com/bfs/garb/item/516ecdf2d495a62f1bac31497c831b711823140c.webp","image_enhance_frame":"http://i0.hdslb.com/bfs/garb/item/c0751afbf950373c260254d02768eabf30ff3906.png"},"nameplate":{"nid":0,"name":"","image":"","image_small":"","level":"","condition":""},"user_honour_info":{"mid":0,"colour":null,"tags":[]},"is_followed":true,"top_photo":"http://i1.hdslb.com/bfs/space/6c6084808ec5bdff1985acc05ce0e126c49ad76e.png","theme":{},"sys_notice":{},"live_room":{"roomStatus":1,"liveStatus":0,"url":"https://live.bilibili.com/5555734?broadcast_type=0\u0026is_room_feed=1","title":"《明日方舟》2022新春前瞻特辑","cover":"http://i0.hdslb.com/bfs/live/new_room_cover/79af83a27f6001c1acfb47d1c0b879290f7c3308.jpg","roomid":5555734,"roundStatus":1,"broadcast_type":0,"watched_show":{"switch":true,"num":13033,"text_small":"1.3万","text_large":"1.3万人看过","icon":"https://i0.hdslb.com/bfs/live/a725a9e61242ef44d764ac911691a7ce07f36c1d.png","icon_location":"","icon_web":"https://i0.hdslb.com/bfs/live/8d9d0f33ef8bf6f308742752d13dd0df731df19c.png"}},"birthday":"","school":null,"profession":{"name":"","department":"","title":"","is_show":0},"tags":null,"series":{"user_upgrade_status":3,"show_upgrade_window":false},"is_senior_member":0}}
|
@ -1,3 +1,4 @@
|
||||
import typing
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
@ -14,6 +15,10 @@ def bing_dy_list():
|
||||
return get_json("bilibili_bing_list.json")["data"]["cards"]
|
||||
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from nonebot_bison.platform.bilibili import Bilibili
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def bilibili(app: App):
|
||||
from nonebot_bison.platform import platform_manager
|
||||
@ -76,3 +81,20 @@ async def test_fetch_new(bilibili, dummy_user_subinfo):
|
||||
post.text
|
||||
== "#罗德厨房——回甘##明日方舟#\r\n明日方舟官方美食漫画,正式开餐。\r\n往事如烟,安然即好。\r\nMenu 01:高脚羽兽烤串与罗德岛的领袖\r\n\r\n哔哩哔哩漫画阅读:https://manga.bilibili.com/detail/mc31998?from=manga_search\r\n\r\n关注并转发本动态,我们将会在5月27日抽取10位博士赠送【兔兔奇境】周边礼盒一份。 互动抽奖"
|
||||
)
|
||||
|
||||
|
||||
async def test_parse_target(bilibili: "Bilibili"):
|
||||
from nonebot_bison.platform.platform import Platform
|
||||
|
||||
res = await bilibili.parse_target(
|
||||
"https://space.bilibili.com/161775300?from=search&seid=130517740606234234234&spm_id_from=333.337.0.0"
|
||||
)
|
||||
assert res == "161775300"
|
||||
res2 = await bilibili.parse_target(
|
||||
"space.bilibili.com/161775300?from=search&seid=130517740606234234234&spm_id_from=333.337.0.0"
|
||||
)
|
||||
assert res2 == "161775300"
|
||||
with pytest.raises(Platform.ParseTargetException):
|
||||
await bilibili.parse_target(
|
||||
"https://www.bilibili.com/video/BV1qP4y1g738?spm_id_from=333.999.0.0"
|
||||
)
|
||||
|
44
tests/platforms/test_bilibili_live.py
Normal file
44
tests/platforms/test_bilibili_live.py
Normal file
@ -0,0 +1,44 @@
|
||||
from datetime import datetime
|
||||
|
||||
import feedparser
|
||||
import pytest
|
||||
import respx
|
||||
from httpx import Response
|
||||
from nonebug.app import App
|
||||
from pytz import timezone
|
||||
|
||||
from .utils import get_file, get_json
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def bili_live(app: App):
|
||||
from nonebot_bison.platform import platform_manager
|
||||
|
||||
return platform_manager["bilibili-live"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_fetch_bilibili_live_status(bili_live, dummy_user_subinfo):
|
||||
mock_bili_live_status = get_json("bili_live_status.json")
|
||||
|
||||
bili_live_router = respx.get(
|
||||
"https://api.bilibili.com/x/space/acc/info?mid=13164144"
|
||||
)
|
||||
bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status))
|
||||
target = "13164144"
|
||||
res = await bili_live.fetch_new_post(target, [dummy_user_subinfo])
|
||||
assert bili_live_router.called
|
||||
assert len(res) == 0
|
||||
mock_bili_live_status["data"]["live_room"]["liveStatus"] = 1
|
||||
bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status))
|
||||
res2 = await bili_live.fetch_new_post(target, [dummy_user_subinfo])
|
||||
post = res2[0][1][0]
|
||||
assert post.target_type == "Bilibili直播"
|
||||
assert post.text == "【Zc】早朝危机合约!"
|
||||
assert post.url == "https://live.bilibili.com/3044248"
|
||||
assert post.target_name == "魔法Zc目录"
|
||||
assert post.pics == [
|
||||
"http://i0.hdslb.com/bfs/live/new_room_cover/cf7d4d3b2f336c6dba299644c3af952c5db82612.jpg"
|
||||
]
|
||||
assert post.compress == True
|
@ -1,4 +1,5 @@
|
||||
import time
|
||||
import typing
|
||||
|
||||
import pytest
|
||||
import respx
|
||||
@ -7,6 +8,9 @@ from nonebug.app import App
|
||||
|
||||
from .utils import get_json
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from nonebot_bison.platform.ncm_artist import NcmArtist
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ncm_artist(app: App):
|
||||
@ -48,3 +52,16 @@ async def test_fetch_new(ncm_artist, ncm_artist_0, ncm_artist_1, dummy_user_subi
|
||||
assert post.target_type == "ncm-artist"
|
||||
assert post.text == "新专辑发布:Y1K"
|
||||
assert post.url == "https://music.163.com/#/album?id=131074504"
|
||||
|
||||
|
||||
async def test_parse_target(ncm_artist: "NcmArtist"):
|
||||
from nonebot_bison.platform.platform import Platform
|
||||
|
||||
res = await ncm_artist.parse_target("32540734")
|
||||
assert res == "32540734"
|
||||
res = await ncm_artist.parse_target("https://music.163.com/#/artist?id=32540734")
|
||||
assert res == "32540734"
|
||||
res = await ncm_artist.parse_target("music.163.com/#/artist?id=32540734")
|
||||
assert res == "32540734"
|
||||
with pytest.raises(Platform.ParseTargetException):
|
||||
await ncm_artist.parse_target("music.163.com/#/rad?id=32540734")
|
||||
|
@ -1,4 +1,5 @@
|
||||
import time
|
||||
import typing
|
||||
|
||||
import pytest
|
||||
import respx
|
||||
@ -7,6 +8,9 @@ from nonebug.app import App
|
||||
|
||||
from .utils import get_json
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from nonebot_bison.platform.ncm_radio import NcmRadio
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ncm_radio(app: App):
|
||||
@ -53,3 +57,14 @@ async def test_fetch_new(ncm_radio, ncm_radio_0, ncm_radio_1, dummy_user_subinfo
|
||||
"http://p1.music.126.net/H5em5xUNIYXcjJhOmeaSqQ==/109951166647436789.jpg"
|
||||
]
|
||||
assert post.target_name == "《明日方舟》游戏原声OST"
|
||||
|
||||
|
||||
async def test_parse_target(ncm_radio: "NcmRadio"):
|
||||
res = await ncm_radio.parse_target("https://music.163.com/#/djradio?id=793745436")
|
||||
assert res == "793745436"
|
||||
res = await ncm_radio.parse_target("music.163.com/#/djradio?id=793745436")
|
||||
assert res == "793745436"
|
||||
res = await ncm_radio.parse_target("793745436")
|
||||
assert res == "793745436"
|
||||
with pytest.raises(ncm_radio.ParseTargetException):
|
||||
await ncm_radio.parse_target("music.163.com/#/alm?id=793745436")
|
||||
|
@ -1,3 +1,4 @@
|
||||
import typing
|
||||
from datetime import datetime
|
||||
|
||||
import feedparser
|
||||
@ -9,6 +10,9 @@ from pytz import timezone
|
||||
|
||||
from .utils import get_file, get_json
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from nonebot_bison.platform.weibo import Weibo
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def weibo(app: App):
|
||||
@ -132,3 +136,16 @@ def test_chaohua_tag(weibo):
|
||||
tags = weibo.get_tags(test_post)
|
||||
assert "刚出生的小羊驼长啥样" in tags
|
||||
assert "小羊驼三三超话" in tags
|
||||
|
||||
|
||||
async def test_parse_target(weibo: "Weibo"):
|
||||
from nonebot_bison.platform.platform import Platform
|
||||
|
||||
res = await weibo.parse_target("https://weibo.com/u/6441489862")
|
||||
assert res == "6441489862"
|
||||
res = await weibo.parse_target("weibo.com/u/6441489862")
|
||||
assert res == "6441489862"
|
||||
res = await weibo.parse_target("6441489862")
|
||||
assert res == "6441489862"
|
||||
with pytest.raises(Platform.ParseTargetException):
|
||||
await weibo.parse_target("https://weibo.com/arknights")
|
||||
|
@ -67,6 +67,7 @@ async def test_abort_add_on_id(app: App):
|
||||
from nonebot_bison.config import Config
|
||||
from nonebot_bison.config_manager import add_sub_matcher, common_platform
|
||||
from nonebot_bison.platform import platform_manager
|
||||
from nonebot_bison.platform.weibo import Weibo
|
||||
|
||||
config = Config()
|
||||
config.user_target.truncate()
|
||||
@ -103,7 +104,7 @@ async def test_abort_add_on_id(app: App):
|
||||
ctx.receive_event(bot, event_2)
|
||||
ctx.should_call_send(
|
||||
event_2,
|
||||
Message(BotReply.add_reply_on_id),
|
||||
Message(BotReply.add_reply_on_id(Weibo)),
|
||||
True,
|
||||
)
|
||||
event_abort = fake_group_message_event(
|
||||
@ -127,6 +128,7 @@ async def test_abort_add_on_cats(app: App):
|
||||
from nonebot_bison.config import Config
|
||||
from nonebot_bison.config_manager import add_sub_matcher, common_platform
|
||||
from nonebot_bison.platform import platform_manager
|
||||
from nonebot_bison.platform.weibo import Weibo
|
||||
|
||||
config = Config()
|
||||
config.user_target.truncate()
|
||||
@ -167,7 +169,7 @@ async def test_abort_add_on_cats(app: App):
|
||||
ctx.receive_event(bot, event_2)
|
||||
ctx.should_call_send(
|
||||
event_2,
|
||||
Message(BotReply.add_reply_on_id),
|
||||
Message(BotReply.add_reply_on_id(Weibo)),
|
||||
True,
|
||||
)
|
||||
event_3 = fake_group_message_event(
|
||||
@ -207,6 +209,7 @@ async def test_abort_add_on_tag(app: App):
|
||||
from nonebot_bison.config import Config
|
||||
from nonebot_bison.config_manager import add_sub_matcher, common_platform
|
||||
from nonebot_bison.platform import platform_manager
|
||||
from nonebot_bison.platform.weibo import Weibo
|
||||
|
||||
config = Config()
|
||||
config.user_target.truncate()
|
||||
@ -247,7 +250,7 @@ async def test_abort_add_on_tag(app: App):
|
||||
ctx.receive_event(bot, event_2)
|
||||
ctx.should_call_send(
|
||||
event_2,
|
||||
Message(BotReply.add_reply_on_id),
|
||||
Message(BotReply.add_reply_on_id(Weibo)),
|
||||
True,
|
||||
)
|
||||
event_3 = fake_group_message_event(
|
||||
|
@ -64,6 +64,7 @@ async def test_add_with_target(app: App):
|
||||
from nonebot_bison.config import Config
|
||||
from nonebot_bison.config_manager import add_sub_matcher, common_platform
|
||||
from nonebot_bison.platform import platform_manager
|
||||
from nonebot_bison.platform.weibo import Weibo
|
||||
|
||||
config = Config()
|
||||
config.user_target.truncate()
|
||||
@ -115,7 +116,7 @@ async def test_add_with_target(app: App):
|
||||
ctx.receive_event(bot, event_3)
|
||||
ctx.should_call_send(
|
||||
event_3,
|
||||
Message(BotReply.add_reply_on_id),
|
||||
Message(BotReply.add_reply_on_id(Weibo)),
|
||||
True,
|
||||
)
|
||||
event_4_err = fake_group_message_event(
|
||||
@ -181,6 +182,7 @@ async def test_add_with_target_no_cat(app: App):
|
||||
from nonebot_bison.config import Config
|
||||
from nonebot_bison.config_manager import add_sub_matcher, common_platform
|
||||
from nonebot_bison.platform import platform_manager
|
||||
from nonebot_bison.platform.ncm_artist import NcmArtist
|
||||
|
||||
config = Config()
|
||||
config.user_target.truncate()
|
||||
@ -208,7 +210,7 @@ async def test_add_with_target_no_cat(app: App):
|
||||
ctx.receive_event(bot, event_3)
|
||||
ctx.should_call_send(
|
||||
event_3,
|
||||
Message(BotReply.add_reply_on_id),
|
||||
Message(BotReply.add_reply_on_id(NcmArtist)),
|
||||
True,
|
||||
)
|
||||
event_4_ok = fake_group_message_event(
|
||||
@ -332,6 +334,7 @@ async def test_add_with_get_id(app: App):
|
||||
from nonebot_bison.config import Config
|
||||
from nonebot_bison.config_manager import add_sub_matcher, common_platform
|
||||
from nonebot_bison.platform import platform_manager
|
||||
from nonebot_bison.platform.weibo import Weibo
|
||||
|
||||
config = Config()
|
||||
config.user_target.truncate()
|
||||
@ -373,7 +376,7 @@ async def test_add_with_get_id(app: App):
|
||||
ctx.receive_event(bot, event_3)
|
||||
ctx.should_call_send(
|
||||
event_3,
|
||||
Message(BotReply.add_reply_on_id),
|
||||
Message(BotReply.add_reply_on_id(Weibo)),
|
||||
True,
|
||||
)
|
||||
event_4_query = fake_group_message_event(
|
||||
@ -387,8 +390,8 @@ async def test_add_with_get_id(app: App):
|
||||
True,
|
||||
)
|
||||
"""
|
||||
关于Message([MessageSegment(*BotReply.add_reply_on_id_input_search())]):
|
||||
异客知道为什么要在这里这样写,
|
||||
关于:Message([MessageSegment(*BotReply.add_reply_on_id_input_search())])
|
||||
鬼知道为什么要在这里这样写,
|
||||
没有[]的话assert不了(should_call_send使用[MessageSegment(...)]的格式进行比较)
|
||||
不在这里MessageSegment()的话也assert不了(指不能让add_reply_on_id_input_search直接返回一个MessageSegment对象)
|
||||
amen
|
||||
@ -405,3 +408,125 @@ async def test_add_with_get_id(app: App):
|
||||
ctx.should_finished()
|
||||
subs = config.list_subscribe(10000, "group")
|
||||
assert len(subs) == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_add_with_bilibili_target_parser(app: App):
|
||||
from nonebot.adapters.onebot.v11.event import Sender
|
||||
from nonebot.adapters.onebot.v11.message import Message
|
||||
from nonebot_bison.config import Config
|
||||
from nonebot_bison.config_manager import add_sub_matcher, common_platform
|
||||
from nonebot_bison.platform import platform_manager
|
||||
from nonebot_bison.platform.bilibili import Bilibili
|
||||
|
||||
config = Config()
|
||||
config.user_target.truncate()
|
||||
|
||||
ak_list_router = respx.get(
|
||||
"https://api.bilibili.com/x/space/acc/info?mid=161775300"
|
||||
)
|
||||
ak_list_router.mock(
|
||||
return_value=Response(200, json=get_json("bilibili_arknights_profile.json"))
|
||||
)
|
||||
|
||||
async with app.test_matcher(add_sub_matcher) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
event_1 = fake_group_message_event(
|
||||
message=Message("添加订阅"),
|
||||
sender=Sender(card="", nickname="test", role="admin"),
|
||||
to_me=True,
|
||||
)
|
||||
ctx.receive_event(bot, event_1)
|
||||
ctx.should_pass_rule()
|
||||
ctx.should_call_send(
|
||||
event_1,
|
||||
Message(
|
||||
BotReply.add_reply_on_platform(
|
||||
platform_manager=platform_manager, common_platform=common_platform
|
||||
)
|
||||
),
|
||||
True,
|
||||
)
|
||||
event_2 = fake_group_message_event(
|
||||
message=Message("全部"), sender=Sender(card="", nickname="test", role="admin")
|
||||
)
|
||||
ctx.receive_event(bot, event_2)
|
||||
ctx.should_rejected()
|
||||
ctx.should_call_send(
|
||||
event_2,
|
||||
BotReply.add_reply_on_platform_input_allplatform(platform_manager),
|
||||
True,
|
||||
)
|
||||
event_3 = fake_group_message_event(
|
||||
message=Message("bilibili"), sender=fake_admin_user
|
||||
)
|
||||
ctx.receive_event(bot, event_3)
|
||||
assert Bilibili.parse_target_promot
|
||||
ctx.should_call_send(
|
||||
event_3,
|
||||
Message(BotReply.add_reply_on_id(Bilibili)),
|
||||
True,
|
||||
)
|
||||
event_4_err1 = fake_group_message_event(
|
||||
message=Message(
|
||||
"https://live.bilibili.com/5555734?broadcast_type=0&is_room_feed=1&spm_id_from=333.999.0.0"
|
||||
),
|
||||
sender=fake_admin_user,
|
||||
)
|
||||
ctx.receive_event(bot, event_4_err1)
|
||||
ctx.should_call_send(
|
||||
event_4_err1, BotReply.add_reply_on_target_parse_input_error, True
|
||||
)
|
||||
ctx.should_rejected()
|
||||
|
||||
event_4_err1 = fake_group_message_event(
|
||||
message=Message(
|
||||
"https://space.bilibili.com/ark161775300?from=search&seid=13051774060625135297&spm_id_from=333.337.0.0"
|
||||
),
|
||||
sender=fake_admin_user,
|
||||
)
|
||||
ctx.receive_event(bot, event_4_err1)
|
||||
ctx.should_call_send(
|
||||
event_4_err1, BotReply.add_reply_on_target_parse_input_error, True
|
||||
)
|
||||
ctx.should_rejected()
|
||||
|
||||
event_4_ok = fake_group_message_event(
|
||||
message=Message(
|
||||
"https://space.bilibili.com/161775300?from=search&seid=13051774060625135297&spm_id_from=333.337.0.0"
|
||||
),
|
||||
sender=fake_admin_user,
|
||||
)
|
||||
ctx.receive_event(bot, event_4_ok)
|
||||
ctx.should_call_send(
|
||||
event_4_ok,
|
||||
BotReply.add_reply_on_target_confirm("bilibili", "明日方舟", "161775300"),
|
||||
True,
|
||||
)
|
||||
ctx.should_call_send(
|
||||
event_4_ok,
|
||||
Message(BotReply.add_reply_on_cats(platform_manager, "bilibili")),
|
||||
True,
|
||||
)
|
||||
event_5_ok = fake_group_message_event(
|
||||
message=Message("视频"), sender=fake_admin_user
|
||||
)
|
||||
ctx.receive_event(bot, event_5_ok)
|
||||
ctx.should_call_send(event_5_ok, Message(BotReply.add_reply_on_tags), True)
|
||||
event_6 = fake_group_message_event(
|
||||
message=Message("全部标签"), sender=fake_admin_user
|
||||
)
|
||||
ctx.receive_event(bot, event_6)
|
||||
ctx.should_call_send(
|
||||
event_6, BotReply.add_reply_subscribe_success("明日方舟"), True
|
||||
)
|
||||
ctx.should_finished()
|
||||
subs = config.list_subscribe(10000, "group")
|
||||
assert len(subs) == 1
|
||||
sub = subs[0]
|
||||
assert sub["target"] == "161775300"
|
||||
assert sub["tags"] == []
|
||||
assert sub["cats"] == [platform_manager["bilibili"].reverse_category["视频"]]
|
||||
assert sub["target_type"] == "bilibili"
|
||||
assert sub["target_name"] == "明日方舟"
|
||||
|
@ -1,6 +1,5 @@
|
||||
import pytest
|
||||
from nonebug import App
|
||||
from nonebug.fixture import nonebug_init
|
||||
|
||||
|
||||
async def test_without_proxy(app: App):
|
||||
@ -8,6 +7,8 @@ async def test_without_proxy(app: App):
|
||||
|
||||
c = http_client()
|
||||
assert not c._mounts
|
||||
req = c.build_request("GET", "http://example.com")
|
||||
assert "Chrome" in req.headers["User-Agent"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -1,3 +1,4 @@
|
||||
from ast import Str
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from typing_extensions import Literal
|
||||
@ -130,8 +131,18 @@ class BotReply:
|
||||
def add_reply_subscribe_success(name):
|
||||
return "添加 {} 成功".format(name)
|
||||
|
||||
@staticmethod
|
||||
def add_reply_on_id(platform: object) -> Str:
|
||||
base_text = "请输入订阅用户的id\n查询id获取方法请回复:“查询”"
|
||||
extra_text = (
|
||||
("1." + platform.parse_target_promot + "\n2.")
|
||||
if platform.parse_target_promot
|
||||
else ""
|
||||
)
|
||||
return extra_text + base_text
|
||||
|
||||
add_reply_on_id_input_error = "id输入错误"
|
||||
add_reply_on_target_parse_input_error = "不能从你的输入中提取出id,请检查你输入的内容是否符合预期"
|
||||
add_reply_on_platform_input_error = "平台输入错误"
|
||||
add_reply_on_id = "请输入订阅用户的id:\n查询id获取方法请回复:“查询”"
|
||||
add_reply_on_tags = '请输入要订阅的tag,订阅所有tag输入"全部标签"'
|
||||
add_reply_abort = "已中止订阅"
|
||||
|
Loading…
x
Reference in New Issue
Block a user