From e2eb13dbc35e2a60e755314fa223e68e8d4e1b3e Mon Sep 17 00:00:00 2001 From: lj091715 <1091062319@qq.com> Date: Tue, 19 May 2026 13:13:34 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=20tp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tp/SKILL.md | 40 +++++++++++++ tp/beauty.cpython-312.pyc | Bin 0 -> 5711 bytes tp/beauty.py | 116 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 tp/SKILL.md create mode 100644 tp/beauty.cpython-312.pyc create mode 100644 tp/beauty.py diff --git a/tp/SKILL.md b/tp/SKILL.md new file mode 100644 index 0000000..1594769 --- /dev/null +++ b/tp/SKILL.md @@ -0,0 +1,40 @@ +--- name: beauty description: "当用户发送「999」时触发。从3个接口同时获取美女图片链接,再调用本地微信机器人发图接口把3张图片一起发给当前用户。" argument-hint: "无需参数,直接调用即可" --- +# Beauty Skill +## 描述 +这是一个用于获取美女图片并直接发送给当前用户的技能。 +当用户发送 `999` 时,从 3 个免费图片接口同时获取图片链接,再调用本地微信机器人接口把 3 张图片一起发出去。 +这个仓库里额外提供了一个可执行脚本 `scripts/beauty.py`,方便宿主机器人直接调用。 +## 触发条件 +- 用户发送 `999` +## 接口信息 +### 图片接口(3个全部获取) +| 接口 | 地址 | +|------|------| +| 美女图片 | `https://api.ust1.cc/api/meinvpic?return=302` | +| 白色丝袜 | `https://api.ust1.cc/api/baisi?return=302` | +| 黑色丝袜 | `https://api.ust1.cc/api/heisi?return=302` | +- 请求方式:`GET` +- 请求头:`key: 14e655df72c1b429` +- 返回机制:`HTTP 302` 重定向到图片地址,通过 `Location` 头获取图片 URL +### 发图接口 +- 发图接口:`http://127.0.0.1:{ROBOT_WECHAT_CLIENT_PORT}/api/v1/robot/message/send/image/url` +- 请求方式:`POST` +- 本地脚本:`scripts/beauty.py` +## 环境变量 +- `ROBOT_WECHAT_CLIENT_PORT`:本地微信机器人服务端口。 +- `ROBOT_FROM_WX_ID`:当前消息来源用户的 wxid。 +- `BEAUTY_KEY`:ust1 API 的 key(可选,默认使用内置 key)。 +## 执行步骤 +1. 当用户发送 `999` 时触发该技能。 +2. 在仓库根目录下执行本地脚本:`python3 scripts/beauty.py`。 +3. 脚本依次请求 3 个接口(美女图片、白色丝袜、黑色丝袜)。 +4. 每个接口返回 `302` 重定向,脚本从 `Location` 头中提取图片地址。 +5. 脚本从环境变量中读取 `ROBOT_WECHAT_CLIENT_PORT` 和 `ROBOT_FROM_WX_ID`。 +6. 脚本将 3 张图片地址合并为数组,发送 `POST` 请求到 `http://127.0.0.1:{ROBOT_WECHAT_CLIENT_PORT}/api/v1/robot/message/send/image/url`,请求体为: +```json +{ "to_wxid": "{ROBOT_FROM_WX_ID}", "image_urls": ["美女图片", "白色丝袜", "黑色丝袜"] } +``` +7. 如果成功获取到至少 2 张图片,则全部发送出去;否则回复兜底文案:`今天的美女图片暂时没拿到,等我再找找。` +## 回复要求 +- 成功时,直接发送图片,不要额外追加解释文字。 +- 失败时,使用固定兜底文案回复。 diff --git a/tp/beauty.cpython-312.pyc b/tp/beauty.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6282f701fec3bb491834b3ded6daa051ddd5bf59 GIT binary patch literal 5711 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 F{|kJ!G|>P6 literal 0 HcmV?d00001 diff --git a/tp/beauty.py b/tp/beauty.py new file mode 100644 index 0000000..63b94fc --- /dev/null +++ b/tp/beauty.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +from __future__ import annotations +import io +import json +import os +import sys +import traceback +import urllib.error +import urllib.request + +# 将 stdout 编码设置为 UTF-8,确保 emoji 等字符能正常输出 +if hasattr(sys.stdout, "reconfigure"): + sys.stdout.reconfigure(encoding="utf-8") +else: + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") +sys.stderr = sys.stdout + +# 三个图片接口 +API_LIST = [ + {"name": "美女图片", "url": "https://api.ust1.cc/api/meinvpic?return=302"}, + {"name": "白色丝袜", "url": "https://api.ust1.cc/api/baisi?return=302"}, + {"name": "黑色丝袜", "url": "https://api.ust1.cc/api/heisi?return=302"}, +] +DEFAULT_KEY = "14e655df72c1b429" +FALLBACK_TEXT = "今天的美女图片暂时没拿到,等我再找找。" + + +def get_key() -> str: + return os.environ.get("BEAUTY_KEY", "").strip() or DEFAULT_KEY + + +class NoRedirect(urllib.request.HTTPRedirectHandler): + """不跟随重定向,直接返回响应(用于捕获 302 Location)""" + def http_error_302(self, req, fp, code, msg, headers): + return fp + + +def fetch_single_image(api: dict) -> str | None: + """从单个接口获取图片地址(通过302重定向的Location头)""" + key = get_key() + opener = urllib.request.build_opener(NoRedirect) + req = urllib.request.Request(api["url"], headers={"key": key}) + + try: + resp = opener.open(req, timeout=10) + location = resp.headers.get("Location") + if location: + return location.strip() + except (urllib.error.URLError, TimeoutError, OSError): + pass + + return None + + +def fetch_all_images() -> list[str]: + """从全部3个接口获取图片,返回图片地址列表""" + urls = [] + for api in API_LIST: + url = fetch_single_image(api) + if url: + urls.append(url) + return urls + + +def send_images(image_urls: list[str]) -> bool: + robot_port = os.environ.get("ROBOT_WECHAT_CLIENT_PORT", "").strip() + to_wxid = os.environ.get("ROBOT_FROM_WX_ID", "").strip() + if not robot_port or not to_wxid: + return False + + api_url = ( + f"http://127.0.0.1:{robot_port}/api/v1/robot/message/send/image/url" + ) + body = json.dumps( + { + "to_wxid": to_wxid, + "image_urls": image_urls, + } + ).encode("utf-8") + + request = urllib.request.Request( + api_url, + data=body, + headers={"Content-Type": "application/json"}, + method="POST", + ) + + try: + with urllib.request.urlopen(request, timeout=10) as response: + if 200 <= response.status < 300: + return True + payload = json.load(response) + except (urllib.error.URLError, TimeoutError, json.JSONDecodeError): + return False + + code = payload.get("code") + return code == 200 or code == 0 + + +def main() -> int: + urls = fetch_all_images() + if len(urls) >= 2 and send_images(urls): + return 0 + sys.stdout.write(FALLBACK_TEXT) + sys.stdout.write("\n") + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except SystemExit: + raise + except Exception: + traceback.print_exc(file=sys.stdout) + raise SystemExit(1)