Files
EP-Hub-Skill/scripts/dingtalk-progress
T
2026-09-02 11:44:52 +08:00

191 lines
5.4 KiB
Bash

#!/bin/sh
set -eu
project_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
config_file="$project_root/.dingtalk/config.env"
usage() {
echo "Usage: ./scripts/dingtalk-progress <start|milestone|blocked|complete|failed> <summary>" >&2
}
if [ "$#" -lt 2 ]; then
usage
exit 2
fi
event_name=$1
shift
summary=$*
case "$event_name" in
start|milestone|blocked|complete|failed) ;;
*)
usage
exit 2
;;
esac
if [ ! -f "$config_file" ]; then
echo "DingTalk progress reporting is not configured; skipping." >&2
exit 0
fi
file_mode=$(stat -f '%Lp' "$config_file" 2>/dev/null || stat -c '%a' "$config_file" 2>/dev/null || true)
case "$file_mode" in
400|600) ;;
*)
echo "Refusing to read $config_file because its mode is ${file_mode:-unknown}; run chmod 600 on it." >&2
exit 2
;;
esac
while IFS= read -r line || [ -n "$line" ]; do
case "$line" in
''|'#'*) continue ;;
esac
key=${line%%=*}
value=${line#*=}
case "$key" in
DINGTALK_PROGRESS_ENABLED|DINGTALK_PROGRESS_DRY_RUN|DINGTALK_PROGRESS_MODE|DINGTALK_PROGRESS_CLIENT_ID|DINGTALK_PROGRESS_TARGET|DINGTALK_PROGRESS_ROBOT_CODE|DINGTALK_PROGRESS_KEYCHAIN_SERVICE|DINGTALK_PROGRESS_PROJECT|DINGTALK_PROGRESS_TITLE_PREFIX)
export "$key=$value"
;;
*)
echo "Unknown key in $config_file: $key" >&2
exit 2
;;
esac
done < "$config_file"
if [ "${DINGTALK_PROGRESS_ENABLED:-0}" != "1" ]; then
echo "DingTalk progress reporting is disabled; skipping." >&2
exit 0
fi
if [ "${DINGTALK_PROGRESS_MODE:-app-bot}" != "app-bot" ]; then
echo "DINGTALK_PROGRESS_MODE must be app-bot." >&2
exit 2
fi
if [ -z "${DINGTALK_PROGRESS_CLIENT_ID:-}" ] ||
[ -z "${DINGTALK_PROGRESS_TARGET:-}" ] ||
[ -z "${DINGTALK_PROGRESS_ROBOT_CODE:-}" ]; then
echo "App-bot mode requires DINGTALK_PROGRESS_CLIENT_ID, DINGTALK_PROGRESS_TARGET, and DINGTALK_PROGRESS_ROBOT_CODE." >&2
exit 2
fi
if ! command -v jq >/dev/null 2>&1; then
echo "jq is required to construct and validate the DingTalk OpenAPI request." >&2
exit 127
fi
if ! command -v dws >/dev/null 2>&1; then
echo "Global dws command was not found in PATH." >&2
exit 127
fi
dws=$(command -v dws)
if [ -n "${DING_SEC:-}" ]; then
client_secret=$DING_SEC
else
if [ ! -x /usr/bin/security ]; then
echo "DING_SEC is unset and macOS Keychain command /usr/bin/security is unavailable." >&2
exit 127
fi
keychain_service=${DINGTALK_PROGRESS_KEYCHAIN_SERVICE:-com.eapil.dingtalk.$(basename -- "$project_root")}
if ! client_secret=$(/usr/bin/security find-generic-password \
-a "$DINGTALK_PROGRESS_CLIENT_ID" \
-s "$keychain_service" \
-w 2>/dev/null); then
echo "DingTalk Client Secret was not found in DING_SEC or this project's macOS Keychain item." >&2
echo "See .dingtalk/README.md for credential setup." >&2
exit 2
fi
fi
if [ -z "$client_secret" ]; then
echo "The DingTalk Client Secret is empty." >&2
exit 2
fi
case "$event_name" in
start) event_label="开始" ;;
milestone) event_label="里程碑" ;;
blocked) event_label="阻塞" ;;
complete) event_label="完成" ;;
failed) event_label="失败" ;;
esac
project_name=${DINGTALK_PROGRESS_PROJECT:-$(basename -- "$project_root")}
title_prefix=${DINGTALK_PROGRESS_TITLE_PREFIX:-[Agent进度]}
title="$title_prefix $event_label"
timestamp=$(date '+%Y-%m-%d %H:%M:%S %Z')
body=$(printf '## %s\n\n- 项目:%s\n- 状态:%s\n- 时间:%s\n\n%s' \
"$title" "$project_name" "$event_label" "$timestamp" "$summary")
msg_param=$(jq -cn --arg title "$title" --arg text "$body" '{title:$title,text:$text}')
request_body=$(jq -cn \
--arg msgParam "$msg_param" \
--arg openConversationId "$DINGTALK_PROGRESS_TARGET" \
--arg robotCode "$DINGTALK_PROGRESS_ROBOT_CODE" \
'{msgParam:$msgParam,msgKey:"sampleMarkdown",openConversationId:$openConversationId,robotCode:$robotCode}')
if [ "${DINGTALK_PROGRESS_DRY_RUN:-0}" = "1" ]; then
if DWS_CLIENT_ID="$DINGTALK_PROGRESS_CLIENT_ID" \
DWS_CLIENT_SECRET="$client_secret" \
"$dws" api POST /v1.0/robot/groupMessages/send \
--data "$request_body" --dry-run --yes --format json; then
send_status=0
else
send_status=$?
fi
unset client_secret DWS_CLIENT_SECRET
exit "$send_status"
fi
max_send_attempts=3
retry_delay_seconds=10
attempt=1
final_status=1
while [ "$attempt" -le "$max_send_attempts" ]; do
response=
if response=$(DWS_CLIENT_ID="$DINGTALK_PROGRESS_CLIENT_ID" \
DWS_CLIENT_SECRET="$client_secret" \
"$dws" api POST /v1.0/robot/groupMessages/send \
--data "$request_body" --yes --format json); then
send_status=0
else
send_status=$?
fi
if [ -n "$response" ]; then
printf '%s\n' "$response"
fi
if [ "$send_status" -eq 0 ] &&
printf '%s\n' "$response" |
jq -e '.processQueryKey | type == "string" and length > 0' >/dev/null 2>&1; then
unset client_secret DWS_CLIENT_SECRET
exit 0
fi
if [ "$send_status" -eq 0 ]; then
echo "DingTalk attempt $attempt returned no valid processQueryKey; service acceptance is unverified." >&2
final_status=1
else
echo "DingTalk attempt $attempt failed with status $send_status." >&2
final_status=$send_status
fi
if [ "$attempt" -lt "$max_send_attempts" ]; then
echo "Retrying DingTalk send in $retry_delay_seconds seconds." >&2
sleep "$retry_delay_seconds"
fi
attempt=$((attempt + 1))
done
unset client_secret DWS_CLIENT_SECRET
echo "DingTalk progress reporting failed after $max_send_attempts attempts; Etunel work should continue." >&2
exit "$final_status"