getAll(['user_id' => $user_ids], compact('_field')); $users = arrayFormatKey($users, 'user_id'); $objGroupInfo = new TableHelper('group_info', 'dw_chat'); $_field = 'group_id, group_name, group_title, cover_photo, is_auth'; $groups = $objGroupInfo->getAll(['group_id' => $group_ids], compact('_field')); $groups = arrayFormatKey($groups, 'group_id'); $userGroups = []; if ($currentGroupId) { $objUserGroup = new UserGroup(); $userGroups = $objUserGroup->objTable->getAll(['user_id' => $user_ids, 'group_id' => $currentGroupId], ['_field' => 'user_id, group_id']); $userGroups = arrayFormatKey($userGroups, 'user_id', 'group_id'); } $objRedis = dwRedis::init(Eos::REDIS_SERV); $fieldTime = []; $fieldPin = []; foreach ($list as $i => $item) { if ($item['is_group']) { $group = $groups[$item['session_id']]; $item['name'] = $group['group_title'] ?: $group['group_name']; $item['cover_photo'] = $group['cover_photo']; $item['is_auth'] = $group['is_auth']; } else { // 把自己的用户名换掉 $key = self::getToUser($user_id, $item['session_id']); $user = $users[$key]; $item['name'] = $user['nick_name']; $item['cover_photo'] = $user['cover_photo']; $item['in_group'] = $userGroups[$key] ? 1 : 0; // 已经在群 } $lastNum = self::getLastMsgNum($item['session_id']); $item['unread'] = max($lastNum - $item['read_num'], 0); $item['cover_photo'] = coverReplaceImage($item['cover_photo']); $item['last_msg'] = self::getLastMsg($item['session_id'], $objRedis); $item['update_time_int'] = $item['last_msg']['time'] ?: 0; $fieldPin[] = $item['pin_time_int']; $fieldTime[] = $item['update_time_int']; $list[$i] = $item; } array_multisort($fieldPin, SORT_DESC, $fieldTime, SORT_DESC, $list); return $list; } public function getMsgList($session_id, $read_hash, $load_type, $is_group) { $where2 = []; if ($is_group) { $_field = '`hash`, `from`, `msg`, `msg_type`, `state`, create_time_int, msg_num, ext_info'; $objMsg = new TableHelper('group_msg', 'dw_chat'); $where2['group_id'] = $session_id; } else { $_field = '`hash`, `from`, `to`, `msg`, `msg_type`, `state`, create_time_int, msg_num, ext_info'; $objMsg = new TableHelper('person_msg', 'dw_chat'); $where2['session_id'] = $session_id; } if ($read_hash) { $currentRow = $objMsg->getRow(['hash' => $read_hash], compact('_field')); $create_time_int = (int) $currentRow['create_time_int']; } else { $create_time_int = 0; } $_limit = 50; if ($load_type == 0) { $create_time_int && $_where = "create_time_int >= {$create_time_int}"; $_sortKey = "create_time_int DESC"; $keyWord2 = compact('_where', '_sortKey', '_limit', '_field'); $list = $objMsg->getAll($where2, $keyWord2); } else { // 加载历史记录 $_where = "create_time_int < {$create_time_int}"; $_sortKey = "create_time_int DESC"; $keyWord3 = compact('_where', '_sortKey', '_limit', '_field'); $list = $objMsg->getAll($where2, $keyWord3); } $list = array_reverse($list); $list = $this->appendExtInfo($list); $user_ids = array_column($list, 'from'); if (!$is_group) { $to_ids = array_column($list, 'to'); $user_ids = array_merge($user_ids, $to_ids); } $user_ids = array_unique($user_ids); $userMap = $this->getUserMap($user_ids); return compact('userMap', 'list'); } /** * 额外信息 * @author solu * @param $list * @return mixed */ private function appendExtInfo($list) { $userId = User::getUserId(); $objRedpackLog = new RedpackLog(); $trxIds = array_map(function($v) { $msg = Utils::decodeRC4($v['msg']); $data = json_decode($msg, true); return $data['trxId']; }, array_filter($list, function($v) { return $v['msg_type'] == self::MSG_TYPE_REDPACK; })); $redpack = []; if ($trxIds) { $objRedpack = new Redpack(); $redpack = $objRedpack->objTable->getAll(['transfer_trx_id' => $trxIds], ['_field' => 'transfer_trx_id, status']); $redpack = arrayFormatKey($redpack, 'transfer_trx_id', 'status'); } foreach ($list as $k => $v) { if ($v['msg_type'] == self::MSG_TYPE_REDPACK) { $msg = Utils::decodeRC4($v['msg']); $data = json_decode($msg, true); $trxId = $data['trxId']; $v['ext']['grabbed'] = $objRedpackLog->userGrabbed($userId, $trxId); $v['ext']['redpack_status'] = intval($redpack[$trxId]); } $v['ext_info'] && $v['ext_info'] = json_decode($v['ext_info'], true); $list[$k] = $v; } return $list; } public function getUserMap($user_ids) { $objUserInfo = new TableHelper('user_info', 'dw_chat'); $datas = $objUserInfo->getAll(['user_id' => $user_ids], ['_field' => 'user_id, user_name, nick_name, cover_photo']); coverReplaceArrImage($datas, 'cover_photo'); return arrayFormatKey($datas, 'user_id'); } /** * 修改状态 * @param $session_id * @param $newData * @param $ext; */ public function updateState($session_id, $newData, $ext = []) { $where = compact('session_id'); $where = array_merge($where, $ext); $newData['update_time'] = NOW; $newData['update_time_int'] = microtime(true) * 1000; $this->objTable->updateObject($newData, $where); } /** * 检测私聊session * @author solu * @param $from * @param $sessionId * @return array * @throws Exception */ public function checkPersonSession($from, $sessionId) { $uids = explode('-', $sessionId); $uids = array_filter($uids, function ($v) {return $v > 0;}); // if (count($uids) != 2 || !$this->objTable->getRow(['user_id' => $from, 'session_id' => $sessionId])) { // throw new Exception('session_id error', CODE_PARAM_ERROR); // } if (!in_array($from, $uids)) { throw new Exception('user not in session', CODE_NO_PERMITION); } $to = 0; foreach ($uids as $uid) { if ($uid != $from) { $to = $uid; break; } } return [$from, $to]; } /** * 检测群session * @author solu * @param $from * @param $sessionId * @return bool * @throws Exception */ public function checkGroupSession($from, $sessionId) { if (!$this->objTable->getRow(['user_id' => $from, 'session_id' => $sessionId])) { throw new Exception('user not in session', CODE_NO_PERMITION); } return true; } public static function getPersonSessionId($from, $to) { if ($from > $to) { return "{$to}-{$from}"; } else { return "{$from}-{$to}"; } } private function initPersonSession($from, $to) { $session_id = self::getPersonSessionId($from, $to); $num = $this->objTable->getCount(compact('session_id')); if ($num < 2) { // 单方面删除会话的情况 // 插入双方的session $datas = [[ 'user_id' => $from, 'session_id' => $session_id, 'is_group' => 0, ], [ 'user_id' => $to, 'session_id' => $session_id, 'is_group' => 0, ] ]; // 第一次初始化,需要初始化 $this->objTable->addObjectsIfNoExist($datas); } } /** * 发送私聊消息 * @author solu * @param $from * @param $sessionId * @param $msg_type * @param $msg * @param $noEvent * @param $extInfo * @return array * @throws Exception */ public function sendPersonMsg($from, $sessionId, $msg_type, $msg, $noEvent = false, $extInfo = []) { list($from, $to) = $this->checkPersonSession($from, $sessionId); $this->initPersonSession($from, $to); $t = self::getMS(); 0 == $msg_type && $msg = htmlentities($msg); $lastNum = self::incrLastMsgNum($sessionId); $data = [ 'session_id' => $sessionId, 'from' => intval($from), 'to' => intval($to), 'msg_type' => $msg_type, 'msg' => $msg, 'create_time' => NOW, 'create_time_int' => $t, 'msg_num' => $lastNum, ]; $extInfo && $data['ext_info'] = json_encode($extInfo); $data['hash'] = self::_genHash($data); $objPersonMsg = new TableHelper('person_msg', 'dw_chat'); if (!$objPersonMsg->addObject($data)) { throw new Exception('send message error', CODE_NORMAL_ERROR); } // 更新发送人已读序号 $this->updateState($sessionId, ['read_num' => $lastNum], ['user_id' => $from]); $name = User::getUserNameById($from); $content = self::_msgHandle($msg, $msg_type); $eventData = [ 'type' => 'msg', 'msg_type' => $msg_type, 'from' => intval($from), 'to' => intval($to), 'name' => $name, 'nick_name' => User::getUserNameById($from), 'content' => $content, 'hash' => $data['hash'], 'timestamp' => $t, 'ext_info' => $extInfo, ]; !$noEvent && ThirdApi::pushPersonEvent($to, $eventData); !$noEvent && ThirdApi::pushPersonEvent($from, $eventData); self::setLastMsg($sessionId, $msg_type, $content, $from, $name); $eventData['content'] = $msg; return $eventData; } /** * 发送群聊消息 * @author solu * @param $from * @param $groupId * @param $msg_type * @param $msg * @param $noEvent * @param $noTg * @param $extInfo * @return array * @throws Exception */ public function sendGroupMsg($from, $groupId, $msg_type, $msg, $noEvent = false, $noTg = false, $extInfo = []) { $userMap = null; $objGroupInfo = new GroupInfo(); if (!$this->objTable->getRow(['user_id' => $from, 'session_id' => $groupId])) { // 聊天就自动加入群 // 检查私有群的权限 $objGroupInfo->checkPermission($groupId, $from); // 第一次发言,需要返回用户信息 $objGroupInfo->joinGroup($from, $groupId); $userMap = $this->getUserMap($from); } else if ((new UserGroup())->isBlock($groupId, $from)) { throw new Exception('Banned', CODE_NORMAL_ERROR); } $t = self::getMS(); 0 == $msg_type && $msg = htmlentities($msg); $lastNum = self::incrLastMsgNum($groupId); $data = [ 'group_id' => intval($groupId), 'from' => $from, 'msg_type' => $msg_type, 'msg' => $msg, 'create_time' => NOW, 'create_time_int' => $t, 'msg_num' => $lastNum, ]; $extInfo && $data['ext_info'] = json_encode($extInfo); $data['hash'] = self::_genHash($data); $objGroupMsg = new TableHelper('group_msg', 'dw_chat'); if (!$objGroupMsg->addObject($data)) { throw new Exception('send message error', CODE_NORMAL_ERROR); } // 更新发送人已读序号 $this->updateState($groupId, ['read_num' => $lastNum], ['user_id' => $from]); $content = self::_msgHandle($msg, $msg_type); $name = GroupInfo::getGroupNameById($groupId); $eventData = [ 'type' => 'msg', 'msg_type' => $msg_type, 'from' => $from, 'name' => $name, 'nick_name' => User::getUserNameById($from), 'content' => $content, 'hash' => $data['hash'], 'timestamp' => $t, 'ext_info' => $extInfo, ]; !$noEvent && ThirdApi::pushGroupEvent($groupId, $eventData); self::setLastMsg($groupId, $msg_type, $content, $from, $name); $eventData['content'] = $msg; $eventData['userMap'] = $userMap; // 推送到Telegram $group = $objGroupInfo->objTable->getRow(['group_id' => $groupId], ['_field' => 'group_name, tg_group_id']); if ($group['tg_group_id'] && !$noTg && $msg_type != self::MSG_TYPE_REPEAL) { // 特殊处理红包 if ($msg_type == self::MSG_TYPE_REDPACK) { $text = " [收到MeeChat红包,请打开MeeChat查看]"; } else { $text = Utils::decodeRC4($msg); } $text = "{$eventData['nick_name']}: {$text}"; Telegram::pushMessageList(['chat_id' => $group['tg_group_id'], "text" => $text, 'parse_mode' => 'HTML']); } return $eventData; } private static function _genHash($data) { return md5(json_encode($data)); } public static function _msgHandle($content, $msg_type, $len = 16) { if ($msg_type > 0) { // 只处理文本 return $content; } $source = Utils::decodeRC4($content); !$source && $source = $content; if (mb_strlen($source) > $len) { $source = mb_substr($source, 0, $len); } return Utils::encodeRC4($source); } public static function getMS() { return intval(microtime(true) * 1000); } /** * 自增session num * @author solu * @param $sessionId * @return int */ public static function incrLastMsgNum($sessionId) { $objRedis = dwRedis::init(Eos::REDIS_SERV); // 存在原子性问题 if ($objRedis->hExists(self::LAST_MSG_NUM_HASH, $sessionId)) { return $objRedis->hIncrBy(self::LAST_MSG_NUM_HASH, $sessionId, 1); } if (is_numeric($sessionId)) { // 群组 $objGroupMsg = new GroupMsg(); $c = $objGroupMsg->objTable->getCount(['group_id' => $sessionId]); } else { $objPersonMsg = new PersonMsg(); $c = $objPersonMsg->objTable->getCount(['session_id' => $sessionId]); } $c += 1; return $objRedis->hIncrBy(self::LAST_MSG_NUM_HASH, $sessionId, $c); } /** * 获取session最新num * @author solu * @param $sessionId * @param null $objRedis * @return int */ public static function getLastMsgNum($sessionId, $objRedis = null) { !$objRedis && $objRedis = dwRedis::init(Eos::REDIS_SERV); return $objRedis->hGet(self::LAST_MSG_NUM_HASH, $sessionId) ?: 0; } public static function setLastMsg($sessionId, $msgType, $content, $from, $name) { $objRedis = dwRedis::init(Eos::REDIS_SERV); $msgType != self::MSG_TYPE_TEXT && $content = ''; $data = [ 'msg_type' => $msgType, 'content' => $content, 'from' => $from, 'name' => $name, 'nick_name' => User::getUserNameById($from), 'time' => self::getMS(), ]; $objRedis->hSet(self::LAST_MSG_HASH, $sessionId, json_encode($data)); } public static function getLastMsg($sessionId, $objRedis = null) { !$objRedis && $objRedis = dwRedis::init(Eos::REDIS_SERV); if ($objRedis->hExists(self::LAST_MSG_HASH, $sessionId)) { $json = $objRedis->hGet(self::LAST_MSG_HASH, $sessionId); return json_decode($json, true); } $isGroup = is_numeric($sessionId); if ($isGroup) { // 群组 $objGroupMsg = new GroupMsg(); $row = $objGroupMsg->objTable->getRow(['group_id' => $sessionId]); } else { $objPersonMsg = new PersonMsg(); $row = $objPersonMsg->objTable->getRow(['session_id' => $sessionId]); } $data = null; if ($row) { $msg_type = $row['msg_type']; $content = ''; $from = $row['from']; if ($msg_type == self::MSG_TYPE_TEXT) { $content = self::_msgHandle($row['msg'], $msg_type); } $nick_name = User::getUserNameById($from); $time = self::getMS(); $data = compact('msg_type', 'content', 'from', 'nick_name', 'time'); $objRedis->hSet(self::LAST_MSG_HASH, $sessionId, json_encode($data)); } return $data; } public static function getToUser($from, $sessionId) { $_tmp = explode('-', $sessionId); foreach ($_tmp as $_uid) { if ($_uid != $from) { return $_uid; } } return 0; } }