123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- <?php
- /**
- * 会话信息
- * @author benzhan
- */
- class Session extends Model {
- protected $tableName = 'session';
- protected $dbKey = 'dw_chat';
- const MSG_TYPE_REPEAL = -1; //被撤销
- const MSG_TYPE_TEXT = 0;
- const MSG_TYPE_IMAGE = 1;
- const MSG_TYPE_VIDEO = 2;
- const MSG_TYPE_AUDIO = 3;
- const MSG_TYPE_REDPACK = 4;
- const MSG_TYPE_EVENT = 5;
- const LAST_MSG_NUM_HASH = 'globals:last_msg_num_hash';
- const LAST_MSG_HASH = 'globals:last_msg_hash';
- /**
- * 填充会话列表
- * @param string $user_id 用户id
- * @param array $list 会话列表
- * @param int $currentGroupId 当前群id
- *
- * @return mixed
- */
- public function fillSession($user_id, $list, $currentGroupId) {
- $group_ids = [];
- $user_ids = [];
- foreach ($list as $item) {
- if ($item['is_group']) {
- $group_ids[] = $item['session_id'];
- } else {
- // 把自己的用户名换掉
- $_tmp = explode('-', $item['session_id']);
- foreach ($_tmp as $_uid) {
- $user_ids[$_uid] = 1;
- }
- }
- }
- unset($user_ids[$user_id]);
- $user_ids = array_keys($user_ids);
- $objUserInfo = new TableHelper('user_info', 'dw_chat');
- $_field = 'user_id, user_name, nick_name, cover_photo';
- $users = $objUserInfo->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, $extInfo);
- $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 ($extInfo['event_type'] != 'leave_group' && !$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, $extInfo);
- $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红包,<a href='https://{$_SERVER['HTTP_HOST']}/s/{$group['group_name']}'>请打开MeeChat查看</a>]";
- } else {
- $text = Utils::decodeRC4($msg);
- }
- $text = "<b>{$eventData['nick_name']}</b>:
- {$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, $extInfo = []) {
- $objRedis = dwRedis::init(Eos::REDIS_SERV);
- $msgType != self::MSG_TYPE_TEXT && $content = '';
- $data = [
- 'msg_type' => $msgType,
- 'event_type' => $msgType == self::MSG_TYPE_EVENT ? $extInfo['event_type'] : '',
- '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;
- }
- }
|