通过酷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|\ \;| |\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;
}
