Files
alembic/pipeline/lib/notify-telegram.sh
T
andrew 55a059b6da 0.6.14: Fix Bandcamp sync silently failing to import new purchases
sync-bandcamp.sh set a hardcoded PATH at startup that left out /opt/venv/bin,
which is where beet lives in this image. Every time the Bandcamp sync had a
new purchase to import, its call into import-track.sh would run beet import
with that broken PATH, fail with "command not found", and import-track.sh
would just log the exit code and carry on, so sync-bandcamp.sh still reported
success. The purchase sat on disk, never entered the beets library, and never
showed up in Navidrome. This went unnoticed for weeks because most daily
syncs have nothing new to import, so the broken code path rarely ran.

Fixed the same copy-pasted PATH line in upgrade-mp3-to-flac.sh (which also
calls beet directly) and notify-telegram.sh (harmless there, but fixed for
consistency).

Also moved the post-import duplicate cleanup (replace-with-better.sh and
dedup-library.sh) out of sync-bandcamp.sh and into import-track.sh itself, so
every import gets the same cleanup, not just Bandcamp purchases. A manual
import or SMB drop that happens to match something already in the library no
longer leaves a duplicate copy sitting there until someone runs the dedup
review by hand.
2026-07-23 10:09:18 -06:00

89 lines
3.2 KiB
Bash
Executable File

#!/bin/bash
# notify-telegram.sh — send a message to the configured Telegram chat.
# Sources credentials from $ALEMBIC_CONFIG_DIR/pipeline/telegram/notify.env (chmod 600).
#
# Usage:
# echo "single line message" | notify-telegram.sh
# notify-telegram.sh "single line message"
# notify-telegram.sh --html < ${ALEMBIC_CONFIG_DIR:-/config}/logs/STATUS.log
#
# --html sends with parse_mode=HTML for messages authored as Telegram-HTML
# (pipeline-status.sh's digest: <b>/<i>/<code>/<pre>/<blockquote>). The
# CALLER is responsible for escaping &, <, > in any free text; this script
# only guarantees the truncation below can't cut a tag in half. Without the
# flag, messages go as plain text exactly as before.
#
# Telegram messages are capped at 4096 chars. Anything longer is truncated;
# in HTML mode the cut lands on a line boundary and re-closes an open <pre>
# so the truncated message still parses (a mid-tag cut, or a bare "<" in the
# tail marker, makes the Bot API reject the ENTIRE message with a 400).
# Returns exit 0 on send-OK, non-zero otherwise.
set -euo pipefail
# /opt/venv/bin leads PATH for consistency with the other pipeline scripts,
# even though this one only shells out to curl.
PATH=/opt/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PARSE_MODE=""
if [[ "${1:-}" == "--html" ]]; then
PARSE_MODE="HTML"
shift
fi
CONFIG="${ALEMBIC_CONFIG_DIR:-/config}/pipeline/telegram/notify.env"
if [[ ! -f "$CONFIG" ]]; then
echo "[notify-telegram] no config at $CONFIG" >&2
exit 1
fi
# shellcheck disable=SC1090
source "$CONFIG"
: "${TG_BOT_TOKEN:?TG_BOT_TOKEN missing}"
: "${TG_CHAT_ID:?TG_CHAT_ID missing}"
# Read message from arg or stdin
if [[ $# -ge 1 ]]; then
MSG="$*"
else
MSG=$(cat)
fi
# Telegram caps at 4096 chars. Trim conservatively at 3900. Use python for
# proper UTF-8 length handling. Plain mode appends a literal marker; HTML
# mode cuts at the last newline inside the budget (our tags never span
# lines except <pre> blocks) and re-closes an unbalanced <pre>.
MSG=$(python3 -c "
import sys
s = sys.argv[1]
html = sys.argv[2] == 'HTML'
if len(s) > 3900:
s = s[:3900]
if html:
cut = s.rfind('\n')
if cut > 0:
s = s[:cut]
if s.count('<pre>') > s.count('</pre>'):
s += '</pre>'
s += '\n<i>… truncated</i>'
else:
s += '\n...(truncated)'
print(s, end='')
" "$MSG" "${PARSE_MODE:-plain}")
# Send via Bot API. Preview disabled; parse_mode only when requested so log
# content with special chars can't be misread as markup in plain sends.
# `|| true`: a curl timeout/network error must fall through to the explicit
# ok-check below (which reports "send failed" and exits 2), not abort here.
resp=$(curl -s --max-time 15 -X POST \
"https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \
--data-urlencode "chat_id=${TG_CHAT_ID}" \
--data-urlencode "text=${MSG}" \
${PARSE_MODE:+--data-urlencode "parse_mode=${PARSE_MODE}"} \
--data-urlencode "disable_web_page_preview=true" || true)
ok=$(echo "$resp" | python3 -c "import sys,json;print(json.load(sys.stdin).get('ok',False))" 2>/dev/null || echo False)
if [[ "$ok" != "True" ]]; then
echo "[notify-telegram] send failed: $resp" >&2
exit 2
fi
exit 0