Beispiel #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']);
                     }
                 }
             }
         }
     }
 }
 /**
  * Push status
  *
  * <b>Request Type: </b>POST<br/>
  * <b>Request Endpoint: </b>http://{server-domain}/api/chat/conversation/state<br/>
  * <b>Content-type: </b>application/json<br/>
  * <b>Summary: </b>This is api is for push the status of the client or helpDesk to the server<br/>
  *
  * <b>Request Parameters: </b><br/>
  *     channelName: string, the name of the channel<br/>
  *     eventName: string, the name of the event, should be 'deskLeft', 'deskJoined', 'clientLeft', 'clientJoined', 'clientPing', 'pendingLeft' <br/>
  *     consersationId: string, the identifier of the conversation.<br/>
  *     desk: array, the information of the help desk.<br/>
  *         desk.badge: string, the badge id of the helpDesk.<br/>
  *         desk.id: string, the identifier of the helpDesk.<br/>
  *         desk.email: string, the email of the helpDesk.<br/>
  *         desk.avatar: string, the url of the avatar of the helpDesk.<br/>
  *     client: array, the information of the client.<br/>
  *         client.nick: string, the nickname of the client.<br/>
  *         client.avatar: string, the url of the avatar of the client.<br/>
  *         client.openId: string, the openId of the client.<br/>
  *         client.source: string, 'web' or 'wechat', the source.<br/>
  *
  * <b>Request Example: </b><br/>
  * <pre>
  * {
  *      "eventName": "clientJoined",
  *      "conversationId": "149035b7b81374aa468b4566",
  *      "desk": {
  *         "badge": "T04247",
  *         "id": "54a24c12db4c0ecc048b4568",
  *          "email": "*****@*****.**",
  *           "avatar": "http://test.com/avatar.png"
  *       },
  *       "client": {
  *           "nick": "devin",
  *           "avatar": "http://test.com/avatar.png",
  *           "openId": "2234231234f2d12",
  *           "source": "website",
  *           "messages": [
  *               ...
  *           ]
  *       },
  *       "extra": {
  *           //extra data
  *       }
  *   }
  *
  * <b>Response Example: </b><br/>
  * <pre>
  *     {"status": "ok"}
  * </pre>
  *
  * for clientJoined
  * </pre>
  * {
  *      "status": "ok",
  *       "helpDesk": {
  *           "id": "54a24c12db4c0ecc048b4568",
  *           "badge": "T04247",
  *           "email": "*****@*****.**",
  *           "avatar": "/images/management/image_hover_default_avatar.png",
  *           "isEnabled": true,
  *           "isActivated": false,
  *           "name": null,
  *           "language": "zh_cn",
  *           "clientCount": 1,
  *           "isOnline": false,
  *           "conversationCount": 2,
  *           "maxClient": 5
  *       },
  *       "channel": "wm-chat-54a24c12db4c0ecc048b4568-2234231234f2d12",
  *       "conversationId": "54a295addb4c0ecd048b456c"
  *   }
  * </pre>
  */
 public function actionState()
 {
     $eventName = $this->getParams('eventName');
     $conversationId = $this->getParams('conversationId');
     $desk = $this->getParams('desk');
     $client = $this->getParams('client');
     $extra = $this->getParams('extra');
     $cache = Yii::$app->cache;
     $accountId = $this->getAccountId();
     $response = [];
     if (!empty($desk['id'])) {
         $deskMongoId = new \MongoId($desk['id']);
     }
     if (empty($eventName)) {
         throw new BadRequestHttpException('Missing required fields');
     }
     switch ($eventName) {
         case 'deskJoined':
             if (empty($desk) || empty($deskMongoId)) {
                 throw new BadRequestHttpException('Missing required fields');
             }
             $response = HelpDesk::join($desk['id'], $accountId);
             Yii::$app->tuisongbao->triggerEvent(HelpDesk::EVENT_ONLINE_STATUS, [], [HelpDesk::CHANNEL_GLOBAL . $accountId]);
             break;
         case 'deskLeft':
             if (empty($desk) || empty($deskMongoId)) {
                 throw new BadRequestHttpException('Missing required fields');
             }
             // cannot get accountId when logout from work order system first
             $cid = new \MongoId($this->getParams('cid'));
             $accountId = empty($accountId) ? $cid : $accountId;
             $response = HelpDesk::leave($desk['id'], $accountId, $extra);
             Yii::$app->tuisongbao->triggerEvent(HelpDesk::EVENT_ONLINE_STATUS, [], [HelpDesk::CHANNEL_GLOBAL . $accountId]);
             break;
         case 'clientJoined':
             if (empty($client)) {
                 throw new BadRequestHttpException('Missing required fields');
             }
             $client['nick'] = "guest-" . $client['openId'];
             $accountId = $this->getParams('cid');
             if (empty($accountId)) {
                 throw new BadRequestHttpException('Missing accountId');
             }
             $accountId = new \MongoId($accountId);
             $client['accountId'] = $accountId;
             $conversation = ChatConversation::findOpenByClientId($client['openId'], $accountId);
             if (!empty($conversation)) {
                 //Return the original conversation information if the convesation is not closed
                 $response = ['status' => 'ok', 'desk' => $conversation->desk, 'client' => $conversation->client, 'channel' => $conversation->conversation, 'conversationId' => (string) $conversation->_id, 'lastChatTime' => $conversation->lastChatTime, 'chatTimes' => ChatConversation::getChatTimes($conversation->client['openId'], $accountId)];
             } else {
                 $response = HelpDesk::connect($client, $accountId);
             }
             break;
         case 'clientLeft':
             if (empty($conversationId)) {
                 throw new BadRequestHttpException('Missing required fields');
             }
             $accountId = $this->getParams('cid');
             if (empty($accountId)) {
                 throw new BadRequestHttpException("Missing accountId");
             }
             $accountId = new \MongoId($accountId);
             $response = HelpDesk::disconnect(new \MongoId($conversationId), $extra);
             break;
         case 'clientPing':
             if (empty($client) || empty($client['openId'])) {
                 throw new BadRequestHttpException("missing client field or openId for client");
             }
             $response = ['status' => 'ok'];
             if (PendingClient::setLastPingTimeByOpenId($client['openId']) == 0) {
                 $response['status'] = 'fail';
             }
             break;
         case 'deskPing':
             if (empty($desk) || empty($desk['id'])) {
                 throw new BadRequestHttpException("missing desk field or id for desk");
             }
             $response = ['status' => 'ok'];
             if (!HelpDesk::setLastPingTimeById($desk['id'], $accountId)) {
                 $response['status'] = 'fail';
             }
             break;
         case 'pendingLeft':
             if (empty($client) || empty($client['openId'])) {
                 throw new BadRequestHttpException("missing client");
             }
             $status = PendingClient::deleteAll(['openId' => $client['openId']]) ? 'ok' : 'fail';
             $response['status'] = $status;
             break;
             //for develop only, query the conversation status
         //for develop only, query the conversation status
         case 'status':
             $response['online'] = $cache->get('conversations' . $accountId);
             $response['onsocket'] = $cache->get('wm-online-desks' . $accountId);
             if (!empty($desk['id'])) {
                 $response['webhook-time'] = $cache->get('wm-update-time' . $desk['id']);
             }
             break;
         default:
             throw new BadRequestHttpException('Illegal event name');
             break;
     }
     return array_merge($response, ['sentTime' => TimeUtil::msTime()]);
 }
 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);
     }
 }
 /**
  * Push the client to the pending client queue
  * @param  array $client
  * @author Devin.Jin
  */
 public static function enQueue($client)
 {
     if (empty(self::findOne(['openId' => $client['openId']]))) {
         $pendingClient = new PendingClient();
         $pendingClient->load($client, '');
         $pendingClient->lastPingTime = TimeUtil::msTime();
         if (!$pendingClient->save()) {
             LogUtil::error(['error' => 'Save pending client failed', 'errors' => $pendingClient->getErrors()], 'helpdesk');
             throw new ServerErrorHttpException("failed to save pending client for unknown reason");
         }
     }
 }
Beispiel #5
0
 /**
  * Get the clients in pending
  * @param string $deskId
  * @param MongoId $accountId
  * @return integer $count
  */
 public static function connectPendingClient($deskId, $accountId, $count = 1)
 {
     $clients = PendingClient::deQueue($deskId, $count);
     foreach ($clients as $client) {
         self::createConnection($client, $deskId, $accountId);
     }
 }