skills/hermes/agent-memes/scripts/wechat-send-image.sh
星月 85e58ae239 refactor: split skills into hermes/ and cursor/ categories
- Move all Hermes skills from skills/ to hermes/
- Add cursor/ for Cursor rules (.mdc)
- Add code-review.mdc (Gitea PR review with tea CLI)
- Update sync.sh to use new hermes/ path
- Update README with new structure
2026-04-22 19:41:29 +08:00

76 lines
3.7 KiB
Bash
Executable File

#!/bin/bash
# Usage: wechat-send-image.sh <target> <image_path> [caption]
# 企业微信 (WeCom) image send via curl (two-step: upload media, then send).
# <target> is a userid or @all.
#
# Env vars (all optional):
# WECHAT_CORP_ID - corp ID (preferred)
# WECHAT_CORP_SECRET - corp secret (preferred)
# WECHAT_AGENT_ID - agent ID (preferred, or reads from openclaw.json)
# WECHAT_PROXY - proxy URL for curl (default: none)
set -euo pipefail
TARGET="${1:?Usage: wechat-send-image.sh <target> <image_path> [caption]}"
IMAGE_PATH="${2:?Missing image path}"
CAPTION="${3:-}"
if [ -n "${WECHAT_CORP_ID:-}" ] && [ -n "${WECHAT_CORP_SECRET:-}" ]; then
CORP_ID="$WECHAT_CORP_ID"
CORP_SECRET="$WECHAT_CORP_SECRET"
AGENT_ID="${WECHAT_AGENT_ID:-}"
else
read -r CORP_ID CORP_SECRET AGENT_ID < <(node -e "
const c=JSON.parse(require('fs').readFileSync(require('os').homedir()+'/.openclaw/openclaw.json','utf8'));
const w=c.channels?.wechat||c.channels?.wecom||{};
const id=w.corpId||w.corp_id||'';const s=w.corpSecret||w.corp_secret||'';const a=w.agentId||w.agent_id||'';
if(!id||!s){console.error('Set WECHAT_CORP_ID+WECHAT_CORP_SECRET or configure openclaw.json channels.wechat');process.exit(1)}
console.log(id+' '+s+' '+a);
")
fi
PROXY="${WECHAT_PROXY:-${https_proxy:-${HTTPS_PROXY:-}}}"
PROXY_ARGS=()
if [ -n "$PROXY" ]; then
PROXY_ARGS=(-x "$PROXY")
fi
# Step 1: Get access token
TOKEN_RESULT=$(curl -s --max-time 10 "${PROXY_ARGS[@]}" \
"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${CORP_ID}&corpsecret=${CORP_SECRET}")
ACCESS_TOKEN=$(echo "$TOKEN_RESULT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const r=JSON.parse(d);if(r.errcode!==0){console.error(r.errmsg||JSON.stringify(r));process.exit(1)}console.log(r.access_token)})")
# Step 2: Upload media
UPLOAD=$(curl -s --max-time 30 "${PROXY_ARGS[@]}" \
-F "media=@${IMAGE_PATH}" \
"https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=${ACCESS_TOKEN}&type=image")
MEDIA_ID=$(echo "$UPLOAD" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const r=JSON.parse(d);if(r.errcode!==0){console.error(r.errmsg||JSON.stringify(r));process.exit(1)}console.log(r.media_id)})")
# Step 3: Send image message
BODY="{\"touser\":\"${TARGET}\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"${MEDIA_ID}\"}}"
if [ -n "$AGENT_ID" ]; then
BODY=$(echo "$BODY" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const r=JSON.parse(d);r.agentid=Number('${AGENT_ID}');console.log(JSON.stringify(r))})")
fi
RESULT=$(curl -s --max-time 15 "${PROXY_ARGS[@]}" \
-H "Content-Type: application/json" \
-d "$BODY" \
"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${ACCESS_TOKEN}")
echo "$RESULT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const r=JSON.parse(d);if(r.errcode===0)console.log('Sent! msgid: '+r.msgid);else{console.error(r.errmsg||JSON.stringify(r));process.exit(1)}}catch{console.error(d);process.exit(1)}})"
# Note: WeCom doesn't support captions on image messages natively.
# If caption is provided, send a follow-up text message.
if [ -n "$CAPTION" ]; then
TEXT_BODY="{\"touser\":\"${TARGET}\",\"msgtype\":\"text\",\"text\":{\"content\":\"${CAPTION}\"}}"
if [ -n "$AGENT_ID" ]; then
TEXT_BODY=$(echo "$TEXT_BODY" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const r=JSON.parse(d);r.agentid=Number('${AGENT_ID}');console.log(JSON.stringify(r))})")
fi
curl -s --max-time 15 "${PROXY_ARGS[@]}" \
-H "Content-Type: application/json" \
-d "$TEXT_BODY" \
"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${ACCESS_TOKEN}" > /dev/null
fi