企业微信——发送消息的bash shell脚本

#!/bin/bash
#如果携带4个参数,$1是附件文件,文件不存在或此处为空,则会只以文本消息发送,反之会发送文本消息和文本文件消息
#$2是应用ID
#$3是用户ID
#$4是文本消息内容
#如果携带3个参数, $1是应用ID
#$2是用户ID
#$3是文本消息内容
if [[ $# -eq 4 ]]; then
	_UpLoadFile_="$1"
fi
CropID=''
Secret=''
GURL="https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$CropID&corpsecret=$Secret"

Gtoken=$(/usr/bin/curl -s -G $GURL | awk -F'"' '{print $10}')

MUPURL="https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=$Gtoken&type=file"
PURL="https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$Gtoken"
if [[ -f "$_UpLoadFile_" ]]; then
	newfilename="$(echo "$(md5sum $_UpLoadFile_ | awk '{print $1}').$(date +%s%N).62e3f7499c2e8aa5af6fe58e0cecdaa3" | md5sum | awk '{print $1}').txt"
	/bin/cp -f $_UpLoadFile_ ./$newfilename
	media_id="$(curl -s --form "file=@$newfilename" "$MUPURL" 2>/dev/null | egrep -o "\"media_id\":\"[^\"]+\"" | awk -F'"' '{print $4}')"
	if [[ -z "$media_id" ]]; then
		media_id="$(curl -s --form "file=@$newfilename" "$MUPURL" 2>/dev/null | egrep -o "\"media_id\":\"[^\"]+\"" | awk -F'"' '{print $4}')"
	fi
fi
function body_file() {
	local int AppID=$1
	local UserID="$2"
	local MediaID="$3"
	local Msg="$(echo "$@" | awk '{$1="";$2="";$3="";printf"%s\n",$0}')"
	printf '{'
	printf '"touser": '"\"$UserID\""","
	printf '"toparty": '"\"\""","
	printf '"totag": '"\"\""","
	printf '"toall" : 0,'
	printf '"msgtype": "file",'
	printf '"agentid": '"\"$AppID\""","
	printf '"file" : {'
	printf '"media_id" : '"\"$MediaID\""
	printf '},'
	printf '"safe":"0"'
	printf '}'

}
function body_text() {
	local int AppID=$1
	local UserID="$2"
	local Msg="$(echo "$@" | awk '{$1="";$2="";printf"%s\n",$0}')"
	printf '{'
	printf '"touser": '"\"$UserID\""","
	printf '"toparty": '"\"$PartyID\""","
	printf '"msgtype": "text",'
	printf '"agentid": '"\"$AppID\""","
	printf '"text": {'
	printf '"content": '
	echo -e "\"$Msg\""
	printf '},'
	printf '"safe":"0"'
	printf '}'

}
nginx_proxys=(-)
for ((i = 0; i < ${#nginx_proxys[@]}; i++)); do
	proxy=${nginx_proxys[$i]}
	case $proxy in
	-)
		if [[ -n "$media_id" ]]; then
			http_code=$(curl --connect-timeout 3 --no-keepalive --retry 3 -w '%{http_code}\n' -s -o /dev/null --data-ascii "$(body_text "$2" "$3" "$4\n消息文本附件:\n$newfilename")" $PURL 2>/dev/null)
			http_code=$(curl --connect-timeout 3 --no-keepalive --retry 3 -w '%{http_code}\n' -s -o /dev/null --data-ascii "$(body_file "$2" "$3" "$media_id")" $PURL 2>/dev/null)
		else
			http_code=$(curl --connect-timeout 3 --no-keepalive --retry 3 -w '%{http_code}\n' -s -o /dev/null --data-ascii "$(body_text "$1" "$2" "$3")" $PURL 2>/dev/null)
		fi
		;;
	*)
		if [[ -n "$media_id" ]]; then
			http_code=$(curl -x $proxy --connect-timeout 3 --no-keepalive --retry 3 -w '%{http_code}\n' -s -o /dev/null --data-ascii "$(body_text "$2" "$3" "$4\n消息文本附件:\n$newfilename")" $PURL 2>/dev/null)
			http_code=$(curl -x $proxy --connect-timeout 3 --no-keepalive --retry 3 -w '%{http_code}\n' -s -o /dev/null --data-ascii "$(body_file "$2" "$3" "$media_id")" $PURL 2>/dev/null)
		else
			http_code=$(curl -x $proxy --connect-timeout 3 --no-keepalive --retry 3 -w '%{http_code}\n' -s -o /dev/null --data-ascii "$(body_text "$1" "$2" "$3")" $PURL 2>/dev/null)
		fi
		;;
	esac
	[[ $http_code == 200 ]] && break
done
#!/bin/bash
##############################################################################
# 脚本名称: wechat_work_msg.sh
# 脚本功能: 企业微信消息发送脚本(支持文本消息/文本+文件附件)
# 使用说明:
#   1. 先配置CropID(企业ID)和Secret(应用Secret)
#   2. 两种调用方式:
#      - 仅文本消息: ./脚本名 应用ID 用户ID 文本内容
#      - 文本+文件:  ./脚本名 附件文件 应用ID 用户ID 文本内容
# 依赖工具: curl、awk、md5sum
# 注意事项:
#   - 附件文件大小需符合企业微信限制(文件类型≤20MB)
#   - 确保服务器能访问企业微信API(https://qyapi.weixin.qq.com)
##############################################################################

# ======================== 配置项(必填!请替换为实际值) ========================
CropID="your_corp_id"          # 企业微信企业ID(从企业微信后台获取)
Secret="your_app_secret"       # 企业微信应用Secret(从应用管理后台获取)
TEMP_DIR="/tmp/wechat_msg"     # 临时文件存储目录
LOG_FILE="/var/log/wechat_msg.log" # 运行日志文件

# ======================== 日志函数 ========================
log() {
    local level=$1
    local msg=$2
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] [$level] $msg" >> ${LOG_FILE}
}

# ======================== 初始化 ========================
# 创建临时目录
mkdir -p ${TEMP_DIR}
chmod 700 ${TEMP_DIR}

# 初始化变量
_UpLoadFile_=""
media_id=""
newfilename=""
http_code=""
errcode=""

# ======================== 参数解析 ========================
# 检查参数数量
if [[ $# -ne 3 && $# -ne 4 ]]; then
    log "ERROR" "参数数量错误!用法:"
    log "ERROR" "  仅文本: $0 <应用ID> <用户ID> <文本内容>"
    log "ERROR" "  文本+文件: $0 <附件文件> <应用ID> <用户ID> <文本内容>"
    echo "参数数量错误!"
    echo "用法:"
    echo "  仅文本: $0 <应用ID> <用户ID> <文本内容>"
    echo "  文本+文件: $0 <附件文件> <应用ID> <用户ID> <文本内容>"
    exit 1
fi

# 解析参数
if [[ $# -eq 4 ]]; then
    _UpLoadFile_="$1"
    AppID="$2"
    UserID="$3"
    MsgContent="$4"
    log "INFO" "模式:文本+文件 | 附件文件: $_UpLoadFile_ | 应用ID: $AppID | 用户ID: $UserID"
elif [[ $# -eq 3 ]]; then
    AppID="$1"
    UserID="$2"
    MsgContent="$3"
    log "INFO" "模式:仅文本 | 应用ID: $AppID | 用户ID: $UserID"
fi

# ======================== 获取企业微信Access Token ========================
GURL="https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${CropID}&corpsecret=${Secret}"
log "INFO" "获取Token请求URL: ${GURL}"

# 调用API获取token
token_response=$(/usr/bin/curl -s --connect-timeout 5 --retry 2 ${GURL})
Gtoken=$(echo ${token_response} | awk -F'"' '{print $10}')

# 校验token是否获取成功
if [[ -z ${Gtoken} || ${Gtoken} == "null" ]]; then
    log "ERROR" "获取Token失败!响应内容: ${token_response}"
    echo "获取企业微信Token失败!"
    exit 1
fi
log "INFO" "获取Token成功: ${Gtoken:0:10}..." # 仅打印前10位,避免泄露

# ======================== 上传附件文件(如有) ========================
MUPURL="https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=${Gtoken}&type=file"
if [[ -f "${_UpLoadFile_}" && -r "${_UpLoadFile_}" ]]; then
    log "INFO" "开始上传附件文件: ${_UpLoadFile_}"
    
    # 生成唯一临时文件名(避免冲突)
    newfilename="${TEMP_DIR}/$(echo "$(md5sum ${_UpLoadFile_} | awk '{print $1}').$(date +%s%N)" | md5sum | awk '{print $1}').txt"
    
    # 复制文件到临时目录
    /bin/cp -f ${_UpLoadFile_} ${newfilename}
    if [[ $? -ne 0 ]]; then
        log "ERROR" "复制附件文件失败: ${_UpLoadFile_} -> ${newfilename}"
        echo "复制附件文件失败!"
        exit 1
    fi
    
    # 上传文件到企业微信
    upload_response=$(curl -s --connect-timeout 10 --retry 2 --form "file=@${newfilename}" "${MUPURL}")
    media_id=$(echo ${upload_response} | egrep -o "\"media_id\":\"[^\"]+\"" | awk -F'"' '{print $4}')
    
    # 第一次上传失败,重试一次
    if [[ -z ${media_id} ]]; then
        log "WARN" "第一次上传文件失败,重试!响应: ${upload_response}"
        sleep 1
        upload_response=$(curl -s --connect-timeout 10 --retry 2 --form "file=@${newfilename}" "${MUPURL}")
        media_id=$(echo ${upload_response} | egrep -o "\"media_id\":\"[^\"]+\"" | awk -F'"' '{print $4}')
    fi
    
    # 校验上传结果
    if [[ -z ${media_id} ]]; then
        log "ERROR" "文件上传失败!响应内容: ${upload_response}"
        echo "附件文件上传失败!"
        # 清理临时文件
        rm -f ${newfilename}
        exit 1
    fi
    log "INFO" "文件上传成功,media_id: ${media_id:0:10}..."
else
    if [[ -n "${_UpLoadFile_}" ]]; then
        log "WARN" "附件文件不存在或不可读: ${_UpLoadFile_},将仅发送文本消息"
        echo "附件文件不存在或不可读,将仅发送文本消息!"
    fi
fi

# ======================== 构造消息JSON函数 ========================
# 构造文件消息JSON
body_file() {
    local AppID=$1
    local UserID=$2
    local MediaID=$3
    
    cat << EOF
{
    "touser": "${UserID}",
    "toparty": "",
    "totag": "",
    "msgtype": "file",
    "agentid": "${AppID}",
    "file": {
        "media_id": "${MediaID}"
    },
    "safe": "0"
}
EOF
}

# 构造文本消息JSON
body_text() {
    local AppID=$1
    local UserID=$2
    local Msg=$3
    
    # 转义JSON特殊字符(\、"、换行)
    Msg=$(echo "${Msg}" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\n/\\n/g')
    
    cat << EOF
{
    "touser": "${UserID}",
    "toparty": "",
    "totag": "",
    "msgtype": "text",
    "agentid": "${AppID}",
    "text": {
        "content": "${Msg}"
    },
    "safe": "0"
}
EOF
}

# ======================== 发送消息 ========================
PURL="https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${Gtoken}"
log "INFO" "消息发送URL: ${PURL}"

# 代理配置(如需使用代理,修改此处)
nginx_proxys=(-)

# 遍历代理发送消息
for proxy in "${nginx_proxys[@]}"; do
    # 构建curl参数(是否使用代理)
    curl_proxy=""
    if [[ ${proxy} != "-" ]]; then
        curl_proxy="-x ${proxy}"
        log "INFO" "使用代理: ${proxy}"
    fi
    
    # 发送消息逻辑
    if [[ -n "${media_id}" ]]; then
        # 1. 发送文本消息(包含附件说明)
        text_msg="${MsgContent}\n消息文本附件:\n$(basename ${_UpLoadFile_})"
        text_json=$(body_text "${AppID}" "${UserID}" "${text_msg}")
        log "INFO" "发送文本消息JSON: ${text_json:0:100}..."
        
        send_response=$(curl ${curl_proxy} --connect-timeout 5 --no-keepalive --retry 3 -w 'HTTP_CODE:%{http_code}\n' -s -o - --data-ascii "${text_json}" ${PURL})
        http_code=$(echo ${send_response} | grep -o "HTTP_CODE:[0-9]*" | awk -F':' '{print $2}')
        errcode=$(echo ${send_response} | egrep -o "\"errcode\":[0-9]+" | awk -F':' '{print $2}')
        
        log "INFO" "文本消息发送响应:HTTP_CODE=${http_code}, errcode=${errcode}"
        
        # 2. 发送文件消息
        file_json=$(body_file "${AppID}" "${UserID}" "${media_id}")
        log "INFO" "发送文件消息JSON: ${file_json:0:100}..."
        
        send_response=$(curl ${curl_proxy} --connect-timeout 5 --no-keepalive --retry 3 -w 'HTTP_CODE:%{http_code}\n' -s -o - --data-ascii "${file_json}" ${PURL})
        http_code=$(echo ${send_response} | grep -o "HTTP_CODE:[0-9]*" | awk -F':' '{print $2}')
        errcode=$(echo ${send_response} | egrep -o "\"errcode\":[0-9]+" | awk -F':' '{print $2}')
        
        log "INFO" "文件消息发送响应:HTTP_CODE=${http_code}, errcode=${errcode}"
    else
        # 仅发送文本消息
        text_json=$(body_text "${AppID}" "${UserID}" "${MsgContent}")
        log "INFO" "发送文本消息JSON: ${text_json:0:100}..."
        
        send_response=$(curl ${curl_proxy} --connect-timeout 5 --no-keepalive --retry 3 -w 'HTTP_CODE:%{http_code}\n' -s -o - --data-ascii "${text_json}" ${PURL})
        http_code=$(echo ${send_response} | grep -o "HTTP_CODE:[0-9]*" | awk -F':' '{print $2}')
        errcode=$(echo ${send_response} | egrep -o "\"errcode\":[0-9]+" | awk -F':' '{print $2}')
        
        log "INFO" "文本消息发送响应:HTTP_CODE=${http_code}, errcode=${errcode}"
    fi
    
    # 校验发送结果(errcode=0表示成功)
    if [[ ${http_code} == 200 && ${errcode} == 0 ]]; then
        log "INFO" "消息发送成功!"
        echo "消息发送成功!"
        break
    else
        log "WARN" "消息发送失败,尝试下一个代理(HTTP_CODE=${http_code}, errcode=${errcode})"
    fi
done

# ======================== 清理临时文件 ========================
if [[ -f "${newfilename}" ]]; then
    rm -f "${newfilename}"
    log "INFO" "清理临时文件: ${newfilename}"
fi

# ======================== 最终结果校验 ========================
if [[ ${http_code} != 200 || ${errcode} != 0 ]]; then
    log "ERROR" "消息发送最终失败!HTTP_CODE=${http_code}, errcode=${errcode}"
    echo "消息发送失败!"
    exit 1
fi

exit 0
Categories: 系统运维