Metadata-Version: 2.5
Name: myfish
Version: 1.2.0
Summary: 一个现代化、纯异步、基于纯Python实现的闲鱼API协议库与Bot开发框架。
Project-URL: Homepage, https://github.com/Kaguya233qwq/myfish
Project-URL: Repository, https://github.com/Kaguya233qwq/myfish
Author-email: Kaguya233qwq <marisa_qwq@qq.com>
License-Expression: MIT
License-File: LICENSE
Keywords: asyncio,bot,goofish,xianyu
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.11
Requires-Dist: cryptography>=48.0.0
Requires-Dist: httpx>=0.28.1
Requires-Dist: loguru>=0.7.3
Requires-Dist: msgpack>=1.1.2
Requires-Dist: pillow>=11.3.0
Requires-Dist: pydantic>=2.13.3
Requires-Dist: qrcode>=8.2
Requires-Dist: websockets>=16.0
Provides-Extra: fishy
Requires-Dist: fastapi<1,>=0.115; extra == 'fishy'
Requires-Dist: uvicorn<1,>=0.30; extra == 'fishy'
Description-Content-Type: text/markdown

# 🐟 Myfish

> *我的鱼，我做主。*
> **一个Python的现代化、纯异步、跨平台的Bot开发框架。现已内置闲鱼适配器与微信ClawBot适配器支持。**

---

> [!WARNING]
> **关于闲鱼适配器的免责声明与风险提示**
> 咸鱼适配器的特殊性与敏感性，**不保证其代码的长期稳定可用**。本项目仅供学习与安全研究使用，必要时可能会archive或private跑路。请勿用于任何非法或黑产用途，否则后果自负。

> [!IMPORTANT]
> **关于闲鱼适配器的滑块风控**
> 短时间内频繁获取 `access_token` 或高频调用 API 有较大率触发阿里系滑块验证码。若触发风控，需要手动通过浏览器滑块并获取 `x5sec` 的值，将其添加至本地鉴权文件后方可继续运行。
> **收不到消息体**
> 如果是 token 失效后收不到消息，重启框架即可。
> 如果是因为频繁登录、断开重连等测试操作导致的收不到消息则是风控的表现。尝试重启程序直到触发滑块验证码后更新获取到的 `x5sec` 的值后即可解决。

> **人脸验证**
> 部分账号存在被系统风控的情况，登录时要求进行人脸验证，只需要扫描登录二维码并确认后，再次使用闲鱼app扫描安全验证的二维码并完成人脸验证即可正常登录。

## 🚀 已实现功能/特性

### 🛠️ 框架核心 (core)
- [x] **跨平台微内核架构**：引擎与协议彻底解耦。一套插件代码，无缝多端运行。
- [x] **开发体验 (DX) 优先**：高度标准化的声明式插件路由与泛型依赖注入 (DI)。
- [x] **云原生 DevOps 规范**：开箱即用的 12-Factor 容器化存储与动态配置引擎。
- [x] **全链路纯异步调度**：基于 `asyncio` 的非阻塞事件循环，轻松应对海量并发。
- [x] 插件启动与关闭钩子、类型化事件处理，以及导入前的插件启用/禁用配置。

### 🔌 内置适配器支持

**🐟 Fish (闲鱼websocket协议) 适配器**
- [x] 完整的扫码登录与动态鉴权生命周期管理。
- [x] 高性能计算 Sign 签名与解包 MessagePack。
- [x] 核心加密算法编译为二进制模块，提供物理级防逆向保护。
- [x] 深度集成 Web 端常用 API 与实时 WebSocket 长连接通讯。
- [x] 商品发布、草稿同步、编辑、上下架、类目查询与擦亮 API。
- [x] 平台交易消息解析、买家会话查询，以及等待发送回执的消息交付。
- [x] WebSocket 心跳、断线重连及关闭原因诊断。

**🦞 ClawBot (微信ilink协议) 适配器**
- [x] 零配置、开箱即用的扫码登录快速接入。
- [x] 全面覆盖绝大部分图文、媒体等核心消息类型的双向收发。
- [x] 基于纯异步底层构建，提供极高的高并发消息吞吐性能。

### 内建插件

**Fishy（渔渔）**

提供 Vue 3 管理界面和 FastAPI 后端，支持商品档案、闲鱼草稿同步、发布与上下架、商品编辑、自动擦亮、订单跟踪及固定文本/链接交付。
支持虚拟商品售出后恢复原商品上架，以及团队账号、任务队列和操作记录；
远端操作结果不明确时保留待核对状态，防止重复发布或发送。

安装 `myfish[fishy]` 并启用插件后，访问默认地址 http://127.0.0.1:8787 。
首次启动使用插件数据目录中的 `setup.token` 创建管理员，没有默认账号或密码。
完整配置、功能边界和升级说明见 [Fishy 文档](src/myfish/builtin_plugins/fishy/README.md)。

**Echo（回声示例插件）**

默认启用，演示以 `echo` 开头的文本回复及图片消息回复。可通过 `[plugins.echo]` 关闭。
插件入口结构和加载规则见 [内建插件说明](src/myfish/builtin_plugins/README.md)。

## 📦 安装与使用

运行环境：Python 3.11 及以上。

### 1. 包管理器 (推荐使用 uv)

```bash
uv pip install myfish

# 初始化项目
myfish init my-project
cd my-project

# 同步依赖并启动（首次同步会生成 config.toml）
myfish sync
myfish run

```

*初次启动会自动生成 `config.toml`，按需修改其中的适配器参数后重启即可。*

#### 插件开关

插件开关使用文件名或包目录名，不使用插件显示名，修改后重启生效。例如：

```toml
[plugins.fishy]
enabled = true
host = "127.0.0.1"
port = 8787

[plugins.echo]
enabled = false
```

Fishy 默认关闭，其他插件默认加载。设置 `enabled = false` 后，加载器跳过插件导入及生命周期钩子。
启用 Fishy 后执行同步，再启动：

```bash
myfish sync
myfish run
```

源码开发时可使用 `uv run myfish sync` 和 `uv run myfish run`。
`myfish sync --upgrade` 会主动更新允许范围内的依赖版本。

同步会按配置收集启用插件的依赖，统一解析到 `.myfish/requirements.lock`，并安装到工作区的 `.myfish/venv`，不修改项目的 `.venv` 或 `uv.lock`。普通同步复用锁定版本；插件开关或依赖声明变化后重新解析，未变化时直接按已有锁文件同步。需要系统中已安装 `uv`。

`myfish run` 只检查同步状态，不联网安装；检查通过后从独立环境启动，仍支持工作区内的 `bot.py`。首次运行、修改插件依赖或开关后，请先执行 `myfish sync`。直接运行 `src/myfish/main.py` 不经过这套检查；使用该入口启用 Fishy 时需自行传入 `--extra fishy`。

外部包插件在目录下声明 `requirements.txt`，单文件插件使用同名的 `名称.requirements.txt`。禁用插件不参与依赖解析。版本冲突会在安装前报错；安装中途失败时启动检查会要求重新同步。`.myfish/` 是本地生成目录，不应提交到版本库。

### 2. 源码构建

```bash
git clone https://github.com/Kaguya233qwq/myfish
cd myfish
uv sync
uv run myfish sync
uv run myfish run
```

### 3. Docker 部署

新建 `docker-compose.yml`：

```yml
services:
  myfish:
    image: kaguya233qwq/myfish:latest
    container_name: myfish
    environment:
      - TZ=Asia/Shanghai
      - MYFISH_CONFIG_PATH=/myfish/data/config.toml
    network_mode: host # 或者指定你的自定义虚拟网络
    volumes:
      - ./plugins:/myfish/plugins
      - ./data:/myfish/data
      - myfish-runtime:/myfish/.myfish

volumes:
  myfish-runtime:
```

后台运行：`docker compose up -d`。

容器启动后先按挂载的配置及插件执行 `myfish sync`，成功后在 `.myfish/venv` 中执行 `myfish run`。
首次启动会在 `data/config.toml` 生成默认配置；修改插件开关或依赖后重启容器即可重新同步。
`myfish-runtime` 卷保留运行环境和依赖锁，普通重启复用锁定版本；首次安装或依赖变化时需要访问包索引。
同步失败时容器退出，不会继续启动 Bot。启动器通过 `exec` 交接给 Bot，以接收容器停止信号。
也可使用 `docker compose run --rm myfish myfish sync --upgrade` 更新锁定依赖，操作前先停止 Bot。

## 🧩 开发用户插件

采用声明式路由与泛型依赖注入 (DI)。

```python
from myfish.core.plugin import Plugin, PluginMetadata
from myfish.core.event import MessageEvent
from myfish.adapters.fish import FishBot 

plugin = Plugin(
    metadata=PluginMetadata(
        id="auto_reply",
        name="客服自动回复",
        version="1.0.0",
        author="YourName",
        support_adapters=["fish"] # 声明为仅支持闲鱼适配器的插件，此项设置可以提升性能
    )
)

#  自定义匹配规则
def reject_rule(event: MessageEvent) -> bool:
    return any(word in event.plain_text for word in ["刀", "低价", "便宜"])

@plugin.on_message(rule=reject_rule)
async def handle_bargain(event: MessageEvent):
    await event.reply("抱歉老板，底价出回血了，不议价哦😭")

# 依赖注入：跨平台通用逻辑
@plugin.on_keywords(("ping",))
async def handle_ping(event: MessageEvent):
    await event.reply("pong!")

# 依赖注入：注入特定平台的 Bot，获取调用对应平台api的能力
@plugin.on_keywords(("我的信息", "我是谁"))
async def handle_user_info(event: MessageEvent, bot: FishBot):
    user_info = await bot.adapter.api.get_user_info(event.sender_id)
    await event.reply(f"您的信息：\n{user_info}")

```

## 🔌 开发适配器 (Adapter)

Myfish 核心采用两阶段初始化与注册表装配协议。开发自定义适配器必须继承 `BaseAdapter` 并实现 `setup` 初始化。

```python
import json
from myfish.core.adapter import BaseAdapter, AdapterMetaData
from myfish.core.registry import AdapterRegistry
from myfish.core.event import MessageEvent
from myfish.core.message import MessageChain, Text

@AdapterRegistry.register
class MockAdapter(BaseAdapter[MockAPI]):
    # 框架通过 SSOT 提取 ID 与注册信息
    meta_data = AdapterMetaData(
        id="mock",
        name="Mock适配器",
        description="用于本地测试的开发适配器",
        version="1.0.0",
        author="YourName"
    )

    def __init__(self, api: MockAPI):
        super().__init__(api=api)
        self.bot_id = "unknown"

    @classmethod
    def setup(cls, **kwargs) -> "MockAdapter":
        """同步装配 (由 Config 驱动)"""
        token = kwargs.get("token")
        if not token:
            raise ValueError("MockAdapter 必须在配置中提供 token")
        return cls(api=MockAPI(token=token))

    async def run(self):
        """异步启动与鉴权"""
        self.bot_id = await self.api.get_self_id()
        await self._listen()

    async def _listen(self):
        raw_payload = '{"user": "1001", "text": "你好，Myfish"}'
        data = json.loads(raw_payload)
        
        event = MessageEvent(
            cid=f"group_{data['user']}",
            sender_id=data["user"],
            sender_name="MockUser",
            messages=MessageChain([Text(text=data["text"])]),
            raw_payload=data
        )
        
        if self._on_receive_callback:
            await self._on_receive_callback(event)

    async def send(self, target_id: str, message: MessageChain, cid: str = ""):
        plain_text = "".join([seg.text for seg in message if isinstance(seg, Text)])
        payload = {"to": target_id, "content": plain_text}
        # await self.api.send_msg(payload)

```

## 📝 TODO

* [ ] **适配器扩展**：支持更多平台内置适配器。
* [ ] **适配器API 扩展**：增加适配器中如闲鱼的自动擦亮等移动端实用 API。
* [ ] **闲鱼适配器增强**：尝试实现纯 JS 补环境，在本地全自动计算 `x5sec` 过滑块。
* [ ] **持久化存储**：引入基于 SQLite 的标准插件数据层 (ORM)。

## 🙏 致谢

* [nonebot/nonebot2](https://github.com/nonebot/nonebot2) 为本项目的微内核架构、依赖注入、适配器模式等底层系统工程提供了启蒙与极具价值的参考范式。
* [cv-cat/XianYuApis](https://github.com/cv-cat/XianYuApis) 闲鱼适配器核心 Sign 加密与消息解包算法的 JS 参考，fish适配器 在此基础上完成了纯 Python 算法的移植与升华。
* [corespeed-io/wechatbot](https://github.com/corespeed-io/wechatbot) 微信ClawBot适配器(ilink协议)参考。

本项目欢迎任何有能力的开发者提交 Pull Request，共同建设生态。
