286 lines
8.5 KiB
Lua
286 lines
8.5 KiB
Lua
local http = require("httpclient")
|
|
local codec = require("fastweb.codec")
|
|
local base64 = require("base64")
|
|
local openssl = require("openssl")
|
|
local M = {}
|
|
|
|
local url_get_access_token = "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
|
|
local url_send_message = "https://qyapi.weixin.qq.com/cgi-bin/message/send"
|
|
local url_get_userid_by_mobile = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserid"
|
|
local url_get_userids = "https://qyapi.weixin.qq.com/cgi-bin/user/list_id"
|
|
|
|
|
|
-- 验证签名
|
|
M.verify_signature = function(token,timestamp,nonce,echostr)
|
|
local params = {token, timestamp, nonce, echostr}
|
|
table.sort(params, function(a, b) return tostring(a) < tostring(b) end)
|
|
return string.lower(codec.sha1(table.concat(params)))
|
|
end
|
|
|
|
-- 解密数据
|
|
M.decrypt_data = function(qywx_aeskey,data)
|
|
|
|
-- 16随机数+4字节消息长度+消息体+接收ID
|
|
local function parse_decrypted_msg(data)
|
|
-- data: string, decrypted_echostr
|
|
if #data < 20 then
|
|
return nil, "data too short"
|
|
end
|
|
-- 跳过16字节随机数
|
|
local msg_len_bytes = data:sub(17, 20)
|
|
local b1, b2, b3, b4 = msg_len_bytes:byte(1, 4)
|
|
local msg_len = b1 * 2^24 + b2 * 2^16 + b3 * 2^8 + b4
|
|
local msg_start = 21
|
|
local msg_end = 20 + msg_len
|
|
local msg = data:sub(msg_start, msg_end)
|
|
return msg
|
|
end
|
|
|
|
local cipher = openssl.cipher.new("aes-256-cbc")
|
|
cipher:init(base64.decode(qywx_aeskey), "0123456789abcdef", false)
|
|
local plaintext = cipher:update(data)
|
|
plaintext = parse_decrypted_msg(plaintext)
|
|
return plaintext
|
|
end
|
|
|
|
|
|
-- 获取access_token
|
|
M.get_access_token = function(corpid,corpsecret)
|
|
|
|
local url = url_get_access_token .. "?corpid=" .. corpid .. "&corpsecret=" .. corpsecret
|
|
local result,err = http.get(url,{
|
|
["Content-Type"] = "application/json"
|
|
})
|
|
if not result then
|
|
return false,err
|
|
end
|
|
local data = cjson.decode(err)
|
|
if data.errcode == 0 then
|
|
return true,data.access_token
|
|
end
|
|
return false,data.errmsg
|
|
end
|
|
-- 获取userid
|
|
M.get_userid_by_mobile = function(access_token,mobile)
|
|
local url = url_get_userid_by_mobile .. "?access_token=" .. access_token .. "&mobile=" .. mobile
|
|
local result,err = http.post(url,{
|
|
["Content-Type"] = "application/json"
|
|
},cjson.encode({
|
|
mobile = mobile
|
|
}))
|
|
if not result then
|
|
return false,err
|
|
end
|
|
local data = cjson.decode(err)
|
|
if data.errcode == 0 then
|
|
return true,data.userid
|
|
end
|
|
return false,data.errmsg
|
|
end
|
|
-- 获取用户列表
|
|
M.get_userids = function(access_token)
|
|
local url = url_get_userids .. "?access_token=" .. access_token
|
|
local result,err = http.post(url,{
|
|
["Content-Type"] = "application/json"
|
|
},cjson.encode({
|
|
limit = 1000
|
|
}))
|
|
if not result then
|
|
return false,err
|
|
end
|
|
local data = cjson.decode(err)
|
|
if data.errcode == 0 then
|
|
return true,data.dept_user
|
|
end
|
|
return false,data.errmsg
|
|
end
|
|
-- 上传临时文件
|
|
M.upload_temp_file = function(access_token,data,type_str)
|
|
local function make_upload_temp_data(data)
|
|
local boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"
|
|
local body = ""
|
|
|
|
local filename = ""
|
|
if type_str == "image" then
|
|
filename = tostring(os.time())..".png"
|
|
elseif type_str == "video" then
|
|
filename = tostring(os.time())..".mp4"
|
|
end
|
|
|
|
-- 构造 multipart 数据
|
|
body = body .. "--" .. boundary .. "\r\n"
|
|
body = body .. "Content-Disposition: form-data; name=\"media\"; filename=\""..filename.."\"\r\n"
|
|
body = body .. "Content-Type: application/octet-stream\r\n\r\n" .. data .. "\r\n"
|
|
body = body .. "--" .. boundary .. "--\r\n"
|
|
|
|
return body
|
|
end
|
|
local body = make_upload_temp_data(data)
|
|
local url = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=" .. access_token.."&type="..type_str
|
|
local result,err = http.post(url,{
|
|
["Content-Type"] = "multipart/form-data; boundary=--WebKitFormBoundary7MA4YWxkTrZu0gW",
|
|
["Content-Length"] = #body
|
|
},body)
|
|
if not result then
|
|
return false,err
|
|
end
|
|
local data = cjson.decode(err)
|
|
if data.errcode == 0 then
|
|
return true,data.media_id
|
|
end
|
|
return false,data.errmsg
|
|
end
|
|
-- 发送消息
|
|
M.send_message = function(access_token,userid,agentid,message)
|
|
local url = url_send_message .. "?access_token=" .. access_token
|
|
local result,err = http.post(url,{
|
|
["Content-Type"] = "application/json"
|
|
},cjson.encode({
|
|
touser = userid,
|
|
msgtype = "text",
|
|
agentid = agentid,
|
|
text = {
|
|
content = message
|
|
}
|
|
}))
|
|
if not result then
|
|
return false,err
|
|
end
|
|
local data = cjson.decode(err)
|
|
if data.errcode == 0 then
|
|
return true
|
|
end
|
|
return false,data.errmsg
|
|
end
|
|
-- 发送图片消息
|
|
M.send_image_message = function(access_token,userid,agentid,image_filepath)
|
|
local file = io.open(image_filepath, "rb")
|
|
if not file then
|
|
return false, "cannot open file: " .. tostring(image_filepath)
|
|
end
|
|
local data = file:read("*a")
|
|
file:close()
|
|
local ok, media_id = M.upload_temp_file(access_token, data,"image")
|
|
if not ok then
|
|
return false, "upload_temp_file_failed:"..media_id
|
|
end
|
|
local url = url_send_message .. "?access_token=" .. access_token
|
|
local result, err = http.post(url, {
|
|
["Content-Type"] = "application/json"
|
|
}, cjson.encode({
|
|
touser = userid,
|
|
msgtype = "image",
|
|
agentid = agentid,
|
|
image = {
|
|
media_id = media_id,
|
|
}
|
|
}))
|
|
if not result then
|
|
return false, err
|
|
end
|
|
local resp = cjson.decode(err)
|
|
if resp.errcode == 0 then
|
|
return true
|
|
end
|
|
return false, resp.errmsg
|
|
end
|
|
-- 发送视频消息
|
|
M.send_video_message = function(access_token,userid,agentid,title,video_filepath)
|
|
local file = io.open(video_filepath, "rb")
|
|
if not file then
|
|
return false, "cannot open file: " .. tostring(video_filepath)
|
|
end
|
|
local data = file:read("*a")
|
|
file:close()
|
|
local ok, media_id = M.upload_temp_file(access_token, data,"video")
|
|
if not ok then
|
|
return false, "upload_temp_file_failed:"..media_id
|
|
end
|
|
local url = url_send_message .. "?access_token=" .. access_token
|
|
local result, err = http.post(url, {
|
|
["Content-Type"] = "application/json"
|
|
}, cjson.encode({
|
|
touser = userid,
|
|
msgtype = "video",
|
|
agentid = agentid,
|
|
video = {
|
|
media_id = media_id,
|
|
title = title
|
|
}
|
|
}))
|
|
if not result then
|
|
return false, err
|
|
end
|
|
local resp = cjson.decode(err)
|
|
if resp.errcode == 0 then
|
|
return true
|
|
end
|
|
return false, resp.errmsg
|
|
end
|
|
-- 发送卡片消息
|
|
M.send_card_message = function(access_token,userid,agentid,message)
|
|
local url = url_send_message .. "?access_token=" .. access_token
|
|
local result,err = http.post(url,{
|
|
["Content-Type"] = "application/json"
|
|
},cjson.encode({
|
|
touser = userid,
|
|
msgtype = "news",
|
|
agentid = agentid,
|
|
news = {
|
|
articles = {
|
|
message
|
|
}
|
|
}
|
|
}))
|
|
if not result then
|
|
return false,err
|
|
end
|
|
local data = cjson.decode(err)
|
|
if data.errcode == 0 then
|
|
return true
|
|
end
|
|
return false,data.errmsg
|
|
end
|
|
-- 发送卡片消息
|
|
M.send_template_card_message = function(access_token,userid,agentid,message)
|
|
local url = url_send_message .. "?access_token=" .. access_token
|
|
local result,err = http.post(url,{
|
|
["Content-Type"] = "application/json"
|
|
},cjson.encode({
|
|
touser = userid,
|
|
msgtype = "template_card",
|
|
agentid = agentid,
|
|
template_card = message
|
|
}))
|
|
if not result then
|
|
return false,err
|
|
end
|
|
local data = cjson.decode(err)
|
|
if data.errcode == 0 then
|
|
return true
|
|
end
|
|
return false,data.errmsg
|
|
end
|
|
-- 发送MMD消息
|
|
M.send_markdown_message = function(access_token,userid,agentid,message)
|
|
local url = url_send_message .. "?access_token=" .. access_token
|
|
local result,err = http.post(url,{
|
|
["Content-Type"] = "application/json"
|
|
},cjson.encode({
|
|
touser = userid,
|
|
msgtype = "markdown",
|
|
agentid = agentid,
|
|
markdown = {
|
|
content = message
|
|
}
|
|
}))
|
|
if not result then
|
|
return false,err
|
|
end
|
|
local data = cjson.decode(err)
|
|
if data.errcode == 0 then
|
|
return true
|
|
end
|
|
return false,data.errmsg
|
|
end
|
|
return M |