PHP接入微信公众号(二)- 如何实现“价值一个亿的AI”代码

PHP接入微信公众号

前言:对公众号进行基本的连接后,我们就可以开始对公众号进行开发了。这其中,就有很多的事可以做了

获取access_token

  • 说明:access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token
  • 请求方式:HTTP GET
  • 请求域名https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
  • 参数说明
    |:–|:–|:–|
    |参数|是否必须|说明|
    |grant_type|是|获取access_token填写client_credential(固定即可)|
    |appid|是|第三方用户唯一凭证,公众号后台有|
    |secret|是|第三方用户唯一凭证**,即appsecret|
  • 正常返回数据{“access_token”:“ACCESS_TOKEN”,“expires_in”:7200}
  • 错误返回数据{“errcode”:40013,“errmsg”:“invalid appid”}
  • 请求代码实现
/**
     * 获取微信access_token
     * 正确返回 {"access_token":"ACCESS_TOKEN","expires_in":7200}
     * 错误返回 {"errcode":40013,"errmsg":"invalid appid"}
     * @param $appID
     * @param $secret
     * @return mixed
     * @throws Exception
     */
    public function getAccessToken($appID, $secret)
    {
        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appID}&secret={$secret}";

        Yii::warning("call wei_xin access_token", CATEGORIES_INFO);

        try {
            $response = (new Client())->get($url);
        } catch (RequestException $e) {
            Yii::error("cannot request to api.weixin.qq.com!", CATEGORIES_ERROR);
            return false;
        }

        $content = $response->getBody()->getContents();
        Yii::warning('request content:'.$content, CATEGORIES_WARN);
        $jsonContent = \GuzzleHttp\json_decode($content, true);
        if (isset($jsonContent['errcode'])) {
            Yii::error("get access token error;msg:".$content, CATEGORIES_ERROR);
            return false;
        }

        return $jsonContent;
    }
  • 业务逻辑:微信文档建议统一获取access_token,所以建议在请求完成后存于session或者其他缓存中,失效后再重新获取。

接收消息

  • 说明:用户在公众号下留言后并已启用了服务器配置,那么微信公众平台就会将消息发送到所配置的服务器地址。如果不能正确处理并回复消息,用户将会收到公众号服务异常的消息。
  • 消息类型:消息分加密和非加密,对于重要的消息可以采用加密模式。(但微信文档中的加解密已经过时,无法支持PHP7以上的版本,目前我还在调试中。。),非加密消息直接处理XML消息即可。由于加密模式示例代码已过时,这里暂时就只提非加密模式了
  • 消息发送方式:HTTP POST 传送XML数据包
  • 数据包格式
<xml> 
<ToUserName>< ![CDATA[toUser] ]></ToUserName> 
 <FromUserName>< ![CDATA[fromUser] ]></FromUserName>
 <CreateTime>1348831860</CreateTime> 
 <MsgType>< ![CDATA[text] ]></MsgType> 
 <Content>< ![CDATA[this is a test] ]></Content>  
 <MsgId>1234567890123456</MsgId> 
</xml>
  • 代码处理数据包
//处理xml结构,转换为数组
libxml_disable_entity_loader(true);
$content = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
  • 消息返回:在接收到消息后,你可以立刻回复消息(需要在5秒内应答)。回复消息时请使用消息模板一样返回。若不想返回,或者无法立刻返回,可以先返回空字符串,然后再调用消息发送接口。

“价值一个亿的AI代码”

  • 说明:大家应该有在朋友圈或其他论坛看到有一张玩笑的图,说是价值一个亿的AI代码,所以我在这里,也接着微信公众号的项目,试验了一下,仅供娱乐,哈哈_
  • 主体:通过逻辑代码,做字符串切割,完成伪智能客服。
  • 截图
    PHP接入微信公众号(二)- 如何实现“价值一个亿的AI”代码
  • 备注:详情请看源码

核心代码

public function actionIndex()
    {
        $params = (new Request())->get();
        Yii::warning('wei_xin get_params:'.json_encode($params), CATEGORIES_WARN);
        if (isset($params['echostr'])) {//公众号接入时的验证
            Yii::warning('update config', CATEGORIES_WARN);
            echo $this->valid($params);
            exit();
        }

        /*
         * 接收消息并处理
         * 处理失败后直接回复空字符串或者content为success
         */
        $data = file_get_contents("php://input");
        Yii::warning('wei_xin post_data:'.$data, CATEGORIES_WARN);
        if (empty($data)) {
            echo '';
            exit();
        }

        //处理xml结构
        libxml_disable_entity_loader(true);
        $content = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true);

        //样本记录数据库
        $recordModel = new WxRecordModel();
        $keys = getArrayKey($recordModel->typeMap, $this->getRealValue($content['MsgType']));//查找类型对应键值
        $data = [
            'msg_id' => $this->getRealValue($content['MsgId']),
            'msg_type' => $keys[0],
            'to_user_name' => $this->getRealValue($content['ToUserName']),
            'from_user_name' => $this->getRealValue($content['FromUserName']),
            'content' => $this->getRealValue($content['Content']),
        ];

        if (!$recordModel->insert($data)) {
            Yii::error('wx insert record failed;data:'.json_encode($data),CATEGORIES_ERROR);
        }

        //消息解密 - 采用明文模式则不需要解密
        /*
        $msg = '';
        $pc = new WxBizMsgCrypt(WX_TOKEN, WX_AES_KEY, WX_APP_ID);
        $rs = $pc->decryptMsg($params['msg_signature'], $params['timestamp'], $params['nonce'], $data, $msg);
        Yii::warning('接收消息解密内容:'.$msg, CATEGORIES_WARN);
        if ($rs != 0) {
            echo '';
            exit();
        } else {
            $replyMsg = $this->dealMsg($msg);
        }
        */

        //消息类型处理
        switch ($keys[0]) {
            case $recordModel::MSG_TYPE_TEXT :
                $content['Content'] = $this->dealTextMsg($content['Content']);
                break;
            default :
                $content['Content'] = '小主对不起,暂时无法支持该类消息哦';
                break;
        }

        $resMsg = $this->transferMsg($content, $content['Content']);//组合xml消息体
        Yii::warning('回复消息xml:'.$resMsg, CATEGORIES_WARN);

        //消息加密
        /*
        $time = time();
        $nonce = '123456';
        $pc->encryptMsg($replyMsg, $time, $nonce, $resMsg);
        $encryptResMsg = "<xml><Encrypt><![CDATA[{$resMsg}]></Encrypt><TimeStamp>{$time}</TimeStamp><Nonce>{$nonce}</Nonce></xml>";
        Yii::warning('加密后的回复消息xml:'.$encryptResMsg, CATEGORIES_WARN);
        */

        echo $resMsg;
    }

/**
     * 接入验证
     * signature 微信加密签名
     * timestamp 时间戳
     * nonce 随机数
     * echostr 随机字符串
     * @param array $params
     * @return string
     */
    public function valid(array $params)
    {
        if (
            empty($params)
            || empty($params['timestamp'])
            || empty($params['nonce'])
            || empty($params['signature'])
            || empty(['echostr'])
        ) {
           return '';
        }

        $tmpArray = array(WX_TOKEN, $params['timestamp'], $params['nonce']);
        sort($tmpArray, SORT_STRING);
        $tmpStr = implode($tmpArray);
        $tmpStr = sha1($tmpStr);
        if ($params['signature'] == $tmpStr) {
            return $params['echostr'];//输出随机字符串
        }

        return '';
    }

    /**
     * 生成回复消息的xml格式
     * @param array $data
     * @param string $msg
     * @return bool
     */
    public function transferMsg($data = array(), $msg = '')
    {
        $format = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[%s]]></Content>
</xml>";

        $result = sprintf($format, $data['FromUserName'], $data['ToUserName'], time(), $msg);
        return $result;
    }

    /**
     * 去除微信的外包层
     * @param $str
     * @return mixed
     */
    public function getRealValue($str)
    {
        if (strpos($str, '<![CDATA[') !== false) {
            $str = substr($str, 9);
        }

        if (strpos($str, ']]>') !== false) {
            $str = substr($str, 0, -3);
        }

        return $str;
    }

    /**
     * 价值一个亿的AI核心代码
     * @param string $msg
     * @return mixed|string
     */
    public function dealTextMsg($msg = '')
    {
        $msg = $this->getRealValue($msg);

        /*
         * 规则替换
         */
        $keyWords = (new WxRulesModel())->getRuleKeys(['status' => WxRulesModel::STATUS_OPEN, 'type' => WxRulesModel::TYPE_KEY_WORD]);
        $illegalWord = (new WxRulesModel())->getRuleKeys(['status' => WxRulesModel::STATUS_OPEN, 'type' => WxRulesModel::TYPE_ILLEGAL_WORD]);
        if (!empty($keyWords)) {//关键字回复
            if (isset($ruleKeys[$msg])) {
                return $ruleKeys[$msg];
            }
        }

        if (!empty($illegalWord)) {//敏感词替换

        }

        $key = [',','.','?',',','。','?', '吗','嘛','吧','的','呀','啊'];

        if (!empty($msg) && strlen($msg) > 1) {
            if (in_array(mb_substr($msg, -1),$key)) {
                $msg = mb_substr($msg, 0, -1);
            }

            if (strpos($msg, '我') !== false && strpos($msg, '你') !== false) {
                $content = '';
                $len = mb_strlen($msg);
                for ($i = 0; $i < $len; $i++) {
                    $tmp = mb_substr($msg, $i, 1);
                    if ($tmp == '我') {
                        $tmp = '你';
                    } elseif ($tmp == '你') {
                        $tmp = '我';
                    }

                    $content = $content . $tmp;
                }

                return $content;
            } elseif (strpos($msg, '我') !== false) {
                $msg = str_replace('我', '你', $msg);
            } elseif (strpos($msg, '你') !== false) {
                $msg = str_replace('你', '我', $msg);
            }
        }

        return $msg;
    }

其他

  • 真的要实现人工智能的话需要接入真正的机器学习模型,这个待之后实现,目前仅收集样本数据
  • 可以实现关键字自动回复功能
  • 可以处理多种消息类型

备注

大家有兴趣也可以关注下我的公众号,我会不定期技术文章和代码滴~~~
也欢迎大家一起交流学习_

扫码测试“AI”代码~
PHP接入微信公众号(二)- 如何实现“价值一个亿的AI”代码