userid, $timerange->updated, $timerange->deleted); // return Base::retSuccess('success', $data); } /** * @api {get} api/dialog/beyond 列表外对话 * * @apiDescription 需要token身份,列表外的未读对话 和 列表外的待办对话 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName beyond * * @apiParam {String} unread_at 在这个时间之前未读的数据 * - 格式1:2021-01-01 00:00:00 * - 格式2:1612051200 * @apiParam {String} todo_at 在这个时间之前待办的数据 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function beyond() { $user = User::auth(); // $unreadAt = Request::input('unread_at'); $todoAt = Request::input('todo_at'); // $data = WebSocketDialog::getDialogBeyond($user->userid, Base::newCarbon($unreadAt), Base::newCarbon($todoAt)); // return Base::retSuccess('success', $data); } /** * @api {get} api/dialog/search 搜索会话 * * @apiDescription 根据消息关键词搜索相关会话,需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName search * * @apiParam {String} key 消息关键词 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function search() { $user = User::auth(); // $key = trim(Request::input('key')); if (empty($key)) { return Base::retError('请输入搜索关键词'); } // 搜索会话 $take = 20; $list = WebSocketDialog::searchDialog($user->userid, $key, $take); // 搜索联系人 if (count($list) < $take && Base::judgeClientVersion("0.21.60")) { $users = User::searchUser($key, $take - count($list)); $users->transform(function (User $item) use ($user) { $id = 'u:' . $item->userid; $lastAt = null; $lastMsg = null; $dialog = WebSocketDialog::getUserDialog($user->userid, $item->userid, now()->addDay()); if ($dialog) { $id = $dialog->id; $row = WebSocketDialogMsg::whereDialogId($dialog->id)->orderByDesc('id')->first(); if ($row) { $lastAt = Carbon::parse($row->created_at)->toDateTimeString(); $lastMsg = WebSocketDialog::lastMsgFormat($row->toArray()); } } return [ 'id' => $id, 'type' => 'user', 'name' => $item->nickname, 'dialog_user' => $item, 'last_at' => $lastAt, 'last_msg' => $lastMsg, ]; }); $list = array_merge($list, $users->toArray()); } // 搜索消息会话 if (count($list) < $take) { $searchResults = ZincSearchDialogMsg::search($user->userid, $key, 0, $take - count($list)); if ($searchResults) { foreach ($searchResults as $item) { if ($dialog = WebSocketDialog::find($item['id'])) { $dialog = array_merge($dialog->toArray(), $item); $list[] = WebSocketDialog::synthesizeData($dialog, $user->userid); } } } } // return Base::retSuccess('success', $list); } /** * @api {get} api/dialog/search/tag 搜索标注会话 * * @apiDescription 根据消息关键词搜索相关会话,需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName search__tag * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function search__tag() { $user = User::auth(); // 搜索会话 $msgs = DB::table('web_socket_dialog_users as u') ->select(['d.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', 'u.updated_at as user_at', 'm.id as search_msg_id']) ->join('web_socket_dialogs as d', 'u.dialog_id', '=', 'd.id') ->join('web_socket_dialog_msgs as m', 'm.dialog_id', '=', 'd.id') ->where('u.userid', $user->userid) ->whereNull('d.deleted_at') ->where('m.tag', '>', 0) ->orderByDesc('m.id') ->take(50) ->get() ->map(function($item) use ($user) { return WebSocketDialog::synthesizeData($item, $user->userid); }) ->all(); // return Base::retSuccess('success', $msgs); } /** * @api {get} api/dialog/one 获取单个会话信息 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName one * * @apiParam {Number} dialog_id 对话ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function one() { $user = User::auth(); // $dialog_id = intval(Request::input('dialog_id')); // $dialog = WebSocketDialog::checkDialog($dialog_id); $data = WebSocketDialog::synthesizeData($dialog, $user->userid); // return Base::retSuccess('success', $data); } /** * @api {get} api/dialog/user 获取会话成员 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName user * * @apiParam {Number} dialog_id 会话ID * @apiParam {Number} [getuser] 获取会员详情(1: 返回会员昵称、邮箱等基本信息,0: 默认不返回) * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function user() { User::auth(); // $dialog_id = intval(Request::input('dialog_id')); $getuser = intval(Request::input('getuser', 0)); // $dialog = WebSocketDialog::checkDialog($dialog_id); // if ($getuser === 1) { $data = $dialog->dialogUserBuilder()->get(); $array = array_filter($data->toArray(), function ($item) { return $item['userid'] > 0; }); foreach ($array as &$item) { $item['online'] = $item['bot'] || OnlineData::live($item['userid']) > 0; } } else { $data = WebSocketDialogUser::select(['web_socket_dialog_users.*', 'users.bot']) ->join('users', 'web_socket_dialog_users.userid', '=', 'users.userid') ->where('web_socket_dialog_users.dialog_id', $dialog_id) ->whereNull('users.disable_at') ->orderBy('web_socket_dialog_users.id') ->get(); $array = $data->toArray(); } return Base::retSuccess('success', $array); } /** * @api {get} api/dialog/todo 获取会话待办 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName todo * * @apiParam {Number} [dialog_id] 会话ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function todo() { $user = User::auth(); // $dialog_id = intval(Request::input('dialog_id')); // $builder = WebSocketDialogMsgTodo::whereUserid($user->userid)->whereDoneAt(null); if ($dialog_id > 0) { WebSocketDialog::checkDialog($dialog_id); $builder->whereDialogId($dialog_id); } // $list = $builder->orderByDesc('id')->take(50)->get(); return Base::retSuccess("success", $list); } /** * @api {get} api/dialog/top 会话置顶 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName top * * @apiParam {Number} dialog_id 会话ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function top() { $user = User::auth(); $dialogId = intval(Request::input('dialog_id')); $dialogUser = WebSocketDialogUser::whereUserid($user->userid)->whereDialogId($dialogId)->first(); if (!$dialogUser) { return Base::retError("会话不存在"); } $dialogUser->top_at = $dialogUser->top_at ? null : Carbon::now(); $dialogUser->save(); return Base::retSuccess("success", [ 'id' => $dialogUser->dialog_id, 'top_at' => $dialogUser->top_at?->toDateTimeString(), ]); } /** * @api {get} api/dialog/hide 会话隐藏 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName hide * * @apiParam {Number} dialog_id 会话ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function hide() { $user = User::auth(); $dialogId = intval(Request::input('dialog_id')); $dialogUser = WebSocketDialogUser::whereUserid($user->userid)->whereDialogId($dialogId)->first(); if (!$dialogUser) { return Base::retError("会话不存在"); } if ($dialogUser->top_at) { return Base::retError("置顶会话无法隐藏"); } $dialogUser->hide = 1; $dialogUser->save(); return Base::retSuccess("success", [ 'id' => $dialogUser->dialog_id, 'hide' => 1, ]); } /** * @api {get} api/dialog/tel 获取对方联系电话 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName tel * * @apiParam {Number} dialog_id 会话ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function tel() { $user = User::auth(); // $dialog_id = intval(Request::input('dialog_id')); // $dialog = WebSocketDialog::checkDialog($dialog_id); if ($dialog->type !== 'user') { return Base::retError("会话类型错误"); } $dialogUser = $dialog->dialogUserBuilder(['tel'])->where('users.userid', '!=', $user->userid)->first(); if (empty($dialogUser)) { return Base::retError("会话对象不存在"); } if (empty($dialogUser->tel)) { return Base::retError("对方未设置联系电话"); } if ($user->isTemp()) { return Base::retError("无法查看联系电话"); } // $add = null; $res = WebSocketDialogMsg::sendMsg(null, $dialog->id, 'notice', [ 'notice' => $user->nickname . " 查看了 " . $dialogUser->nickname . " 的联系电话" ]); if (Base::isSuccess($res)) { $add = $res['data']; } // return Base::retSuccess("success", [ 'tel' => $dialogUser->tel, 'add' => $add ?: null ]); } /** * @api {get} api/dialog/open/user 打开会话 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName open__user * * @apiParam {Number} userid 对话会员ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function open__user() { $user = User::auth(); // $userid = intval(Request::input('userid')); if (empty($userid)) { return Base::retError('错误的会话'); } // $dialog = WebSocketDialog::checkUserDialog($user, $userid); if (empty($dialog)) { return Base::retError('打开会话失败'); } $data = WebSocketDialog::synthesizeData($dialog->id, $user->userid); return Base::retSuccess('success', $data); } /** * @api {get} api/dialog/open/event 打开会话事件 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName open__event * * @apiParam {Number} dialog_id 对话ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function open__event() { $user = User::auth(); // $dialog_id = intval(Request::input('dialog_id')); // $dialog = WebSocketDialog::checkDialog($dialog_id); if (empty($dialog)) { return Base::retError('打开会话失败'); } // Cache::remember("webhook_dialog_open_{$dialog->id}_{$user->userid}", Carbon::now()->addMinute(), function () use ($dialog, $user) { $dialog->dispatchMemberWebhook(UserBot::WEBHOOK_EVENT_DIALOG_OPEN, $user->userid, $user->userid); return true; }); // return Base::retSuccess('success'); } /** * @api {get} api/dialog/msg/list 获取消息列表 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__list * * @apiParam {Number} dialog_id 对话ID * @apiParam {Number} [msg_id] 消息ID * @apiParam {Number} [position_id] 此消息ID前后的数据 * @apiParam {Number} [prev_id] 此消息ID之前的数据 * @apiParam {Number} [next_id] 此消息ID之后的数据 * - position_id、prev_id、next_id 只有一个有效,优先循序为:position_id > prev_id > next_id * @apiParam {String} [msg_type] 消息类型 * - tag: 标记 * - link: 链接 * - text: 文本 * - image: 图片 * - file: 文件 * - record: 录音 * - meeting: 会议 * * @apiParam {Number} [take] 获取条数,默认:50,最大:100 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__list() { $user = User::auth(); // $dialog_id = intval(Request::input('dialog_id')); $msg_id = intval(Request::input('msg_id')); $position_id = intval(Request::input('position_id')); $prev_id = intval(Request::input('prev_id')); $next_id = intval(Request::input('next_id')); $msg_type = trim(Request::input('msg_type')); $take = Base::getPaginate(100, 50); $data = []; // $dialog = WebSocketDialog::checkDialog($dialog_id); $reDialog = true; // $builder = WebSocketDialogMsg::select([ 'web_socket_dialog_msgs.*', 'read.mention', 'read.dot', 'read.read_at', ])->leftJoin('web_socket_dialog_msg_reads as read', function ($leftJoin) use ($user) { $leftJoin ->on('read.userid', '=', DB::raw($user->userid)) ->on('read.msg_id', '=', 'web_socket_dialog_msgs.id'); })->where('web_socket_dialog_msgs.dialog_id', $dialog_id); // if ($dialog->session_id > 0) { $builder->whereSessionId($dialog->session_id); } if ($msg_type) { if ($msg_type === 'tag') { $builder->where('tag', '>', 0); } elseif ($msg_type === 'todo') { $builder->where('todo', '>', 0); } elseif ($msg_type === 'link') { $builder->whereLink(1); } elseif (in_array($msg_type, ['text', 'image', 'file', 'record', 'meeting'])) { $builder->whereMtype($msg_type); } else { return Base::retError('参数错误'); } $reDialog = false; } if ($msg_id > 0) { $builder->whereReplyId($msg_id); $reDialog = false; } // if ($position_id > 0) { $array = $builder->clone() ->where('web_socket_dialog_msgs.id', '>=', $position_id) ->orderBy('web_socket_dialog_msgs.id') ->take(intval($take / 2)) ->get(); $prev_id = intval($array->last()?->id); } // $cloner = $builder->clone(); if ($prev_id > 0) { $cloner->where('web_socket_dialog_msgs.id', '<=', $prev_id)->orderByDesc('web_socket_dialog_msgs.id'); $reDialog = false; } elseif ($next_id > 0) { $cloner->where('web_socket_dialog_msgs.id', '>=', $next_id)->orderBy('web_socket_dialog_msgs.id'); $reDialog = false; } else { $cloner->orderByDesc('web_socket_dialog_msgs.id'); } $list = $cloner->take($take)->get()->sortByDesc('id', SORT_NUMERIC)->values(); // if ($list->isNotEmpty()) { $list->transform(function (WebSocketDialogMsg $item) { $item->todo_done = $item->isTodoDone(); $item->next_id = 0; $item->prev_id = 0; return $item; }); $first = $list->first(); $first->next_id = intval($builder->clone() ->where('web_socket_dialog_msgs.id', '>', $first->id) ->orderBy('web_socket_dialog_msgs.id') ->value('id')); $last = $list->last(); $last->prev_id = intval($builder->clone() ->where('web_socket_dialog_msgs.id', '<', $last->id) ->orderByDesc('web_socket_dialog_msgs.id') ->value('id')); } $data['list'] = $list; $data['time'] = Timer::time(); // 记录当前打开的任务对话 if ($dialog->type == 'group' && $dialog->group_type == 'task') { $user->task_dialog_id = $dialog->id; $user->save(); } // 去掉标记未读 $isMarkDialogUser = WebSocketDialogUser::whereDialogId($dialog->id)->whereUserid($user->userid)->whereMarkUnread(1)->first(); if ($isMarkDialogUser) { $isMarkDialogUser->mark_unread = 0; $isMarkDialogUser->save(); } // if ($reDialog) { $data['dialog'] = WebSocketDialog::synthesizeData($dialog, $user->userid, true); $data['todo'] = $data['dialog']['todo_num'] > 0 ? WebSocketDialogMsgTodo::whereDialogId($dialog->id)->whereUserid($user->userid)->whereDoneAt(null)->orderByDesc('id')->take(50)->get() : []; $data['top'] = $dialog->top_msg_id ? WebSocketDialogMsg::whereId($dialog->top_msg_id)->first() : null; } return Base::retSuccess('success', $data); } /** * @api {get} api/dialog/msg/latest 获取最新消息列表 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__latest * * @apiParam {Array} [dialogs] 对话ID列表 * - 格式:[{id:会话ID, latest_id:此消息ID之后的数据}, ...] * @apiParam {Number} [take] 每个会话获取多少条,默认:25,最大:50 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__latest() { if (!Base::judgeClientVersion('0.34.47')) { return Base::retSuccess('success', ['data' => []]); } // $user = User::auth(); // $dialogs = Request::input('dialogs'); if (empty($dialogs) || !is_array($dialogs)) { return Base::retError('参数错误'); } $builder = WebSocketDialogMsg::select([ 'web_socket_dialog_msgs.*', 'read.mention', 'read.dot', 'read.read_at', ])->leftJoin('web_socket_dialog_msg_reads as read', function ($leftJoin) use ($user) { $leftJoin ->on('read.userid', '=', DB::raw($user->userid)) ->on('read.msg_id', '=', 'web_socket_dialog_msgs.id'); }); $data = []; $num = 0; foreach ($dialogs as $item) { $dialog_id = intval($item['id']); $latest_id = intval($item['latest_id']); if ($dialog_id <= 0) { continue; } if ($num >= 5) { break; } $num++; WebSocketDialog::checkDialog($dialog_id); // $cloner = $builder->clone(); $cloner->where('web_socket_dialog_msgs.dialog_id', $dialog_id); if ($latest_id > 0) { $cloner->where('web_socket_dialog_msgs.id', '>', $latest_id); } $cloner->orderByDesc('web_socket_dialog_msgs.id'); $list = $cloner->take(Base::getPaginate(50, 25, 'take'))->get(); if ($list->isNotEmpty()) { $data = array_merge($data, $list->toArray()); } } return Base::retSuccess('success', compact('data')); } /** * @api {get} api/dialog/msg/search 搜索消息 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__search * * @apiParam {String} key 搜索关键词 * @apiParam {Number} [dialog_id] 对话ID(存在则搜索消息在对话的位置) * @apiParam {Number} [take] 搜索数量 * - dialog_id > 0, 默认:200,最大:200 * - dialog_id <= 0, 默认:20,最大:50 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__search() { $user = User::auth(); // $key = trim(Request::input('key')); $dialogId = intval(Request::input('dialog_id')); // if (empty($key)) { return Base::retError('关键词不能为空'); } // if ($dialogId > 0) { // 搜索位置 WebSocketDialog::checkDialog($dialogId); // $data = WebSocketDialogMsg::whereDialogId($dialogId) ->where('key', 'LIKE', "%{$key}%") ->take(Base::getPaginate(200, 200, 'take')) ->pluck('id'); return Base::retSuccess('success', compact('data')); } else { // 搜索消息 $list = []; $searchResults = ZincSearchDialogMsg::search($user->userid, $key, 0, Base::getPaginate(50, 20, 'take')); if ($searchResults) { foreach ($searchResults as $item) { if ($dialog = WebSocketDialog::find($item['id'])) { $dialog = array_merge($dialog->toArray(), $item); $list[] = WebSocketDialog::synthesizeData($dialog, $user->userid); } } } return Base::retSuccess('success', ['data' => $list]); } } /** * @api {get} api/dialog/msg/one 获取单条消息 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__one * * @apiParam {Number} msg_id 消息ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__one() { User::auth(); // $msg_id = intval(Request::input('msg_id')); // $msg = WebSocketDialogMsg::whereId($msg_id)->first(); if (empty($msg)) { return Base::retError("消息不存在或已被删除"); } WebSocketDialog::checkDialog($msg->dialog_id); // return Base::retSuccess('success', $msg); } /** * @api {get} api/dialog/msg/dot 聊天消息去除点 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__dot * * @apiParam {Number} id 消息ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__dot() { $user = User::auth(); // $id = intval(Request::input('id')); // $msg = WebSocketDialogMsg::find($id); if (empty($msg)) { return Base::retError("消息不存在或已被删除"); } // WebSocketDialogMsgRead::whereMsgId($id)->whereUserid($user->userid)->change(['dot' => 0]); // return Base::retSuccess('success', [ 'id' => $msg->id, 'dot' => 0, ]); } /** * @api {get} api/dialog/msg/read 已读聊天消息 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__read * * @apiParam {Object} id 消息ID(组) * - 1、多个ID用逗号分隔,如:1,2,3 * - 2、另一种格式:{"id": "会话ID|0"},如:{"2": 0, "3": 10} * -- 会话ID:标记id之后的消息已读 * -- 其他:标记已读 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__read() { $user = User::auth(); // $id = Request::input('id'); $ids = $id && is_array($id) ? $id : array_fill_keys(Base::explodeInt($id), 'r'); // $dialogIds = []; $markIds = []; WebSocketDialogMsg::whereIn('id', array_keys($ids))->chunkById(100, function($list) use ($ids, $user, &$dialogIds, &$markIds) { /** @var WebSocketDialogMsg $item */ foreach ($list as $item) { $item->readSuccess($user->userid); $dialogIds[$item->dialog_id] = $item->dialog_id; if ($ids[$item->id] == $item->dialog_id) { $markIds[$item->dialog_id] = min($item->id, $markIds[$item->dialog_id] ?? 0); } } }); // foreach ($markIds as $dialogId => $msgId) { WebSocketDialogMsgRead::whereDialogId($dialogId) ->whereUserid($user->userid) ->whereReadAt(null) ->where('msg_id', '>=', $msgId) ->chunkById(100, function ($list) { WebSocketDialogMsgRead::onlyMarkRead($list); }); } // $data = []; $dialogUsers = WebSocketDialogUser::with(['webSocketDialog'])->whereUserid($user->userid)->whereIn('dialog_id', array_values($dialogIds))->get(); foreach ($dialogUsers as $dialogUser) { if (!$dialogUser->webSocketDialog) { continue; } $dialogUser->updated_at = Carbon::now(); $dialogUser->save(); // $unreadData = WebSocketDialog::generateUnread($dialogUser->dialog_id, $user->userid); $data[] = [ 'id' => $dialogUser->dialog_id, 'unread' => $unreadData['unread'], 'unread_one' => $unreadData['unread_one'], 'mention' => $unreadData['mention'], 'mention_ids' => $unreadData['mention_ids'], 'user_at' => Carbon::parse($dialogUser->updated_at)->toDateTimeString('millisecond'), 'user_ms' => Carbon::parse($dialogUser->updated_at)->valueOf() ]; } return Base::retSuccess('success', $data); } /** * @api {get} api/dialog/msg/unread 获取未读消息数据 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__unread * * @apiParam {Number} dialog_id 对话ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 * @apiSuccessExample {json} data: { "id": 43, "unread": 308, "mention": 11, "user_at": "2020-12-12 00:00:00.000", "user_ms": 1677558147167, } */ public function msg__unread() { $dialog_id = intval(Request::input('dialog_id')); // $dialogUser = WebSocketDialogUser::with(['webSocketDialog'])->whereDialogId($dialog_id)->whereUserid(User::userid())->first(); if (empty($dialogUser?->webSocketDialog)) { return Base::retError('会话不存在'); } $unreadData = WebSocketDialog::generateUnread($dialog_id, $dialogUser->userid); // return Base::retSuccess('success', [ 'id' => $dialog_id, 'unread' => $unreadData['unread'], 'unread_one' => $unreadData['unread_one'], 'mention' => $unreadData['mention'], 'mention_ids' => $unreadData['mention_ids'], 'user_at' => Carbon::parse($dialogUser->updated_at)->toDateTimeString('millisecond'), 'user_ms' => Carbon::parse($dialogUser->updated_at)->valueOf() ]); } /** * @api {get} api/dialog/msg/checked 设置消息checked * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__checked * * @apiParam {Number} dialog_id 对话ID * @apiParam {Number} msg_id 消息ID * @apiParam {Number} index li 位置 * @apiParam {Number} checked 标记、取消标记 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 * @apiSuccessExample {json} data: { "id": 43, "msg": { // .... }, } */ public function msg__checked() { $user = User::auth(); // $dialog_id = intval(Request::input('dialog_id')); $msg_id = intval(Request::input('msg_id')); $index = intval(Request::input('index')); $checked = intval(Request::input('checked')); // $dialogMsg = WebSocketDialogMsg::whereId($msg_id)->whereDialogId($dialog_id)->first(); if (empty($dialogMsg)) { return Base::retError('消息不存在'); } if ($dialogMsg->userid != $user->userid) { return Base::retError('仅支持修改自己的消息'); } if ($dialogMsg->type !== 'text') { return Base::retError('仅支持文本消息'); } // $oldMsg = Base::json2array($dialogMsg->getRawOriginal('msg')); $oldText = $oldMsg['text'] ?? ''; $newText = preg_replace_callback('/
{$leave_message}
"; } // return WebSocketDialogMsg::sendMsgBatch($user, $userids, $dialogids, $fileMsg); } /** * @api {get} api/dialog/msg/sendtaskid 通过任务ID发送任务 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__sendtaskid * * @apiParam {Number} task_id 消息ID * @apiParam {Array} dialogids 转发给的对话ID * @apiParam {Array} userids 转发给的成员ID * @apiParam {String} leave_message 转发留言 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__sendtaskid() { $user = User::auth(); // $task_id = intval(Request::input("task_id")); $dialogids = Request::input('dialogids'); $userids = Request::input('userids'); $leave_message = Request::input('leave_message'); // if (empty($dialogids) && empty($userids)) { return Base::retError("请选择对话或成员"); } // $task = ProjectTask::userTask($task_id, null); $taskMsg = "#{$task->name}
"; if ($leave_message) { $taskMsg .= "{$leave_message}
"; } // return WebSocketDialogMsg::sendMsgBatch($user, $userids, $dialogids, $taskMsg); } /** * @api {post} api/dialog/msg/sendanon 发送匿名消息 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__sendanon * * @apiParam {Number} userid 对方会员ID * @apiParam {String} text 消息内容 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__sendanon() { User::auth(); // $userid = intval(Request::input('userid')); $text = trim(Request::input('text')); // $anonMessage = Base::settingFind('system', 'anon_message', 'open'); if ($anonMessage != 'open') { return Base::retError("匿名消息功能暂停使用"); } // $toUser = User::whereUserid($userid)->first(); if (empty($toUser) || $toUser->bot) { return Base::retError("匿名消息仅允许发送给个人"); } if ($toUser->isDisable()) { return Base::retError("对方已离职"); } $strlen = mb_strlen($text); if ($strlen < 1) { return Base::retError('消息内容不能为空'); } if ($strlen > 2000) { return Base::retError('消息内容最大不能超过2000字'); } // $botUser = User::botGetOrCreate('anon-msg'); if (empty($botUser)) { return Base::retError('匿名机器人不存在'); } $dialog = WebSocketDialog::checkUserDialog($botUser, $toUser->userid); if (empty($dialog)) { return Base::retError('匿名机器人会话不存在'); } return WebSocketDialogMsg::sendMsg(null, $dialog->id, 'template', [ 'type' => 'content', 'content' => $text, ], $botUser->userid, true); } /** * @api {post} api/dialog/msg/sendbot 发送机器人消息 * * @apiDescription 需要token身份,通过机器人发送消息给指定用户 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__sendbot * * @apiParam {Number} userid 对方会员ID * @apiParam {String} text 消息内容,markdown格式 * @apiParam {String} [bot_type] 机器人类型 * - system-msg: 系统消息(默认) * - task-alert: 任务提醒 * - check-in: 签到打卡 * - approval-alert: 审批 * - meeting-alert: 会议通知 * - xxxxxx: 其他机器人,xxxxxx 是任意6-20个字符串(如果不存在,则自动创建) * @apiParam {String} [bot_name] 机器人名称(bot_type 为 xxxxxx 时有效) * @apiParam {Boolean} [silence] 静默发送 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__sendbot() { $user = User::auth(); // $userid = intval(Request::input('userid')); $text = trim(Request::input('text')); $botType = trim(Request::input('bot_type', 'system-msg')); $botName = trim(Request::input('bot_name')); $silence = Request::input('silence', false); // $toUser = User::whereUserid($userid)->first(); if (empty($toUser) || $toUser->bot) { return Base::retError("机器人消息仅允许发送给个人"); } if ($toUser->isDisable()) { return Base::retError("对方已离职"); } $strlen = mb_strlen($text); if ($strlen < 1) { return Base::retError('消息内容不能为空'); } if ($strlen > 2000) { return Base::retError('消息内容最大不能超过2000字'); } // $botUpdate = []; if (!in_array($botType, [ 'system-msg', 'task-alert', 'check-in', 'approval-alert', 'meeting-alert', 'bot-manager', ])) { if (strlen($botType) < 6 || strlen($botType) > 20) { return Base::retError("机器人类型由6-20个字符组成。"); } if ($botName && (strlen($botName) < 2 || strlen($botName) > 20)) { return Base::retError("机器人名称由2-20个字符组成。"); } $botType = 'user-auto-' . $botType; $botUpdate['nickname'] = $botName; } $botUser = User::botGetOrCreate($botType, $botUpdate, $user->userid); if (empty($botUser)) { return Base::retError('机器人不存在'); } $dialog = WebSocketDialog::checkUserDialog($botUser, $toUser->userid); if (empty($dialog)) { return Base::retError('机器人会话不存在'); } $msgData = [ 'type' => 'md', 'text' => $text, ]; return WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', $msgData, $botUser->userid, false, false, $silence); } /** * @api {post} api/dialog/msg/sendlocation 发送位置消息 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__sendlocation * * @apiParam {Number} dialog_id 对话ID * @apiParam {String} type 位置类型 * - baidu: 百度地图 * - amap: 高德地图 * - tencent: 腾讯地图 * @apiParam {Number} lng 经度 * @apiParam {Number} lat 纬度 * @apiParam {String} title 位置名称 * @apiParam {Number} [distance] 距离(米) * @apiParam {String} [address] 位置地址 * @apiParam {String} [thumb] 预览图片(url) * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__sendlocation() { $user = User::auth(); // $dialog_id = intval(Request::input('dialog_id')); $type = strtolower(trim(Request::input('type'))); $lng = floatval(Request::input('lng')); $lat = floatval(Request::input('lat')); $title = trim(Request::input('title')); $distance = intval(Request::input('distance')); $address = trim(Request::input('address')); $thumb = trim(Request::input('thumb')); // if (empty($lng) || $lng < -180 || $lng > 180 || empty($lat) || $lat < -90 || $lat > 90) { return Base::retError('经纬度错误'); } if (empty($title)) { return Base::retError('位置名称不能为空'); } // WebSocketDialog::checkDialog($dialog_id); // if (in_array($type, ['baidu', 'amap', 'tencent'])) { $msgData = [ 'type' => $type, 'lng' => $lng, 'lat' => $lat, 'title' => $title, 'distance' => $distance, 'address' => $address, 'thumb' => $thumb, ]; return WebSocketDialogMsg::sendMsg(null, $dialog_id, 'location', $msgData, $user->userid); } return Base::retError('位置类型错误'); } /** * @api {get} api/dialog/msg/readlist 获取消息阅读情况 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__readlist * * @apiParam {Number} msg_id 消息ID(需要是消息的发送人) * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__readlist() { $user = User::auth(); // $msg_id = intval(Request::input('msg_id')); // $msg = WebSocketDialogMsg::whereId($msg_id)->whereUserid($user->userid)->first(); if (empty($msg)) { return Base::retError('不是发送人'); } // $read = WebSocketDialogMsgRead::whereMsgId($msg_id)->get(); return Base::retSuccess('success', $read ?: []); } /** * @api {get} api/dialog/msg/detail 消息详情 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__detail * * @apiParam {Number} msg_id 消息ID * @apiParam {String} only_update_at 仅获取update_at字段 * - no (默认) * - yes * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__detail() { $user =User::auth(); // $msg_id = intval(Request::input('msg_id')); $only_update_at = Request::input('only_update_at', 'no'); // $dialogMsg = WebSocketDialogMsg::whereId($msg_id)->first(); if (empty($dialogMsg)) { return Base::retError("文件不存在"); } // if ($only_update_at == 'yes') { return Base::retSuccess('success', [ 'id' => $dialogMsg->id, 'update_at' => Carbon::parse($dialogMsg->updated_at)->toDateTimeString() ]); } // $data = $dialogMsg->toArray(); // if ($data['type'] == 'file') { $msg = Base::json2array($dialogMsg->getRawOriginal('msg')); $msg = File::formatFileData($msg); $data['content'] = $msg['content']; $data['file_mode'] = $msg['file_mode']; } elseif ($data['type'] == 'longtext') { $data['content'] = [ 'type' => 'htm', 'content' => Doo::translate("内容不存在") ]; if (isset($data['msg']['file']['path'])) { $filePath = public_path($data['msg']['file']['path']); if (file_exists($filePath)) { $data['content']['type'] = $data['msg']['type']; $data['content']['content'] = file_get_contents($filePath); } } } // if ($dialogMsg->type === 'file') { UserRecentItem::record( $user->userid, UserRecentItem::TYPE_MESSAGE_FILE, $dialogMsg->id, UserRecentItem::SOURCE_DIALOG, $dialogMsg->dialog_id ); } return Base::retSuccess('success', $data); } /** * @api {get} api/dialog/msg/download 文件下载 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__download * * @apiParam {Number} msg_id 消息ID * @apiParam {String} down 直接下载 * - yes: 下载(默认) * - preview: 转预览地址 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__download() { User::auth(); // $msg_id = intval(Request::input('msg_id')); $down = Request::input('down', 'yes'); // $msg = WebSocketDialogMsg::whereId($msg_id)->first(); abort_if(empty($msg), 403, "This file not exist."); abort_if($msg->type != 'file', 403, "This file not support download."); $array = Base::json2array($msg->getRawOriginal('msg')); // if ($down === 'preview') { return Redirect::to(FileContent::toPreviewUrl($array)); } // $filePath = public_path($array['path']); return Base::DownloadFileResponse($filePath, $array['name']); } /** * @api {get} api/dialog/msg/withdraw 聊天消息撤回 * * @apiDescription 消息撤回限制24小时内,需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__withdraw * * @apiParam {Number} msg_id 消息ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__withdraw() { $user = User::auth(); $msg_id = intval(Request::input("msg_id")); $msg = WebSocketDialogMsg::whereId($msg_id)->whereUserid($user->userid)->first(); if (empty($msg)) { return Base::retError("消息不存在或已被删除"); } $dialog = WebSocketDialog::checkDialog($msg->dialog_id); // if (!$user->bot && !$dialog->isSelfDialog()) { Setting::validateMsgLimit('rev', $msg); } $msg->withdrawMsg(); return Base::retSuccess("success"); } /** * @api {get} api/dialog/msg/voice2text 语音消息转文字 * * @apiDescription 将语音消息转文字,需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__voice2text * * @apiParam {Number} msg_id 消息ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__voice2text() { $user = User::auth(); // $msg_id = intval(Request::input("msg_id")); $msg = WebSocketDialogMsg::whereId($msg_id)->first(); if (empty($msg)) { return Base::retError("消息不存在或已被删除"); } if ($msg->type !== 'record') { return Base::retError("仅支持语音消息"); } $msgData = Base::json2array($msg->getRawOriginal('msg')); if ($msgData['text']) { $textUserid = is_array($msgData['text_userid']) ? $msgData['text_userid'] : []; if (!in_array($user->userid, $textUserid)) { $textUserid[] = $user->userid; $msg->updateInstance([ 'msg' => array_merge($msgData, ['text_userid' => $textUserid]), ]); $msg->save(); } return Base::retSuccess("success", $msg); } WebSocketDialog::checkDialog($msg->dialog_id); // $result = AI::transcriptions(public_path($msgData['path'])); if (Base::isError($result)) { return $result; } // $msg->updateInstance([ 'msg' => array_merge($msgData, [ 'text' => $result['data']['text'], 'text_userid' => [$user->userid] ]), ]); $msg->save(); return Base::retSuccess("success", $msg); } /** * @api {get} api/dialog/msg/translation 翻译消息 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__translation * * @apiParam {Number} msg_id 消息ID * @apiParam {Number} [force] 强制翻译(1是、0否) * - 默认不强制翻译,已翻译过的消息不再翻译 * @apiParam {String} [language] 目标语言,默认当前语言 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__translation() { User::auth(); // $msg_id = intval(Request::input("msg_id")); $force = intval(Request::input("force")); $language = Base::inputOrHeader('language'); $targetLanguage = Doo::getLanguages($language); // if (empty($targetLanguage)) { return Base::retError("参数错误"); } $msg = WebSocketDialogMsg::whereId($msg_id)->first(); if (empty($msg)) { return Base::retError("消息不存在或已被删除"); } if (!in_array($msg->type, ['text', 'record'])) { return Base::retError("此消息不支持翻译"); } WebSocketDialog::checkDialog($msg->dialog_id); // $row = WebSocketDialogMsgTranslate::whereMsgId($msg_id)->whereLanguage($language)->first(); if ($row) { if ($force) { $row->delete(); } else { return Base::retSuccess("success", $row->only(['msg_id', 'language', 'content'])); } } // $msgData = Base::json2array($msg->getRawOriginal('msg')); if (empty($msgData['text'])) { return Base::retError("消息内容为空"); } if ($msg->type === 'text' && $msgData['type'] === 'md') { $msgData['text'] = preg_replace('/:::\s*reasoning.*?:::/s', '', $msgData['text']); } $result = AI::translations($msgData['text'], $targetLanguage, $force); if (Base::isError($result)) { return $result; } $row = WebSocketDialogMsgTranslate::createInstance([ 'dialog_id' => $msg->dialog_id, 'msg_id' => $msg_id, 'language' => $language, 'content' => $result['data']['translated_text'], ]); $row->save(); // return Base::retSuccess("success", $row->only(['msg_id', 'language', 'content'])); } /** * @api {get} api/dialog/msg/mark 消息标记操作 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__mark * * @apiParam {Number} dialog_id 会话ID * @apiParam {String} type 类型 * - read: 已读 * - unread: 未读 * @apiParam {Number} [after_msg_id] 仅标记已读指定之后(含)的消息 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__mark() { $user = User::auth(); // $dialog_id = intval(Request::input('dialog_id')); $type = Request::input('type'); $after_msg_id = intval(Request::input('after_msg_id')); // $dialogUser = WebSocketDialogUser::with(['webSocketDialog'])->whereDialogId($dialog_id)->whereUserid($user->userid)->first(); if (empty($dialogUser?->webSocketDialog)) { return Base::retError('会话不存在'); } switch ($type) { case 'read': // 标记已读 $builder = WebSocketDialogMsgRead::whereDialogId($dialog_id) ->whereUserid($user->userid) ->whereReadAt(null) ->select(['id', 'msg_id']); if ($after_msg_id > 0) { $builder->where('msg_id', '>=', $after_msg_id); } $builder->chunkById(100, function ($list) { WebSocketDialogMsgRead::onlyMarkRead($list); }); break; case 'unread': // 标记未读 break; default: return Base::retError("参数错误"); } $dialogUser->mark_unread = $type == 'unread' ? 1 : 0; $dialogUser->save(); $unreadData = WebSocketDialog::generateUnread($dialog_id, $user->userid); return Base::retSuccess("success", [ 'id' => $dialog_id, 'unread' => $unreadData['unread'], 'unread_one' => $unreadData['unread_one'], 'mention' => $unreadData['mention'], 'mention_ids' => $unreadData['mention_ids'], 'user_at' => Carbon::parse($dialogUser->updated_at)->toDateTimeString('millisecond'), 'user_ms' => Carbon::parse($dialogUser->updated_at)->valueOf(), 'mark_unread' => $dialogUser->mark_unread, ]); } /** * @api {get} api/dialog/msg/silence 消息免打扰 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__silence * * @apiParam {Number} dialog_id 会话ID * @apiParam {String} type 类型 * - set * - cancel * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__silence() { $user = User::auth(); $dialogId = intval(Request::input('dialog_id')); $type = Request::input('type'); $dialogUser = WebSocketDialogUser::whereUserid($user->userid)->whereDialogId($dialogId)->first(); if (!$dialogUser) { return Base::retError("会话不存在"); } // $dialogData = WebSocketDialog::find($dialogId); if (empty($dialogData)) { return Base::retError("会话不存在"); } if ($dialogData->type === 'group' && $dialogData->group_type !== 'user') { return Base::retError("此会话不允许设置免打扰"); } // switch ($type) { case 'set': $data['silence'] = 0; WebSocketDialogMsgRead::whereUserid($user->userid) ->whereReadAt(null) ->whereDialogId($dialogId) ->chunkById(100, function ($list) { WebSocketDialogMsgRead::onlyMarkRead($list); }); $dialogUser->silence = 1; $dialogUser->save(); break; case 'cancel': $dialogUser->silence = 0; $dialogUser->save(); break; default: return Base::retError("参数错误"); } $data = [ 'id' => $dialogId, 'silence' => $dialogUser->silence, ]; return Base::retSuccess("success", $data); } /** * @api {get} api/dialog/msg/forward 转发消息给 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__forward * * @apiParam {Number} msg_id 消息ID * @apiParam {Array} dialogids 转发给的对话ID * @apiParam {Array} userids 转发给的成员ID * @apiParam {Number} show_source 是否显示原发送者信息 * @apiParam {String} leave_message 转发留言 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__forward() { $user = User::auth(); // $msg_id = intval(Request::input("msg_id")); $dialogids = Request::input('dialogids'); $userids = Request::input('userids'); $show_source = intval(Request::input("show_source")); $leave_message = Request::input('leave_message'); // if (empty($dialogids) && empty($userids)) { return Base::retError("请选择对话或成员"); } // $msg = WebSocketDialogMsg::whereId($msg_id)->first(); if (empty($msg)) { return Base::retError("消息不存在或已被删除"); } WebSocketDialog::checkDialog($msg->dialog_id); // return $msg->forwardMsg($dialogids, $userids, $user, $show_source, $leave_message); } /** * @api {get} api/dialog/msg/emoji emoji回复 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__emoji * * @apiParam {Number} msg_id 消息ID * @apiParam {String} symbol 回复或取消的emoji表情 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__emoji() { $user = User::auth(); // $msg_id = intval(Request::input("msg_id")); $symbol = Request::input("symbol"); // $emojiPattern = '/(?:' . '[\x{1F600}-\x{1F64F}]|' . // 表情符号 '[\x{1F300}-\x{1F5FF}]|' . // 符号和象形文字 '[\x{1F680}-\x{1F6FF}]|' . // 交通和地图符号 '[\x{1F1E0}-\x{1F1FF}]|' . // 区域指示符号(国旗) '[\x{2600}-\x{26FF}]|' . // 杂项符号 '[\x{2700}-\x{27BF}]|' . // 装饰符号 '[\x{1F900}-\x{1F9FF}]|' . // 补充符号和象形文字 '[\x{1F000}-\x{1F02F}]|' . // 麻将牌 '[\x{1F0A0}-\x{1F0FF}]|' . // 扑克牌 '[\x{1F100}-\x{1F64F}]|' . // 封闭字母数字补充 '[\x{FE0F}]|' . // 变体选择器-16 '[\x{20E3}]|' . // 组合封闭键帽 '[\x{30}-\x{39}\x{23}\x{2A}][\x{FE0F}]?[\x{20E3}]' . // 键帽序列 (0-9, #, *) ')/u'; if (!preg_match($emojiPattern, $symbol)) { return Base::retError("参数错误"); } // $msg = WebSocketDialogMsg::whereId($msg_id)->first(); if (empty($msg)) { return Base::retError("消息不存在或已被删除"); } WebSocketDialog::checkDialog($msg->dialog_id); // return $msg->emojiMsg($symbol, $user->userid); } /** * @api {get} api/dialog/msg/tag 标注/取消标注 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__tag * * @apiParam {Number} msg_id 消息ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__tag() { $user = User::auth(); // $msg_id = intval(Request::input("msg_id")); // $msg = WebSocketDialogMsg::whereId($msg_id)->first(); if (empty($msg)) { return Base::retError("消息不存在或已被删除"); } WebSocketDialog::checkDialog($msg->dialog_id); // return $msg->toggleTagMsg($user->userid); } /** * @api {get} api/dialog/msg/todo 设待办/取消待办 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__todo * * @apiParam {Number} msg_id 消息ID * @apiParam {String} type 设待办对象 * - all: 会话全部成员(默认) * - user: 会话指定成员 * @apiParam {Array} userids 会员ID组 * - type=user 有效,格式: [userid1, userid2, userid3] * - 可通过 type=user 及 userids:[] 一起使用来清除所有人的待办 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__todo() { Base::checkClientVersion('0.37.18'); $user = User::auth(); // $msg_id = intval(Request::input("msg_id")); $type = trim(Request::input("type", "all")); $userids = Request::input('userids'); // $msg = WebSocketDialogMsg::whereId($msg_id)->first(); if (empty($msg)) { return Base::retError("消息不存在或已被删除"); } $dialog = WebSocketDialog::checkDialog($msg->dialog_id); // if ($type === 'all') { $userids = $dialog->dialogUser->pluck('userid')->toArray(); } else { $userids = is_array($userids) ? $userids : []; } return $msg->toggleTodoMsg($user->userid, $userids); } /** * @api {get} api/dialog/msg/todolist 获取消息待办情况 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__todolist * * @apiParam {Number} msg_id 消息ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__todolist() { User::auth(); // $msg_id = intval(Request::input('msg_id')); // $msg = WebSocketDialogMsg::whereId($msg_id)->first(); if (empty($msg)) { return Base::retError("消息不存在或已被删除"); } WebSocketDialog::checkDialog($msg->dialog_id); // $todo = WebSocketDialogMsgTodo::whereMsgId($msg_id)->get(); return Base::retSuccess('success', $todo ?: []); } /** * @api {get} api/dialog/msg/done 完成待办 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__done * * @apiParam {Number} id 待办数据ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__done() { $user = User::auth(); // $id = intval(Request::input("id")); // $add = []; $todo = WebSocketDialogMsgTodo::whereId($id)->whereUserid($user->userid)->first(); if ($todo && empty($todo->done_at)) { $todo->done_at = Carbon::now(); $todo->save(); // $msg = WebSocketDialogMsg::find($todo->msg_id); if ($msg) { $res = WebSocketDialogMsg::sendMsg(null, $todo->dialog_id, 'todo', [ 'action' => 'done', 'data' => [ 'id' => $msg->id, 'type' => $msg->type, 'msg' => $msg->quoteTextMsg(), ] ]); if (Base::isSuccess($res)) { $add = $res['data']; } // $msg->webSocketDialog?->pushMsg('update', [ 'id' => $msg->id, 'todo' => $msg->todo, 'todo_done' => $msg->isTodoDone(true), 'dialog_id' => $msg->dialog_id, ]); } } // return Base::retSuccess("待办已完成", [ 'add' => $add ?: null ]); } /** * @api {get} api/dialog/msg/color 设置颜色 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__color * * @apiParam {Number} dialog_id 会话ID * @apiParam {String} color 颜色 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__color() { $user = User::auth(); $dialogId = intval(Request::input('dialog_id')); $color = Request::input('color',''); $dialogUser = WebSocketDialogUser::whereUserid($user->userid)->whereDialogId($dialogId)->first(); if (!$dialogUser) { return Base::retError("会话不存在"); } // $dialogData = WebSocketDialog::find($dialogId); if (empty($dialogData)) { return Base::retError("会话不存在"); } // $dialogUser->color = $color; $dialogUser->save(); // $data = [ 'id' => $dialogId, 'color' => $color ]; return Base::retSuccess("success", $data); } /** * @api {post} api/dialog/msg/webhookmsg2ai 转换为AI对话 * * @apiDescription 需要token身份,将webhook消息转换为适合AI对话的格式消息,用于AI对话 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__webhookmsg2ai * * @apiParam {String} msg 消息内容 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__webhookmsg2ai() { User::auth(); // $msg = Request::input('msg'); try { $res = BotReceiveMsgTask::convertMentionForAI($msg); return Base::retSuccess("success", ['msg' => $res]); } catch (\Exception $e) { return Base::retError($e->getMessage()); } } /** * @api {get} api/dialog/group/add 新增群组 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName group__add * * @apiParam {String} [avatar] 群头像 * @apiParam {String} [chat_name] 群名称 * @apiParam {Array} userids 群成员,格式: [userid1, userid2, userid3] * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function group__add() { $user = User::auth(); // $avatar = Request::input('avatar'); $avatar = $avatar ? Base::unFillUrl(is_array($avatar) ? $avatar[0]['path'] : $avatar) : ''; $chatName = trim(Request::input('chat_name')); $userids = Request::input('userids'); // if (!is_array($userids)) { return Base::retError('请选择群成员'); } $userids = array_merge([$user->userid], $userids); $userids = array_values(array_filter(array_unique($userids))); if (count($userids) < 2) { return Base::retError('群成员至少2人'); } // if (empty($chatName)) { $array = []; foreach ($userids as $userid) { $array[] = User::userid2nickname($userid); if (count($array) >= 8 || strlen(implode(", ", $array)) > 100) { $array[] = "..."; break; } } $chatName = implode(", ", $array); } if ($user->isTemp()) { return Base::retError('无法创建群组'); } $dialog = WebSocketDialog::createGroup($chatName, $userids, 'user', $user->userid); if (empty($dialog)) { return Base::retError('创建群组失败'); } if ($avatar) { $dialog->avatar = $avatar; $dialog->save(); } $data = WebSocketDialog::synthesizeData($dialog, $user->userid); $userids = array_values(array_diff($userids, [$user->userid])); $dialog->pushMsg("groupAdd", null, $userids); return Base::retSuccess('创建成功', $data); } /** * @api {get} api/dialog/group/edit 修改群组 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName group__edit * * @apiParam {Number} dialog_id 会话ID * @apiParam {String} [avatar] 群头像 * @apiParam {String} [chat_name] 群名称 * @apiParam {Number} [admin] 系统管理员操作(1:只判断是不是系统管理员,否则判断是否群管理员) * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function group__edit() { $user = User::auth(); // $dialog_id = intval(Request::input('dialog_id')); $admin = intval(Request::input('admin')); // if ($admin === 1) { $user->checkAdmin(); $dialog = WebSocketDialog::find($dialog_id); if (empty($dialog)) { WebSocketDialogMsgRead::forceRead($dialog_id, $user->userid); return Base::retError('对话不存在或已被删除', ['dialog_id' => $dialog_id], -4003); } } else { $dialog = WebSocketDialog::checkDialog($dialog_id, true); } // $data = ['id' => $dialog->id]; $array = []; if (Request::exists('avatar')) { $avatar = Request::input('avatar'); $avatar = $avatar ? Base::unFillUrl(is_array($avatar) ? $avatar[0]['path'] : $avatar) : ''; $data['avatar'] = Base::fillUrl($array['avatar'] = $avatar); } $existName = Request::exists('chat_name') || Request::exists('name'); if ($existName && $dialog->group_type === 'user') { $chatName = trim(Request::input('chat_name') ?: Request::input('name')); if (mb_strlen($chatName) < 2) { return Base::retError('群名称至少2个字'); } if (mb_strlen($chatName) > 100) { return Base::retError('群名称最长限制100个字'); } $data['name'] = $array['name'] = $chatName; } // if ($array) { $dialog->updateInstance($array); $dialog->save(); WebSocketDialogUser::whereDialogId($dialog->id)->change(['updated_at' => Carbon::now()->toDateTimeString('millisecond')]); } // return Base::retSuccess('修改成功', $data); } /** * @api {get} api/dialog/group/adduser 添加群成员 * * @apiDescription 需要token身份 * - 有群主时:只有群主可以邀请 * - 没有群主时:群内成员都可以邀请 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName group__adduser * * @apiParam {Number} dialog_id 会话ID * @apiParam {Array} userids 新增的群成员,格式: [userid1, userid2, userid3] * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function group__adduser() { $user = User::auth(); // $dialog_id = intval(Request::input('dialog_id')); $userids = Base::json2array(Request::input('userids')); // if (!is_array($userids)) { return Base::retError('请选择群成员'); } // $dialog = WebSocketDialog::checkDialog($dialog_id, "auto"); // $dialog->checkGroup(); $dialog->joinGroup($userids, $user->userid); $dialog->pushMsg("groupJoin", null, $userids); return Base::retSuccess('添加成功'); } /** * @api {get} api/dialog/group/deluser 移出(退出)群成员 * * @apiDescription 需要token身份 * - 只有群主、邀请人可以踢人 * - 群主、任务人员、项目人员不可被踢或退出 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName group__adduser * * @apiParam {Number} dialog_id 会话ID * @apiParam {Array} [userids] 移出的群成员,格式: [userid1, userid2, userid3] * - 留空表示自己退出 * - 有值表示移出,仅限群主操作 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function group__deluser() { $user = User::auth(); // $dialog_id = intval(Request::input('dialog_id')); $userids = Base::json2array(Request::input('userids')); // $type = 'remove'; if (empty($userids)) { $type = 'exit'; $userids = [$user->userid]; } // if (!is_array($userids)) { return Base::retError('请选择群成员'); } // $dialog = WebSocketDialog::checkDialog($dialog_id); // $dialog->checkGroup(); $dialog->exitGroup($userids, $type); $dialog->pushMsg("groupExit", null, $userids); return Base::retSuccess($type === 'remove' ? '移出成功' : '退出成功'); } /** * @api {get} api/dialog/group/transfer 转让群组 * * @apiDescription 需要token身份 * - 只有群主且是个人类型群可以解散 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName group__transfer * * @apiParam {Number} dialog_id 会话ID * @apiParam {Number} userid 新的群主 * @apiParam {String} check_owner 转让验证 yes-需要验证 no-不需要验证 * @apiParam {String} key 密钥(APP_KEY) * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function group__transfer() { if (!Base::is_internal_ip(Base::getIp()) || Request::input("key") !== env('APP_KEY')) { $user = User::auth(); } // $dialog_id = intval(Request::input('dialog_id')); $userid = intval(Request::input('userid')); $check_owner = trim(Request::input('check_owner', 'yes')) === 'yes'; // if ($check_owner && $userid === $user?->userid) { return Base::retError('你已经是群主'); } if (!User::whereUserid($userid)->exists()) { return Base::retError('请选择有效的新群主'); } // $dialog = WebSocketDialog::checkDialog($dialog_id, $check_owner); // $dialog->checkGroup($check_owner ? 'user' : null); $dialog->owner_id = $userid; if ($dialog->save()) { $dialog->joinGroup($userid, 0); $dialog->pushMsg("groupUpdate", [ 'id' => $dialog->id, 'owner_id' => $dialog->owner_id, ]); } return Base::retSuccess('转让成功'); } /** * @api {get} api/dialog/group/disband 解散群组 * * @apiDescription 需要token身份 * - 只有群主且是个人类型群可以解散 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName group__disband * * @apiParam {Number} dialog_id 会话ID * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function group__disband() { User::auth(); // $dialog_id = intval(Request::input('dialog_id')); // $dialog = WebSocketDialog::checkDialog($dialog_id, true); // $dialog->checkGroup('user'); $dialog->deleteDialog(); return Base::retSuccess('解散成功'); } /** * @api {get} api/dialog/group/searchuser 搜索个人群(仅限管理员) * * @apiDescription 需要token身份,用于创建部门搜索个人群组 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName group__searchuser * * @apiParam {String} key 关键词 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function group__searchuser() { User::auth('admin'); // $key = trim(Request::input('key')); // $builder = WebSocketDialog::whereType('group')->whereGroupType('user'); if ($key) { $builder->where('name', 'like', "%{$key}%"); } return Base::retSuccess('success', [ 'list' => $builder->take(20)->get() ]); } /** * @api {get} api/dialog/common/list 共同群组群聊 * * @apiDescription 需要token身份,按置顶时间、用户在群组中的最后活跃时间倒序排列 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName common__list * * @apiParam {Number} [target_userid] 目标用户ID(和谁的共同群组,不传则获取自己所有群组) * @apiParam {Number} [page] 当前页数,默认为1 * @apiParam {Number} [pagesize] 每页显示条数,默认为20,最大100 * @apiParam {String} [only_count] 是否只返回数量,传入 'yes' 则只返回数量不返回列表 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 * * - 当 only_count=yes 时: * @apiSuccess {Number} data.total 群组数量 * * - 当获取列表时,返回 Laravel 标准分页格式: * @apiSuccess {Array} data.data 群组列表数据 * @apiSuccess {Number} data.current_page 当前页数 * @apiSuccess {Number} data.per_page 每页显示条数 * @apiSuccess {Number} data.total 总数量 * @apiSuccess {String} data.first_page_url 第一页链接 * @apiSuccess {String} data.last_page_url 最后页链接 * @apiSuccess {String} data.next_page_url 下一页链接 * @apiSuccess {String} data.prev_page_url 上一页链接 */ public function common__list() { $user = User::auth(); // $target_userid = intval(Request::input('target_userid')); $only_count = trim(Request::input('only_count')) === 'yes'; // 参考getDialogList的查询模式 $builder = DB::table('web_socket_dialog_users as u') ->select(['d.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', 'u.updated_at as user_at']) ->join('web_socket_dialogs as d', 'u.dialog_id', '=', 'd.id') ->where('u.userid', $user->userid) ->where('d.type', 'group') ->where('d.group_type', 'user') ->whereNull('d.deleted_at'); if ($target_userid) { // 获取与目标用户的共同群组 $builder->whereExists(function($query) use ($target_userid) { $query->select(DB::raw(1)) ->from('web_socket_dialog_users as du2') ->whereColumn('du2.dialog_id', 'd.id') ->where('du2.userid', $target_userid); }); } if ($only_count) { // 只返回数量 return Base::retSuccess('success', [ 'total' => $builder->count() ]); } // 返回分页列表,参考getDialogList的排序逻辑 $list = $builder ->orderByDesc('u.top_at') ->orderByDesc('u.last_at') ->paginate(Base::getPaginate(100, 20)); // 处理分页数据,与getDialogList保持一致的处理方式 $list->transform(function ($item) use ($user) { return WebSocketDialog::synthesizeData($item, $user->userid); }); return Base::retSuccess('success', $list); } /** * @api {post} api/dialog/okr/add 创建OKR评论会话 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName okr__add * * @apiParam {String} name 标题 * @apiParam {Number} link_id 关联id * @apiParam {Array} userids 群成员,格式: [userid1, userid2, userid3] * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function okr__add() { $user = User::auth(); // $name = trim(Request::input('name')); $link_id = intval(Request::input('link_id')); $userids = Request::input('userids'); // if (empty($name)) { return Base::retError('群名称至少2个字'); } // $dialog = WebSocketDialog::createGroup($name, $userids, 'okr', $user->userid); if (empty($dialog)) { return Base::retError('创建群组失败'); } if ($link_id) { $dialog->link_id = $link_id; $dialog->save(); } return Base::retSuccess('创建成功', $dialog); } /** * @api {post} api/dialog/okr/push 推送OKR相关信息 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName okr__push * * @apiParam {String} text 发送内容 * @apiParam {Number} userid 成员ID * @apiParam {String} key 密钥(APP_KEY) * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function okr__push() { if (!Base::is_internal_ip(Base::getIp()) || Request::input("key") !== env('APP_KEY')) { User::auth(); } $text = trim(Request::input('text')); $userid = intval(Request::input('userid')); // $botUser = User::botGetOrCreate('okr-alert'); if (empty($botUser)) { return Base::retError('机器人不存在'); } // $dialog = WebSocketDialog::checkUserDialog($botUser, $userid); if ($dialog) { WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid); } return Base::retSuccess('success', $dialog); } /** * @api {post} api/dialog/msg/wordchain 发送接龙消息 * * @apiDescription 需要token身份 * @apiVersion 1.0.0 * @apiGroup dialog * @apiName msg__wordchain * * @apiParam {Number} dialog_id 对话ID * @apiParam {String} uuid 接龙ID * @apiParam {String} text 接龙内容 * @apiParam {Array} list 接龙列表 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {Object} data 返回数据 */ public function msg__wordchain() { $user = User::auth(); // $dialog_id = intval(Request::input('dialog_id')); $uuid = trim(Request::input('uuid')); $text = trim(Request::input('text')); $list = Request::input('list') ?? []; // WebSocketDialog::checkDialog($dialog_id); $strlen = mb_strlen($text); $reallen = mb_strlen(preg_replace("/