This commit is contained in:
student_2333 2024-10-25 23:29:04 +08:00
parent 008d3021ad
commit 80e25e5b69
11 changed files with 2860 additions and 217 deletions

View File

@ -1,76 +0,0 @@
Metadata-Version: 2.1
Name: nonebot-plugin-nailongremove
Version: 1.0.1
Summary: 一个基于图像分类模型的撤回图片插件
Home-page: https://github.com/Refound-445/onoebot-plugin-nailongremove
Keywords: nonebot,plugin,image
Author: 445
Author-email: 2877834692@qq.com
Requires-Python: >=3.7,<4.0
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Dist: aiohttp (>=3.7.4,<4.0.0)
Requires-Dist: httpx (>=0.27.0)
Requires-Dist: keras (>=2.4.3,<3.0.0)
Requires-Dist: nonebot-adapter-onebot (>=2.0.0,<3.0.0)
Requires-Dist: nonebot-plugin-apscheduler (>=0.4.0,<0.5.0)
Requires-Dist: nonebot2 (>=2.0.0,<3.0.0)
Requires-Dist: numpy (>=1.19.5,<2.0.0)
Requires-Dist: opencv-python (>=4.5.1.48,<5.0.0.0)
Requires-Dist: pillow (==10.4.0)
Requires-Dist: tensorflow (>=2.4.1,<3.0.0)
Requires-Dist: torch (==2.4.1)
Requires-Dist: torchvision (==0.19.1)
Project-URL: Repository, https://github.com/Refound-445/onoebot-plugin-nailongremove
Description-Content-Type: text/markdown
# nonebot_plugin_nailongremove
<!-- prettier-ignore-start -->
<!-- markdownlint-disable-next-line MD036 -->
_✨ 一个基于分类模型的简单插件~ ✨_
<!-- prettier-ignore-end -->
## 声明
本插件仅供娱乐和学习交流。
## 简介
NailongRemove是一款由简单的分类模型建立的奶龙识别插件可以识别群中的奶龙表情包并撤回该表情。
## 技术
本插件模型由三层卷积+两个全连接层构成基于cifar10数据集的10分类加入200多张奶龙表情包并进行预处理为大小3232形成11分类模型在此基础上训练得到。
模型会有误差,目前仍在优化改进中...
## 使用
只要有人发奶龙表情包被识别出来,就会被撤回并提醒。
## 即刻开始
- 使用 nb-cli
```
nb plugin install nonebot_plugin_nailongremove
```
- 使用 pip
```
pip install nonebot_plugin_nailongremove
```
## 其他
- 人工智能学习交流群949992679
- 机器人插件学习交流群200980266
欢迎大家进群一起学习交流~
## 许可证
`noneBot_plugin_picsbank` 采用 `MIT` 协议开源,协议文件参考 [LICENSE](../plugins/nonebot_plugin_picsbank-master/LICENSE)。

173
README.md
View File

@ -1,52 +1,159 @@
# nonebot_plugin_nailongremove
<!-- markdownlint-disable MD031 MD033 MD036 MD041 -->
<div align="center">
<a href="https://v2.nonebot.dev/store">
<img src="https://raw.githubusercontent.com/A-kirami/nonebot-plugin-template/resources/nbp_logo.png" width="180" height="180" alt="NoneBotPluginLogo">
</a>
<p>
<img src="https://raw.githubusercontent.com/lgc-NB2Dev/readme/main/template/plugin.svg" alt="NoneBotPluginText">
</p>
# Nonebot-Plugin-NaiLongRemove
<!-- prettier-ignore-start -->
<!-- markdownlint-disable-next-line MD036 -->
_✨ 一个基于分类模型的简单插件~ ✨_
<!-- prettier-ignore-end -->
## 声明
<img src="https://img.shields.io/badge/python-3.9+-blue.svg" alt="python">
<a href="https://pdm.fming.dev">
<img src="https://img.shields.io/badge/pdm-managed-blueviolet" alt="pdm-managed">
</a>
<br />
<a href="https://pydantic.dev">
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/lgc-NB2Dev/readme/main/template/pyd-v1-or-v2.json" alt="Pydantic Version 1 Or 2" >
</a>
<a href="./LICENSE">
<img src="https://img.shields.io/github/license/Refound-445/nonebot-plugin-nailongremove.svg" alt="license">
</a>
<a href="https://pypi.python.org/pypi/nonebot-plugin-nailongremove">
<img src="https://img.shields.io/pypi/v/nonebot-plugin-nailongremove.svg" alt="pypi">
</a>
<a href="https://pypi.python.org/pypi/nonebot-plugin-nailongremove">
<img src="https://img.shields.io/pypi/dm/nonebot-plugin-nailongremove" alt="pypi download">
</a>
<br />
<a href="https://registry.nonebot.dev/plugin/nonebot-plugin-nailongremove:nonebot_plugin_nailongremove">
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fnbbdg.lgc2333.top%2Fplugin%2Fnonebot-plugin-nailongremove" alt="NoneBot Registry">
</a>
<a href="https://registry.nonebot.dev/plugin/nonebot-plugin-nailongremove:nonebot_plugin_nailongremove">
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fnbbdg.lgc2333.top%2Fplugin-adapters%2Fnonebot-plugin-nailongremove" alt="Supported Adapters">
</a>
</div>
## 📖 介绍
### 声明
本插件仅供娱乐和学习交流。
关于一些问题:
- 模型精度目前模型只是轮廓所以精度相对较低还在进一步优化中如果您也是人工智能方面的兴趣爱好者欢迎来群里交流学习和开发人工智能学习交流群949992679届时我们会署上贡献者名单~
- 关于插件安装目前插件需要通过nonebot2进行加载关于nonebot2及其插件安装请移到官方群768887710交流咨询~
- 如果只是安装插件请先到官方群咨询安装768887710
- 如果只是安装插件请先到官方群咨询安装768887710
- 如果只是安装插件请先到官方群咨询安装768887710
## 简介
NailongRemove是一款由简单的分类模型建立的奶龙识别插件可以识别群中的奶龙表情包并撤回该表情。
- 模型精度:目前模型只是轮廓所以精度相对较低,还在进一步优化中,如果您也是人工智能方面的兴趣爱好者,欢迎来群里交流学习和开发(人工智能学习交流群 949992679届时我们会署上贡献者名单~
- 关于插件安装:目前插件需要通过 nonebot2 进行加载,关于 nonebot2 及其插件安装请移到官方群 768887710 交流咨询~
- 如果只是安装插件,请先到官方群咨询安装 768887710
- 如果只是安装插件,请先到官方群咨询安装 768887710
- 如果只是安装插件,请先到官方群咨询安装 768887710
## 技术
本插件模型由三层卷积+两个全连接层构成基于cifar10数据集的10分类加入200多张奶龙表情包并进行预处理为大小3232形成11分类模型在此基础上训练得到。
### 简介
模型会有误差,目前仍在优化改进中...
NailongRemove 是一款由简单的分类模型建立的奶龙识别插件,可以识别群中的奶龙表情包并撤回该表情。
### 技术
本插件模型由三层卷积+两个全连接层构成,基于 cifar10 数据集的 10 分类,加入 200 多张奶龙表情包并进行预处理为大小3232形成 11 分类模型,在此基础上训练得到。
模型会有误差,目前仍在优化改进中……
## 💿 安装
以下提到的方法 任选**其一** 即可
<details open>
<summary>[推荐] 使用 nb-cli 安装</summary>
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装
```bash
nb plugin install nonebot-plugin-nailongremove
```
</details>
<details>
<summary>使用包管理器安装</summary>
在 nonebot2 项目的插件目录下, 打开命令行, 根据你使用的包管理器, 输入相应的安装命令
<details>
<summary>pip</summary>
```bash
pip install nonebot-plugin-nailongremove
```
</details>
<details>
<summary>pdm</summary>
```bash
pdm add nonebot-plugin-nailongremove
```
</details>
<details>
<summary>poetry</summary>
```bash
poetry add nonebot-plugin-nailongremove
```
</details>
<details>
<summary>conda</summary>
```bash
conda install nonebot-plugin-nailongremove
```
</details>
打开 nonebot2 项目根目录下的 `pyproject.toml` 文件, 在 `[tool.nonebot]` 部分的 `plugins` 项里追加写入
```toml
[tool.nonebot]
plugins = [
# ...
"nonebot_plugin_nailongremove"
]
```
</details>
## ⚙️ 配置
在 nonebot2 项目的 `.env` 文件中添加下表中的必填配置
| 配置项 | 必填 | 默认值 | 说明 |
| :-------------------: | :--: | :--------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------: |
| `NAILONG_MODEL_DIR` | 否 | `./data/nailongremove` | 模型的下载位置 |
| `NAILONG_LIST_SCENES` | 否 | `[]` | 聊天场景 ID 黑白名单列表<br />在单级聊天下为该聊天 ID如 QQ 群号;<br />在多级聊天下为以 `_` 分割的各级聊天 ID如频道下的子频道或频道下私聊 |
| `NAILONG_BLACKLIST` | 否 | `True` | 是否使用黑名单模式 |
## 🎉 使用
## 使用
只要有人发奶龙表情包被识别出来,就会被撤回并提醒。
## 即刻开始
## 📞 联系
- 使用 nb-cli
```
nb plugin install nonebot_plugin_nailongremove
```
- 使用 pip
```
pip install nonebot_plugin_nailongremove
```
## 其他
- 人工智能学习交流群949992679<span style="color: yellow;">(开源代码,数据集,从本群获取)</span>
- 机器人插件学习交流群200980266<span style="color: yellow;">(机器人及插件安装包,从本群获取)</span>
欢迎大家进群一起学习交流~
## 许可证
`noneBot_plugin_picsbank` 采用 `MIT` 协议开源,协议文件参考 [LICENSE](../plugins/nonebot_plugin_picsbank-master/LICENSE)。
<!-- ## 📝 更新日志
芝士刚刚发布的插件,还没有更新日志的说 qwq~ -->

View File

@ -1,10 +1,15 @@
# ruff: noqa: E402
from nonebot.plugin import PluginMetadata
from nonebot import get_plugin_config, require
from nonebot.plugin import PluginMetadata, inherit_supported_adapters
require("nonebot_plugin_alconna")
require("nonebot_plugin_uninfo")
from . import handler as handler
from .config import Config
from nonebot import get_plugin_config
from .handler import *
__version__ = "2.0.0"
__plugin_meta__ = PluginMetadata(
name="自动撤回奶龙",
description="一个基于图像分类模型的简单插件~",
@ -12,8 +17,9 @@ __plugin_meta__ = PluginMetadata(
type="application",
homepage="https://github.com/Refound-445/onoebot-plugin-nailongremove",
config=Config,
supported_adapters={"~onebot.v11"},
supported_adapters=inherit_supported_adapters(
"nonebot_plugin_alconna",
"nonebot_plugin_uninfo",
),
)
config = get_plugin_config(Config)

View File

@ -1,5 +1,16 @@
from pydantic import BaseModel
from pathlib import Path
from typing import List
from nonebot import get_plugin_config
from pydantic import BaseModel, Field
class Config(BaseModel):
"""Plugin Config Here"""
nailong_model_dir: Path = Field(
default_factory=lambda: Path.cwd() / "data" / "nailongremove",
)
nailong_list_scenes: List[str] = Field(default_factory=list)
nailong_blacklist: bool = True
config = get_plugin_config(Config)

View File

@ -1,70 +1,120 @@
import logging
import io
from typing import Iterable, TypeVar, cast
import httpx
import cv2
import numpy as np
from nonebot import logger, on_message
from nonebot.adapters import Bot as BaseBot, Event as BaseEvent
from nonebot.drivers import Request
from nonebot.matcher import Matcher
from nonebot.permission import SUPERUSER
from nonebot.rule import Rule
from nonebot import on_message
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent
from PIL import Image
import io
from .model import *
from nonebot.typing import T_State
from nonebot.utils import run_sync
from nonebot_plugin_alconna.builtins.uniseg.market_face import MarketFace
from nonebot_plugin_alconna.uniseg import Image, UniMsg, image_fetch
from nonebot_plugin_uninfo import Uninfo
from PIL import Image as PilImage
from .config import config
from .model import check_image
from .recall import recall
T = TypeVar("T")
logger = logging.getLogger(__name__)
async def download_image(url: str) -> np.ndarray:
async with httpx.AsyncClient() as client:
response = await client.get(url)
if response.status_code == 200:
image_data = response.content
def transform_image(image_data: bytes) -> np.ndarray:
image = PilImage.open(io.BytesIO(image_data))
# 将字节数据加载为Pillow图像对象
image = Image.open(io.BytesIO(image_data))
if (
# image.format == "GIF" and
getattr(image, "is_animated", False)
):
# 处理动图:仅获取第一帧
image = image.convert("RGB")
image = image.copy() # 获取第一帧
else:
image = image.convert("RGB")
# 检查图像格式并处理
if image.format == 'GIF' and hasattr(image, 'is_animated') and image.is_animated:
# 处理GIF动图仅获取第一帧
image = image.convert("RGB")
image = image.copy() # 获取第一帧
else:
# 对于非GIF图像如JPEG
image = image.convert("RGB")
image_array = np.array(image)
# OpenCV通常使用BGR格式因此需要转换
if len(image_array.shape) == 3 and image_array.shape[2] == 3:
image_array = cv2.cvtColor(image_array, cv2.COLOR_RGB2BGR)
# 将Pillow图像转换为NumPy数组
image_array = np.array(image)
# OpenCV通常使用BGR格式因此需要转换
if len(image_array.shape) == 3 and image_array.shape[2] == 3:
image_array = cv2.cvtColor(image_array, cv2.COLOR_RGB2BGR)
return image_array
return image_array
async def group_message_contains_image(event: MessageEvent) -> bool:
if isinstance(event, GroupMessageEvent) and any(seg.type == 'image' or seg.type=='mface' for seg in event.message):
return True
return False
def judge_list(lst: Iterable[T], val: T, blacklist: bool) -> bool:
return (val not in lst) if blacklist else (val in lst)
async def nailong_rule(
bot: BaseBot,
event: BaseEvent,
ss_info: Uninfo,
msg: UniMsg,
) -> bool:
return (
bool(ss_info.member) # 检查是否是群聊消息,此值仅在群聊与频道中存在
and (
# bypass superuser
(not await SUPERUSER(bot, event))
# bypass group admin
or ((not ss_info.member.role) or ss_info.member.role.level <= 1)
)
and ((Image in msg) or (MarketFace in msg)) # msg has image
and judge_list(
config.nailong_list_scenes,
ss_info.scene_path,
config.nailong_blacklist,
)
)
nailong = on_message(rule=Rule(nailong_rule))
nailong =on_message(rule=Rule(group_message_contains_image))
@nailong.handle()
async def handle_function(bot: Bot, event: GroupMessageEvent):
group_id = event.group_id
member_info = await bot.get_group_member_info(group_id=group_id, user_id=event.self_id) # 管理员检测
if member_info['role'] != 'admin' and member_info['role'] != 'owner':
return
for seg in event.message:
if seg.type == 'image' or seg.type == 'mface':
async def handle_function(
m: Matcher,
bot: BaseBot,
ev: BaseEvent,
msg: UniMsg,
state: T_State,
):
for seg in msg:
if isinstance(seg, Image):
image = await image_fetch(ev, bot, state, seg)
if not image:
logger.warning(f"Failed to fetch image: {seg!r}")
continue
elif isinstance(seg, MarketFace):
url = f"https://gxh.vip.qq.com/club/item/parcel/item/{seg.id[:2]}/{seg.id}/raw300.gif"
req = Request("GET", url)
try:
image_url = seg.data['url']
image = await download_image(image_url)
if check_image(image):
await bot.call_api('delete_msg', message_id=event.message_id)
await bot.send(event, "本群禁止发送奶龙!")
logger.info(f"撤回了包含奶龙的图片并发送警告:{image_url}")
else:
logger.debug("Image does not contain monkey.")
resp = await bot.adapter.request(req)
except Exception as e:
logger.error(f"处理图片时出错:{e}")
logger.warning(f"Failed to fetch {seg!r}: {type(e).__name__}: {e}")
continue
image = cast(bytes, resp.content)
else:
continue
try:
ok = await run_sync(check_image)(
await run_sync(transform_image)(image),
)
except Exception:
logger.exception(f"Failed to process image: {seg!r}")
continue
if ok:
logger.info(f"尝试撤回包含奶龙的图片并发送警告:{seg!r}")
await m.send("本群禁止发送奶龙!")
try:
await recall(bot, ev)
except Exception as e:
logger.warning(f"{type(e).__name__}: {e}")
await m.finish()

View File

@ -1,14 +1,19 @@
import os
from pathlib import Path
import cv2
import numpy as np
import torch
from torch import nn
from torchvision import transforms
from torch import Tensor, nn
from torch.hub import load_state_dict_from_url
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize(mean=0.5, std=0.5)])
from torchvision import transforms
from .config import config
transform = transforms.Compose(
[transforms.ToTensor(), transforms.Normalize(mean=0.5, std=0.5)],
)
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
@ -29,7 +34,7 @@ class Net(nn.Module):
self.drop = nn.Dropout(0.25)
self.lin2 = nn.Linear(64, 11)
def forward(self, x):
def forward(self, x: Tensor):
x = self.conv1(x)
x = self.max1(x)
x = self.bn1(x)
@ -45,15 +50,31 @@ class Net(nn.Module):
x = self.fla(x)
x = self.lin1(x)
x = self.drop(x)
x = self.lin2(x)
return x
model=Net()
if os.path.exists(os.path.join(Path(__file__).parent, 'nailong.pth')):
model.load_state_dict(torch.load(os.path.join(Path(__file__).parent, 'nailong.pth'), weights_only=True, map_location='cpu'))
return self.lin2(x)
model_filename = "nailong.pth"
model = Net()
if config.nailong_model_dir.exists():
model.load_state_dict(
torch.load(
config.nailong_model_dir / model_filename,
weights_only=True,
map_location="cpu",
),
)
else:
url='https://github.com/Refound-445/nonebot-plugin-nailongremove/releases/download/weights/nailong.pth'
state_dict=load_state_dict_from_url(url=url,model_dir=Path(__file__).parent,map_location='cpu',check_hash=True,progress=True)
url = f"https://github.com/Refound-445/nonebot-plugin-nailongremove/releases/download/weights/{model_filename}"
state_dict = load_state_dict_from_url(
url=url,
model_dir=str(config.nailong_model_dir),
map_location="cpu",
check_hash=True,
progress=True,
)
model.load_state_dict(state_dict)
def check_image(image: np.ndarray) -> bool:
"""
:param image: OpenCV图像数组
@ -61,9 +82,6 @@ def check_image(image: np.ndarray) -> bool:
"""
image = cv2.resize(image, (32, 32))
image = transform(image)
image = image.unsqueeze(0)
image = image.unsqueeze(0) # type: ignore
output = model(image)
if output.argmax(1)==10:
return True
else:
return False
return output.argmax(1) == 10

View File

@ -0,0 +1,181 @@
from typing import Awaitable, Callable, Dict, TypeVar
from typing_extensions import TypeAlias
from nonebot.adapters import Bot as BaseBot, Event as BaseEvent
Recaller: TypeAlias = Callable[[BaseBot, BaseEvent], Awaitable[None]]
RT = TypeVar("RT", bound=Recaller)
recall_map: Dict[str, Recaller] = {}
def recaller(adapter_name: str):
def deco(func: RT) -> RT:
recall_map[adapter_name] = func
return func
return deco
@recaller("Discord")
async def discord(bot: BaseBot, ev: BaseEvent):
from nonebot.adapters.discord import Bot, MessageEvent
if not (isinstance(bot, Bot) and isinstance(ev, MessageEvent)):
raise TypeError("Unsupported bot or event type")
await bot.delete_message(channel_id=ev.channel_id, message_id=ev.message_id)
@recaller("DoDo")
async def dodo(bot: BaseBot, ev: BaseEvent):
from nonebot.adapters.dodo import Bot, MessageEvent
if not (isinstance(bot, Bot) and isinstance(ev, MessageEvent)):
raise TypeError("Unsupported bot or event type")
await bot.set_channel_message_withdraw(message_id=ev.message_id)
@recaller("Feishu")
async def feishu(bot: BaseBot, ev: BaseEvent):
from nonebot.adapters.feishu import Bot, MessageEvent
if not (isinstance(bot, Bot) and isinstance(ev, MessageEvent)):
raise TypeError("Unsupported bot or event type")
return await bot.call_api(f"im/v1/messages/{ev.message_id}", method="DELETE")
@recaller("Kaiheila")
async def kook(bot: BaseBot, ev: BaseEvent):
from nonebot.adapters.kaiheila import Bot
from nonebot.adapters.kaiheila.event import MessageEvent, PrivateMessageEvent
if not (isinstance(bot, Bot) and isinstance(ev, MessageEvent)):
raise TypeError("Unsupported bot or event type")
if isinstance(ev, PrivateMessageEvent):
await bot.directMessage_delete(msg_id=ev.msg_id)
else:
await bot.message_delete(msg_id=ev.msg_id)
@recaller("Kritor")
async def kritor(bot: BaseBot, ev: BaseEvent):
from nonebot.adapters.kritor import Bot, MessageEvent
if not (isinstance(bot, Bot) and isinstance(ev, MessageEvent)):
raise TypeError("Unsupported bot or event type")
await bot.recall_message(message_id=ev.message_id)
@recaller("Mirai")
async def mirai(bot: BaseBot, ev: BaseEvent):
from nonebot.adapters.mirai import Bot, MessageEvent
if not (isinstance(bot, Bot) and isinstance(ev, MessageEvent)):
raise TypeError("Unsupported bot or event type")
await bot.recall_message(message=ev.message_id)
@recaller("OneBot V11")
async def onebot_v11(bot: BaseBot, ev: BaseEvent):
from nonebot.adapters.onebot.v11 import Bot, MessageEvent
if not (isinstance(bot, Bot) and isinstance(ev, MessageEvent)):
raise TypeError("Unsupported bot or event type")
await bot.delete_msg(message_id=ev.message_id)
@recaller("OneBot V12")
async def onebot_v12(bot: BaseBot, ev: BaseEvent):
from nonebot.adapters.onebot.v12 import Bot, MessageEvent
if not (isinstance(bot, Bot) and isinstance(ev, MessageEvent)):
raise TypeError("Unsupported bot or event type")
await bot.delete_message(message_id=ev.message_id)
@recaller("QQ")
async def qq(bot: BaseBot, ev: BaseEvent):
from nonebot.adapters.qq import (
AtMessageCreateEvent,
Bot,
C2CMessageCreateEvent,
DirectMessageCreateEvent,
GroupAtMessageCreateEvent,
MessageCreateEvent,
MessageEvent,
)
if isinstance(bot, Bot) and isinstance(ev, MessageEvent):
if isinstance(ev, C2CMessageCreateEvent): # 私聊
await bot.delete_c2c_message(openid=ev.author.id, message_id=ev.id)
elif isinstance(ev, GroupAtMessageCreateEvent): # 群聊
await bot.delete_group_message(
group_openid=ev.group_openid,
message_id=ev.id,
)
elif isinstance(ev, DirectMessageCreateEvent): # 频道私聊
await bot.delete_dms_message(guild_id=ev.guild_id, message_id=ev.id)
elif isinstance(ev, (AtMessageCreateEvent, MessageCreateEvent)): # 频道
await bot.delete_message(channel_id=ev.channel_id, message_id=ev.id)
raise TypeError("Unsupported bot or event type")
@recaller("RedProtocol")
async def red(bot: BaseBot, ev: BaseEvent):
from nonebot.adapters.red import Bot, MessageEvent
if not (isinstance(bot, Bot) and isinstance(ev, MessageEvent)):
raise TypeError("Unsupported bot or event type")
await bot.recall_message(ev.chatType, ev.peerUin, ev.msgId)
@recaller("Satori")
async def satori(bot: BaseBot, ev: BaseEvent):
from nonebot.adapters.satori import Bot, MessageEvent
if not (isinstance(bot, Bot) and isinstance(ev, MessageEvent)):
raise TypeError("Unsupported bot or event type")
await bot.message_delete(channel_id=ev.channel.id, message_id=ev.message.id)
@recaller("Telegram")
async def telegram(bot: BaseBot, ev: BaseEvent):
from nonebot.adapters.telegram import Bot
from nonebot.adapters.telegram.event import MessageEvent
if not (isinstance(bot, Bot) and isinstance(ev, MessageEvent)):
raise TypeError("Unsupported bot or event type")
await bot.delete_message(chat_id=ev.chat.id, message_id=ev.message_id)
@recaller("Tailchat")
async def tailchat(bot: BaseBot, ev: BaseEvent):
from nonebot_adapter_tailchat import Bot
from nonebot_adapter_tailchat.event import MessageEvent
if not (isinstance(bot, Bot) and isinstance(ev, MessageEvent)):
raise TypeError("Unsupported bot or event type")
try:
await bot.recallMessage(messageId=ev.get_message_id())
except Exception:
await bot.deleteMessage(messageId=ev.get_message_id())
async def recall(bot: BaseBot, ev: BaseEvent):
f = recall_map.get(bot.adapter.get_name())
if f:
return await f(bot, ev)
raise NotImplementedError

2330
pdm.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +1,51 @@
[tool.poetry]
[project]
name = "nonebot-plugin-nailongremove"
version = "1.1.7"
dynamic = ["version"]
description = "一个基于图像分类模型的撤回图片插件"
authors = ["445 <2877834692@qq.com>"]
authors = [{ name = "445", email = "2877834692@qq.com" }]
requires-python = "<4.0,>=3.9"
dependencies = [
"nonebot2>=2.2.0",
"opencv-python>=4.5",
"numpy>=1.19",
"keras>=2.4",
"pillow>=10",
"torch>=2.4",
"torchvision>=0.19",
"nonebot-plugin-alconna>=0.53.1",
"nonebot-plugin-uninfo>=0.5.0",
]
license = { text = "MIT" }
readme = "README.md"
keywords = ["nonebot", "plugin", "image"]
[project.urls]
homepage = "https://github.com/Refound-445/onoebot-plugin-nailongremove"
repository = "https://github.com/Refound-445/onoebot-plugin-nailongremove"
keywords = ["nonebot", "plugin", "image"]
include=['nonebot_plugin_nailongremove/Nailong(0.7123).pth']
[tool.poetry.dependencies]
python = "^3.9"
nonebot2 = "^2.2.0"
nonebot-adapter-onebot = "^2.0.0"
opencv-python = "^4.5"
numpy = "^1.19"
keras = "^2.4"
nonebot-plugin-apscheduler = "^0.4.0"
pillow="^10"
torch="^2.4"
torchvision="^0.19"
httpx = ">=0.27.0"
[tool.poetry.dev-dependencies]
pytest = "^5.2"
[[tool.poetry.source]]
name = "mirrors"
url = "https://pypi.tuna.tsinghua.edu.cn/simple/"
priority = "primary"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
requires = ["pdm-backend"]
build-backend = "pdm.backend"
[tool.pdm.dev-dependencies]
dev = [
"nonebot-adapter-discord>=0.1.8",
"nonebot-adapter-dodo>=0.2.1",
"nonebot-adapter-feishu>=2.6.0",
"nonebot-adapter-kaiheila>=0.3.4",
"nonebot-adapter-kritor>=0.3.2",
"nonebot-adapter-mirai>=2.3.3",
"nonebot-adapter-onebot>=2.4.5",
"nonebot-adapter-qq>=1.5.2",
"nonebot-adapter-red>=0.9.0",
"nonebot-adapter-satori>=0.12.6",
"nonebot-adapter-telegram>=0.1.0b18",
"nonebot-adapter-tailchat>=0.1.0b12",
]
[tool.pdm.build]
includes = []
[tool.pdm.version]
source = "file"
path = "nonebot_plugin_nailongremove/__init__.py"