| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743 |
- #!/bin/bash
- # 企业微信机器人增强版消息发送工具
- # 支持多种消息类型:文本、文件、图片、语音、Markdown、模板卡片
- # 作者: AI Assistant
- # 版本: 2.0
- # 配置文件路径
- CONFIG_FILE="$HOME/.wecom_config"
- # 颜色定义
- RED='\033[0;31m'
- GREEN='\033[0;32m'
- YELLOW='\033[1;33m'
- BLUE='\033[0;34m'
- PURPLE='\033[0;35m'
- CYAN='\033[0;36m'
- NC='\033[0m' # No Color
- # 打印彩色消息
- print_info() {
- echo -e "${BLUE}[INFO]${NC} $1"
- }
- print_success() {
- echo -e "${GREEN}[SUCCESS]${NC} $1"
- }
- print_warning() {
- echo -e "${YELLOW}[WARNING]${NC} $1"
- }
- print_error() {
- echo -e "${RED}[ERROR]${NC} $1"
- }
- print_debug() {
- echo -e "${PURPLE}[DEBUG]${NC} $1"
- }
- print_title() {
- echo -e "${CYAN}$1${NC}"
- }
- # 加载或创建配置
- load_config() {
- if [ -f "$CONFIG_FILE" ]; then
- source "$CONFIG_FILE"
- if [ -n "$WEBHOOK_KEY" ]; then
- print_info "已加载配置文件中的webhook key"
- return 0
- fi
- fi
-
- print_warning "未找到配置文件或webhook key为空"
- setup_webhook_key
- }
- # 设置webhook key
- setup_webhook_key() {
- print_title "=== 企业微信机器人配置 ==="
- echo "请输入你的企业微信机器人webhook key:"
- echo "(从 https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY 中提取key部分)"
- echo ""
- echo -n "Webhook Key: "
- read webhook_key
-
- if [ -z "$webhook_key" ]; then
- print_error "Webhook key不能为空!"
- exit 1
- fi
-
- # 保存配置
- echo "WEBHOOK_KEY=\"$webhook_key\"" > "$CONFIG_FILE"
- print_success "配置已保存到 $CONFIG_FILE"
-
- WEBHOOK_KEY="$webhook_key"
- setup_urls
- }
- # 设置API URLs
- setup_urls() {
- BASE_URL="https://qyapi.weixin.qq.com/cgi-bin/webhook"
- SEND_URL="${BASE_URL}/send?key=${WEBHOOK_KEY}"
- UPLOAD_URL="${BASE_URL}/upload_media?key=${WEBHOOK_KEY}"
- }
- # 发送文本消息
- send_text() {
- local content="$1"
- local mention="$2"
-
- local json_data
- if [ -n "$mention" ]; then
- json_data=$(cat << EOF
- {
- "msgtype": "text",
- "text": {
- "content": "$content",
- "mentioned_list": ["$mention"]
- }
- }
- EOF
- )
- else
- json_data=$(cat << EOF
- {
- "msgtype": "text",
- "text": {
- "content": "$content"
- }
- }
- EOF
- )
- fi
-
- send_request "$json_data" "文本消息"
- }
- # 上传文件/语音
- upload_media() {
- local file_path="$1"
- local file_type="${2:-file}"
-
- if [ ! -f "$file_path" ]; then
- print_error "文件不存在: $file_path" >&2
- return 1
- fi
-
- # 检查文件大小
- local file_size=$(get_file_size "$file_path")
- if [ "$file_size" -le 5 ]; then
- print_error "文件大小必须大于5个字节,当前大小: ${file_size} 字节" >&2
- return 1
- fi
-
- print_info "正在上传${file_type}文件: $file_path" >&2
- local response=$(curl -s -X POST "${UPLOAD_URL}&type=${file_type}" \
- -F "media=@$file_path")
-
- local errcode=$(echo "$response" | grep -o '"errcode":[0-9]*' | cut -d':' -f2)
- if [ "$errcode" = "0" ]; then
- local media_id=$(echo "$response" | grep -o '"media_id":"[^"]*"' | cut -d'"' -f4)
- print_success "文件上传成功!media_id: $media_id" >&2
- echo "$media_id"
- return 0
- else
- print_error "文件上传失败: $response" >&2
- return 1
- fi
- }
- # 获取文件大小(跨平台兼容)
- get_file_size() {
- local file_path="$1"
-
- if command -v stat >/dev/null 2>&1; then
- # Linux系统
- if stat -c%s "$file_path" >/dev/null 2>&1; then
- stat -c%s "$file_path"
- # macOS系统
- elif stat -f%z "$file_path" >/dev/null 2>&1; then
- stat -f%z "$file_path"
- else
- wc -c < "$file_path" | tr -d ' '
- fi
- else
- wc -c < "$file_path" | tr -d ' '
- fi
- }
- # 发送文件消息
- send_file() {
- local file_path="$1"
-
- local media_id=$(upload_media "$file_path" "file")
- if [ $? -ne 0 ]; then
- return 1
- fi
-
- local json_data=$(cat << EOF
- {
- "msgtype": "file",
- "file": {
- "media_id": "$media_id"
- }
- }
- EOF
- )
-
- send_request "$json_data" "文件消息"
- }
- # 发送语音消息
- send_voice() {
- local voice_path="$1"
-
- # 检查文件格式(简单检查扩展名)
- if [[ ! "$voice_path" =~ \.(amr|mp3|wav|m4a)$ ]]; then
- print_warning "语音文件建议使用 .amr、.mp3、.wav 或 .m4a 格式"
- fi
-
- local media_id=$(upload_media "$voice_path" "voice")
- if [ $? -ne 0 ]; then
- return 1
- fi
-
- local json_data=$(cat << EOF
- {
- "msgtype": "voice",
- "voice": {
- "media_id": "$media_id"
- }
- }
- EOF
- )
-
- send_request "$json_data" "语音消息"
- }
- # 发送图片消息
- send_image() {
- local image_path="$1"
-
- if [ ! -f "$image_path" ]; then
- print_error "图片文件不存在: $image_path"
- return 1
- fi
-
- # 检查文件格式
- if [[ ! "$image_path" =~ \.(jpg|jpeg|png|gif)$ ]]; then
- print_warning "图片文件建议使用 .jpg、.png 或 .gif 格式"
- fi
-
- # 读取图片并转换为base64
- if ! command -v base64 >/dev/null 2>&1; then
- print_error "系统缺少base64命令,无法发送图片"
- return 1
- fi
-
- local file_size=$(get_file_size "$image_path")
- if [ "$file_size" -gt 2097152 ]; then # 2MB
- print_error "图片文件不能超过2M,当前大小: $((file_size/1024))KB"
- return 1
- fi
-
- print_info "正在处理图片: $image_path"
- local image_data=$(base64 "$image_path" | tr -d '\n')
- local md5_hash
-
- if command -v md5sum >/dev/null 2>&1; then
- md5_hash=$(md5sum "$image_path" | cut -d' ' -f1)
- elif command -v md5 >/dev/null 2>&1; then
- md5_hash=$(md5 -q "$image_path")
- else
- print_error "系统缺少md5计算工具,无法发送图片"
- return 1
- fi
-
- local json_data=$(cat << EOF
- {
- "msgtype": "image",
- "image": {
- "base64": "$image_data",
- "md5": "$md5_hash"
- }
- }
- EOF
- )
-
- send_request "$json_data" "图片消息"
- }
- # 发送Markdown消息
- send_markdown() {
- local content="$1"
-
- local json_data=$(cat << EOF
- {
- "msgtype": "markdown",
- "markdown": {
- "content": "$content"
- }
- }
- EOF
- )
-
- send_request "$json_data" "Markdown消息"
- }
- # 发送图文消息
- send_news() {
- local title="$1"
- local description="$2"
- local url="$3"
- local pic_url="$4"
-
- local json_data=$(cat << EOF
- {
- "msgtype": "news",
- "news": {
- "articles": [
- {
- "title": "$title",
- "description": "$description",
- "url": "$url",
- "picurl": "$pic_url"
- }
- ]
- }
- }
- EOF
- )
-
- send_request "$json_data" "图文消息"
- }
- # 发送文本通知模板卡片
- send_text_card() {
- local title="$1"
- local content="$2"
- local source_desc="${3:-企业微信机器人}"
-
- local json_data=$(cat << EOF
- {
- "msgtype": "template_card",
- "template_card": {
- "card_type": "text_notice",
- "source": {
- "desc": "$source_desc",
- "desc_color": 0
- },
- "main_title": {
- "title": "$title"
- },
- "sub_title_text": "$content"
- }
- }
- EOF
- )
-
- send_request "$json_data" "文本通知卡片"
- }
- # 发送图文展示模板卡片
- send_news_card() {
- local title="$1"
- local desc="$2"
- local image_url="$3"
- local jump_url="$4"
- local source_desc="${5:-企业微信机器人}"
-
- local json_data=$(cat << EOF
- {
- "msgtype": "template_card",
- "template_card": {
- "card_type": "news_notice",
- "source": {
- "desc": "$source_desc",
- "desc_color": 0
- },
- "main_title": {
- "title": "$title",
- "desc": "$desc"
- },
- "card_image": {
- "url": "$image_url",
- "aspect_ratio": 2.25
- },
- "card_action": {
- "type": 1,
- "url": "$jump_url"
- }
- }
- }
- EOF
- )
-
- send_request "$json_data" "图文展示卡片"
- }
- # 发送HTTP请求
- send_request() {
- local json_data="$1"
- local msg_type="$2"
-
- print_info "正在发送${msg_type}..."
- local response=$(curl -s -X POST "$SEND_URL" \
- -H "Content-Type: application/json" \
- -d "$json_data")
-
- local errcode=$(echo "$response" | grep -o '"errcode":[0-9]*' | cut -d':' -f2)
- if [ "$errcode" = "0" ]; then
- print_success "${msg_type}发送成功!"
- return 0
- else
- print_error "${msg_type}发送失败: $response"
- return 1
- fi
- }
- # 发送组合消息
- send_combo() {
- local text_content="$1"
- local file_path="$2"
- local mention="$3"
-
- print_info "开始发送组合消息(文本+文件)"
-
- if ! send_text "$text_content" "$mention"; then
- return 1
- fi
-
- sleep 1 # 避免发送太快
-
- if ! send_file "$file_path"; then
- return 1
- fi
-
- print_success "组合消息发送完成!"
- }
- # 显示帮助信息
- show_help() {
- print_title "企业微信机器人增强版消息发送工具 v2.0"
- echo ""
- echo "用法: $0 [选项]"
- echo ""
- echo "基础消息选项:"
- echo " -t, --text TEXT 发送文本消息"
- echo " -f, --file FILE 发送文件"
- echo " -i, --image IMAGE 发送图片"
- echo " -v, --voice VOICE 发送语音"
- echo " -m, --markdown TEXT 发送Markdown消息"
- echo " -c, --combo TEXT FILE 发送文本+文件组合"
- echo ""
- echo "高级消息选项:"
- echo " -n, --news 发送图文消息(交互式输入)"
- echo " --text-card 发送文本通知卡片(交互式输入)"
- echo " --news-card 发送图文展示卡片(交互式输入)"
- echo ""
- echo "其他选项:"
- echo " -@ USERID @指定用户(与文本消息配合使用)"
- echo " --config 重新配置webhook key"
- echo " --test 测试连接"
- echo " -h, --help 显示此帮助信息"
- echo ""
- echo "示例:"
- echo " $0 -t \"Hello World\" # 发送文本消息"
- echo " $0 -f \"report.pdf\" # 发送文件"
- echo " $0 -i \"screenshot.png\" # 发送图片"
- echo " $0 -v \"voice.amr\" # 发送语音"
- echo " $0 -c \"请查看附件\" \"document.pdf\" # 发送文本+文件"
- echo " $0 -t \"重要通知\" -@ \"@all\" # @所有人"
- echo " $0 -n # 交互式创建图文消息"
- echo " $0 --text-card # 交互式创建文本卡片"
- echo ""
- echo "配置文件位置: $CONFIG_FILE"
- }
- # 主菜单
- show_menu() {
- print_title "=================================="
- print_title " 企业微信机器人增强版发送工具 v2.0"
- print_title "=================================="
- echo "1. 发送文本消息"
- echo "2. 发送文件"
- echo "3. 发送图片"
- echo "4. 发送语音"
- echo "5. 发送文本+文件组合"
- echo "6. 发送Markdown消息"
- echo "7. 发送图文消息"
- echo "8. 发送文本通知卡片"
- echo "9. 发送图文展示卡片"
- echo "10. 测试连接"
- echo "11. 重新配置webhook key"
- echo "12. 退出"
- print_title "=================================="
- echo -n "请选择操作 (1-12): "
- }
- # 测试连接
- test_connection() {
- print_info "测试机器人连接..."
- local test_msg="🤖 机器人连接测试 - $(date '+%Y-%m-%d %H:%M:%S')"
-
- if send_text "$test_msg"; then
- print_success "机器人连接正常!"
- else
- print_error "机器人连接失败,请检查webhook key是否正确"
- fi
- }
- # 交互式输入图文消息
- interactive_news() {
- echo "=== 创建图文消息 ==="
- echo -n "标题: "
- read title
- echo -n "描述: "
- read description
- echo -n "点击跳转URL: "
- read url
- echo -n "图片URL(可选): "
- read pic_url
-
- if [ -z "$title" ] || [ -z "$url" ]; then
- print_error "标题和URL不能为空"
- return 1
- fi
-
- send_news "$title" "$description" "$url" "$pic_url"
- }
- # 交互式输入文本卡片
- interactive_text_card() {
- echo "=== 创建文本通知卡片 ==="
- echo -n "卡片标题: "
- read title
- echo -n "卡片内容: "
- read content
- echo -n "来源描述(可选,默认:企业微信机器人): "
- read source_desc
-
- if [ -z "$title" ]; then
- print_error "卡片标题不能为空"
- return 1
- fi
-
- send_text_card "$title" "$content" "${source_desc:-企业微信机器人}"
- }
- # 交互式输入图文卡片
- interactive_news_card() {
- echo "=== 创建图文展示卡片 ==="
- echo -n "卡片标题: "
- read title
- echo -n "卡片描述: "
- read desc
- echo -n "图片URL: "
- read image_url
- echo -n "点击跳转URL: "
- read jump_url
- echo -n "来源描述(可选,默认:企业微信机器人): "
- read source_desc
-
- if [ -z "$title" ] || [ -z "$image_url" ] || [ -z "$jump_url" ]; then
- print_error "标题、图片URL和跳转URL不能为空"
- return 1
- fi
-
- send_news_card "$title" "$desc" "$image_url" "$jump_url" "${source_desc:-企业微信机器人}"
- }
- # 主程序
- main() {
- # 加载配置
- load_config
- setup_urls
-
- local text_content=""
- local file_path=""
- local mention=""
- local markdown_content=""
- local action=""
-
- # 解析命令行参数
- while [[ $# -gt 0 ]]; do
- case $1 in
- -t|--text)
- text_content="$2"
- action="text"
- shift 2
- ;;
- -f|--file)
- file_path="$2"
- action="file"
- shift 2
- ;;
- -i|--image)
- file_path="$2"
- action="image"
- shift 2
- ;;
- -v|--voice)
- file_path="$2"
- action="voice"
- shift 2
- ;;
- -c|--combo)
- text_content="$2"
- file_path="$3"
- action="combo"
- shift 3
- ;;
- -m|--markdown)
- markdown_content="$2"
- action="markdown"
- shift 2
- ;;
- -n|--news)
- action="news"
- shift
- ;;
- --text-card)
- action="text-card"
- shift
- ;;
- --news-card)
- action="news-card"
- shift
- ;;
- -@)
- mention="$2"
- shift 2
- ;;
- --config)
- setup_webhook_key
- exit 0
- ;;
- --test)
- test_connection
- exit 0
- ;;
- -h|--help)
- show_help
- exit 0
- ;;
- *)
- print_error "未知选项: $1"
- show_help
- exit 1
- ;;
- esac
- done
-
- # 如果没有参数,显示交互式菜单
- if [ -z "$action" ]; then
- while true; do
- show_menu
- read choice
- echo ""
-
- case $choice in
- 1)
- echo -n "请输入文本内容: "
- read text_input
- echo -n "是否@某人(可选,直接回车跳过): "
- read mention_input
- send_text "$text_input" "$mention_input"
- ;;
- 2)
- echo -n "请输入文件路径: "
- read file_input
- send_file "$file_input"
- ;;
- 3)
- echo -n "请输入图片路径: "
- read image_input
- send_image "$image_input"
- ;;
- 4)
- echo -n "请输入语音文件路径: "
- read voice_input
- send_voice "$voice_input"
- ;;
- 5)
- echo -n "请输入文本内容: "
- read text_input
- echo -n "请输入文件路径: "
- read file_input
- send_combo "$text_input" "$file_input"
- ;;
- 6)
- echo -n "请输入Markdown内容: "
- read markdown_input
- send_markdown "$markdown_input"
- ;;
- 7)
- interactive_news
- ;;
- 8)
- interactive_text_card
- ;;
- 9)
- interactive_news_card
- ;;
- 10)
- test_connection
- ;;
- 11)
- setup_webhook_key
- setup_urls
- ;;
- 12)
- print_info "再见!"
- break
- ;;
- *)
- print_error "无效选择,请重新输入"
- ;;
- esac
- echo ""
- echo "按任意键继续..."
- read -n 1
- clear
- done
- exit 0
- fi
-
- # 执行相应的操作
- case $action in
- "text")
- send_text "$text_content" "$mention"
- ;;
- "file")
- send_file "$file_path"
- ;;
- "image")
- send_image "$file_path"
- ;;
- "voice")
- send_voice "$file_path"
- ;;
- "combo")
- send_combo "$text_content" "$file_path" "$mention"
- ;;
- "markdown")
- send_markdown "$markdown_content"
- ;;
- "news")
- interactive_news
- ;;
- "text-card")
- interactive_text_card
- ;;
- "news-card")
- interactive_news_card
- ;;
- esac
- }
- # 运行主程序
- main "$@"
|