1- # .github/workflows/telegram-notifier.yml
2- name : Telegram Notifier
1+ name : Telegram – Repo Notifications (Clean Text)
32
43on :
4+ push :
5+ branches : ["**"]
6+ tags : ["*"]
57 pull_request :
68 types : [opened, reopened, synchronize, closed]
79 release :
810 types : [published]
9- issues :
10- types : [opened, reopened, closed, edited, labeled, unlabeled]
1111 workflow_dispatch :
1212 inputs :
1313 title :
14- description : " Manual title (default: 📢 Manual)"
14+ description : " Manual title (default: 📣 Manual)"
1515 required : false
16- default : " 📢 Manual"
16+ default : " 📣 Manual"
1717 text :
18- description : " Message body "
18+ description : " Message to send "
1919 required : true
2020 chat_id :
2121 description : " Override TG_CHAT_ID (e.g. @Channel or numeric id)"
@@ -25,99 +25,160 @@ permissions:
2525 contents : read
2626
2727concurrency :
28- group : telegram-notify- ${{ github.ref }}-${{ github.event_name }}
28+ group : telegram-${{ github.ref }}-${{ github.event_name }}
2929 cancel-in-progress : false
3030
3131jobs :
3232 notify :
3333 runs-on : ubuntu-latest
34- environment : SANDBOX
34+ environment : SANDBOX # must contain secrets TG_BOT_TOKEN and TG_CHAT_ID
35+
3536 steps :
37+ - name : Checkout (for commit list)
38+ uses : actions/checkout@v4
39+ with :
40+ fetch-depth : 100
41+
3642 - name : Build message (plain text)
3743 id : msg
3844 shell : bash
3945 env :
40- REPO : ${{ github.repository }}
41- ACTOR : ${{ github.actor }}
42- EVENT : ${{ github.event_name }}
43- REF : ${{ github.ref_name }}
44- RUN : ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
45- # PR
46- PR_NUM : ${{ github.event.pull_request.number }}
47- PR_TITLE : ${{ github.event.pull_request.title }}
48- PR_URL : ${{ github.event.pull_request.html_url }}
49- PR_ACTION : ${{ github.event.action }}
50- PR_MERGED : ${{ github.event.pull_request.merged }}
51- # Release
52- REL_TAG : ${{ github.event.release.tag_name }}
53- REL_NAME : ${{ github.event.release.name }}
54- REL_URL : ${{ github.event.release.html_url }}
55- # Issue
56- ISS_NUM : ${{ github.event.issue.number }}
57- ISS_TITLE : ${{ github.event.issue.title }}
58- ISS_URL : ${{ github.event.issue.html_url }}
59- # Manual
60- WD_TITLE : ${{ github.event.inputs.title }}
61- WD_TEXT : ${{ github.event.inputs.text }}
46+ # common
47+ REPO : ${{ github.repository }}
48+ ACTOR : ${{ github.actor }}
49+ EVENT : ${{ github.event_name }}
50+ REF_NAME : ${{ github.ref_name }}
51+ REPO_URL : ${{ github.server_url }}/${{ github.repository }}
52+
53+ # push context
54+ GIT_BEFORE : ${{ github.event.before }}
55+ GIT_SHA : ${{ github.sha }}
56+
57+ # pr context
58+ PR_NUM : ${{ github.event.pull_request.number }}
59+ PR_TITLE : ${{ github.event.pull_request.title }}
60+ PR_URL : ${{ github.event.pull_request.html_url }}
61+ PR_ACTION : ${{ github.event.action }}
62+ PR_MERGED : ${{ github.event.pull_request.merged }}
63+ PR_HEAD : ${{ github.event.pull_request.head.ref }}
64+ PR_BASE : ${{ github.event.pull_request.base.ref }}
65+
66+ # release context
67+ REL_TAG : ${{ github.event.release.tag_name }}
68+ REL_NAME : ${{ github.event.release.name }}
69+ REL_URL : ${{ github.event.release.html_url }}
70+
71+ # manual
72+ WD_TITLE : ${{ github.event.inputs.title }}
73+ WD_TEXT : ${{ github.event.inputs.text }}
6274 run : |
6375 set -euo pipefail
64- header() { printf '%s\nRepo: %s\nBy: %s\nRef: %s\nRun: %s\n' "$1" "$REPO" "$ACTOR" "$REF" "$RUN"; }
65-
66- case "$EVENT" in
67- workflow_dispatch)
68- TITLE="${WD_TITLE:-📢 Manual}"
69- MSG="$(header "$TITLE")"
70- [[ -n "${WD_TEXT:-}" ]] && MSG+=$'\n'"${WD_TEXT}"
71- ;;
72- pull_request)
73- if [[ "${PR_ACTION}" == "closed" ]]; then
74- [[ "${PR_MERGED}" == "true" ]] && STATUS="PR merged ✅" || STATUS="PR closed ❌"
75- else
76- STATUS="PR updated ✏️"
77- fi
78- MSG="$(header "$STATUS")"$'\n'"#${PR_NUM}: ${PR_TITLE}"$'\n'"${PR_URL}"
79- ;;
80- release)
81- NAME="${REL_NAME:-$REL_TAG}"
82- MSG="$(header 'Release published 🏷️')"$'\n'"Tag: ${REL_TAG}"$'\n'"Name: ${NAME}"$'\n'"${REL_URL}"
83- ;;
84- issues)
85- case "${{ github.event.action }}" in
86- opened) STATUS="Issue opened 🐞" ;;
87- reopened) STATUS="Issue reopened ♻️" ;;
88- closed) STATUS="Issue closed ✅" ;;
89- edited) STATUS="Issue edited ✏️" ;;
90- labeled) STATUS="Issue labeled 🏷️" ;;
91- unlabeled) STATUS="Issue unlabeled 🏷️" ;;
92- *) STATUS="Issue update 📌" ;;
93- esac
94- MSG="$(header "$STATUS")"$'\n'"#${ISS_NUM}: ${ISS_TITLE}"$'\n'"${ISS_URL}"
95- ;;
96- *)
97- MSG="$(header "Event: $EVENT")"
98- ;;
99- esac
76+
77+ ACTOR_URL="https://github.com/${ACTOR}"
78+
79+ header_push() {
80+ printf '📣 Repository Update\nRepo: %s\nBranch: %s\nPushed by: %s\n\n' \
81+ "$REPO" "$REF_NAME" "$ACTOR_URL"
82+ }
83+
84+ header_pr() {
85+ printf '📣 Pull Request Update\nRepo: %s\nBy: %s\n\n' \
86+ "$REPO" "$ACTOR_URL"
87+ }
88+
89+ header_release() {
90+ printf '📣 New Release\nRepo: %s\nBy: %s\n\n' \
91+ "$REPO" "$ACTOR_URL"
92+ }
93+
94+ header_manual() {
95+ printf '%s\nBy: %s\n\n' "${WD_TITLE:-📣 Manual}" "$ACTOR_URL"
96+ }
97+
98+ MESSAGE=""
99+
100+ if [[ "$EVENT" == "workflow_dispatch" ]]; then
101+ # manual: clean title + actor + your text
102+ MESSAGE="$(header_manual)${WD_TEXT}"
103+
104+ elif [[ "$EVENT" == "push" ]]; then
105+ BEFORE="${GIT_BEFORE:-}"
106+ AFTER="$GIT_SHA"
107+
108+ # Grab last 10 commits (subject, short, author)
109+ if [[ -z "$BEFORE" || "$BEFORE" =~ ^0+$ ]]; then
110+ mapfile -t LINES < <(git log -n 10 --pretty=format:'%s%x1f%h%x1f%an' "$AFTER" || true)
111+ else
112+ mapfile -t LINES < <(git log --pretty=format:'%s%x1f%h%x1f%an' "$BEFORE..$AFTER" | head -n 10 || true)
113+ fi
114+
115+ if [[ ${#LINES[@]} -eq 0 ]]; then
116+ COMMITS="- (no commit messages found)"
117+ else
118+ COMMITS=""
119+ for row in "${LINES[@]}"; do
120+ IFS=$'\x1f' read -r subj short author <<<"$row"
121+ COMMITS+="- ${subj} (${short}) by ${author}"$'\n'
122+ done
123+ COMMITS="${COMMITS%$'\n'}"
124+ fi
125+
126+ MESSAGE="$(header_push)Last 10 commits:\n${COMMITS}\n\nRepo: ${REPO_URL}"
127+
128+ elif [[ "$EVENT" == "pull_request" ]]; then
129+ # Nice status line
130+ if [[ "$PR_ACTION" == "closed" && "${PR_MERGED}" == "true" ]]; then
131+ STATUS="🔀 Merged PR #${PR_NUM}"
132+ elif [[ "$PR_ACTION" == "closed" ]]; then
133+ STATUS="❌ Closed PR #${PR_NUM}"
134+ elif [[ "$PR_ACTION" == "opened" ]]; then
135+ STATUS="🆕 Opened PR #${PR_NUM}"
136+ else
137+ STATUS="✏️ Updated PR #${PR_NUM}"
138+ fi
139+
140+ MESSAGE="$(header_pr)${STATUS}\nTitle: ${PR_TITLE}\nFrom: ${PR_HEAD} → ${PR_BASE}\nLink: ${PR_URL}\n\nRepo: ${REPO_URL}"
141+
142+ elif [[ "$EVENT" == "release" ]]; then
143+ NAME="${REL_NAME:-$REL_TAG}"
144+ MESSAGE="$(header_release)Tag: ${REL_TAG}\nName: ${NAME}\nLink: ${REL_URL}\n\nRepo: ${REPO_URL}"
145+
146+ else
147+ MESSAGE="📣 Event: ${EVENT}\nRepo: ${REPO}\nBy: ${ACTOR_URL}\nRef: ${REF_NAME}\n\nRepo: ${REPO_URL}"
148+ fi
149+
150+ # Telegram message hard limit ~4096 chars; keep margin
151+ BYTES=$(printf %s "$MESSAGE" | wc -c)
152+ if (( BYTES > 3900 )); then
153+ MESSAGE="$(printf %s "$MESSAGE" | head -c 3900)$'\n… (truncated)'"
154+ fi
100155
101156 {
102- printf 'text<<MSGEOF\n'; printf '%s\n' "$MSG"; printf 'MSGEOF\n'
157+ printf 'text<<MSGEOF\n'
158+ printf '%s\n' "$MESSAGE"
159+ printf 'MSGEOF\n'
103160 } >> "$GITHUB_OUTPUT"
104161
105- - name : Send to Telegram (plain text)
162+ - name : Send to Telegram
106163 shell : bash
107164 env :
108- TG_BOT_TOKEN : ${{ secrets.TG_BOT_TOKEN }}
109- TG_CHAT_ID_DEFAULT : ${{ secrets.TG_CHAT_ID }}
110- TEXT : ${{ steps.msg.outputs.text }}
111- OVERRIDE_CHAT_ID : ${{ github.event.inputs.chat_id }}
165+ TG_BOT_TOKEN : ${{ secrets.TG_BOT_TOKEN }}
166+ TG_CHAT_ID_DEFAULT : ${{ secrets.TG_CHAT_ID }}
167+ TEXT : ${{ steps.msg.outputs.text }}
168+ OVERRIDE_CHAT_ID : ${{ github.event.inputs.chat_id }}
112169 run : |
113170 set -euo pipefail
114171 CHAT_ID="${OVERRIDE_CHAT_ID:-$TG_CHAT_ID_DEFAULT}"
115172 [[ -n "${TG_BOT_TOKEN:-}" && -n "${CHAT_ID:-}" ]] || { echo "Missing TG secrets"; exit 1; }
116173
174+ # Plain text (no parse_mode) keeps it simple and crisp
117175 RESP="$(curl -sS -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \
118- -H 'Content-Type: application/json' \
119- -d "$(jq -n --arg chat_id "$CHAT_ID" --arg text "$TEXT" \
120- '{chat_id:$chat_id, text:$text, disable_web_page_preview:true}')")"
176+ -H 'Content-Type: application/json' \
177+ -d "$(jq -n \
178+ --arg chat_id "$CHAT_ID" \
179+ --arg text "$TEXT" \
180+ '{chat_id:$chat_id, text:$text, disable_web_page_preview:true}')")"
181+
121182 echo "Telegram response: $RESP"
122183 echo "$RESP" | jq -e '.ok == true' >/dev/null
123- echo "✅ Message sent."
184+ echo "✅ Message sent to Telegram ."
0 commit comments