Esempio n. 1
0
 /**
  * @args {"description": "Delay: Clean offline client and helpdesk every minute"}
  */
 public function perform()
 {
     $accounts = Account::findAll([]);
     foreach ($accounts as $account) {
         $accountId = $account->_id;
         $setting = HelpDeskSetting::findOne(['accountId' => $accountId]);
         if (!empty($setting)) {
             $maxWaitTime = $setting->maxWaitTime;
             // Close timeout conversation
             $chatConversations = ChatConversation::findAll(['accountId' => $accountId, 'status' => ChatConversation::STATUS_OPEN, 'lastChatTime' => ['$lt' => TimeUtil::msTime(time() - $maxWaitTime * 60)]]);
             foreach ($chatConversations as $chatConversation) {
                 HelpDesk::disconnect($chatConversation->_id, ['type' => 'brake']);
             }
             // Delete timeout pending client
             $pendingClients = PendingClient::findAll(['accountId' => $accountId, 'lastPingTime' => ['$lt' => TimeUtil::msTime(time() - PendingClient::PING_THRESHOLD)]]);
             foreach ($pendingClients as $pendingClient) {
                 $pendingClient->delete();
             }
             // Clean offline helpdesk
             $cache = Yii::$app->cache;
             $onlineHelpDesks = $cache->get(HelpDesk::CACHE_PREFIX_HELPDESK_PING . $accountId);
             if (!empty($onlineHelpDesks)) {
                 foreach ($onlineHelpDesks as $deskId => $lastPingTime) {
                     if ($lastPingTime < TimeUtil::msTime(time() - HelpDesk::PING_THRESHOLD)) {
                         HelpDesk::leave($deskId, $accountId, ['type' => 'droping']);
                     }
                 }
             }
         }
     }
 }
Esempio n. 2
0
 /**
  * Login
  *
  * <b>Request Type</b>: POST<br/><br/>
  * <b>Request Endpoint</b>:http://{server-domain}/chat/site/login<br/><br/>
  * <b>Content-type</b>: application/json<br/><br/>
  * <b>Summary</b>: This api is used for the help desk to login.
  * <br/><br/>
  *
  * <b>Request Params</b>:<br/>
  *     email: string, the user email, required<br/>
  *     password: string, the user password, required<br/>
  *     <br/><br/>
  *
  * <b>Response Params:</b><br/>
  *     ack: integer, mark the create result, 0 means create successfully, 1 means create fail<br/>
  *     msg: string, if create fail, it contains the error message<br/>
  *     data: array, json array to describe the users detail information<br/>
  *     <br/><br/>
  *
  * <b>Request Example:</b><br/>
  * <pre>
  * {
  *     "email"    : "*****@*****.**",
  *     "password" : "abc123"
  * }
  * </pre>
  * <br/><br/>
  *
  * <b>Response Example</b>:<br/>
  * <pre>
  * {
  *    'ack'  : 1,
  *    'data' : {
  *        "accessToken" : "7f2d1e92-9629-8429-00be-2d9c6d64acdb",
  *        "userInfo"    : {
  *            "name"   : "harry",
  *            "avatar" : "path/to/avatar"
  *        }
  *    }
  * }
  * </pre>
  */
 public function actionLogin()
 {
     $params = $this->getParams();
     $deviceToken = $this->getParams('deviceToken');
     $environment = $this->getParams('environment');
     if (empty($params['email']) || empty($params['password'])) {
         throw new BadRequestHttpException("parameters missing");
     }
     $helpdesk = HelpDesk::getByEmail($params['email']);
     if (empty($helpdesk)) {
         throw new ForbiddenHttpException("用戶不存在");
     }
     if (!$helpdesk->isActivated) {
         throw new ForbiddenHttpException("用戶未激活,请激活后使用");
     }
     if (!$helpdesk->isEnabled) {
         throw new ForbiddenHttpException("该账号已被禁用,请与管理员联系");
     }
     if ($helpdesk->validatePassword($params['password'])) {
         $tokens = Token::getUnexpiredByUserId($helpdesk->_id);
         if (!empty($tokens)) {
             $data = ['isForcedOffline' => true, 'id' => $helpdesk->_id . ''];
             $accountId = $tokens[0]->accountId;
             Yii::$app->tuisongbao->triggerEvent(ChatConversation::EVENT_FORCED_OFFLINE, $data, [ChatConversation::CHANNEL_GLOBAL . $accountId]);
             //deviceToken changed, push forcedOffline
             if (empty($deviceToken) && !empty($helpdesk->deviceToken) || !empty($deviceToken) && !empty($helpdesk->deviceToken) && $deviceToken != $helpdesk->deviceToken) {
                 $extra = ['deskId' => $helpdesk->_id . '', 'sentTime' => TimeUtil::msTime()];
                 ChatConversation::pushMessage($helpdesk->_id, ChatConversation::EVENT_FORCED_OFFLINE, $extra);
             }
             Token::updateAll(['$set' => ['expireTime' => new \MongoDate()]], ['_id' => ['$in' => Token::getIdList($tokens)]]);
         }
         $isFirstLogin = empty($helpdesk->lastLoginAt);
         $accessToken = Token::createByHelpDesk($helpdesk);
         if (isset($deviceToken)) {
             $helpdesk->loginDevice = HelpDesk::MOBILEAPP;
         } else {
             $helpdesk->loginDevice = HelpDesk::BROWSER;
         }
         $helpdesk->deviceToken = $deviceToken;
         $helpdesk->environment = $environment;
         $helpdesk->lastLoginAt = new \MongoDate();
         $helpdesk->save(true, ['deviceToken', 'loginDevice', 'environment', 'lastLoginAt']);
         $userInfo = ['badge' => $helpdesk->badge, 'name' => $helpdesk->name, 'email' => $helpdesk->email, 'language' => $helpdesk->language, 'avatar' => empty($helpdesk->avatar) ? '' : $helpdesk->avatar, 'id' => (string) $helpdesk->_id, 'accountId' => (string) $helpdesk['accountId'], 'notificationType' => $helpdesk->notificationType, 'isFirstLogin' => $isFirstLogin];
         return ["accessToken" => $accessToken['accessToken'], 'userInfo' => $userInfo];
     } else {
         throw new ForbiddenHttpException("密码错误");
     }
 }
Esempio n. 3
0
 /**
  * Provide card
  *
  * <b>Request Type</b>: POST<br/><br/>
  * <b>Request Endpoint</b>:http://{server-domain}/api/member/card/provide-card<br/><br/>
  * <b>Response Content-type</b>: application/json<br/><br/>
  * <b>Summary</b>: This api is used for provide card.
  * <br/><br/>
  *
  * <b>Request Params</b>:<br/>
  *     cardId: string<br/>
  *     cardNumbers: Array, card number<br/>
  *     names: Array
  *     tags: Array<br/>
  *     cardExpiredAt: timestamp<br/>
  *     <br/><br/>
  *
  * <b>Response Params:</b><br/>
  *     message:
  *     <br/><br/>
  *
  * <br/><br/>
  *
  * <b>Response Example</b>:<br/>
  * <pre>
  *  {"message" : "OK"}
  * </pre>
  */
 public function actionProvideCard()
 {
     $params = $this->getParams();
     $accountId = $this->getAccountId();
     $params['accountId'] = $accountId . '';
     if (empty($params['cardId'])) {
         throw new BadRequestHttpException('param error');
     }
     if (empty($params['cardExpiredAt'])) {
         throw new InvalidParameterException(['schedule-picker' => \Yii::t('common', 'required_filed')]);
     }
     $cardId = new \MongoId($params['cardId']);
     $card = MemberShipCard::findByPk($cardId);
     if (empty($card)) {
         throw new BadRequestHttpException(\Yii::t('member', 'no_card_find'));
     }
     if ($card->isAutoUpgrade) {
         throw new InvalidParameterException(Yii::t('member', 'error_issue_auto_card'));
     }
     if ($params['cardExpiredAt'] < TimeUtil::msTime()) {
         throw new InvalidParameterException(['schedule-picker' => \Yii::t('member', 'not_less_than_current')]);
     }
     $members = [];
     if (!empty($params['cardNumbers']) && is_array($params['cardNumbers'])) {
         $members = Member::getByCardNumbers($params['cardNumbers']);
         if (empty($members)) {
             throw new InvalidParameterException(['cardNumber' => \Yii::t('member', 'no_member_find')]);
         }
     } else {
         if (!empty($params['names']) && is_array($params['names'])) {
             $members = Member::getByNames($params['names']);
             if (empty($members)) {
                 throw new InvalidParameterException(['memberNames' => \Yii::t('member', 'no_member_find')]);
             }
         } else {
             if (!empty($params['tags']) && is_array($params['tags'])) {
                 $members = Member::getByTags($params['tags']);
                 if (empty($members)) {
                     throw new InvalidParameterException(['memberTags' => \Yii::t('member', 'no_member_find')]);
                 }
             }
         }
     }
     $memberIds = Member::getIdList($members);
     Member::updateAll(['$set' => ['cardId' => $cardId, 'cardExpiredAt' => $params['cardExpiredAt']]], ['_id' => ['$in' => $memberIds]]);
     return ['message' => 'OK'];
 }
 /**
  * Send Mass message
  *
  * <b>Request Type</b>: POST<br/><br/>
  * <b>Request Endpoint</b>:http://{server-domain}/api/channel/mass-messages<br/><br/>
  * <b>Content-type</b>: application/json<br/><br/>
  * <b>Summary</b>: This api is used for sending mass message.
  * <br/><br/>
  *
  * <b>Request Params</b>:<br/>
  *     channelId: string<br/>
  *     scheduleTime: long, the scheduled time, or empty which means to send right now<br/>
  *     userQuery.tags: array<br/>
  *     userQuery.gender: male, female or empty<br/>
  *     userQuery.country: string<br/>
  *     userQuery.province: string<br/>
  *     userQuery.city: string<br/>
  *     msgType: TEXT or MPNEWS<br/>
  *     content: string, if TEXT<br/>
  *     content.articles, if MPNEWS<br/>
  *     mixed: bool<br/>
  *     <br/><br/>
  *
  * <b>Response Params:</b><br/>
  *     msg: string, if query fail, it contains the error message<br/>
  *     <br/><br/>
  *
  * <br/><br/>
  *
  * <b>Response Example</b>:<br/>
  * <pre>
  * {
  *   "message": "OK"
  * }
  * </pre>
  */
 public function actionCreate()
 {
     $massmessage = $this->getParams();
     $channelId = $this->getChannelId();
     if (!empty($massmessage['scheduleTime']) && $massmessage['scheduleTime'] < TimeUtil::msTime()) {
         throw new InvalidParameterException(['schedule-picker' => Yii::t('channel', 'schedule_time_error')]);
     }
     unset($massmessage['channelId']);
     $result = Yii::$app->weConnect->createMassMessage($channelId, $massmessage);
     if ($result) {
         return ['message' => 'OK'];
     } else {
         throw new ServerErrorHttpException('Create mass message fail.');
     }
 }
Esempio n. 5
0
 /**
  * Check bind.
  *
  * <b>Request Type</b>: GET<br/><br/>
  * <b>Request Endpoint</b>:http://{server-domain}/api/mobile/check-bind<br/><br/>
  * <b>Response Content-type</b>: application/json<br/><br/>
  * <b>Summary</b>: This api is used for check bind.
  * <br/><br/>
  *
  * <b>Request Params</b>:<br/>
  *     <br/><br/>
  *
  * <b>Response Params:</b><br/>
  *     redirect<br/>
  *     <br/><br/>
  *
  * <br/><br/>
  *
  * <b>Response Example</b>:<br/>
  * <pre>
  *  redirect('http://dev.cp.augmarketing.cn/mobile/center?openId=3DoTAN2jmRmInqhC_CDLN7aSTzvfzo') or
  *  redirect('http://dev.cp.augmarketing.cn/mobile/center?memberId=549a73c3e9c2fb8d7c8b4569')
  * </pre>
  */
 public function actionCheckBind($type = '', $param = '')
 {
     $params = $this->getQuery();
     if (empty($params['state'])) {
         throw new BadRequestHttpException('missing params state');
     }
     $channelId = $params['state'];
     $channelInfo = Yii::$app->weConnect->getAccounts($channelId);
     if (empty($channelInfo[0]['channel'])) {
         throw new BadRequestHttpException('invalid channelId');
     }
     $userChannel = $channelInfo[0]['channel'];
     $redirect = !empty($type) ? base64_decode($param) : '';
     $mainDomain = Yii::$app->request->hostInfo;
     if ($userChannel == Account::WECONNECT_CHANNEL_ALIPAY) {
         if (empty($params['auth_code'])) {
             throw new BadRequestHttpException('missing param auth_code');
         }
         LogUtil::info(['params' => $params, 'channelId' => $channelId, 'message' => 'alipay info'], 'channel');
         if ($params['scope'] == 'auth_userinfo') {
             $alipayUserInfo = Yii::$app->weConnect->getAlipayUserInfo($channelId, $params['auth_code']);
             $openId = $alipayUserInfo['originId'];
         } else {
             //call weconnect to get openId
             $openId = Yii::$app->weConnect->getAlipayOpenId($channelId, $params['auth_code']);
         }
     } else {
         if (empty($params['openId'])) {
             $openIdInfo = $this->getOriginAndOpenId($params);
             $origin = $openIdInfo['origin'];
             $openId = $openIdInfo['openId'];
         } else {
             $origin = Member::WECHAT;
             $openId = $params['openId'];
         }
     }
     //get member unionId from weconnect
     try {
         if (empty($alipayUserInfo)) {
             //to suport alipay,because alipay did not get followers again
             $follower = Yii::$app->weConnect->getFollowerByOriginId($openId, $channelId);
         } else {
             $follower = $alipayUserInfo;
         }
     } catch (ApiDataException $e) {
         LogUtil::info(['WeConnect Exception' => 'Get follower info error', 'exception' => $e], 'channel');
         $follower = null;
     }
     //if follower not subscribed and (follower must subscribe before redirect or origin is weibo), redirect
     if ((empty($follower) || isset($follower['subscribed']) && $follower['subscribed'] != true) && ($this->mustSubscribe($redirect) || $origin === Member::WEIBO)) {
         return $this->redirect($this->getSubscribePage($origin, $channelId, $type, $redirect));
     }
     //if the channel is alipay,we need to judge the member info whether exists,if the info is empty,wo call the first url
     if ($userChannel == Account::WECONNECT_CHANNEL_ALIPAY) {
         if (isset($follower['authorized']) && $follower['authorized'] == false) {
             if ($params['scope'] == 'auth_userinfo') {
                 LogUtil::info(['message' => 'weConnect authorized fail', 'follower' => $follower], 'channel');
             } else {
                 $redirectUrl = $mainDomain . '/api/mobile/check-bind';
                 if (!empty($type) && !empty($param)) {
                     $redirectUrl .= "/{$type}/{$param}";
                 }
                 $redirectUrl .= '?state=' . $channelId . '&appId=' . $params['appId'];
                 $redirectUrl = urlencode($redirectUrl);
                 $url = "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?" . "app_id=" . $params['appId'] . "&auth_skip=false&scope=auth_userinfo&redirect_uri={$redirectUrl}";
                 LogUtil::info(['message' => 'can not get detailed follower info', 'url' => $url, 'follower' => $follower], 'channel');
                 return $this->redirect($url);
             }
         } else {
             LogUtil::info(['message' => 'authorized', 'follower' => $follower], 'channel');
         }
     }
     if (!empty($follower['unionId'])) {
         //unionId exists
         $unionId = $follower['unionId'];
         $member = Member::getByUnionid($unionId);
         if (empty($member)) {
             //no unionId but openId
             $member = Member::getByOpenId($openId);
             if (!empty($member)) {
                 $member->unionId = $unionId;
                 $member->save(true, ['unionId']);
             }
         }
     } else {
         if (!empty($follower['originId'])) {
             $unionId = '';
             $member = Member::getByOpenId($openId);
         } else {
             if (empty($follower) && !empty($params['appid']) && $userChannel == Account::WECONNECT_CHANNEL_WEIXIN) {
                 LogUtil::info(['message' => 'Failed to get follower info', 'follower' => $follower, 'params' => $params]);
                 $appId = $params['appid'];
                 // not a follower, oAuth2.0 to get user_info
                 $member = Member::getByOpenId($openId);
                 if (empty($member)) {
                     $component = Yii::$app->weConnect->getComponentToken();
                     $componentAppId = $component['componentAppId'];
                     $state = $channelId;
                     $redirectUrl = Yii::$app->request->hostInfo . '/api/mobile/user-info';
                     if (!empty($redirect)) {
                         $redirectUrl = $redirectUrl . '/' . $type . '/' . $param;
                     }
                     LogUtil::info(['message' => 'oauth2 user_info redirecturl', 'url' => $redirectUrl]);
                     $redirectUrl = urlencode($redirectUrl);
                     $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$appId}&redirect_uri={$redirectUrl}&response_type=code&scope=snsapi_userinfo&state={$state}&component_appid={$componentAppId}#wechat_redirect";
                     return $this->redirect($url);
                 } else {
                     //member exists, will redirect to member center or 403
                 }
             } else {
                 LogUtil::error(['Mobile member' => 'Failed to get follower info']);
                 return $this->redirect('/mobile/common/403');
             }
         }
     }
     LogUtil::info(['message' => 'Bind with follower', 'follower' => $follower], 'channel');
     if (empty($member)) {
         //if not exist redirect, get unionId to bind
         //urlencode to avoid lose $redirect query string when frontend get $redirect
         $redirect = urlencode($redirect);
         $redirectUrl = $mainDomain . '/mobile/member';
         if ($type == self::TYPE_REDIRECT) {
             $redirectUrl .= '/activate';
         } else {
             $redirectUrl .= '/center';
         }
         $redirectUrl .= "?openId={$openId}&channelId={$channelId}&unionId={$unionId}&redirect={$redirect}&redirectType={$type}";
     } else {
         //if member is disabled, redirect to 403
         if ($member->isDisabled) {
             return $this->redirect('/mobile/common/403');
         }
         //if exist redirect to member center
         $social = ['channel' => $channelId, 'openId' => $openId, 'origin' => $origin, 'originScene' => empty($follower['firstSubscribeSource']) ? '' : $follower['firstSubscribeSource']];
         $this->addNewSocial($member, $social);
         $memberId = $member['_id'] . '';
         if ($type == self::TYPE_REDIRECT) {
             $str = strpos($redirect, '?') !== false ? '&' : '?';
             $redirectUrl = $redirect . $str . "quncrm_member={$memberId}";
         } else {
             $accountId = new \MongoId($member['accountId']);
             $token = Token::createForMobile($accountId);
             if (empty($token['accessToken'])) {
                 throw new ServerErrorHttpException('Failed to create token for unknown reason.');
             }
             $accessToken = $token['accessToken'];
             $this->setAccessToken($accessToken);
             if ($type == self::TYPE_REDIRECT_INSIDE) {
                 $str = strpos($redirect, '?') !== false ? '&' : '?';
                 $redirectUrl = $redirect . $str . "memberId={$memberId}&channelId={$channelId}";
             } else {
                 $redirectUrl = $mainDomain . "/mobile/member/center?memberId={$memberId}&channelId={$channelId}";
                 if (!empty($member->cardExpiredAt) && $member->cardExpiredAt < TimeUtil::msTime()) {
                     $redirectUrl = $redirectUrl . '&cardExpired=1';
                 } else {
                     $redirectUrl = $redirectUrl . '&cardExpired=0';
                 }
             }
         }
     }
     return $this->redirect($redirectUrl);
 }
 /**
  * Tranfer client to another helpdesk
  *
  * <b>Request Type: </b>POST<br/>
  * <b>Request Endpoint: </b>http://{server-domain}/api/chat/conversation/transfer
  * <b>Summary: </b> This api is for transfer helpdesk.<br/>
  *
  * <b>Request Parameters: </b><br/>
  *     accesstoken: string<br/>
  *     deskId: string, id of the desk.<br/>
  *     clientOpenId: string, the openId of the client.<br/>
  *     conversationId: string, the id of chatConversation.<br/>
  *     targetDeskId: string, the id of the target desk
  *
  * <b>Response Example: </b><br/>
  * {
  *     "status" => "ok"
  * }
  */
 public function actionTransfer()
 {
     $cache = Yii::$app->cache;
     $targetDeskId = $this->getParams('targetDeskId');
     $conversationId = $this->getParams('conversationId');
     $accountId = $this->getAccountId();
     $conversations = $cache->get('conversations' . $accountId);
     if ($conversations) {
         $lastChatConversation = ChatConversation::findByPk(new \MongoId($conversationId));
         if ($lastChatConversation->status === ChatConversation::STATUS_CLOSED) {
             throw new BadRequestHttpException('ChatConversation has been closed');
         }
         $client = $lastChatConversation->client;
         $clientOpenId = $lastChatConversation->client['openId'];
         $deskMongoId = $lastChatConversation->desk['id'];
         $deskId = (string) $deskMongoId;
         $targetDeskMongoId = new \MongoId($targetDeskId);
         $helpDesk = HelpDesk::findByPk($deskMongoId);
         $targetHelpDesk = HelpDesk::findByPk($targetDeskMongoId);
         $maxClientLimit = HelpDeskSetting::getMaxClientCount($accountId);
         if ($targetHelpDesk->clientCount >= $maxClientLimit) {
             throw new BadRequestHttpException('Target helpdesk has exceed the max serve number');
         }
         // Set the previous chat conversation status to 'closed'
         $lastChatConversation->status = ChatConversation::STATUS_CLOSED;
         if (!$lastChatConversation->update()) {
             LogUtil::error(['message' => 'Update previous helpdesk chatConversation status failed', 'error' => $lastChatConversation->errors], 'helpdesk');
             throw new ServerErrorHttpException('Update previous helpdesk chatConversation status failed');
         } else {
             HelpDesk::decClientCount($deskMongoId);
         }
         // Remove the client from original desk client list, and add to transfered desk client list
         foreach ($conversations[$deskId] as $index => $openId) {
             if ($openId == $clientOpenId) {
                 unset($conversations[$deskId][$index]);
             }
         }
         $conversations[$targetDeskId][] = $clientOpenId;
         Yii::$app->cache->set('conversations' . $accountId, $conversations);
         // Create chatConversation record in db
         $chatConversation = ChatConversation::saveRecord($targetHelpDesk, $client, ChatConversation::STATUS_OPEN, $accountId);
         // Generate response data
         $newChannelName = ChatConversation::getChannelName($targetDeskId, $clientOpenId);
         $lastChatTime = ChatConversation::getLastChatTime($targetDeskMongoId, $clientOpenId, $accountId);
         $chatTimes = ChatConversation::getChatTimes($clientOpenId, $accountId);
         $previousDesk = $lastChatConversation->desk;
         $previousDesk['id'] = $deskId;
         $desk = $chatConversation->desk;
         $desk['id'] = $targetDeskId;
         $data = ['conversationId' => (string) $chatConversation->_id, 'desk' => $desk, 'previousDesk' => $previousDesk, 'client' => $client, 'channel' => $newChannelName, 'lastChatTime' => $lastChatTime, 'chatTimes' => $chatTimes, 'startTime' => TimeUtil::msTime()];
         // Trigger transfer event to global channel, and the desk-client channel to make the client change listening channel
         Yii::$app->tuisongbao->triggerEvent(ChatConversation::EVENT_DESK_TRANSFER, $data, [ChatConversation::CHANNEL_GLOBAL . $accountId, $lastChatConversation->conversation]);
         // Push state
         $pushExtra = ['openId' => $clientOpenId, 'conversationId' => (string) $chatConversation->_id, 'previousDeskId' => $deskId, 'sentTime' => TimeUtil::msTime()];
         $previousDeskName = empty($previousDesk['name']) ? '' : $previousDesk['name'];
         $pushMessage = str_replace('{desk}', $previousDeskName, ChatConversation::PUSH_MESSAGE_TRANSFER);
         ChatConversation::pushMessage($previousDesk['id'], ChatConversation::EVENT_DESK_TRANSFER, $pushExtra);
         ChatConversation::pushMessage($desk['id'], ChatConversation::EVENT_DESK_TRANSFER, $pushExtra, $pushMessage);
         LogUtil::info(['event' => ChatConversation::EVENT_DESK_TRANSFER, 'desk' => $desk, 'previousDesk' => $previousDesk, 'client' => $client], 'helpdesk');
         return array_merge($data, ['status' => 'ok']);
     }
 }
Esempio n. 7
0
 /**
  * conver the value for date
  */
 public static function converTimeValue($sourceKey)
 {
     //to surport to use dot
     if (strpos($sourceKey, '.')) {
         $sourceKey = str_replace('.', '-', $sourceKey);
     }
     if (!is_numeric($sourceKey) || count($sourceKey) < 10) {
         $sourceKey = TimeUtil::msTime(strtotime($sourceKey));
     }
     return $sourceKey;
 }
Esempio n. 8
0
 /**
  * Get the latest documents of the pending clients.
  * The clients should ping the server in the threshold or sourced from WeChat.
  * @param Integer $count the count of the elements to be dequeued
  * @return array the client.
  */
 public static function deQueue($deskId, $count = 1)
 {
     $helpdesk = HelpDesk::findOne($deskId);
     $condition = ['$or' => [['lastPingTime' => ['$gt' => TimeUtil::msTime(time() - self::PING_THRESHOLD)]], ['source' => ['$in' => [ChatConversation::TYPE_WECHAT, ChatConversation::TYPE_WEIBO, ChatConversation::TYPE_ALIPAY]]]], 'accountId' => $helpdesk->accountId];
     if (!empty($helpdesk->tags)) {
         $condition['tags'] = ['$in' => $helpdesk->tags];
         $pendingClients = self::find()->where($condition)->orderBy(['requestTime' => SORT_ASC])->limit($count)->all();
         if (count($pendingClients) < $count) {
             $condition['tags'] = ['$nin' => $helpdesk->tags];
             $excludeTagClients = self::find()->where($condition)->orderBy(['requestTime' => SORT_ASC])->limit($count - count($pendingClients))->all();
             $pendingClients = array_merge_recursive($pendingClients, $excludeTagClients);
         }
     } else {
         $pendingClients = self::find()->where($condition)->orderBy(['requestTime' => SORT_ASC])->limit($count)->all();
     }
     $clients = [];
     foreach ($pendingClients as $pendingClient) {
         $client = ['nick' => $pendingClient->nick, 'avatar' => $pendingClient->avatar, 'openId' => $pendingClient->openId, 'source' => $pendingClient->source, 'sourceChannel' => $pendingClient->sourceChannel];
         if ($pendingClient->accountInfo) {
             $client['accountInfo'] = $pendingClient->accountInfo;
         }
         $clients[] = $client;
         $pendingClient->delete();
     }
     return $clients;
 }
Esempio n. 9
0
 public static function saveRecord($desk, $client, $status, $accountId)
 {
     $chatConversation = new ChatConversation();
     $channelName = self::getChannelName($desk->_id, $client['openId']);
     $chatConversation->conversation = $channelName;
     $chatConversation->status = $status;
     $chatConversation->desk = ['id' => $desk->_id, 'badge' => $desk->badge, 'email' => $desk->email, 'avatar' => $desk->avatar, 'name' => $desk->name];
     $chatConversation->client = $client;
     $chatConversation->accountId = $accountId;
     $chatConversation->lastChatTime = TimeUtil::msTime();
     if (!$chatConversation->save()) {
         LogUtil::error(['message' => 'save chat conversation failed', 'error' => $chatConversation->errors], 'helpdesk');
         throw new ServerErrorHttpException('save chatConversation failed');
     } else {
         HelpDesk::incClientCount($desk->_id);
     }
     return $chatConversation;
 }
Esempio n. 10
0
 public function perform()
 {
     $args = $this->args;
     $message = $args['message'];
     $type = $message['msgType'];
     $content = $message['content'];
     $WEConnectAccountInfo = $args['account'];
     $WEConnectUserInfo = $args['user'];
     $accountId = new \MongoId($args['accountId']);
     $accountInfoType = null;
     $source = null;
     switch ($WEConnectAccountInfo['channel']) {
         case 'WEIBO':
             $source = ChatConversation::TYPE_WEIBO;
             $accountInfoType = 'WEIBO';
             break;
         case 'ALIPAY':
             $source = ChatConversation::TYPE_ALIPAY;
             $accountInfoType = 'ALIPAY';
             break;
         case 'WEIXIN':
             $source = ChatConversation::TYPE_WECHAT;
             $accountInfoType = $WEConnectAccountInfo['accountType'];
             break;
         default:
             throw new BadRequestHttpException("Unsupported channel type");
             break;
     }
     $client = ['nick' => $WEConnectUserInfo['nickname'], 'avatar' => $WEConnectUserInfo['headerImgUrl'], 'openId' => $WEConnectUserInfo['id'], 'originId' => $WEConnectUserInfo['originId'], 'source' => $source, 'sourceChannel' => $WEConnectUserInfo['accountId'], 'accountId' => $accountId, 'accountInfo' => ['type' => $accountInfoType, 'name' => $WEConnectAccountInfo['name']]];
     ResqueUtil::log(['message' => 'get message from wechat', 'WEConnectAccountInfo' => $WEConnectAccountInfo, 'WEConnectUserInfo' => $WEConnectUserInfo, 'accountId' => $accountId]);
     if (empty($type) || empty($content)) {
         ResqueUtil::log(['message' => 'missing required fields', 'senario' => 'accepting messages from WeConnect', 'WEConnectAccountInfo' => $WEConnectAccountInfo, 'WEConnectUserInfo' => $WEConnectUserInfo]);
         HelpDesk::sendSystemReplyByType($client, $accountId, HelpDeskSetting::REPLY_ERROR);
         return;
     }
     try {
         if ('EVENT' === $type) {
             switch ($content) {
                 case 'CONNECT':
                     $isInWorkingHour = HelpDeskSetting::isInWorkingHours($accountId);
                     if ($isInWorkingHour) {
                         if (empty($WEConnectAccountInfo)) {
                             ResqueUtil::log(['message' => 'Account parameters missing', 'senario' => 'Wechat message, event connect']);
                             HelpDesk::sendSystemReplyByType($client, $accountId, HelpDeskSetting::REPLY_ERROR);
                             return;
                         }
                         //check if the wechat enduser has connected to a helpdesk
                         $conversation = ChatConversation::findOpenByClientId($WEConnectUserInfo['id'], $accountId);
                         if (!empty($conversation)) {
                             ResqueUtil::log(['message' => 'Have connect to helpdesk already', 'senario' => 'Wechat message, event connect', 'user' => $WEConnectUserInfo, 'accountId' => $accountId]);
                             HelpDesk::sendSystemReplyByType($client, $accountId, HelpDeskSetting::REPLY_CUSTOM, ChatConversation::NO_DUPLICATE_CLIENT);
                             return;
                         }
                         return HelpDesk::connect($client, $accountId);
                     } else {
                         //send the disconnect event to WeConnect
                         Yii::$app->weConnect->sendCustomerServiceMessage($WEConnectUserInfo['id'], $WEConnectUserInfo['accountId'], ['msgType' => ChatConversation::WECHAT_MESSAGE_TYPE_EVENT, 'content' => 'DISCONNECT']);
                         HelpDesk::sendSystemReplyByType($client, $accountId, HelpDeskSetting::REPLY_NONWORKING);
                     }
                     break;
                 case 'DISCONNECT':
                     //get the conversationId
                     $conversation = ChatConversation::findOpenByClientId($WEConnectUserInfo['id'], $accountId, ['type' => 'left']);
                     if (empty($conversation)) {
                         return ['status' => 'ok'];
                     }
                     //disconnect
                     return HelpDesk::disconnect($conversation->_id, ['type' => 'brake']);
                     break;
                 default:
                     throw new BadRequestHttpException("Unsupported event content type");
                     break;
             }
         } else {
             //get the conversation information
             $conversation = ChatConversation::findOpenByClientId($WEConnectUserInfo['id'], $accountId);
             if (empty($conversation)) {
                 $pendingClient = PendingClient::findOne(['openId' => $WEConnectUserInfo['id'], 'accountId' => $accountId]);
                 if (!empty($pendingClient)) {
                     HelpDesk::sendSystemReplyByType($client, $accountId, HelpDeskSetting::REPLY_WAITTING);
                     return ['status' => 'ok'];
                 } else {
                     return HelpDesk::connect($client, $accountId);
                 }
             }
             $desk = $conversation->desk;
             $desk['id'] = (string) $desk['id'];
             $client = $conversation->client;
             $sentTime = TimeUtil::msTime();
             //trigger send message event
             $name = ChatConversation::EVENT_CHAT_MESSAGE;
             $data = ['conversationId' => (string) $conversation->_id, 'desk' => $desk, 'client' => $client, 'chatMessage' => ['content' => ['msgType' => $type, 'body' => $content], 'sentTime' => $sentTime], 'isReply' => false];
             //add chatMessage record
             $chatMessage = ChatMessage::saveRecord(['msgType' => $type, 'body' => $content], $sentTime, false, $conversation->_id, $accountId);
             //helpDesk web client message
             $channels = [ChatConversation::getChannelName($desk['id'], $client['openId'])];
             $data['messageId'] = (string) $chatMessage->_id;
             Yii::$app->tuisongbao->triggerEvent($name, $data, $channels);
             //helpDesk mobile client message
             $pushExtra = ['messageId' => (string) $chatMessage->_id, 'openId' => $client['openId'], 'conversationId' => (string) $conversation->_id, 'sentTime' => $chatMessage->sentTime];
             $pushMessage = empty($client['nick']) ? $content : $client['nick'] . ':' . $content;
             ChatConversation::pushMessage($desk['id'], ChatConversation::EVENT_CHAT_MESSAGE, $pushExtra, $pushMessage);
             return ['status' => 'ok'];
         }
     } catch (Exception $e) {
         LogUtil::error(['message' => $e->getMessage()], 'helpdesk');
         return HelpDesk::sendSystemReplyByType($client, $account, HelpDeskSetting::REPLY_ERROR);
     }
 }
Esempio n. 11
0
 /**
  * Create interact message
  *
  * <b>Request Type</b>: POST<br/><br/>
  * <b>Request Endpoint</b>:http://{server-domain}/api/channel/messages<br/><br/>
  * <b>Response Content-type</b>: application/json<br/><br/>
  * <b>Summary</b>: This api is used for create interact messages.
  * <br/><br/>
  *
  * <b>Request Params</b>:<br/>
  *     channelId: string<br/>
  *     fromUser: string, channel account id like "gh_68ac2bff67ba"<br/>
  *     toUser: string, the user's origin id<br/>
  *     msgType: TEXT or NEWS<br/>
  *     content: string, if TEXT<br/>
  *     content.articles, if NEWS<br/>
  *     <br/><br/>
  *
  * <b>Response Params:</b><br/>
  *     msg: string, if query fail, it contains the error message<br/>
  *     <br/><br/>
  *
  * <br/><br/>
  *
  * <b>Response Example</b>:<br/>
  * <pre>
  * {
  *   "message": "OK"
  * }
  * </pre>
  */
 public function actionCreate()
 {
     $toUserId = $this->getParams('toUser');
     $channelId = $this->getChannelId();
     $message = ['msgType' => $this->getParams('msgType'), 'createTime' => TimeUtil::msTime()];
     switch ($message['msgType']) {
         case 'TEXT':
             $message['content'] = $this->getParams('content');
             break;
         case 'NEWS':
             $content = $this->getParams('content');
             $message['content'] = $content;
             break;
         default:
             # code...
             break;
     }
     $result = \Yii::$app->weConnect->sendCustomerServiceMessage($toUserId, $channelId, $message);
     if ($result) {
         return ['message' => 'OK'];
     } else {
         throw new ServerErrorHttpException('Create message fail.');
     }
 }
Esempio n. 12
0
 /**
  * This function is used for set last ping time in cache for helpdesk
  * @param string $deskId
  * @param string $accountId
  * @return boolean Whether the data is stored correctly
  */
 public static function setLastPingTimeById($deskId, $accountId)
 {
     $cache = Yii::$app->cache;
     $pingTimes = $cache->get(self::CACHE_PREFIX_HELPDESK_PING . $accountId);
     if (empty($pingTimes)) {
         $pingTimes = [];
     }
     $pingTimes[$deskId] = TimeUtil::msTime();
     return $cache->set(self::CACHE_PREFIX_HELPDESK_PING . $accountId, $pingTimes);
 }