一、背景
由于公司客服系统使用的udesk,办公通讯工具是钉钉。客服人员与技术人员需要同时登录两个系统来工作,加上周六日工单接收不及时,用户问题得不到及时解决,客服经理便想怎么高效的让技术人员及时看到并处理工单,同时客服人员也能及时看到工单的处理进度。
刚开始我们在udesk工单管理后台配置触发器,使用udesk本身的功能来触发消息通知,通知邮件。邮件使用钉邮,钉邮接收到邮件,钉钉会有提示。
但是邮件经常有延迟的情况,便打算用钉钉群机器人了。
二、思路
一开始,我们在udesk中添加提醒目标,URL为钉钉机器人地址(如何创建钉钉机器人就不赘述了)
然后通过触发器,添加提醒动作。但是很多信息拿不到,比如工单号、工单标题等,都不能推送到钉钉。
询问udesk方后,建议使用API来实现。官方文档 http://www.udesk.cn/doc/apiv2/tickets/ 可以看到大部分都是get请求,使用python处理起来还是很方便的。
要求达到的目的是:
1.不管是新的工单还是工单有更新第一时间推送消息到钉钉群。
2.消息包括:工单号、工单标题、工单内容、创建时间等。如工单有回复,显示完整信息,包括图片。
3.消息还需包括对应的工单链接。
三、方法
整个代码分三部分:
1.登录udesk,拿到token,使用token进行签名
2.udesk创建过滤器,获取某过滤器下的最新工单id。(由于公司业务线路较多,如果直接拿工单列表,很难筛选出对应的工单,而且还有可能漏掉。)拿到工单id后,查看工单详情,编辑相关信息,交给钉钉机器人
3.钉钉机器人推送的json使用markdown类型,以便展示图片等。
代码如下(粘贴过来的代码格式有问题,请注意制表符):
第一部分:
import json
import requests
import time
import random
from dingtalkchatbot.chatbotimport DingtalkChatbot
# 获取 鉴权 Token
def login(host,email,password):
headers = {
"content-type":"application/json",
}
body = {
"email":email,
"password":password
}
login_url = host +"open_api_v1/log_in"
response = requests.post(url=login_url, headers=headers, data=json.dumps(body))
# print(response.status_code)
# print(json.dumps(response.json(), indent=2))
token = response.json()["open_api_auth_token"]
# print(token)
return token
# 签名方法,字符串sha256加密
def createSign(email,token,timestamp,nonce,sign_version):
sign_params = email +"&" + token +"&" + timestamp +"&" + nonce +"&" + sign_version
signature = hashlib.sha256()
signature.update(sign_params.encode(encoding='UTF8'))
# print(signature.hexdigest())
return signature.hexdigest()
第二部分:
#通用API获取response,first_word和last_word均不填即获取工单列表
def get_response(host,first_word,email,token,last_word):
timestamp =str(int(time.time()))
hex_str ="".join([choice("0123456789abcdef")for i in range(32)])
hex_list =list(hex_str)
hex_list.insert(20, "-")
hex_list.insert(16, "-")
hex_list.insert(12, "-")
hex_list.insert(8, "-")
nonce ="".join(hex_list)
sign_version ="v2"
sign = createSign(email, token, timestamp, nonce, sign_version)
get_url = host +"open_api_v1/tickets/" +first_word+"?email=" + email +"×tamp=" + timestamp +"&sign=" + sign +"&nonce=" + nonce +"&sign_version=" + sign_version+last_word
# print(info_url)
response = requests.get(url=get_url)
# print(response.status_code)
# print(json.dumps(response.json(), indent=2))
return response.json()
# 获取某个工单过滤器下的最后一个工单
def tickets_in_filter(host,email,token,filter_id):
udesk_response = get_response(host, "tickets_in_filter", email, token, "&filter_id="+filter_id)
ticket_id = udesk_response["contents"][0]["ticket"]["id"]if udesk_response["contents"][0]["ticket"]["id"]else None
# print(ticket_id)
return ticket_id
# 获取工单详情
def list_info(host,email,token,ticket_id):
udesk_response = get_response(host,"detail",email,token,"&id="+str(ticket_id))
ticket_id = udesk_response["ticket"]["id"]if udesk_response["ticket"]["subject"]else ""
ticket_subject = udesk_response["ticket"]["subject"]if udesk_response["ticket"]["subject"]else ""
ticket_content = udesk_response["ticket"]["content"]if udesk_response["ticket"]["content"]else ""
# 工单内容展示图片
img_place =str(ticket_content).find("img")
if img_place >0:
ticket_content =str(ticket_content).replace('', ')')
ticket_attachments = udesk_response["ticket"]["attachments"]
if len(ticket_attachments) >0:
ticket_content = ticket_content +" 注意有附件!"
ticket_status = udesk_response["ticket"]["status"]if udesk_response["ticket"]["status"]else ""
ticket_priority = udesk_response["ticket"]["priority"]if udesk_response["ticket"]["priority"]else ""
if ticket_priority =="紧急":
ticket_priority =" >优先级:" + ticket_priority +"!!!"
if ticket_priority =="高":
ticket_priority =" >优先级:" + ticket_priority +"!!!"
if ticket_priority =="标准":
ticket_priority =" >优先级:__" + ticket_priority +"__"
ticket_assignee_name = udesk_response["ticket"]["assignee_name"]if udesk_response["ticket"]["assignee_name"]else ""
ticket_create_time = udesk_response["ticket"]["created_at"]if udesk_response["ticket"]["created_at"]else ""
# 处理时间格式
ticket_create_time = ticket_create_time.replace('T', ' ').replace('Z', '').split('.')[0]
ticket_update_time = udesk_response["ticket"]["updated_at"]if udesk_response["ticket"]["updated_at"]else ""
ticket_text ="## 标题:[" + ticket_subject +"](https://bitz.s2.udesk.cn/entry/ticket/show/" +str(ticket_id) +")\n- - -\n" + ticket_priority +"\n- - -\n >内容:__" + ticket_content +"__ \n- - -\n >受理人:__" + ticket_assignee_name +"__ \n- - -\n >创建时间:__" + ticket_create_time +"__ \n- - -\n"
# print(ticket_text)
return ticket_text
# 获取工单最新回复
def ticket_replies(host,email,token,ticket_id):
udesk_response = get_response(host, str(ticket_id)+"/replies", email, token, "")
reply_text =""
if len(udesk_response["replies"])>0:
reply_content = udesk_response["replies"][0]["content"]
img_place =str(reply_content).find("img")
if img_place >0:
reply_content =str(reply_content).replace('', ')')
# print(reply_content)
reply_time = udesk_response["replies"][0]["created_at"]
reply_user = udesk_response["replies"][0]["author"]["nick_name"]
reply_text =" >回复人员:__"+reply_user+"__\n- - -\n >回复内容:__"+ reply_content+"__"
# print(reply_text)
return reply_text
# 钉钉消息
def dingtalk(webhook,text):
xiaoding = DingtalkChatbot(webhook)
markdown_text = {
"msgtype":"markdown",
"markdown": {
"title":"您有新的工单!!!",
"text":text
},
"at": {
"atMobiles": [
"18xx6"
],
"isAtAll":True
}
}
xiaoding.send_markdown(title = markdown_text["markdown"]["title"],text = markdown_text["markdown"]["text"])
主程序
udesk_host ="https://demo.s2.udesk.cn/"
email ="XXXXXX"
password ="XXXXXXX"
test1_webhook = "https://oapi.dingtalk.com/robot/send?access_token=d78dXXXX"
test2_webhook = "https://oapi.dingtalk.com/robot/send?access_token=d78dXXXX"
test3_webhook = "https://oapi.dingtalk.com/robot/send?access_token=d78dXXXX"
token = login(udesk_host,email,password)
ticket_id1,ticket_id2=0,0
try:
filter_list = ["12345","12xx","323x4""]
webhook_list = [test1_webhook,test2_webhook,test3_webhook]
ticket_id,ticket_text,ticket_replay = [],[],[]
for i in range(len(filter_list)):
ticket_id.append(tickets_in_filter(udesk_host, email, token, filter_list[i]))
ticket_text.append(list_info(udesk_host, email, token, ticket_id[i]))
ticket_replay.append(ticket_replies(udesk_host, email, token, ticket_id[i]))
except Exception as e:
print("First Exception:")
print(str(e))
while True:
time.sleep(10)# 10秒查询一次
try:
ticket_id_new, ticket_text_new, ticket_replay_new = [], [], []
for i in range(len(filter_list)):
ticket_id_new.append(tickets_in_filter(udesk_host, email, token, filter_list[i]))
ticket_text_new.append(list_info(udesk_host, email, token, ticket_id_new[i]))
ticket_replay_new.append(ticket_replies(udesk_host, email, token, ticket_id_new[i]))
if ticket_id_new[i] != ticket_id[i]or ticket_text_new[i]+ticket_replay_new[i] != ticket_text[i]+ticket_replay[i]:
dingtalk(webhook_list[i], ticket_text_new[i] + ticket_replay_new[i])
ticket_id[i] = ticket_id_new[i]
ticket_text[i] = ticket_text_new[i]
ticket_replay[i] = ticket_replay_new[i]
time.sleep(1)# 防止 429 错误 api接口超过请求限制
except Exception as e:
print("Exception:\n")
print(str(e))```