通过酷Q实现qq机器人,实现qq客服、定时推送qq消息、群管理等功能。


方式:

1.windows下载酷Q并安装http-api插件。

 下载地址:https://pan.baidu.com/s/1qY55zp6

2.直接使用docker,通过wine运行在linux系统上。

$ docker pull richardchien/cqhttp:latest
$ mkdir coolq  # 用于存储酷 Q 的程序文件
$ docker run -ti --rm --name cqhttp-test \
             -v $(pwd)/coolq:/home/user/coolq \  # 将宿主目录挂载到容器内用于持久化酷 Q 的程序文件
             -p 9000:9000 \  # noVNC 端口,用于从浏览器控制酷 Q
             -p 5700:5700 \  # HTTP API 插件开放的端口
             -e COOLQ_ACCOUNT=123456 \ # 要登录的 QQ 账号,可选但建议填
             -e CQHTTP_POST_URL=http://example.com:8080 \  # 事件上报地址
             -e CQHTTP_SERVE_DATA_FILES=yes \  # 允许通过 HTTP 接口访问酷 Q 数据文件
             richardchien/cqhttp:latest

其中,CQHTTP_POST_URL、CQHTTP_SERVE_DATA_FILES 是用于配置插件运行的,格式为「CQHTTP_ + 插件配置项的大写」,具体的配置项,见 配置。


然后访问 http://


注意,默认情况下,容器启动时会检查是否已经存在 config 目录,如果不存在,则会将 CQHTTP_ 开头的环境变量写入到配置文件中;如果 config 目录存在,即不是首次启动,则不会做任何修改。因此,你可以在容器运行时手动修改配置文件,重启容器后仍然有效,这和旧版本的行为不一样!如果你要让容器每次启动时都使用环境变量重新创建配置文件,以保持插件行为和容器启动命令的一致性,可以设置环境变量 FORCE_ENV 的值为 true。


完成上述配置后即可通过调用api完成qq的发送消息及qq群管理等功能,剩下的具体的业务逻辑使用自己熟悉的语言去实现即可。


详细的接口文档:https://richardchien.gitee.io/coolq-http-api/


附点餐小呆萌代码:

/**
     * 接收事件上报
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function receive(){
		try{
			$getData = input('post.') ?  : file_get_contents('php://input');
			if (!empty($getData['group_id']) && (int)$getData['group_id'] == $this->groupId) {
				if ($this->userId = $this->getGroupUserid()){
					$content = $getData['raw_message'] ? : $getData['message'];
					//分离酷q的前置数据与内容文本
					preg_match('/(.*[CQ:]{3}.*[]]{1})(.*)/u', $content, $arr);
					if (!empty($arr[1]) && !empty($arr[2])){
						//提取前置数据中被@的qq号
						preg_match('/(.*[CQ:at,qq=]{9})([0-9]{5,11})([]]{1})/u', $arr[1], $arr1);
						if (!empty($arr1[2]) && (int)$arr1[2] == 167114078){
							//来到这里证明是被at了,取得纯文本内容进行下一步处理
							$tasks = db("group_task")
								->whereTime('create_time', 'today')
								->select();
							if (count($tasks)>=2){
								preg_match_all("/([周]{1})([一二三四五六日]{1}).{0,3}([a-cA-C]{1}|[\u{4e0d}]{1}[\u{70b9}]{1})/u", $arr[2],$arr8);
								$dateArr = $this->getLastSevenDays();
								if (!empty($arr8[2])) {
									//有选项
									$data = [];
									//获取任务id与时间的对应数组,用于判断用户输入的时间有没有任务
									$taskArr = array_column($tasks,  'id', 'time');
									foreach ($arr8[2] as $key => $value) {
										$data[strtotime($dateArr[$this->changeDateType($value)])] = $arr8[3][$key];
										if (!empty($taskArr[strtotime($dateArr[$this->changeDateType($value)])])){
											$taskId = $taskArr[strtotime($dateArr[$this->changeDateType($value)])];
											$this->order($taskId, $arr8[3][$key]);
										}else{
											$this->sendGroup($this->groupId, "{$this->groupUserInfo['user_name']}周{$value},没有可点套餐。");
										}
									}
								}else{
									//无选项
									preg_match('/([不]{1}[点]{1})/u', $arr[2],$arr2);
									preg_match('/([A-Ca-c])/u', $arr[2], $arr3);
									if (!empty($arr2[0]) && $arr2[0] == "不点" || !empty($arr3[0]) && in_array(ucfirst($arr3[0]), ['A', 'B', 'C'])){
										$lists = [];
										$listsNote = [];
										foreach ($tasks as $v){
											$lists[] = "周".$this->changeDateType((int)date("w", $v['time']));
											$listsNote[] = "周".$this->changeDateType((int)date("w", $v['time'])) . "-A";
										}
										$msg = "大佬,今天要点" . count($tasks) ."天的餐,你这样我不知道你想点哪一个呀。". PHP_EOL ."今日可点餐有" . implode('、', $lists) . ",点餐请按示例格式回复。". PHP_EOL ."如:" . implode(' ', $listsNote);
										$this->sendGroup($this->groupId, $msg);
									}
								}
							}else{
								if (!empty($tasks[0]['id'])){
									preg_match('/([不]{1}[点]{1})/u', $arr[2],$arr2);
									preg_match('/([A-Ca-c])/u', $arr[2], $arr3);
									if (!empty($arr2[0]) && $arr2[0] == "不点") {
										$this->order($tasks[0]['id'], $arr2[0]);
										exit;
									}else if (!empty($arr3[0]) && in_array(ucfirst($arr3[0]), ['A', 'B', 'C'])) {
										$this->order($tasks[0]['id'], ucfirst($arr3[0]));
										exit;
									}else {
										//$this->sendGroup($this->groupId, "不要@我,大佬不给我说话!");
										exit;
									}
								}
							}
						}
					}
				}
				exit;
			}
        }catch (\Exception $e){
            Log::write("Receive message:".$e->getMessage());
        }
    }
    
    /**
     * 发送消息
     * 多行内容部分记得urlencode
     * @param bool $toUserId
     * @param bool $content
     */
    public function send($toUserId = false, $content = false){
        if ($toUserId && $content){
        	$content = urlencode($content);
            $url = "http://127.0.0.1:5701/send_private_msg?user_id={$toUserId}&message={$content}";
            file_get_contents($url);
        }
    }
    
    /**
     * 发送群消息
     * @param bool $groupId
     * @param bool $message
     */
    public function sendGroup($groupId = false, $message = false){
        if ($groupId && $message){
        	$message = urlencode($message);
            $url = "http://127.0.0.1:5701/send_group_msg?group_id={$groupId}&message={$message}";
            file_get_contents($url);
        }
    }
    
    /**
     * 获取群用户id
     * @return bool|mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    private function getGroupUserid(){
        $userModel = db('group_user');
        $userInfo = $userModel->where(['qq'=>$this->params['user_id']])->find();
        if (isset($userInfo['id'])){
            if ((int)$userInfo['status']!=1) return false;
            $this->groupUserInfo = $userInfo;
            return $userInfo['id'];
        }
        $userParam = $this->params['sender'];
        $userInfo = [
            'qq'    => $userParam['user_id'],
            'user_name' => $userParam['nickname'],
            'age'    => $userParam['age'],
            'sex'    => $this->getSex($userParam['sex']),
            'create_time'   => time(),
            'update_time'   =>time()
        ];
        $this->groupUserInfo = $userInfo;
        return $userModel->insertGetId($userInfo) ? : false;
    }
    
    private function order($taskId='', $type=''){
        try {
            $type = ucfirst($type);
        	$task = db("group_task")
        	->find($taskId);
        	if ($task) {
        		$dbModel = db('group_order');
	            $record = $dbModel
	                ->where([
	                    'user_id'   => $this->userId,
                        'task_id'   => $task['id']
                        ])
	                ->find();
	            $msg = "{$this->groupUserInfo['user_name']},";
	            if ($record) {
	                $result = $dbModel->update([
	                    'id' => $record['id'],
	                    'task_id'   => $task['id'],
	                    'type' => $this->changeOrderType($type),
	                    'update_time' => time()
	                ]);
	                $msg .= "更新点餐成功,";
	            } else {
	                $result = $dbModel->insert([
	                    'user_id' => $this->userId,
                        'task_id'   => $task['id'],
	                    'type' => $this->changeOrderType($type),
	                    'create_time' => time()
	                ]);
	                $msg .= "点餐成功,";
	            }
	            if ((int)$this->changeOrderType($type) == 9) {
	                $date = $this->changeDateType((int)date("w", $task['time']));
	            	$msg = "{$this->groupUserInfo['user_name']},好的知道了,你周{$date}不点。";
	            }else if ($result) {
	            	$data = json_decode($task['data'], true);
	            	$msg .= "您点的是:{$type}餐,【{$data[$type]}】。";
	            }else{
	            	$msg = "哦吼,点餐失败,系统出错了。";
	            }
	            $this->sendGroup($this->groupId, $msg);
        	}else{
        		$this->sendGroup($this->groupId, "{$this->groupUserInfo['user_name']},还没到点餐时间,到时间会通知你的,认真上班吧。");
        	}
        }catch (\Exception $e){
            $this->sendGroup($this->groupId, "卡bug了,错误信息:".$e->getMessage().'line:'.$e->getLine());
        }
    }

    /**
     * 添加点餐任务
     * @return mixed
     */
    public function add_task(){
        if (request()->isPost()){
            $content = input('post.content');
            //提取文本中的日期
            preg_match_all("'\d{1,2}月\d{1,2}'", $content,$arr1);
        	$time = empty($arr1[0][0]) ? time() : strtotime(date('Y') . '/' . str_replace(['月'], '/', $arr1[0][0]));
        	$dbModel = db("group_task");
        	$where = [
        		'time'	=> $time
        	];
           	$check = $dbModel->where($where)
           		->find();
           	if ($check){
           		$date = date('Y年m月d日', $time);
           		$this->error("{$date},该日期已存在订餐任务,不能重复添加!");
           	}
            preg_match_all('/([A-Za-z#].+)/u', $content,$arr);
            if (!empty($arr[1])) {
            	$result = [];
            	foreach ($arr[1] as $v){
	                $v = preg_replace("/(\s|\&nbsp\;| |\xc2\xa0)/","",$v);
	                $data = explode('餐',$v);
	                if (!empty($data[0]) && in_array($data[0], ['A', 'B', 'C']) && !empty($data[1])) {
	                	$result[$data[0]] = $data[1];
	                }
	            }
	            if (count($result)>=3) {
	            	$data = [
	            		'time'			=> $time,
	            		'data'			=> json_encode($result, JSON_UNESCAPED_UNICODE),
	            		'content'		=> $content,
	            		'create_time'	=> time()
	            	];
	            	$dbModel->insert($data);
	            	$this->success('添加点餐任务成功!');
	            }
            }
            $this->error('添加任务失败!');
            exit;
        }
        return $this->fetch();
    }
    
    /**
     * 推送qq消息
     */
    public function push_message(){
        if (request()->isPost()) {
            $content = input('post.content');
            $this->sendGroup($this->groupId, $content);
            //$this->sendGroup($this->groupId, "来活了,老铁们,开始点餐啦,A、B、C随便选,不点的回复不点,点餐时需要先@小助手,否则不管用哦,点餐12点准时结束,过时不候。");
            $this->success("推送成功!");
        }
        return $this->fetch();
    }
    
    /**
     * @return mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function views(){
        $userModel = db('group_user');
        $day = input("param.day") ? : (date('d'));
        $startTime = strtotime(date("Y-m-{$day} 00:00:00"));
        $task = db("group_task")
            ->where(['time'=> $startTime])
            ->find();
        $data = empty($task['id'])? [] : $userModel->alias('u')
            ->field("u.id,u.user_name,o.type,o.create_time")
            ->join("group_order o", "o.task_id = {$task['id']} and o.user_id = u.id", 'LEFT')
            ->where(['u.status' => 1])
            ->order("o.type asc")
            ->select();
        $result = $this->array_group_by($data, 'type');
        $notChoose = isset($result['']) ? implode(',', array_column($result[''], 'user_name')) : '';
        $this->assign('not_choose', $notChoose);
        ksort($result);
        $record = [];
        $msg = [];
        foreach ($result as $k => $v){
            if (!empty((int)$k) && in_array((int)$k, [1,2,3])){
                $num = count($v);
                $msg[] = $this->changeOrderType($k) ." {$num}份";
                $record[$k] = $num;
            }
        }
        $info =  '云商项目开发部: '.implode(', ', $msg) . " 共计:".array_sum($record) . "份";
        foreach ($data as $k => &$v){
            if (!empty($v['type']) && in_array((int)$v['type'], [1, 2, 3])){
                $v['status'] = 1;
            }else{
                $v['status'] = '';
            }
            $v['type'] = $this->changeOrderType($v['type']);
            if ($v['create_time']){
                $v['create_time'] = date('Y-m-d H:i:s', $v['create_time']);
            }
        }
        krsort($msg);
        if (request()->isPost()){
            $result = [
                'code'  => 0,
                'data'  => $data
            ];
            echo json_encode($result);
            exit;
        }
        if ($task){
            $task = json_decode($task['data'], true);
        }
        $days =  date('t', strtotime('Y-m')) + 1 ;
        $this->assign('days', $days);
        $this->assign('day', $day);
        $this->assign('task', $task);
        $this->assign('data', $data);
        $this->assign('info', $info);
        return $this->fetch();
    }

    /**
     * @return mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function view_month(){
        $days =  date('t', strtotime('2019-03'));
        $userModel = db('group_user');
        $orderModel = db('group_order');
        $month = input("param.month") ? : date('m');
        if (request()->isPost()){
            $startTime = strtotime(date("Y-{$month}-1"));
            $endTime = strtotime(date("Y-m-d", strtotime("last day of this month", strtotime(date("Y-{$month}-d")))));
            $data = $userModel
                ->field("id,user_name")
                ->where(['status' => 1])
                ->select();
            foreach ($data as &$v){
                $results = $orderModel->alias('o')->field("t.time")
                    ->join('group_task t', "t.id = o.task_id", "LEFT")
                    ->where([
                        'o.user_id' => $v['id'],
                        't.time'    => ['between', [$startTime, $endTime]],
                        'o.type'    => ['IN', [1,2,3]]
                    ])->select();
                $v['type']  = "午餐";
                $timeArr = array_column($results, 'time');
                for ($i=1;$i 0,
                'data'  => $data
            ];
            echo json_encode($result);
            exit;
        }
        $this->assign('months', [1,2,3,4,5,6,7,8,9,10,11,12]);
        $this->assign('month', $month);
        return $this->fetch();
    }

    public static function array_group_by($arr, $key)
    {
        $grouped = [];
        foreach ($arr as $value) {
            $grouped[$value[$key]][] = $value;
        }
        if (func_num_args() > 2) {
            $args = func_get_args();
            foreach ($grouped as $key => $value) {
                $parms = array_merge([$value], array_slice($args, 2, func_num_args()));
                $grouped[$key] = call_user_func_array('array_group_by', $parms);
            }
        }
        return $grouped;
    }

    /**
     * 点餐结束,推送点餐情况
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function push_order_result(){
        if (!empty($this->params['token']) && $this->params['token'] == $this->push_token)
        {
            $task = db("group_task")
                ->whereTime('create_time', 'today')
                ->select();
            if ($task)
            {
                $userModel = db('group_user');
                $msg = '今日点餐时间结束,点餐情况如下:'.PHP_EOL;
                foreach ($task as $v){
                    $orderDate = date('Y年m月d日', $v['time']);
                    $msg .= "用餐时间:{$orderDate}".PHP_EOL;
                    $data = $userModel->alias('u')
                        ->field("u.id,u.user_name,o.type,o.create_time")
                        ->join('group_order o', "o.user_id = u.id and task_id = {$v['id']}", 'LEFT')
                        ->where(['u.status' => 1])
                        ->select();
                    $orderData = json_decode($v['data'], true);
                    foreach ($data as $vo){
                        if (!empty($vo['type']) && in_array((int)$vo['type'], [1,2,3])){
                            $type = $this->changeOrderType($vo['type']);
                            $msg .= "{$vo['user_name']} : {$type}餐 【{$orderData[$type]}】".PHP_EOL;
                        }else{
                            $msg .= "{$vo['user_name']} : 不点".PHP_EOL;
                        }
                    }
                    $result = $this->array_group_by($data, 'type');
                    $typeMsg = [];
                    $record = [];
                    foreach ($result as $k => $v){
                        if (!empty((int)$k) && in_array((int)$k, [1,2,3])){
                            $num = count($v);
                            $typeMsg[$k] = $this->changeOrderType($k) ." {$num}份";
                            $record[$k] = $num;
                        }
                    }
                    ksort($typeMsg);
                    $msg .=  PHP_EOL."{$orderDate} 云商项目开发部: ".implode(', ', $typeMsg) . " 共计:".array_sum($record) . "份".PHP_EOL;
                }
                $this->sendGroup($this->groupId, $msg);
            }
        }
    }

    /**
     *  开饭前推送今天的点餐情况
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function push_today_order(){
        if (!empty($this->params['token']) && $this->params['token'] == $this->push_token)
        {
            $task = db("group_task")
                ->whereTime('time', 'today')
                ->find();
            if ($task){
                $userModel = db('group_user');
                $orderDate = date('m月d日', $task['time']);
                $msg = "下班开饭,{$orderDate}点餐情况如下,请对号入座:".PHP_EOL;
                $data = $userModel->alias('u')
                    ->field("u.id,u.user_name,o.type,o.create_time")
                    ->join('group_order o', "o.user_id = u.id and task_id = {$task['id']}", 'LEFT')
                    ->where(['u.status' => 1])
                    ->select();
                $orderData = json_decode($task['data'], true);
                foreach ($data as $vo){
                    if (!empty($vo['type']) && in_array((int)$vo['type'], [1,2,3])){
                        $type = $this->changeOrderType($vo['type']);
                        $msg .= "{$vo['user_name']} : {$type}餐 【{$orderData[$type]}】".PHP_EOL;
                    }else{
                        $msg .= "{$vo['user_name']} : 不点".PHP_EOL;
                    }
                }
                //$msg .=  PHP_EOL."你喜欢岁月静好,其实现实是大江奔流。";
                $this->sendGroup($this->groupId, $msg);
            }
        }
    }


    /**
     * 获取未来7天分别是周几
     * @param bool $time
     * @return array
     */
    public function getLastSevenDays($time = false){
        if (!$time) $time = time();
        $data = [];
        for ($i=0;$i<7;$i++){
            $data[date('w', $time)] = date('Y-m-d 00:00:00', $time);
            $time += 60*60*24;
        }
        return $data;
    }

    /**
     * 把中文的周几转换为数字并对应php date()方法
     * @param string $date
     * @return bool|int
     */
    public function changeDateType($date = ''){
        $result = false;
        if (is_int($date)){
            switch ($date){
                case 1:
                    $result = '一';
                    break;
                case 2:
                    $result = '二';
                    break;
                case 3:
                    $result = '三';
                    break;
                case 4:
                    $result = '四';
                    break;
                case 5:
                    $result = '五';
                    break;
                case 6:
                    $result = '六';
                    break;
                case 0:
                    $result = '日';
                    break;
            }
        }else{
            if (empty($date)) return $result;
            switch ($date){
                case '一':
                    $result = 1;
                    break;
                case '二':
                    $result = 2;
                    break;
                case '三':
                    $result = 3;
                    break;
                case '四':
                    $result = 4;
                    break;
                case '五':
                    $result = 5;
                    break;
                case '六':
                    $result = 6;
                    break;
                case '日':
                    $result = 0;
                    break;
            }
        }
        return $result;
    }