上传文件至 tp
This commit is contained in:
parent
afb691fa2b
commit
e2eb13dbc3
40
tp/SKILL.md
Normal file
40
tp/SKILL.md
Normal file
@ -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 张图片,则全部发送出去;否则回复兜底文案:`今天的美女图片暂时没拿到,等我再找找。`
|
||||||
|
## 回复要求
|
||||||
|
- 成功时,直接发送图片,不要额外追加解释文字。
|
||||||
|
- 失败时,使用固定兜底文案回复。
|
||||||
BIN
tp/beauty.cpython-312.pyc
Normal file
BIN
tp/beauty.cpython-312.pyc
Normal file
Binary file not shown.
116
tp/beauty.py
Normal file
116
tp/beauty.py
Normal file
@ -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)
|
||||||
Loading…
Reference in New Issue
Block a user