Gracece's Blog

生命不息,折腾不止。

一个简单的微信机器人

闲来无事(明明是还有好多作业没有做T_T),想把前一阵子给班里面注册的微信公共帐号给搞起来。具体的目的是,通过微信简单的指令,能够查询m.sysucs.org 上面的通知或者作业,方便其他同学使用。进一步的想法是能增加点趣味性,实现一些简单的关键词回复,并且用户可以按照规定的方式增加新的关键词与回答。 微信公共平台自带了简单的自动回复,不过直接忽略了,因为不能够和服务器交互,只能是自己手动输入。选择开发模式,按照文档验证好token之后,以后就不需要再验证了,代码只需调用一次。整个代码是在腾讯给的示例代码上更改增加得来。所以其实算是自己写的只有 sysucs这个类,类里面有两个函数,分别是reply()和talk(),其中reply()用于查询通知和作业,talk()用于回复简单的关键词。

在代码里面还有几个自己比较生疏的东西,在这里标记一下:

1、整个消息的接收与传送都是用xml来完成的。对于php,可以用simplexml_load_string 将一个xml转换为一个对象,使得数据更容易操作。具体说明可以看simplexml-load-string说明文档。

2、如需使用 PHP 在服务器上生成 XML 响应,请使用下面的代码:

<?php
header("Content-type:text/xml");
echo "<?xml version='1.0' encoding='ISO-8859-1'?>";
echo "<note>";
echo "<from>John</from>";
echo "<to>George</to>";
echo "<message>Don't forget the meeting!</message>";
echo "</note>";
?>

请注意,响应头部的内容类型必须设置为 “text/xml”。来自w3c xml_server

在此次示例中,使用了sprintf进行模式替换生存xml进行响应。

3、CDATA (参考xml_cdata.asp

术语 CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data)。 在 XML 元素中,”<” 和 “&” 是非法的。 “<” 会产生错误,因为解析器会把该字符解释为新元素的开始。 “&” 也会产生错误,因为解析器会把该字符解释为字符实体的开始。 某些文本,比如 JavaScript 代码,包含大量 “<” 或 “&” 字符。为了避免错误,可以将脚本代码定义为 CDATA。 CDATA 部分中的所有内容都会被解析器忽略。 CDATA 部分由 <![CDATA[ 开始,由 ]]> 结束: CDATA 部分不能包含字符串 “]]>”。也不允许嵌套的 CDATA 部分。 标记 CDATA 部分结尾的 “]]>” 不能包含空格或折行。

4、在插入数据的过程中要对数据进行转义,以减去特殊符号带来的错误。比如表情 “哭”的代码就是 /::’( ,含有单引号会引起错误。另外更多表情代码关系在这里

然后上代码吧,整个代码的逻辑还算是比较简单。

<?php

//define your token
define("TOKEN", "sysucs");


$wechatObj = new wechatCallbackapiTest();
//$wechatObj->valid(); //第一次验证token时候才使用
$wechatObj->responseMsg();

class wechatCallbackapiTest
{
    public function valid()
    {
        $echoStr = $_GET["echostr"];

        //valid signature , option
        if($this->checkSignature()){
            echo $echoStr;
            exit;
        }
    }

    public function responseMsg()
    {
        //get post data, May be due to the different environments
        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];

        //extract post data
        if (!empty($postStr)){

            $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $fromUsername = $postObj->FromUserName;
            $toUsername = $postObj->ToUserName;
            $keyword = trim($postObj->Content);
            $time = time();
            $textTpl = "<xml>
                <ToUserName><![CDATA[%s]]></ToUserName>
                <FromUserName><![CDATA[%s]]></FromUserName>
                <CreateTime>%s</CreateTime>
                <MsgType><![CDATA[%s]]></MsgType>
                <Content><![CDATA[%s]]></Content>
                <FuncFlag>0</FuncFlag>
                </xml>";             
            if(!empty( $keyword ))
            {
                $msgType = "text";
                $sysucs =new sysucs();
                if(preg_match('#通知|作业#i',$keyword))
                {
                    $contentStr=$sysucs->reply($keyword);
                }
                else if(preg_match('#subscribe|Hello2BizUser#i',$keyword))
                    $contentStr ="欢迎关注计科一班微信!";
                else
                    $contentStr = $sysucs->talk($keyword);

                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                echo $resultStr;
            }else{
                echo "空消息..";
            }
        }else {
            echo "欢迎使用计科一班微信公共帐号 (sysucs)!";
            exit;
        }
    }

    private function checkSignature()
    {
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];  

        $token = TOKEN;
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr);
        $tmpStr = implode( $tmpArr );
        $tmpStr = sha1( $tmpStr );

        if( $tmpStr == $signature ){
            return true;
        }else{
            return false;
        }
    }
}

class sysucs
{
    public   function reply($keyword){
        error_reporting(E_ALL);
        $dbc = new mysqli('localhost','user','password','sysucs');
        mysqli_query($dbc,"SET NAMES utf8");
        date_default_timezone_set('PRC');
        $resultStr =" ";

        if(preg_match('#通知#i',$keyword))
        {
            $query = "SELECT * FROM info where 'content' not regexp '本周作业' ORDER BY date DESC;";
            $result =mysqli_query($dbc,$query);
            $num_results = $result -> num_rows;

            if($num_results >3)
                $num_results =3;
            for($i=0;$i < $num_results;$i++)
            {
                $row = mysqli_fetch_array($result);
                $resultStr .=  "【".($i+1)."】".date("Y-m-d H:i ",$row['date'])."\n";
                $resultStr .=$row['content']."\n";
            }
            $resultStr .= "更多通知请访问 http://m.sysucs.org/more.php";
        }
        else if(preg_match('#作业#i',$keyword))
        {
            $query = "SELECT * FROM info where content like '%本周作业%' ORDER BY date DESC;";
            $result =mysqli_query($dbc,$query);
            $num_results = $result -> num_rows;

            if($num_results >1)
                $num_results =1;
            for($i=0;$i < $num_results;$i++)
            {
                $row = mysqli_fetch_array($result);
                echo "<p>".date("Y-m-d H:i ",$row['date'])."</p>";
                echo $row['content'];
                $resultStr .= "【".($i+1)."】".$row['content']."\n";
            }
        }
        $resultStr=html_entity_decode($resultStr);
        return strip_tags($resultStr);
    }

    function talk($keyword)
    {
        $dbc = new mysqli('localhost','user','password','sysucs');
        mysqli_query($dbc,"SET NAMES utf8");
        date_default_timezone_set('PRC');
        if($keyword =="帮助")
        {
            return "回复“通知”查看最近通知,回复“作业”查看本周作业。\n   要教我回答,请英文下划线开头,接着问题,接着#,接着回答,例如“_计科一班#威武”。\n   其他功能还在完善中,欢迎提建议!http://m.sysucs.org";
        }
        if (substr($keyword, 0,1) == '_')
        {
            $pos = strpos($keyword, '#');
            if ($pos > -1)
            {
                $q = substr($keyword, 1,$pos - 1);
                if(preg_match('#帮助|作业|通知#i',$q))
                    return "去去去,瞎捣乱,关键词你也来改";
                $a = substr($keyword, $pos + 1);
                if($q==""||$a=="")
                    return "问题答案未填完整,换个姿势再来一次!";
                $orginQ = $q;
                $orgina = $a;
                $q =addslashes($q);
                $a =addslashes($a);
                $query ="select * from talk where question = '".$q."' and answer = '".$a."'";
                $exist = mysqli_query($dbc,$query);
                if($exist->num_rows >0)
                {
                    return "讨厌,已经有一模一样的就不要再插了嘛 /::~";
                }
                else
                {
                    $query ="insert into talk values('".$q."','".$a."')";
                    $result = mysqli_query($dbc,$query);
                    if($result)
                        return "/:8-)已记录:" . $orginQ . '/' . $orgina ;
                    else 
                        return "插入失败!";
                }
            }
            else
                return "分隔符改成#了(在英文标点里找),可以输入表情 /::B";
        }
        else 
        {
            $orginKeyword =$keyword;
            $keyword=addslashes($keyword);
            $query ="select * from talk where question like '%".$keyword."%' order by rand() limit 1";
            $result = mysqli_query($dbc,$query);
            if($result)
            {
                $num_results = $result -> num_rows;
                if($num_results == 0)
                    return "我不知道【".$orginKeyword."】是神马东东/:&-(,教教我吧,下次我就能回答你了/:8*~可以按照 “_问题#回答” 的格式使我变得更聪(xié)明(è),可以加表情的~。输入“帮助”看看更多功能~";
                else
                {
                    $row = mysqli_fetch_array($result);
                    return $row['answer'];
                }
            }
            else
            {
                return "数据库查询失败";
            }
        }
    }
}
?>

最后,上一下二维码吧~

blog comments powered by Disqus