wecom_reboot_cli_tool.sh 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. #!/bin/bash
  2. # 企业微信机器人增强版消息发送工具
  3. # 支持多种消息类型:文本、文件、图片、语音、Markdown、模板卡片
  4. # 作者: AI Assistant
  5. # 版本: 2.0
  6. # 配置文件路径
  7. CONFIG_FILE="$HOME/.wecom_config"
  8. # 颜色定义
  9. RED='\033[0;31m'
  10. GREEN='\033[0;32m'
  11. YELLOW='\033[1;33m'
  12. BLUE='\033[0;34m'
  13. PURPLE='\033[0;35m'
  14. CYAN='\033[0;36m'
  15. NC='\033[0m' # No Color
  16. # 打印彩色消息
  17. print_info() {
  18. echo -e "${BLUE}[INFO]${NC} $1"
  19. }
  20. print_success() {
  21. echo -e "${GREEN}[SUCCESS]${NC} $1"
  22. }
  23. print_warning() {
  24. echo -e "${YELLOW}[WARNING]${NC} $1"
  25. }
  26. print_error() {
  27. echo -e "${RED}[ERROR]${NC} $1"
  28. }
  29. print_debug() {
  30. echo -e "${PURPLE}[DEBUG]${NC} $1"
  31. }
  32. print_title() {
  33. echo -e "${CYAN}$1${NC}"
  34. }
  35. # 加载或创建配置
  36. load_config() {
  37. if [ -f "$CONFIG_FILE" ]; then
  38. source "$CONFIG_FILE"
  39. if [ -n "$WEBHOOK_KEY" ]; then
  40. print_info "已加载配置文件中的webhook key"
  41. return 0
  42. fi
  43. fi
  44. print_warning "未找到配置文件或webhook key为空"
  45. setup_webhook_key
  46. }
  47. # 设置webhook key
  48. setup_webhook_key() {
  49. print_title "=== 企业微信机器人配置 ==="
  50. echo "请输入你的企业微信机器人webhook key:"
  51. echo "(从 https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY 中提取key部分)"
  52. echo ""
  53. echo -n "Webhook Key: "
  54. read webhook_key
  55. if [ -z "$webhook_key" ]; then
  56. print_error "Webhook key不能为空!"
  57. exit 1
  58. fi
  59. # 保存配置
  60. echo "WEBHOOK_KEY=\"$webhook_key\"" > "$CONFIG_FILE"
  61. print_success "配置已保存到 $CONFIG_FILE"
  62. WEBHOOK_KEY="$webhook_key"
  63. setup_urls
  64. }
  65. # 设置API URLs
  66. setup_urls() {
  67. BASE_URL="https://qyapi.weixin.qq.com/cgi-bin/webhook"
  68. SEND_URL="${BASE_URL}/send?key=${WEBHOOK_KEY}"
  69. UPLOAD_URL="${BASE_URL}/upload_media?key=${WEBHOOK_KEY}"
  70. }
  71. # 发送文本消息
  72. send_text() {
  73. local content="$1"
  74. local mention="$2"
  75. local json_data
  76. if [ -n "$mention" ]; then
  77. json_data=$(cat << EOF
  78. {
  79. "msgtype": "text",
  80. "text": {
  81. "content": "$content",
  82. "mentioned_list": ["$mention"]
  83. }
  84. }
  85. EOF
  86. )
  87. else
  88. json_data=$(cat << EOF
  89. {
  90. "msgtype": "text",
  91. "text": {
  92. "content": "$content"
  93. }
  94. }
  95. EOF
  96. )
  97. fi
  98. send_request "$json_data" "文本消息"
  99. }
  100. # 上传文件/语音
  101. upload_media() {
  102. local file_path="$1"
  103. local file_type="${2:-file}"
  104. if [ ! -f "$file_path" ]; then
  105. print_error "文件不存在: $file_path" >&2
  106. return 1
  107. fi
  108. # 检查文件大小
  109. local file_size=$(get_file_size "$file_path")
  110. if [ "$file_size" -le 5 ]; then
  111. print_error "文件大小必须大于5个字节,当前大小: ${file_size} 字节" >&2
  112. return 1
  113. fi
  114. print_info "正在上传${file_type}文件: $file_path" >&2
  115. local response=$(curl -s -X POST "${UPLOAD_URL}&type=${file_type}" \
  116. -F "media=@$file_path")
  117. local errcode=$(echo "$response" | grep -o '"errcode":[0-9]*' | cut -d':' -f2)
  118. if [ "$errcode" = "0" ]; then
  119. local media_id=$(echo "$response" | grep -o '"media_id":"[^"]*"' | cut -d'"' -f4)
  120. print_success "文件上传成功!media_id: $media_id" >&2
  121. echo "$media_id"
  122. return 0
  123. else
  124. print_error "文件上传失败: $response" >&2
  125. return 1
  126. fi
  127. }
  128. # 获取文件大小(跨平台兼容)
  129. get_file_size() {
  130. local file_path="$1"
  131. if command -v stat >/dev/null 2>&1; then
  132. # Linux系统
  133. if stat -c%s "$file_path" >/dev/null 2>&1; then
  134. stat -c%s "$file_path"
  135. # macOS系统
  136. elif stat -f%z "$file_path" >/dev/null 2>&1; then
  137. stat -f%z "$file_path"
  138. else
  139. wc -c < "$file_path" | tr -d ' '
  140. fi
  141. else
  142. wc -c < "$file_path" | tr -d ' '
  143. fi
  144. }
  145. # 发送文件消息
  146. send_file() {
  147. local file_path="$1"
  148. local media_id=$(upload_media "$file_path" "file")
  149. if [ $? -ne 0 ]; then
  150. return 1
  151. fi
  152. local json_data=$(cat << EOF
  153. {
  154. "msgtype": "file",
  155. "file": {
  156. "media_id": "$media_id"
  157. }
  158. }
  159. EOF
  160. )
  161. send_request "$json_data" "文件消息"
  162. }
  163. # 发送语音消息
  164. send_voice() {
  165. local voice_path="$1"
  166. # 检查文件格式(简单检查扩展名)
  167. if [[ ! "$voice_path" =~ \.(amr|mp3|wav|m4a)$ ]]; then
  168. print_warning "语音文件建议使用 .amr、.mp3、.wav 或 .m4a 格式"
  169. fi
  170. local media_id=$(upload_media "$voice_path" "voice")
  171. if [ $? -ne 0 ]; then
  172. return 1
  173. fi
  174. local json_data=$(cat << EOF
  175. {
  176. "msgtype": "voice",
  177. "voice": {
  178. "media_id": "$media_id"
  179. }
  180. }
  181. EOF
  182. )
  183. send_request "$json_data" "语音消息"
  184. }
  185. # 发送图片消息
  186. send_image() {
  187. local image_path="$1"
  188. if [ ! -f "$image_path" ]; then
  189. print_error "图片文件不存在: $image_path"
  190. return 1
  191. fi
  192. # 检查文件格式
  193. if [[ ! "$image_path" =~ \.(jpg|jpeg|png|gif)$ ]]; then
  194. print_warning "图片文件建议使用 .jpg、.png 或 .gif 格式"
  195. fi
  196. # 读取图片并转换为base64
  197. if ! command -v base64 >/dev/null 2>&1; then
  198. print_error "系统缺少base64命令,无法发送图片"
  199. return 1
  200. fi
  201. local file_size=$(get_file_size "$image_path")
  202. if [ "$file_size" -gt 2097152 ]; then # 2MB
  203. print_error "图片文件不能超过2M,当前大小: $((file_size/1024))KB"
  204. return 1
  205. fi
  206. print_info "正在处理图片: $image_path"
  207. local image_data=$(base64 "$image_path" | tr -d '\n')
  208. local md5_hash
  209. if command -v md5sum >/dev/null 2>&1; then
  210. md5_hash=$(md5sum "$image_path" | cut -d' ' -f1)
  211. elif command -v md5 >/dev/null 2>&1; then
  212. md5_hash=$(md5 -q "$image_path")
  213. else
  214. print_error "系统缺少md5计算工具,无法发送图片"
  215. return 1
  216. fi
  217. local json_data=$(cat << EOF
  218. {
  219. "msgtype": "image",
  220. "image": {
  221. "base64": "$image_data",
  222. "md5": "$md5_hash"
  223. }
  224. }
  225. EOF
  226. )
  227. send_request "$json_data" "图片消息"
  228. }
  229. # 发送Markdown消息
  230. send_markdown() {
  231. local content="$1"
  232. local json_data=$(cat << EOF
  233. {
  234. "msgtype": "markdown",
  235. "markdown": {
  236. "content": "$content"
  237. }
  238. }
  239. EOF
  240. )
  241. send_request "$json_data" "Markdown消息"
  242. }
  243. # 发送图文消息
  244. send_news() {
  245. local title="$1"
  246. local description="$2"
  247. local url="$3"
  248. local pic_url="$4"
  249. local json_data=$(cat << EOF
  250. {
  251. "msgtype": "news",
  252. "news": {
  253. "articles": [
  254. {
  255. "title": "$title",
  256. "description": "$description",
  257. "url": "$url",
  258. "picurl": "$pic_url"
  259. }
  260. ]
  261. }
  262. }
  263. EOF
  264. )
  265. send_request "$json_data" "图文消息"
  266. }
  267. # 发送文本通知模板卡片
  268. send_text_card() {
  269. local title="$1"
  270. local content="$2"
  271. local source_desc="${3:-企业微信机器人}"
  272. local json_data=$(cat << EOF
  273. {
  274. "msgtype": "template_card",
  275. "template_card": {
  276. "card_type": "text_notice",
  277. "source": {
  278. "desc": "$source_desc",
  279. "desc_color": 0
  280. },
  281. "main_title": {
  282. "title": "$title"
  283. },
  284. "sub_title_text": "$content"
  285. }
  286. }
  287. EOF
  288. )
  289. send_request "$json_data" "文本通知卡片"
  290. }
  291. # 发送图文展示模板卡片
  292. send_news_card() {
  293. local title="$1"
  294. local desc="$2"
  295. local image_url="$3"
  296. local jump_url="$4"
  297. local source_desc="${5:-企业微信机器人}"
  298. local json_data=$(cat << EOF
  299. {
  300. "msgtype": "template_card",
  301. "template_card": {
  302. "card_type": "news_notice",
  303. "source": {
  304. "desc": "$source_desc",
  305. "desc_color": 0
  306. },
  307. "main_title": {
  308. "title": "$title",
  309. "desc": "$desc"
  310. },
  311. "card_image": {
  312. "url": "$image_url",
  313. "aspect_ratio": 2.25
  314. },
  315. "card_action": {
  316. "type": 1,
  317. "url": "$jump_url"
  318. }
  319. }
  320. }
  321. EOF
  322. )
  323. send_request "$json_data" "图文展示卡片"
  324. }
  325. # 发送HTTP请求
  326. send_request() {
  327. local json_data="$1"
  328. local msg_type="$2"
  329. print_info "正在发送${msg_type}..."
  330. local response=$(curl -s -X POST "$SEND_URL" \
  331. -H "Content-Type: application/json" \
  332. -d "$json_data")
  333. local errcode=$(echo "$response" | grep -o '"errcode":[0-9]*' | cut -d':' -f2)
  334. if [ "$errcode" = "0" ]; then
  335. print_success "${msg_type}发送成功!"
  336. return 0
  337. else
  338. print_error "${msg_type}发送失败: $response"
  339. return 1
  340. fi
  341. }
  342. # 发送组合消息
  343. send_combo() {
  344. local text_content="$1"
  345. local file_path="$2"
  346. local mention="$3"
  347. print_info "开始发送组合消息(文本+文件)"
  348. if ! send_text "$text_content" "$mention"; then
  349. return 1
  350. fi
  351. sleep 1 # 避免发送太快
  352. if ! send_file "$file_path"; then
  353. return 1
  354. fi
  355. print_success "组合消息发送完成!"
  356. }
  357. # 显示帮助信息
  358. show_help() {
  359. print_title "企业微信机器人增强版消息发送工具 v2.0"
  360. echo ""
  361. echo "用法: $0 [选项]"
  362. echo ""
  363. echo "基础消息选项:"
  364. echo " -t, --text TEXT 发送文本消息"
  365. echo " -f, --file FILE 发送文件"
  366. echo " -i, --image IMAGE 发送图片"
  367. echo " -v, --voice VOICE 发送语音"
  368. echo " -m, --markdown TEXT 发送Markdown消息"
  369. echo " -c, --combo TEXT FILE 发送文本+文件组合"
  370. echo ""
  371. echo "高级消息选项:"
  372. echo " -n, --news 发送图文消息(交互式输入)"
  373. echo " --text-card 发送文本通知卡片(交互式输入)"
  374. echo " --news-card 发送图文展示卡片(交互式输入)"
  375. echo ""
  376. echo "其他选项:"
  377. echo " -@ USERID @指定用户(与文本消息配合使用)"
  378. echo " --config 重新配置webhook key"
  379. echo " --test 测试连接"
  380. echo " -h, --help 显示此帮助信息"
  381. echo ""
  382. echo "示例:"
  383. echo " $0 -t \"Hello World\" # 发送文本消息"
  384. echo " $0 -f \"report.pdf\" # 发送文件"
  385. echo " $0 -i \"screenshot.png\" # 发送图片"
  386. echo " $0 -v \"voice.amr\" # 发送语音"
  387. echo " $0 -c \"请查看附件\" \"document.pdf\" # 发送文本+文件"
  388. echo " $0 -t \"重要通知\" -@ \"@all\" # @所有人"
  389. echo " $0 -n # 交互式创建图文消息"
  390. echo " $0 --text-card # 交互式创建文本卡片"
  391. echo ""
  392. echo "配置文件位置: $CONFIG_FILE"
  393. }
  394. # 主菜单
  395. show_menu() {
  396. print_title "=================================="
  397. print_title " 企业微信机器人增强版发送工具 v2.0"
  398. print_title "=================================="
  399. echo "1. 发送文本消息"
  400. echo "2. 发送文件"
  401. echo "3. 发送图片"
  402. echo "4. 发送语音"
  403. echo "5. 发送文本+文件组合"
  404. echo "6. 发送Markdown消息"
  405. echo "7. 发送图文消息"
  406. echo "8. 发送文本通知卡片"
  407. echo "9. 发送图文展示卡片"
  408. echo "10. 测试连接"
  409. echo "11. 重新配置webhook key"
  410. echo "12. 退出"
  411. print_title "=================================="
  412. echo -n "请选择操作 (1-12): "
  413. }
  414. # 测试连接
  415. test_connection() {
  416. print_info "测试机器人连接..."
  417. local test_msg="🤖 机器人连接测试 - $(date '+%Y-%m-%d %H:%M:%S')"
  418. if send_text "$test_msg"; then
  419. print_success "机器人连接正常!"
  420. else
  421. print_error "机器人连接失败,请检查webhook key是否正确"
  422. fi
  423. }
  424. # 交互式输入图文消息
  425. interactive_news() {
  426. echo "=== 创建图文消息 ==="
  427. echo -n "标题: "
  428. read title
  429. echo -n "描述: "
  430. read description
  431. echo -n "点击跳转URL: "
  432. read url
  433. echo -n "图片URL(可选): "
  434. read pic_url
  435. if [ -z "$title" ] || [ -z "$url" ]; then
  436. print_error "标题和URL不能为空"
  437. return 1
  438. fi
  439. send_news "$title" "$description" "$url" "$pic_url"
  440. }
  441. # 交互式输入文本卡片
  442. interactive_text_card() {
  443. echo "=== 创建文本通知卡片 ==="
  444. echo -n "卡片标题: "
  445. read title
  446. echo -n "卡片内容: "
  447. read content
  448. echo -n "来源描述(可选,默认:企业微信机器人): "
  449. read source_desc
  450. if [ -z "$title" ]; then
  451. print_error "卡片标题不能为空"
  452. return 1
  453. fi
  454. send_text_card "$title" "$content" "${source_desc:-企业微信机器人}"
  455. }
  456. # 交互式输入图文卡片
  457. interactive_news_card() {
  458. echo "=== 创建图文展示卡片 ==="
  459. echo -n "卡片标题: "
  460. read title
  461. echo -n "卡片描述: "
  462. read desc
  463. echo -n "图片URL: "
  464. read image_url
  465. echo -n "点击跳转URL: "
  466. read jump_url
  467. echo -n "来源描述(可选,默认:企业微信机器人): "
  468. read source_desc
  469. if [ -z "$title" ] || [ -z "$image_url" ] || [ -z "$jump_url" ]; then
  470. print_error "标题、图片URL和跳转URL不能为空"
  471. return 1
  472. fi
  473. send_news_card "$title" "$desc" "$image_url" "$jump_url" "${source_desc:-企业微信机器人}"
  474. }
  475. # 主程序
  476. main() {
  477. # 加载配置
  478. load_config
  479. setup_urls
  480. local text_content=""
  481. local file_path=""
  482. local mention=""
  483. local markdown_content=""
  484. local action=""
  485. # 解析命令行参数
  486. while [[ $# -gt 0 ]]; do
  487. case $1 in
  488. -t|--text)
  489. text_content="$2"
  490. action="text"
  491. shift 2
  492. ;;
  493. -f|--file)
  494. file_path="$2"
  495. action="file"
  496. shift 2
  497. ;;
  498. -i|--image)
  499. file_path="$2"
  500. action="image"
  501. shift 2
  502. ;;
  503. -v|--voice)
  504. file_path="$2"
  505. action="voice"
  506. shift 2
  507. ;;
  508. -c|--combo)
  509. text_content="$2"
  510. file_path="$3"
  511. action="combo"
  512. shift 3
  513. ;;
  514. -m|--markdown)
  515. markdown_content="$2"
  516. action="markdown"
  517. shift 2
  518. ;;
  519. -n|--news)
  520. action="news"
  521. shift
  522. ;;
  523. --text-card)
  524. action="text-card"
  525. shift
  526. ;;
  527. --news-card)
  528. action="news-card"
  529. shift
  530. ;;
  531. -@)
  532. mention="$2"
  533. shift 2
  534. ;;
  535. --config)
  536. setup_webhook_key
  537. exit 0
  538. ;;
  539. --test)
  540. test_connection
  541. exit 0
  542. ;;
  543. -h|--help)
  544. show_help
  545. exit 0
  546. ;;
  547. *)
  548. print_error "未知选项: $1"
  549. show_help
  550. exit 1
  551. ;;
  552. esac
  553. done
  554. # 如果没有参数,显示交互式菜单
  555. if [ -z "$action" ]; then
  556. while true; do
  557. show_menu
  558. read choice
  559. echo ""
  560. case $choice in
  561. 1)
  562. echo -n "请输入文本内容: "
  563. read text_input
  564. echo -n "是否@某人(可选,直接回车跳过): "
  565. read mention_input
  566. send_text "$text_input" "$mention_input"
  567. ;;
  568. 2)
  569. echo -n "请输入文件路径: "
  570. read file_input
  571. send_file "$file_input"
  572. ;;
  573. 3)
  574. echo -n "请输入图片路径: "
  575. read image_input
  576. send_image "$image_input"
  577. ;;
  578. 4)
  579. echo -n "请输入语音文件路径: "
  580. read voice_input
  581. send_voice "$voice_input"
  582. ;;
  583. 5)
  584. echo -n "请输入文本内容: "
  585. read text_input
  586. echo -n "请输入文件路径: "
  587. read file_input
  588. send_combo "$text_input" "$file_input"
  589. ;;
  590. 6)
  591. echo -n "请输入Markdown内容: "
  592. read markdown_input
  593. send_markdown "$markdown_input"
  594. ;;
  595. 7)
  596. interactive_news
  597. ;;
  598. 8)
  599. interactive_text_card
  600. ;;
  601. 9)
  602. interactive_news_card
  603. ;;
  604. 10)
  605. test_connection
  606. ;;
  607. 11)
  608. setup_webhook_key
  609. setup_urls
  610. ;;
  611. 12)
  612. print_info "再见!"
  613. break
  614. ;;
  615. *)
  616. print_error "无效选择,请重新输入"
  617. ;;
  618. esac
  619. echo ""
  620. echo "按任意键继续..."
  621. read -n 1
  622. clear
  623. done
  624. exit 0
  625. fi
  626. # 执行相应的操作
  627. case $action in
  628. "text")
  629. send_text "$text_content" "$mention"
  630. ;;
  631. "file")
  632. send_file "$file_path"
  633. ;;
  634. "image")
  635. send_image "$file_path"
  636. ;;
  637. "voice")
  638. send_voice "$file_path"
  639. ;;
  640. "combo")
  641. send_combo "$text_content" "$file_path" "$mention"
  642. ;;
  643. "markdown")
  644. send_markdown "$markdown_content"
  645. ;;
  646. "news")
  647. interactive_news
  648. ;;
  649. "text-card")
  650. interactive_text_card
  651. ;;
  652. "news-card")
  653. interactive_news_card
  654. ;;
  655. esac
  656. }
  657. # 运行主程序
  658. main "$@"