JS-SDK + PHP developed by WeChat to realize recording, uploading and speech recognition

Keywords: PHP SHA1 JSON Javascript

First look at the effect picture: first record, after the recording is successful, add the recording to the list, click the list to play; after the recording is completed, upload the recording, and then voice recognition.

 

Official wechat documents https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html

Implementation process:

Official account configuration

1.JS security domain configuration: landing WeChat public platform: Official Account Settings > function Settings > JS security domain name, domain name written to the root domain name line, download the txt file to the domain name corresponding to the root directory.

 

2. Configure ip white list

 

 

 

 

2, Code display

1. Front end code

'startRecord', 'stopRecord', 'playVoice', 'uploadVoice' are used, Five interfaces of 'translatevoice'. First call startRecord to start recording, and then call stopRecord to stop recording. The local ID of an audio will be returned. Add the recording to the Html recording list to facilitate recording. Use playVoice to play the recording in the recording list, and then use uploadVoice Upload the recording to the wechat server, and it will return to the serverId on the wechat server (I feel that the uploaded recording is not used), and recognize the voice by using the local audio ID

<!DOCTYPE html>
<html>
<head>
    <title>speech recognition</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<script type="text/javascript" src="/static/index/js/jquery.js"></script>
<script src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<link rel="stylesheet" type="text/css" href="/static/index/layui/css/layui.css">

      <style>
          button{
              height: 40px;
              width: 120px;
              margin: 25px;
          }
          #ul{
              margin-top: 15px;
              height: 40px;
              line-height: 40px;
              text-align: center;
              padding: 5px;
          }
          #ul li{
              width: 80%;
          }
          #ullist button{
            width:98%;
            height:40px;
            border-radius:5;
            text-align: center;
            margin: 5px;
        }

      </style>
</head>
<body>
    <div class="container" style="width:100%">
        <div class="row">
            <ul class="list-unstyled" id="ullist">
                
            </ul>
        </div>
        <div id="btn" class="navbar-fixed-bottom" style="user-select:none:align-content:center">
            <center>
                <button id="talk_btn" type="button" class="layui-btn">Sound recording</button>
                <button id="uploadVoice" type="button" class="layui-btn layui-btn-normal">Upload recording</button><br>
                <button id="translateVoice" type="button" class="layui-btn layui-btn-danger" style="width:90%;">Speech recognition</button><br>
            </center>
        </div>
    </div>
    
<script type="text/javascript">

    // global variable
    var recordTimer = 300;
    var voice={
                  localId:'',
                  serverId:''
              }
    
    wx.config({
        debug: false,
        appId: '{$signPackage.appId}',
        timestamp: {$signPackage.timestamp},
        nonceStr: '{$signPackage.nonceStr}',
        signature: '{$signPackage.signature}',
        jsApiList: [
          // All to call API All added to this list
          'startRecord', 'stopRecord', 'playVoice', 'uploadVoice', 'translateVoice'
        ]
      });
      
      // Call here API
    wx.ready(function () {
        var START;
        var END;
        
        // Start recording
        $("#talk_btn").on('touchstart',function (event) {
            // console.log(event)
            event.preventDefault();
            START = new Date().getTime();

            // Recording after delay to avoid misoperation
            recordTimer = setTimeout(function () {

                wx.startRecord({
                    success:function () {
                        // Authorized recording
                        localStorage.rainAllowRecord = 'true';
                    },
                    cancel:function () {
                        console.log('User rejected recording');
                    }
                });
            },300)
        });
        
        //Let go and finish recording
        $('#talk_btn').on('touchend', function(event){
            event.preventDefault();
            END = new Date().getTime();
            
            if((END - START) < 3000){
                END = 0;
                START = 0;
                alert('Recording time cannot be less than 3 seconds');
                //Less than 300 ms,No recording
                clearTimeout(recordTimer);
            }else{
                
                var mytime = new Date().toLocaleTimeString();  //Get current time
                
                wx.stopRecord({
                  success: function (res) {
                      
                    voice.localId = res.localId;
                    console.log(voice.localId)
                    var str="<li audioid='"+voice.localId+"'><button class='layui-btn layui-btn-primary'>Audio task"+mytime+"</button></li>";
                    $("#ullist").append(str);//Show to list
                  },
                  fail: function (res) {
                    alert(JSON.stringify(res));
                  }
                });
            }
        });
    });
    wx.error(function (res) {
        console.log(res)
    });
    
    //list Play voice
    $("ul").on("click", "li", function() {
        var audioid = $(this).attr("audioid");
        
        wx.playVoice({
            localId: audioid
        });
    })

    // Uploading voice
    $("#uploadVoice").click(function(){
        
        //Call the upload recording interface of wechat to upload the local recording to the wechat server first
        wx.uploadVoice({
            localId:voice.localId,    // Local of audio to be uploaded ID,from stopRecord Interface acquisition
            isShowProgressTips: 1,    // 1 by default, display progress prompt
            success:function(res){
                
                if(res.errMsg == 'uploadVoice:ok'){
                    voice.serverId = res.serverId
                    alert('Recording uploaded successfully');
                }else{
                    alert(res.errMsg)
                }
            }
        })
    })
    
    // speech recognition
    $("#translateVoice").click(function(){
        wx.translateVoice({
            localId:voice.localId,    // Local of audio to be identified Id,Obtained by recording related interface
            isShowProgressTips:1,    // 1 by default, display progress prompt
            success:function(res){
                console.log(res)
                if(res.errMsg == "translateVoice:ok"){
                    alert(res.translateResult);    // Results of speech recognition
                }else{
                    alert(res.errMsg)
                }
                
            }
        })
    })

</script>

</body>

</html>

Back end code (php)

Wechat.php mainly obtains accessToken and jsapiTicket

<?php
namespace app\index\controller;
use think\Controller;

/**
 * WeChat class
 */
class Wechat extends Controller
{

    protected  $APPID = 'XXXXXXXXXXXXX';
    protected  $APPSECRET = 'xxxxxxxxxxxxxxxxxx';

    /**
    * Verify the url of token when configuring wechat server
    */
    public function checkToken()
    {
        header("Content-type: text/html; charset=utf-8");

        //1.take timestamp,nonce,toke Sort by dictionary order
        $timestamp = $_GET['timestamp'];
        $nonce = $_GET['nonce'];
        $token = 'asd123456zxc';
        $signature = $_GET['signature'];
        $array = array($timestamp,$nonce,$token);
        //2.After splicing the three sorted parameters, use the sha1 encryption
        $tmpstr = implode('',$array);
        $tmpstr = sha1($tmpstr);
        //3.The encrypted string and signature Compare to determine whether the request is from wechat
        if($tmpstr == $signature){
            echo $_GET['echostr'];
            exit;
        }
    }

    /**
    * curl request 
    */
    public function http_curl($url, $type = 'get', $res = 'json', $arr = ''){
      $cl = curl_init();
      curl_setopt($cl, CURLOPT_URL, $url);
      curl_setopt($cl, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($cl, CURLOPT_SSL_VERIFYPEER, false);
      curl_setopt($cl, CURLOPT_SSL_VERIFYHOST, false);
      if($type == 'post'){
        curl_setopt($cl, CURLOPT_POST, 1);
        curl_setopt($cl, CURLOPT_POSTFIELDS, $arr);
      }
      $output = curl_exec($cl);
      curl_close($cl);
      return json_decode($output, true);
      if($res == 'json'){
        if( curl_error($cl)){
          return curl_error($cl);
        }else{
          return json_decode($output, true);
        }
      }
    }

    /**
     * Get AccessToken
     */
    public function getAccessToken()
    {
        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=".$this->APPID."&secret=".$this->APPSECRET;

        // First judge access_token In file token Whether it is expired or not, continue to use if it is not expired, and update if it is expired
        $data = json_decode($this->get_php_file(ROOT_PATH."public".DS."wxtxt".DS."access_token.txt"));
        // Overdue updates
        if ($data->expire_time < time()) {
            
            $res = $this->http_curl($url);
            $access_token = $res['access_token'];
            if ($access_token) {
                // Add 7000 to the current timestamp s (Two hours)
                $data->expire_time = time() + 7000;
                $data->access_token = $res['access_token'];
                $this->set_php_file(ROOT_PATH."public".DS."wxtxt".DS."access_token.txt",json_encode($data));
            }
        }else{
            // Direct use without expiration
            $access_token = $data->access_token;
        }
        return $access_token;
    }
    
      /**
     * Get JsApiTicket
     */
      public function getJsApiTicket()
      {
          // First judge jsapi_ticket Whether to continue to use after expiration or not, and update after expiration
          $data = json_decode($this->get_php_file(ROOT_PATH."public".DS."wxtxt".DS."jsapi_ticket.txt"));

          if ($data->expire_time < time()) {
              // Overdue updates
              $accessToken = $this->getAccessToken();
              $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=$accessToken";
              $res = $this->http_curl($url);
              $ticket = $res['ticket'];
              if ($ticket) {
                  $data->expire_time = time() + 7000;
                  $data->jsapi_ticket = $ticket;
                  $this->set_php_file(ROOT_PATH."public".DS."wxtxt".DS."jsapi_ticket.txt",json_encode($data));
              }
          }else{
              $ticket = $data->jsapi_ticket;
          }
          return $ticket;
      }


    // Get the token ticket
    private function get_php_file($filename) {
        return trim(file_get_contents($filename));
      }
      // hold token ticket Store in file
      private function set_php_file($filename, $content) {
        $fp = fopen($filename, "w");
        fwrite($fp,  $content);
        fclose($fp);
      }

}

Wxmedia.php this class is to return the configuration information of speech recognition

<?php
namespace app\index\controller;
use think\Controller;
use app\index\controller\Wechat;

/**
 * Wechat speech recognition
 */
class Wxmedia extends Wechat
{

    /**
     * speech recognition
     */
    public function index()
    {
        $signPackage = json_decode($this->getSignPackage(),true);
    
        $this->assign('signPackage',$signPackage);
        return $this->fetch();
    }

    /**
     * Generate signature
     */
    public function getSignPackage() 
    {

        // Instantiate wechat operation class
        $wx = new Wechat();

        // Obtain ticket
        $jsapiTicket = $wx->getJsApiTicket();

        // Be careful URL It must be acquired dynamically, not hardcode.
        $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
        // Of the current page url
        $url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";

        $timestamp = time();    //Generate signature timestamp
        $nonceStr = $this->createNonceStr();    //Generate previous random string

        // Here, the order of parameters should be in ascending order of key value ASCII code
        $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
        // Yes string Conduct sha1 encryption
        $signature = sha1($string);

        $signPackage = array(
          "appId"     => $wx->APPID,
          "nonceStr"  => $nonceStr,
          "timestamp" => $timestamp,
          "url"       => $url,
          "signature" => $signature,
          "rawString" => $string
        );
        return json_encode($signPackage); 
    }

    /**
     * Generate a random string of signatures
     */
    private function createNonceStr($length = 16) {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
          $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }
    
}

Posted by tolputt-craig on Fri, 17 Apr 2020 08:34:00 -0700