示例#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']);
                     }
                 }
             }
         }
     }
 }
示例#2
0
 public static function isHelpDeskSet($channelId, $accountId)
 {
     $accountId = new \MongoId($accountId);
     $setting = HelpDeskSetting::findOne(['accountId' => $accountId, 'channels.id' => $channelId]);
     if (!empty($setting)) {
         return true;
     }
     return false;
 }
 /**
  * Get the detail information of the helpDesk setting
  *
  * <b>Request Type: </b>GET<br/>
  * <b>Request Endpoint: </b>http://{server-domain}/api/helpdesk/settings<br/>
  * <b>Content-type: </b>Application/json<br/>
  * <b>Summary: </b>This api is for get the detail information of the helpDesk setting.<br/>
  *
  * <b>Response Example: </b>
  * <pre>
  *     {
  *          "maxW*aitTim": 3,
  *          "maxClient": 5,
  *          "ondutyTime": "8:00",
  *          "offdutyTime": "18:00",
  *          "systemReplies": [
  *              {
  *                  "name": "wait_for_service",
  *                  "type": "waitting",
  *                  "replyText": "wait",
  *                  "isEnabled": true
  *              },
  *              {
  *                  "name": "close_service",
  *                  "type": "close",
  *                  "replyText": "close",
  *                  "isEnabled": true
  *              },
  *              {
  *                  "name": "non_working_time",
  *                  "type": "nonworking",
  *                  "replyText": "non working time",
  *                  "isEnabled": true
  *              },
  *              {
  *                  "name": "auto_brake",
  *                  "type": "brake",
  *                  "replyText": "brake",
  *                  "isEnabled": true
  *              },
  *              {
  *                  "name": "connect_success",
  *                  "type": "success",
  *                  "replyText": "success",
  *                  "isEnabled": true
  *              },
  *              {
  *                  "name": "desk_droping",
  *                  "type": "droping",
  *                  "replyText": "droping",
  *                  "isEnabled": true
  *              }
  *          ]
  *      }
  * </pre>
  */
 public function actionIndex()
 {
     $accountId = $this->getAccountId();
     if (!$accountId) {
         $accountId = $this->getQuery('cid');
     }
     if (empty($accountId)) {
         throw new BadRequestHttpException("AccountId is required");
     }
     return HelpDeskSetting::getInstance($accountId);
 }
 /**
  * Remove bound a channel account
  *
  * <b>Request Type</b>: DELETE<br/><br/>
  * <b>Request Endpoint</b>:http://{server-domain}/api/management/channel/{channelaccountId}<br/><br/>
  * <b>Content-type</b>: application/json<br/><br/>
  * <b>Summary</b>: This api is used for removing bound a channel account.
  * <br/><br/>
  *
  * <b>Request Params</b>:<br/>
  *     channelAccount: string, the channel account id<br/>
  *     channelType: string, channel type("weibo" , "wechat", ...)<br/>
  *     <br/><br/>
  *
  * <b>Response Params:</b><br/>
  *     ack: integer, mark the delete result, 0 means query fail, 1 means delete successfully<br/>
  *     msg: string, if query fail, it contains the error message<br/>
  *     data: array, json array to deleted channel detail information<br/>
  *     <br/><br/>
  *
  * <b>Request Example:</b><br/>
  * <pre>
  * {
  *     "channelAccount": "gh_fdba39256c8e",
  *     "channelType": "wechat",
  * }
  * </pre>
  * <br/><br/>
  *
  * <b>Response Example</b>:<br/>
  * <pre>
  * {
  *    'data' : []
  * }
  * </pre>
  */
 public function actionDelete($id)
 {
     $accountId = $this->getAccountId();
     $params = $this->getParams();
     if (empty($params['type']) || !in_array($params['type'], [Channel::WEIBO, Channel::ALIPAY])) {
         throw new BadRequestHttpException(Yii::t('common', 'data_error'));
     }
     $channelType = $params['type'];
     // Recovery micro-blog authorized access token
     if ($channelType == Channel::WEIBO) {
         $token = $params['weiboToken'];
         Yii::$app->weiboConnect->revokeWeiboToken($token);
     }
     //refine to call wechat-connection system api(Delete One Account)
     $result = Yii::$app->weConnect->deleteAccount($id);
     if ($result) {
         if (!Channel::disableByChannelIds($accountId, [$id])) {
             throw new \yii\web\ServerErrorHttpException("delete account channel failed");
         }
         if (false === HelpDeskSetting::updateAll(['$pull' => ['channels' => ['id' => $id]]], ['accountId' => $accountId])) {
             throw new \yii\web\ServerErrorHttpException("delete helpdesk setting channel failed");
         }
     } else {
         throw new \yii\web\ServerErrorHttpException("delete channel failed");
     }
     return [];
 }
 /**
  * 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']);
     }
 }
 /**
  * Remove a website into help desk setting
  *
  * <b>Request Type: </b>PUT<br/>
  * <b>Request Endpoint: </b>http://{server-domain}/api/helpdesk/setting/remove-website<br/>
  * <b>Content-type: </b>Application/json<br/>
  * <b>Summary: </b>This api is for removing a website into help desk setting.<br/>
  *
  * <b>Request Example: </b>
  * <pre>
  *     {
  *          "settingId": '52d791327ae252f9149547cb',
  *          "websiteId": '52d791307ae252f9149547c9'
  *     }
  * </pre>
  */
 public function actionRemoveWebsite()
 {
     $settingId = $this->getParams('settingId');
     $websiteId = $this->getParams('websiteId');
     if (!empty($websiteId) && !empty($settingId)) {
         $settingId = new \MongoId($settingId);
         // Add a channel into help desk setting
         $result = HelpDeskSetting::updateAll(['$pull' => ['websites' => ['id' => $websiteId]]], ['_id' => $settingId]);
         if ($result) {
             return $websiteId . '';
         }
         throw new ServerErrorHttpException('remove website fail');
     }
     throw new BadRequestHttpException('parameters missing');
 }
示例#7
0
 /**
  * Create menu
  *
  * <b>Request Type</b>: POST<br/><br/>
  * <b>Request Endpoint</b>:http://{server-domain}/api/channel/menus<br/><br/>
  * <b>Response Content-type</b>: application/json<br/><br/>
  * <b>Summary</b>: This api is used to create/update menu.
  * <br/><br/>
  *
  * <b>Request Params</b>:<br/>
  *     channelId: string<br/>
  *     menu.name: string<br/>
  *     menu.keycode: string<br/>
  *     menu.type: string, VIEW or CLICK<br/>
  *     menu.subMenus.name: string<br/>
  *     menu.subMenus.type: string<br/>
  *     menu.subMenus.msgType: TEXT or NEWS<br/>
  *     menu.subMenus.content: string, if TEXT<br/>
  *     menu.subMenus.content.articles: array, if NEWS<br/>
  *     <br/><br/>
  *
  * <br/><br/>
  *
  * <b>Request Example</b>:<br/>
  * <pre>
  * {
  *  "channelId": "5473ffe7db7c7c2f0bee5c71",
  *  "menu": [
  *      {
  *          ...
  *      },
  *      {
  *          "name": "去踢球",
  *          "type": "CLICK",
  *          "subMenus": [
  *              {
  *                  "name": "menu1-sub1",
  *                  "type": "VIEW",
  *                  "msgType": "TEXT",
  *                  "content": "hello world"
  *              },
  *              {
  *                  "name": "menu1-sub2",
  *                  "type": "VIEW",
  *                  "msgType": "URL",
  *                  "content": "http://www.baidu.com"
  *              },
  *              {
  *                  "name": "menu1-sub3",
  *                  "type": "VIEW",
  *                  "msgType": "NEWS",
  *                  "content": {
  *                      "articles": [
  *                          {
  *                              "title": "新闻",
  *                              "description": "APEC会议举行第三天",
  *                              "sourceUrl": "http://www.baidu.com",
  *                              "picUrl": "http://www.baidu.com/image.jpg"
  *                          },
  *                          {
  *                              "title": "新闻",
  *                              "description": "APEC会议举行第三天",
  *                              "sourceUrl": "http://www.baidu.com",
  *                              "picUrl": "http://www.baidu.com/image.jpg"
  *                          }
  *                      ]
  *                  }
  *              }
  *          ]
  *      },
  *      {
  *          ...
  *      }
  *  ]
  * }
  *
  * <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()
 {
     $menu = $this->getParams();
     $channelId = $this->getChannelId();
     $accountId = $this->getAccountId();
     unset($menu['channelId']);
     $actions = Yii::$app->channelMenu->getMenuActions($channelId, $accountId, true);
     $result = Yii::$app->weConnect->createMenu($channelId, $menu['menu'], $actions);
     if ($result) {
         $conditon = ['accountId' => $accountId, 'channels.id' => $channelId];
         $helpDeskSetting = HelpDeskSetting::findOne($conditon);
         if (!empty($helpDeskSetting)) {
             $isSet = Yii::$app->weConnect->isSetHelpDesk($menu['menu']);
             $channelResult = HelpDeskSetting::updateAll(['$set' => ['channels.$.isSet' => $isSet]], $conditon);
             if (!$channelResult) {
                 throw new ServerErrorHttpException('Set menu channel status fail.');
             }
         }
         return ['message' => 'OK'];
     } else {
         throw new ServerErrorHttpException('Create menu fail.');
     }
 }
示例#8
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);
     }
 }
示例#9
0
 /**
  * Send reply message to WeConnect user by type
  * @param array $client
  * @param string $accountId
  * @param string $type
  */
 public static function sendSystemReplyByType($client, $accountId, $type, $content = null)
 {
     $needReplySourceArray = [ChatConversation::TYPE_WECHAT, ChatConversation::TYPE_WEIBO, ChatConversation::TYPE_ALIPAY];
     if (in_array($client['source'], $needReplySourceArray)) {
         $helpDeskSetting = HelpDeskSetting::getInstance($accountId);
         $defaultSystemReplies = $helpDeskSetting->systemReplies;
         if ($type !== HelpDeskSetting::REPLY_CUSTOM) {
             foreach ($defaultSystemReplies as $defaultSystemReply) {
                 if ($defaultSystemReply['type'] === $type) {
                     $content = $defaultSystemReply['isEnabled'] ? $defaultSystemReply['replyText'] : '';
                 }
             }
         }
         if (!empty($content)) {
             $message = ['msgType' => ChatMessage::MSG_TYPE_TEXT, 'content' => $content, 'createTime' => TimeUtil::msTime()];
             Yii::$app->weConnect->sendCustomerServiceMessage($client['openId'], $client['sourceChannel'], $message);
         } else {
             if ($content === null) {
                 throw new ServerErrorHttpException('Incorrect name for default helpdesk system reply');
             }
         }
     }
 }