Skip to content

Project Summary Notification (Issue & PR) #431

Project Summary Notification (Issue & PR)

Project Summary Notification (Issue & PR) #431

name: Project Summary Notification (Issue & PR)
on:
schedule:
# UTC 时间对应北京时间 +8
- cron: '0 1 * * *' # 北京时间 09:00
- cron: '0 6 * * *' # 北京时间 14:00
- cron: '0 10 * * *' # 北京时间 18:00
workflow_dispatch:
inputs:
test_mode:
description: '测试模式:汇总当天0点至今的活动'
required: false
type: boolean
default: false
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
summary-report:
runs-on: ubuntu-latest
steps:
- name: Check Out Repository
uses: actions/checkout@v4
- name: Calculate Time Windows
id: time_calc
run: |
# 获取当前时间
CURRENT_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "current_time=$CURRENT_TIME" >> $GITHUB_OUTPUT
# 如果是手动触发的测试模式
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ inputs.test_mode }}" == "true" ]; then
# 当天0点 UTC
START_TIME=$(date -u -d "00:00:00" +"%Y-%m-%dT%H:%M:%SZ")
TIME_LABEL="今天汇总"
else
# 根据定时任务确定时间窗口
CURRENT_HOUR=$(date -u +"%H")
case $CURRENT_HOUR in
"01") # 对应北京时间 09:00
START_TIME=$(date -u -d "18:00:00 yesterday" +"%Y-%m-%dT%H:%M:%SZ")
TIME_LABEL="昨夜汇总"
;;
"06") # 对应北京时间 14:00
START_TIME=$(date -u -d "01:00:00" +"%Y-%m-%dT%H:%M:%SZ")
TIME_LABEL="上午汇总"
;;
"10") # 对应北京时间 18:00
START_TIME=$(date -u -d "06:00:00" +"%Y-%m-%dT%H:%M:%SZ")
TIME_LABEL="下午汇总"
;;
*)
# 默认情况,用于手动触发非测试模式
START_TIME=$(date -u -d "6 hours ago" +"%Y-%m-%dT%H:%M:%SZ")
TIME_LABEL="近期汇总"
;;
esac
fi
echo "start_time=$START_TIME" >> $GITHUB_OUTPUT
echo "time_label=$TIME_LABEL" >> $GITHUB_OUTPUT
echo "Time window: $START_TIME to $CURRENT_TIME"
- name: Fetch Activities (Issues & PRs)
id: fetch_activities
env:
START_TIME: ${{ steps.time_calc.outputs.start_time }}
END_TIME: ${{ steps.time_calc.outputs.current_time }}
REPO: ${{ github.repository }}
run: |
sudo apt-get update && sudo apt-get install -y jq
echo "开始获取活动数据..."
# ---------------------------------------------------
# 1. 获取 PR 数据
# ---------------------------------------------------
echo "Fetching PRs..."
PRS_JSON=$(curl -s -H "Authorization: token $GH_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/$REPO/pulls?state=all&sort=updated&direction=desc&per_page=30")
PR_COUNT=0
PRS_ARRAY="["
# 检查 JSON 是否有效且非空
if [ -n "$PRS_JSON" ] && echo "$PRS_JSON" | jq -e 'length > 0' >/dev/null; then
LEN=$(echo "$PRS_JSON" | jq 'length')
# 使用 seq 生成序列时,确保 LEN > 0
if [ "$LEN" -gt 0 ]; then
for i in $(seq 0 $(($LEN - 1))); do
ITEM=$(echo "$PRS_JSON" | jq ".[$i]")
CREATED_AT=$(echo "$ITEM" | jq -r '.created_at')
CLOSED_AT=$(echo "$ITEM" | jq -r '.closed_at')
MERGED_AT=$(echo "$ITEM" | jq -r '.merged_at')
# 状态判定
STATUS=""
# 使用 [[ ]] 进行字符串/时间比较更安全
if [[ "$MERGED_AT" != "null" && "$MERGED_AT" > "$START_TIME" ]]; then
STATUS="🟣 合并"
elif [[ "$CLOSED_AT" != "null" && "$CLOSED_AT" > "$START_TIME" ]]; then
STATUS="🚫 关闭"
elif [[ "$CREATED_AT" > "$START_TIME" ]]; then
STATUS="🆕 新建"
fi
# 如果有状态变化,加入列表
if [ -n "$STATUS" ]; then
NUMBER=$(echo "$ITEM" | jq -r '.number')
TITLE=$(echo "$ITEM" | jq -r '.title' | sed 's/"/\\"/g')
USER=$(echo "$ITEM" | jq -r '.user.login')
if [ $PR_COUNT -gt 0 ]; then PRS_ARRAY="$PRS_ARRAY,"; fi
PRS_ARRAY="$PRS_ARRAY{\"number\": $NUMBER, \"title\": \"$TITLE\", \"user\": \"$USER\", \"status\": \"$STATUS\"}"
PR_COUNT=$((PR_COUNT + 1))
fi
done
fi
fi
PRS_ARRAY="$PRS_ARRAY]"
# ---------------------------------------------------
# 2. 获取 Issue 评论 (修正了 split 错误)
# ---------------------------------------------------
echo "Fetching Comments..."
COMMENTS_JSON=$(curl -s -H "Authorization: token $GH_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/$REPO/issues/comments?since=$START_TIME&sort=created&direction=desc" | \
jq -c '.[] | select(.created_at >= "'$START_TIME'" and .created_at <= "'$END_TIME'")')
COMMENT_COUNT=0
COMMENTS_ARRAY="["
if [ -n "$COMMENTS_JSON" ]; then
while IFS= read -r comment; do
if [ -n "$comment" ]; then
ISSUE_URL=$(echo "$comment" | jq -r '.issue_url')
# --- 修复点:使用 basename 提取编号 ---
ISSUE_NUMBER=$(basename "$ISSUE_URL")
ISSUE_TITLE=$(curl -s -H "Authorization: token $GH_TOKEN" "$ISSUE_URL" | jq -r '.title' | sed 's/"/\\"/g')
AUTHOR=$(echo "$comment" | jq -r '.user.login')
BODY=$(echo "$comment" | jq -r '.body' | sed 's/"/\\"/g' | tr '\n' ' ')
PREVIEW=$(echo "$BODY" | cut -c 1-30)
if [ $COMMENT_COUNT -gt 0 ]; then COMMENTS_ARRAY="$COMMENTS_ARRAY,"; fi
COMMENTS_ARRAY="$COMMENTS_ARRAY{\"number\": $ISSUE_NUMBER, \"title\": \"$ISSUE_TITLE\", \"author\": \"$AUTHOR\", \"preview\": \"$PREVIEW\"}"
COMMENT_COUNT=$((COMMENT_COUNT + 1))
fi
done <<< "$COMMENTS_JSON"
fi
COMMENTS_ARRAY="$COMMENTS_ARRAY]"
# ---------------------------------------------------
# 3. 获取 Issue 事件 (标签/分配)
# ---------------------------------------------------
echo "Fetching Events..."
EVENTS_JSON=$(curl -s -H "Authorization: token $GH_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/$REPO/issues/events?since=$START_TIME" | \
jq -c '.[] | select(.created_at >= "'$START_TIME'" and .created_at <= "'$END_TIME'")')
LABEL_COUNT=0
LABELS_ARRAY="["
ASSIGN_COUNT=0
ASSIGNMENTS_ARRAY="["
if [ -n "$EVENTS_JSON" ]; then
while IFS= read -r event; do
if [ -n "$event" ]; then
EVENT_TYPE=$(echo "$event" | jq -r '.event')
ISSUE_NUMBER=$(echo "$event" | jq -r '.issue.number')
ISSUE_TITLE=$(echo "$event" | jq -r '.issue.title' | sed 's/"/\\"/g')
case "$EVENT_TYPE" in
"labeled")
LABEL_NAME=$(echo "$event" | jq -r '.label.name')
if [ $LABEL_COUNT -gt 0 ]; then LABELS_ARRAY="$LABELS_ARRAY,"; fi
LABELS_ARRAY="$LABELS_ARRAY{\"number\": $ISSUE_NUMBER, \"title\": \"$ISSUE_TITLE\", \"change\": \"添加了$LABEL_NAME\"}"
LABEL_COUNT=$((LABEL_COUNT + 1))
;;
"unlabeled")
LABEL_NAME=$(echo "$event" | jq -r '.label.name')
if [ $LABEL_COUNT -gt 0 ]; then LABELS_ARRAY="$LABELS_ARRAY,"; fi
LABELS_ARRAY="$LABELS_ARRAY{\"number\": $ISSUE_NUMBER, \"title\": \"$ISSUE_TITLE\", \"change\": \"移除了$LABEL_NAME\"}"
LABEL_COUNT=$((LABEL_COUNT + 1))
;;
"assigned")
ASSIGNEE=$(echo "$event" | jq -r '.assignee.login')
if [ $ASSIGN_COUNT -gt 0 ]; then ASSIGNMENTS_ARRAY="$ASSIGNMENTS_ARRAY,"; fi
ASSIGNMENTS_ARRAY="$ASSIGNMENTS_ARRAY{\"number\": $ISSUE_NUMBER, \"title\": \"$ISSUE_TITLE\", \"assignee\": \"$ASSIGNEE\"}"
ASSIGN_COUNT=$((ASSIGN_COUNT + 1))
;;
esac
fi
done <<< "$EVENTS_JSON"
fi
LABELS_ARRAY="$LABELS_ARRAY]"
ASSIGNMENTS_ARRAY="$ASSIGNMENTS_ARRAY]"
# ---------------------------------------------------
# 4. 汇总输出
# ---------------------------------------------------
cat > summary_data.json << EOF
{
"prs": $PRS_ARRAY,
"comments": $COMMENTS_ARRAY,
"labels": $LABELS_ARRAY,
"assignments": $ASSIGNMENTS_ARRAY
}
EOF
TOTAL_COUNT=$((PR_COUNT + COMMENT_COUNT + LABEL_COUNT + ASSIGN_COUNT))
echo "pr_count=$PR_COUNT" >> $GITHUB_OUTPUT
echo "comment_count=$COMMENT_COUNT" >> $GITHUB_OUTPUT
echo "label_count=$LABEL_COUNT" >> $GITHUB_OUTPUT
echo "assign_count=$ASSIGN_COUNT" >> $GITHUB_OUTPUT
echo "total_count=$TOTAL_COUNT" >> $GITHUB_OUTPUT
echo "数据统计: PR=$PR_COUNT, Comment=$COMMENT_COUNT, Label=$LABEL_COUNT"
- name: Generate Summary Message
id: generate_message
env:
TIME_LABEL: ${{ steps.time_calc.outputs.time_label }}
PR_COUNT: ${{ steps.fetch_activities.outputs.pr_count }}
COMMENT_COUNT: ${{ steps.fetch_activities.outputs.comment_count }}
LABEL_COUNT: ${{ steps.fetch_activities.outputs.label_count }}
ASSIGN_COUNT: ${{ steps.fetch_activities.outputs.assign_count }}
TOTAL_COUNT: ${{ steps.fetch_activities.outputs.total_count }}
run: |
# 清理数据
CLEANED_JSON=$(cat summary_data.json | tr -d '\000-\031')
SUMMARY_DATA=$(echo "$CLEANED_JSON")
MESSAGE="📊 [${{ github.repository }}] 项目动态 ($TIME_LABEL)"
# --- 1. 处理 PR (新增) ---
if [ "$PR_COUNT" -gt 0 ]; then
MESSAGE="$MESSAGE"$'\n'"🔀 PR 变更 ($PR_COUNT)"
# 循环读取
LEN=$(echo "$SUMMARY_DATA" | jq '.prs | length')
LIMIT=5 # PR 比较重要,多显示几条
for i in $(seq 0 $((LEN-1))); do
if [ "$i" -ge "$LIMIT" ]; then
MESSAGE="$MESSAGE"$'\n'" ...等 $((PR_COUNT-LIMIT)) 个 PR"
break
fi
ITEM=$(echo "$SUMMARY_DATA" | jq -r ".prs[$i]")
NUM=$(echo "$ITEM" | jq -r '.number')
TITLE=$(echo "$ITEM" | jq -r '.title')
STATUS=$(echo "$ITEM" | jq -r '.status')
USER=$(echo "$ITEM" | jq -r '.user')
MESSAGE="$MESSAGE"$'\n'" - $STATUS [#$NUM] $TITLE ($USER)"
done
fi
# --- 2. 处理评论 ---
if [ "$COMMENT_COUNT" -gt 0 ]; then
MESSAGE="$MESSAGE"$'\n'"💬 新评论 ($COMMENT_COUNT)"
LEN=$(echo "$SUMMARY_DATA" | jq '.comments | length')
LIMIT=5
for i in $(seq 0 $((LEN-1))); do
if [ "$i" -ge "$LIMIT" ]; then
MESSAGE="$MESSAGE"$'\n'" ...等 $((COMMENT_COUNT-LIMIT)) 条评论"
break
fi
ITEM=$(echo "$SUMMARY_DATA" | jq -r ".comments[$i]")
NUM=$(echo "$ITEM" | jq -r '.number')
TITLE=$(echo "$ITEM" | jq -r '.title')
AUTHOR=$(echo "$ITEM" | jq -r '.author')
PREVIEW=$(echo "$ITEM" | jq -r '.preview')
MESSAGE="$MESSAGE"$'\n'" - [#$NUM] $TITLE:$AUTHOR:$PREVIEW..."
done
fi
# --- 3. 处理标签 ---
# 标签区块已禁用
# if [ "$LABEL_COUNT" -gt 0 ]; then
# MESSAGE="$MESSAGE"$'\n'"🏷️ 标签 ($LABEL_COUNT)"
# LEN=$(echo "$SUMMARY_DATA" | jq '.labels | length')
# LIMIT=3
# for i in $(seq 0 $((LEN-1))); do
# if [ "$i" -ge "$LIMIT" ]; then break; fi
# ITEM=$(echo "$SUMMARY_DATA" | jq -r ".labels[$i]")
# NUM=$(echo "$ITEM" | jq -r '.number')
# CHANGE=$(echo "$ITEM" | jq -r '.change')
# MESSAGE="$MESSAGE"$'\n'" - [#$NUM] $CHANGE"
# done
# fi
# --- 4. 处理分配 ---
if [ "$ASSIGN_COUNT" -gt 0 ]; then
MESSAGE="$MESSAGE"$'\n'"👤 分配 ($ASSIGN_COUNT)"
LEN=$(echo "$SUMMARY_DATA" | jq '.assignments | length')
LIMIT=3
for i in $(seq 0 $((LEN-1))); do
if [ "$i" -ge "$LIMIT" ]; then break; fi
ITEM=$(echo "$SUMMARY_DATA" | jq -r ".assignments[$i]")
NUM=$(echo "$ITEM" | jq -r '.number')
ASSIGNEE=$(echo "$ITEM" | jq -r '.assignee')
MESSAGE="$MESSAGE"$'\n'" - [#$NUM] -> $ASSIGNEE"
done
fi
if [ "$TOTAL_COUNT" -eq 0 ]; then
MESSAGE="$MESSAGE"$'\n'"💤 本时段无动态"
fi
# 输出
{
echo "message<<EOF"
echo -e "$MESSAGE"
echo "EOF"
} >> $GITHUB_OUTPUT
- name: Send QQ Notification
if: steps.fetch_activities.outputs.total_count != '0'
uses: Y2Nk4/qmsg-action@master
with:
# qq: ${{ needs.meta.outputs.version_type == 'ci' && secrets.QMSG_QQ_DEV || secrets.QMSG_QQ_PUB }}
# groups: ${{ needs.meta.outputs.version_type == 'ci' && secrets.QMSG_GROUPS_DEV || secrets.QMSG_GROUPS_PUB }}
groups: ${{ secrets.QMSG_GROUPS_PUB }}
key: ${{ secrets.QMSG_KEY }}
message: ${{ steps.generate_message.outputs.message }}