python3 实现自动化 IM 回复用户

公司使用逸创的工单系统,平时也有企业用户通过在线 IM 接入,老大给我们组定了个KPI,5分钟内响应企业用户问题,包括在线 IM 和工单,工单之前写过一个自动化脚本(现在回过头看我几个月前写的代码真的是垃圾,抽空重构下),工单嘛,蛮简单的,反正逸创有接口,直接调用就行,但是 IM 我发现他们对外没提供接口,这个就尴尬了。

我的需求是 在用户接入 IM 后 能够先1分钟内响应他一下,避免 KPI 超时,如果问的是一些常见式的固定问题就正确匹配下用户的回复然后去回复。

直到某一天,我好奇的抓个 HTTP 请求看看他们在线 IM 到底是怎么请求和响应数据的,顺便把他的请求 URL 和 Request 头直接复制到 postman 去跑一下,咦,居然能够发送消息。但是我怕里面带个 cookie 啥的,那我还得模拟登陆才行,仔细观察下发现里面根本就没有什么过期的值,全部都是固定的内容,包含当前账号的 ID 企业信息, token,这个 token 也是不会过期的。

既然如此,这个就好办了,直接构造一个请求去发送信息即可。

最近跟核心开发的老工程师学了点写代码的套路,啥套路呢,就是代码要写的整洁点。尽量不要写的非常难读,避免多层嵌套。

然后想到这里,我就想着这次写这个 IM 的代码一定不要跟上次一样了,怎么写呢。因为我根据用户的回复去判断怎么回复一些基础性问题,所以我之前的代码里面夹杂了太多的汉字,非常难看,这次我直接定义一个 json 文件,然后把正则和要回复的内容写进去,类似这样

 {
  "reply":[
      {
    "id":1,
    "status":"new",
    "source": "im",
    "rule":".{0,}((云通信|短信(签名|模板|审核|服务|平台|群发|包))|.{0,}验证码接入)",
    "msg":"您好,目前在线客服系统仅受理网易云基础服务咨询,网易云信相关咨询请您联系您的云信客户经理或者云信客服电话4009-000-123。也可以点击<a href='https://app.netease.im/index#/issue/submit'>云信工单系统</a> 跳转提交工单咨询。或登录控制台-找到产品与服务--通信与视频-点击进入,在左侧导航栏点击工单,提交您的问题。感谢您对网易云基础服务的支持!"
    },

    {
      "id":2,
      "status":"new",
      "source": "im",
      "rule":".{0,}(发票|开票)",
      "msg":"您好,根据您的问题,猜您可能想了解:<br> <strong>1.发票提交后多久寄出</strong><br>答:发票一般从您提交到开出需要5-7个工作日。<br><strong>2.为什么我刚充的钱发票管理里面不显示?</strong><br>答: 发票的金额需要是已经消费的金额才可以申请,并且本月的(预付费和后付费)消费要到次月2号才可以申请发票。<br><strong>3.开发票有最低金额限制吗?寄送发票免快递费吗?</strong><br>答: 开具发票目前暂无金额限制,寄送发票我们都是免费的。<br><strong>4.在哪里开发票?</strong><br>答:您可以在控制台找到业务支持-业务消费-发票管理,点击申请发票,或直接点击跳转到<a href='https://c.163yun.com/dashboard#/m/account/expense/invoice/' target='_blank'>发票管理</a> 页面。<br><strong>5.怎么开具增值税专用发票?</strong><br>答:通过企业认证和专票信息审核的用户可以开具增值税专用发票;<br><strong>6. 为什么我不能开具发票?</strong><br>答:可能出现的情况:1.账户欠费情况下不能申请发票。2.本月消费需要次月初才能开具发票。3.您未使用网易云基础服务的产品,例如使用的是云信的服务,则需联系云信申请发票。<br><strong>7.开具发票需求注意什么?</strong><br>答:应国家税务总局要求,自2017年7月1日起,若开具增值税普通发票,企业类型必须具备税号,否则发票将无法用于企业报销。"
  },

   {
      "id":3,
      "status":"new",
      "source": "im",
      "rule":".{0,}(修改|更改)安全手机",
      "msg":"您好,因安全手机是用于云计算基础服务进行资源关键操作的安全验证的,如无法自助修改安全手机,请根据如下情况提交对应的资料。审核通过后可以协助您修改,我们会在 1-2 个工作日之内给您处理: <br><strong>对于个人用户,请提交以下资料:</strong><br> 1.请工单提交注册时的身份证反面照片(有国徽的那面)以及手持身份证正面(含个人照片的那面),并提供原安全手机号和需要修改的安全手机号<br> <strong>对于企业用户,需要您完善并打印以下模板内容,并加盖公司章拍照上传给我们:</strong>                 <br> 公司名称:xxx公司                         <br> 问题描述:因 xxx 原因无法自助修改安全手机,需要网易云协助修改安全手机。<br> 修改内容:<br> 原安全手机:xxx   修改为:xxx"
  },
    {
      "id": 4,
      "status": "new",
      "rule": "^没(了|问题了|有问题了)",
      "msg": "好的,那这边如果没有问题我稍后就关闭会话了哈,如您还有问题可以再次联系我们。感谢您对网易云的大力支持,祝您生活愉快!"
    }
  ]
}

然后读取这个文件去判断,这样代码就看着整洁多了,此外对于多重嵌套循环,直接判断后 return 掉,避免形成多重嵌套,导致易读性差。

我们看下逸创的发消息接口,里面包含了消息类型,消息和chat_id,另外还有发送消息的时间,这个时间是个 unix 时间戳精确到毫秒,然后请求头里面xxxx 的内容敏感,我这里隐去了。

def send_chat(msg_info, chat_id):
    url = "https://im2-agent.kf5.com/messages"
    unix_time = str(int(round(time.time() * 1000)))
    payload = {
        "message": {"msg": msg_info, "type": "chat.msg", "chat_id": chat_id, "token": None, "custom_id": unix_time,
                    "recalled": False}}
    headers = {
        'Accept': "application/json, text/javascript, */*; q=0.01",
        'Accept-Encoding': "gzip, deflate, br",
        'Accept-Language': "zh-CN,zh;q=0.9,en;q=0.8",
        'Connection': "keep-alive",
        'Content-Type': "application/json; charset=UTF-8",
        'DNT': "1",
        'Host': "im2-agent.kf5.com",
        'Origin': "https://xxxxx.kf5.com",
        'Referer': "https://xxxxx.kf5.com/agent/",
        'User-Agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
        'X-Agent-ID': "xxxxx",
        'X-Company-ID': "xxxx",
        'X-Token': "xxxxxxx",
        'Cache-Control': "no-cache",
    }
    response = requests.request("POST", url, data=json.dumps(payload), headers=headers)
    if response.status_code != 200:
        return
    if response.json()['message'] is not None:
        return True

然后读取 json 文件并进行判断

def read_imjson_config():
    with open('im.json', 'r') as f:
        data = json.load(f)
        status = [(d['status']) for d in data['reply']]
        rule = [(d['rule']) for d in data['reply']]
        msg =  [(d['msg']) for d in data['reply']]
        return [status,rule,msg]

def check_rule(msg_info,chat_id):
    read_json = read_imjson_config()
    status = read_json[0]
    rule_str = read_json[1]
    msg_str = read_json[2]
    for k,v in enumerate(status):
        rule = rule_str[k]
        msg = msg_str[k]
        if re.search(rule,msg_info):
            send_chat(msg,chat_id)

嗯,反正就是这个样子玩嘛,至少在我上厕所或者忙其他时间没看见工单的时候,这个工单响应时间不会超时,另外,用户接入后,我可以直接去做一些预先的准备工作,比如去查询用户的信息,了解下用户的基本情况后更快速的处理用户问题。

老实讲,技术支持这个工作本质上就是个高级点的客服,只不过比客服懂点技术。如果不让自己的工作有点价值有点意义,其实和普通客服也没啥本质区别。大多数情况下都是重复相同的内容。其实也不止技术支持啦,很多岗位其实都是一个萝卜一个坑。干的工作也大多数都没技术含量。都是日复一日的挖坑、填坑。

你以为开发就很高大上吗?其实大部分开发也是挖坑、填坑。只不过待遇确实要比我们好很多。但是人家那待遇2万,时薪也蛮低的吧,天天加班赶项目。况且也并不是所有开发都是2万以上月薪。

未来,像这种岗位估计 99% 都得被机器替代掉,或者只需要很少的人去完成这项工作。如果只是满足于当前这种手动处理问题效率低且质量差,来个用户手动回复工单,你的打字速度再快你有机器快啊。况且人工处理还容易打错字,而机器只要代码逻辑正确,基本不会回复错误的。一步一步按照指令去处理。

我已经意识到这一点了,所以我得想办法提升自己的价值,那就得学好编程啊!之前想找运维岗位也做过运维,不过我发现大部分运维其实也不比技术支持好到哪里去,干的体力活还要加班熬夜。

昨天拒掉阿里内推的一个 CDN 技术支持工作,待遇15k 以上的,因为他写的薪资是15-30k。级别是 P6 ,问我能不能加班熬夜,说有时候得干到24点以后。我想着还是算了吧,加班熬夜的活我也干过,况且还是 CDN 这个干过的工作,没啥挑战性,肯定又是重复以前差不多的工作内容,而且阿里这个加班太严重了。嗯,最关键是要熬夜。挣这点钱,还不够以后去医院的。身为一个底层人民,我想想还是不做这个吃亏的生意。

坚持原创技术分享,如果觉得这篇文章对你有帮助,可以小额打赏