• swoft 客服系统websocket。即时通讯小功能


    挥手,握手就不解释了。直接上代码

    启动swoft ws的服务

    <?php declare(strict_types=1);
    /**
     * This file is part of Swoft.
     *
     * @link     https://swoft.org
     * @document https://swoft.org/docs
     * @contact  group@swoft.org
     * @license  https://github.com/swoft-cloud/swoft/blob/master/LICENSE
     */
    
    namespace AppWebSocket;
    
    use SwoftHttpMessageRequest;
    use SwoftSessionSession;
    use SwoftHttpMessageResponse;
    use SwoftWebSocketServerAnnotationMappingOnClose;
    use SwoftWebSocketServerAnnotationMappingOnMessage;
    use SwoftWebSocketServerAnnotationMappingOnHandshake;
    use SwoftWebSocketServerAnnotationMappingOnOpen;
    use SwoftWebSocketServerAnnotationMappingWsModule;
    use SwooleWebSocketFrame;
    use SwooleWebSocketServer;
    use function server;
    
    use SwoftRedisExceptionRedisException;
    use SwoftRedisPool;
    use SwoftRedisRedis;
    use SwoftLogHelperCLog;
    use SwoftDbDB;
    use SwoftTaskTask;
    
    
    /**
     * Class EchoModule
     *
     * @WsModule("echo")
     */
    class EchoModule
    {
        /**
         * @OnOpen()
         * @param Request $request
         * @param int     $fd
         */
        public function onOpen(Request $request, int $fd): void
        {
            Session::current()->push("Opened, welcome #{$fd}!");
        }
    
        /**
         * @OnMessage()
         * @param Server $server
         * @param Frame  $frame
         * blacklist 黑名单
         * Blockkeywords 屏蔽关键字
         * 检测客服是否在线
         * 判断条件,搜索关键字。匹配自动回复内容
         */
        public function onMessage(Server $server, Frame $frame): void
        {
            CLog::info('talk: %s ', $frame->data);
    
            $data = json_decode($frame->data, true);
    
            if ($data['flag'] == 'init') {
                $res = Redis::set('user_fd'.$data['from'], $frame->fd);
            }
    
            $blacklist = Redis::get('blacklist');
    
                if (empty($blacklist)) {
    
                    $list = Db::table('db_customer_blacklist')->where('status',1)->select('user_id')->get();
    
                    $blacklist = [];
    
                    foreach ($list as $k => $v) {
                        $blacklist[] = $v['user_id'];
                    }
    
                }
    
                $blackStr = implode(",", $blacklist);
    
                if(strpos($blackStr, $data['from']) !== false){
                    $server->push($frame->fd, json_encode(['content'=>'卧槽~!被禁言了','from'=>$data['to']]));die;
                }
    
            $BlockkeywordsStr = Redis::get('Blockkeywords');
    
                if (empty($Blockkeywords)) {
                    $Blocklist = Db::table('db_customer_blockkeyword')->where('status',1)->select('keywords')->get();
    
                    $BlockkeywordsStr = '';
    
                    foreach ($Blocklist as $k1 => $vo) {
                        if ($k1 == 0) {
                            $BlockkeywordsStr .= $vo['keywords'];
                        }else{
                            $BlockkeywordsStr .= ','.$vo['keywords'];
                        } 
                    }
    
                }
    
                $BlockkeywordsArr = explode(',', $BlockkeywordsStr);
    
                foreach ($BlockkeywordsArr as $k2 => $voo) {
                            
                    if ( strpos($frame->data,trim($voo)) ){
                        $frame->data = str_replace($voo ,'*',$frame->data);
                    }
    
                }
    
            Task::async('TalkTask', 'UserToCustomerTalkLog', [$data['from'], $data['to'], 1, $data['content']]);
    
            $customer = Redis::get('user_fd'.$data['to']);
    
                if (empty($customer)) {
                    //$autoReply = Redis::hget($data['content']);
    
                    CLog::info('talk: %s ', '2222');
    
                    $server->push($frame->fd, json_encode(['content'=>'这里是人工智能回复','from'=>$data['to']]));
                }else{
    
                    $server->push($customer, json_encode(['content'=>$data['content'],'from'=>$data['to']]));
                    CLog::info('talk: %s ', 'Recv: '.$customer.'--'. $data['content'].'--'.$data['to']);
                }
    
        }
        /**
         * @onClose()
         * @param Server $server
         * @param int    $fd
         */
        public function onClose(Server $server, int $fd): void
        {
            $res = Redis::del('user_fd'.$fd);
    
            CLog::info('#' . $fd. 'Redis:' . $res);
    
            $server->close($fd);
        }
    
    }

    以上可以去掉 CLog::info('#' . $fd. 'Redis:' . $res);   主要是能在后台看到实时的情况

    <?php
    
        $MyInfo = ['avatar'=>'https://wx.qlogo.cn/mmopen/vi_32/NhCl8wDjCM0IoZLgjZNtwb8rN26jic4Rxbk8lc0HIfvP3FY6HuZOtrArW45eBnS6htsJ9uMYPC4lxoXYchtwQQA/132','nickname'=>'黑锦鲤','id'=>66296];
        $ToInfo = ['avatar'=>'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif','nickname'=>'客服锦鲤','id'=>151];
    
    ?>
    
    <!DOCTYPE html>
    <html>
    <head>
        <title>XST-app</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
        <meta name="viewport" content="width=device-width, initial-scale=0.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
        <meta name="keywords" content="test" />
        <meta name="description" content="test" />
        <meta name="author" content="XST-APP" />
        <meta content="yes" name="apple-mobile-web-app-capable" />
        <meta content="black" name="apple-mobile-web-app-status-bar-style" />
        <meta content="telephone=no" name="format-detection" />
        <script type="text/javascript" src="./jquery-1.8.3.min.js"></script>
      <style type="text/css">
        body{background:url(/static/images/yuyin_bg.jpg);background-size:100%;}
        @media all and (min- 640px) {
            body,html,.wenwen-footer,.speak_window{640px!important;margin:0 auto}
            .speak_window,.wenwen-footer{left:50%!important;margin-left:-320px}
        }
        input,button{outline:none;}
        .wenwen-footer{100%;position:fixed;bottom:-5px;left:0;background:#fff;padding:3%;border-top:solid 1px #ddd;box-sizing:border-box;}
        .wenwen_btn,.wenwen_help{15%;text-align:center;}
        .wenwen_btn img,.wenwen_help img{height:40px;}
        .wenwen_text{height:40px;border-radius:5px;border:solid 1px #636162;box-sizing:border-box;66%;text-align:center;overflow:hidden;margin-left:2%;}
        .circle-button{padding:0 5px;}
        .wenwen_text .circle-button{font-size:14px;color:#666;line-height:38px;}
        .write_box{background:#fff;100%;height:40px;line-height:40px;}
        .write_box input{height:40px;padding:0 5px;line-height:40px;100%;box-sizing:border-box;border:0;}
        .wenwen_help button{95%;background:#42929d;color:#fff;border-radius:5px;border:0;height:40px;}
        #wenwen{height:100%;}
        .speak_window{overflow-y:scroll;height:100%;100%;position:fixed;top:50px;left:0;}
        .speak_box{margin-bottom:70px;padding:10px;}
        .question,.answer{margin-bottom:1rem;}
        .question{text-align:right;}
        .question>div{display:inline-block;}
        .left{float:left;}
        .right{float:right;}
        .clear{clear:both;}
        .heard_img{height:60px;60px;border-radius:5px;overflow:hidden;background:#ddd;}
        .heard_img img{100%;height:100%}
        .question_text,.answer_text{box-sizing:border-box;position:relative;display:table-cell;min-height:60px;}
        .question_text{padding-right:20px;}
        .answer_text{padding-left:20px;}
        .question_text p,.answer_text p{border-radius:10px;padding:.5rem;margin:0;font-size:14px;line-height:28px;box-sizing:border-box;vertical-align:middle;display:table-cell;height:30px;word-wrap:break-word;}
        .answer_text p{background:#fff;}
        .question_text p{background:#42929d;color:#fff;text-align:left;}
        .question_text i,.answer_text i{0;height:0;border-top:5px solid transparent;border-bottom:5px solid transparent;position:absolute;top:25px;}
        .answer_text i{border-right:10px solid #fff;left:10px;}
        .question_text i{border-left:10px solid #42929d;right:10px;}
        .answer_text p a{color:#42929d;display:inline-block;}
        .write_list{position:absolute;left:0;100%;background:#fff;border-top:solid 1px #ddd;padding:5px;line-height:30px;}
      </style>
    </head>
    
    <body>
    <div id="header" class="head">
            <div class="wrap">
                    <i class="menu_back"><a href="javascript:history.go(-1);"></a></i>
                    <div class="title">
                            <span class="title_d"><p>与   <?php echo $ToInfo['nickname']; ?>   的聊天</p></span>
                            <div class="clear"></div>
                    </div>
                    <!--i class="menu_share"></i-->
            </div>
    </div>
    
    <input type="hidden" name="myemail" id="myemail" value="<?php echo $MyInfo['id']; ?>" />
    <input type="hidden" name="mynickname" id="mynickname" value="<?php echo $MyInfo['nickname']; ?>" />
    <input type="hidden" name="myavatar" id="myavatar" value="<?php echo $MyInfo['avatar']; ?>" />
    
    <input type="hidden" name="toemail" id="toemail" value="<?php echo $ToInfo['id']; ?>" />
    <input type="hidden" name="tonickname" id="tonickname" value="<?php echo $ToInfo['nickname']; ?>" />
    <input type="hidden" name="toavatar" id="toavatar" value="<?php echo $ToInfo['avatar']; ?>" />
    
    <!-- 对话内容 -->
    <div class="speak_window">
        <div class="speak_box">
    
        </div>
    </div>
    
    
    <!-- 内容输入-->
    <div class="wenwen-footer">
        <div class="wenwen_btn left"><img src="/static/images/jp_btn.png"></div>
        <div class="wenwen_text left">
            <div class="write_box"><input type="text" class="left" onKeyUp="keyup()" maxlength="100" placeholder="请输入信息(100字以内)..." /></div>   
        </div>
        <div class="wenwen_help right">
                <button onClick="send()" class="right">发送</button>
        </div>
        <div style="opacity:0;" class="clear"></div>
    </div>
    
    
    <script type="text/javascript">
        if ("WebSocket" in window){
            var ws = new WebSocket("ws://47.111.253.56:18308/echo");
            ws.onopen = function(){
                console.log("握手成功");
                var myemail = $("#myemail").val();
                var mynickname = $("#mynickname").val();
                var toemail = $("#toemail").val();
                var arr = {"flag":"init","from":myemail,"to":toemail,"content":mynickname+'需要帮助'};
                var str = JSON.stringify(arr);
                alert(str)
                ws.send(str);
            };
            ws.onmessage = function(e){
                var toemail = $("#toemail").val();
                var toavatar = $("#toavatar").val();
    
                if (e.data) {
                    console.log(e.data);
    
                    var obj = JSON.parse(e.data);
                    
                    console.log(obj.from);
                    console.log(toemail);
                    console.log(obj.content);
                    //但同时与两个人聊天时,可能两个人的消息都会出现在当前窗口,所以此处加个判断,此窗口只接收当前聊天对象的消息,其他则忽略
                    //if(obj.from === toemail){
                        var ans  = '<div class="answer"><div class="heard_img left"><img src="'+toavatar+'"></div>';
                            ans += '<div class="answer_text"><p>'+obj.content+'</p><i></i>';
                            ans += '</div></div>';
                            $('.speak_box').append(ans);
                            for_bottom();
                    //} 
                };
                
            };
            ws.onerror = function(){
                console.log("error");
                var  str  = '<div class="question">';
                str += '<div class="heard_img right"><img src="/static/images/xitong.jpg"></div>';
                str += '<div class="question_text clear"><p>聊天服务器出现异常,暂时无法提供服务。</p><i></i>';
                str += '</div></div>';
                $('.speak_box').append(str);
                $('.write_box input').val('');
                $('.write_box input').focus();
                autoWidth();
                for_bottom();
            };
    
            function send() {
                var content = $('.write_box input').val();
                if(content === ''){
                    alert('请输入消息!');
                    $('.write_box input').focus();
                }else{
                        var toemail = $("#toemail").val();
                        var myemail = $("#myemail").val();
                        var myavatar = $("#myavatar").val();
                        var arr = {"flag":"msg","to":toemail,"from":myemail,"content":content};
                        var msg = JSON.stringify(arr);
                        console.log(msg);
                        ws.send(msg);    
                        var  str  = '<div class="question">';
                        str += '<div class="heard_img right"><img src="'+myavatar+'"></div>';
                        str += '<div class="question_text clear"><p>'+content+'</p><i></i>';
                        str += '</div></div>';
                    $('.speak_box').append(str);
                    $('.write_box input').val('');
                    $('.write_box input').focus();
                    autoWidth();
                    for_bottom();
                }
            
            }
        }else{
            alert("您的浏览器不支持 WebSocket!");
        }
               
        function for_bottom(){
        var speak_height = $('.speak_box').height();
        $('.speak_box,.speak_window').animate({scrollTop:speak_height},500);
        }
        
        function autoWidth(){
        $('.question_text').css('max-width',$('.question').width()-60);
        }
        
        autoWidth();
        
    </script>
    
    </body>
    </html>
    

     这个客户端就是为了测试。

  • 相关阅读:
    专题三--1005
    专题三--1009
    专题三--1017
    背包九讲
    专题三--1003
    专题三--1004
    专题三--1015
    [洛谷P1220]关路灯
    [洛谷P1776]宝物筛选
    [USACO14JAN]Recording the Moolympics
  • 原文地址:https://www.cnblogs.com/heijinli/p/12935495.html
Copyright © 2020-2023  润新知