diff --git a/src/Bean/OfficialAccount/MediaResponse.php b/src/Bean/OfficialAccount/MediaResponse.php index 389d7e6..f76bd8b 100644 --- a/src/Bean/OfficialAccount/MediaResponse.php +++ b/src/Bean/OfficialAccount/MediaResponse.php @@ -11,6 +11,8 @@ use EasySwoole\HttpClient\Bean\Response; use EasySwoole\Utility\Mime\MimeDetectorException; use EasySwoole\Utility\MimeType; +use EasySwoole\WeChat\Exception\RequestError; +use EasySwoole\WeChat\Utility\NetWork; use InvalidArgumentException; use Swoole\Coroutine; @@ -44,6 +46,22 @@ public function isJson(): bool return false; } + + public function isVideo() + { + if (isset($this->httpResponse()->getHeaders()['content-type']) && $this->httpResponse()->getHeaders()['content-type'] === 'text/plain') { + $data = json_decode($this->getContent(), true); + if (isset($data['down_url'])) { + return $data['down_url']; + } + if (isset($data['video_url'])) { + return $data['video_url']; + } + } + + return false; + } + public function isEmpty(): bool { return empty($this->getContent()); @@ -61,7 +79,7 @@ public function getContent(): string * @return string * @throws MimeDetectorException */ - public function save(string $directory, string $filename = null) + public function save(string $directory, string $filename = null, int $timeout = 15) { $directory = rtrim($directory, '/'); @@ -73,18 +91,32 @@ public function save(string $directory, string $filename = null) throw new InvalidArgumentException(sprintf("'%s' is not writable.", $directory)); } + if ($fileUrl = $this->isVideo()) { + NetWork::$TIMEOUT = $timeout; + + $fileData = NetWork::get($fileUrl); + + if (($fileData->getClient()->errCode)) { + throw new RequestError($fileData->getClient()->errMsg); + } + + $this->httpResponse = $fileData; + } + $contents = $this->httpResponse()->getBody(); if (empty($filename)) { - if (preg_match('/filename="(?.*?)"/', $this->httpResponse()->getHeaders()['content-disposition'], $match)) { + if (isset($this->httpResponse()->getHeaders()['content-disposition']) && preg_match('/filename=["]{0,1}(?.*)/', $this->httpResponse()->getHeaders()['content-disposition'], $match)) { $filename = $match['filename']; } else { $filename = md5($contents); } } + $filename = trim($filename, '"'); + if (empty(pathinfo($filename, PATHINFO_EXTENSION))) { - $filename .= MimeType::getExtFromStream($contents); + $filename .= '.'.MimeType::getExtFromStream($contents); } Coroutine::writeFile($directory . '/' . $filename, $contents); diff --git a/src/Bean/OfficialAccount/RequestConst.php b/src/Bean/OfficialAccount/RequestConst.php index 2cc7835..ad88b89 100644 --- a/src/Bean/OfficialAccount/RequestConst.php +++ b/src/Bean/OfficialAccount/RequestConst.php @@ -41,15 +41,40 @@ class RequestConst */ const MSG_TYPE_MPVIDEO = 'mpvideo'; + /* * 事件大小写不同,主要是微信自己本身事件大小写就不一样,不规范 */ - const EVENT_SUBSCRIBE = 'subscribe'; - const EVENT_UNSUBSCRIBE = 'unsubscribe'; - const EVENT_SCAN = 'SCAN'; - const EVENT_LOCATION = 'LOCATION'; - const EVENT_CLICK = 'CLICK'; - const EVENT_VIEW = 'VIEW'; + // 用户关注事件 + const EVENT_SUBSCRIBE = 'subscribe'; + // 用户取消关注事件 + const EVENT_UNSUBSCRIBE = 'unsubscribe'; + // 用户已关注事件 + const EVENT_SCAN = 'SCAN'; + // 上报地理位置事件 + const EVENT_LOCATION = 'LOCATION'; + // 点击自定义菜单拉取消息事件 + const EVENT_CLICK = 'CLICK'; + // 点击自定义菜单跳转事件 + const EVENT_VIEW = 'VIEW'; + // 发送模板消息通知事件 const EVENT_TEMPLATE_SEND_JOB_FINISH = 'TEMPLATESENDJOBFINISH'; + // 群发结果事件 const EVENT_MASS_SEND_JOB_FINISH = 'MASSSENDJOBFINISH'; + //扫码推事件的事件推送 + const EVENT_SCANCODE_PUSH = 'scancode_push'; + //扫码推事件且弹出“消息接收中”提示框的事件推送 + const EVENT_SCANCODE_WAITMSG = 'scancode_waitmsg'; + //弹出系统拍照发图的事件推送 + const EVENT_PIC_SYSPHOTO = 'pic_sysphoto'; + //弹出拍照或者相册发图的事件推送 + const EVENT_PIC_PHOTO_OR_ALBUM = 'pic_photo_or_album'; + //弹出微信相册发图器的事件推送 + const EVENT_PIC_WEIXIN = 'pic_weixin'; + //弹出地理位置选择器的事件推送 + const EVENT_LOCATION_SELECT = 'location_select'; + //点击菜单跳转小程序的事件推送 + const EVENT_VIEW_MINIPROGRAM = 'view_miniprogram'; + + } \ No newline at end of file diff --git a/src/Bean/OfficialAccount/TemplateMsg.php b/src/Bean/OfficialAccount/TemplateMsg.php index 94a3262..306eeed 100644 --- a/src/Bean/OfficialAccount/TemplateMsg.php +++ b/src/Bean/OfficialAccount/TemplateMsg.php @@ -18,9 +18,25 @@ class TemplateMsg extends SplBean protected $url; protected $miniprogram; protected $scene; - protected $appid; - protected $pagepath; + protected $title; protected $data = []; + + /** + * @return mixed + */ + public function getTitle() + { + return $this->title; + } + + /** + * @param string $title + */ + public function setTitle(string $title): void + { + $this->title = $title; + } + /** * @return mixed */ @@ -96,7 +112,7 @@ public function getMiniprogram() /** * @param mixed $miniprogram */ - public function setMiniprogram($miniprogram): void + public function setMiniprogram(array $miniprogram): void { $this->miniprogram = $miniprogram; } @@ -106,7 +122,7 @@ public function setMiniprogram($miniprogram): void */ public function getAppid() { - return $this->appid; + return $this->miniprogram['appid']; } /** @@ -114,7 +130,7 @@ public function getAppid() */ public function setAppid($appid): void { - $this->appid = $appid; + $this->miniprogram['appid'] = $appid; } /** @@ -122,7 +138,7 @@ public function setAppid($appid): void */ public function getPagepath() { - return $this->pagepath; + return $this->miniprogram['pagepath']; } /** @@ -130,7 +146,7 @@ public function getPagepath() */ public function setPagepath($pagepath): void { - $this->pagepath = $pagepath; + $this->miniprogram['pagepath'] = $pagepath; } /** diff --git a/src/OfficialAccount/ApiUrl.php b/src/OfficialAccount/ApiUrl.php index 934830f..53c8b0a 100644 --- a/src/OfficialAccount/ApiUrl.php +++ b/src/OfficialAccount/ApiUrl.php @@ -40,7 +40,12 @@ class ApiUrl /* * 获取临时素材 */ - const MEDIA_GET = 'https://file.api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID'; + const MEDIA_GET = 'https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID'; + + /* + * 获取高清语音素材 + */ + const MEDIA_HD_GET = 'https://api.weixin.qq.com/cgi-bin/media/get/jssdk?access_token=ACCESS_TOKEN&media_id=MEDIA_ID'; /* * 上传临时素材 diff --git a/src/OfficialAccount/Comment.php b/src/OfficialAccount/Comment.php new file mode 100644 index 0000000..4d23153 --- /dev/null +++ b/src/OfficialAccount/Comment.php @@ -0,0 +1,214 @@ +send(ApiUrl::COMMENT_REPLY_DELETE, [ + 'msg_data_id' => $msgDataId, + 'index' => $index, + 'user_comment_id' => $userCommentId + ]); + } + + /** + * 回复评论 + * + * @param $msgDataId + * @param $userCommentId + * @param $content + * @param int $index + * + * @return array + * @throws OfficialAccountError + * @throws \EasySwoole\HttpClient\Exception\InvalidUrl + * @throws \EasySwoole\WeChat\Exception\RequestError + */ + public function reply($msgDataId, $userCommentId, $content, $index = 0) + { + return $this->send(ApiUrl::COMMENT_REPLY_ADD, [ + 'msg_data_id' => $msgDataId, + 'index' => $index, + 'user_comment_id' => $userCommentId, + 'content' => $content + ]); + } + + /** + * 删除评论 + * + * @param $msgDataId + * @param $userCommentId + * @param int $index + * + * @return array + * @throws OfficialAccountError + * @throws \EasySwoole\HttpClient\Exception\InvalidUrl + * @throws \EasySwoole\WeChat\Exception\RequestError + */ + public function delete($msgDataId, $userCommentId, $index = 0) + { + return $this->send(ApiUrl::COMMENT_DELETE, [ + 'msg_data_id' => $msgDataId, + 'index' => $index, + 'user_comment_id' => $userCommentId + ]); + } + + /** + * 将评论取消精选 + * + * @param $msgDataId + * @param $userCommentId + * @param int $index + * + * @return array + * @throws OfficialAccountError + * @throws \EasySwoole\HttpClient\Exception\InvalidUrl + * @throws \EasySwoole\WeChat\Exception\RequestError + */ + public function UnMarkelect($msgDataId, $userCommentId, $index = 0) + { + return $this->send(ApiUrl::COMMENT_UNMARKELECT, [ + 'msg_data_id' => $msgDataId, + 'index' => $index, + 'user_comment_id' => $userCommentId + ]); + } + + /** + * 将评论标记精选 + * + * @param $msgDataId + * @param $userCommentId + * @param int $index + * + * @return array + * @throws OfficialAccountError + * @throws \EasySwoole\HttpClient\Exception\InvalidUrl + * @throws \EasySwoole\WeChat\Exception\RequestError + */ + public function Markelect($msgDataId, $userCommentId, $index = 0) + { + return $this->send(ApiUrl::COMMENT_MARKELECT, [ + 'msg_data_id' => $msgDataId, + 'index' => $index, + 'user_comment_id' => $userCommentId + ]); + } + + /** + * 查看指定文章的评论数据 + * + * @param $msgDataId + * @param $begin + * @param $count + * @param $type + * @param int $index + * + * @return array + * @throws OfficialAccountError + * @throws \EasySwoole\HttpClient\Exception\InvalidUrl + * @throws \EasySwoole\WeChat\Exception\RequestError + */ + public function View($msgDataId, $begin, $count, $type, $index = 0) + { + return $this->send(ApiUrl::COMMENT_LIST, [ + 'msg_data_id' => $msgDataId, + 'index' => $index, + 'begin' => $begin, + 'count' => $count, + 'type' => $type + ]); + } + + /** + * 打开已群发文章评论 + * + * @param $msgDataId + * @param int $index + * + * @return array + * @throws OfficialAccountError + * @throws \EasySwoole\HttpClient\Exception\InvalidUrl + * @throws \EasySwoole\WeChat\Exception\RequestError + */ + public function close($msgDataId, $index = 0) + { + return $this->send(ApiUrl::COMMENT_CLOSE, [ + 'msg_data_id' => $msgDataId, + 'index' => $index + ]); + } + + /** + * 打开已群发文章评论 + * + * @param $msgDataId + * @param int $index + * + * @return array + * @throws OfficialAccountError + * @throws \EasySwoole\HttpClient\Exception\InvalidUrl + * @throws \EasySwoole\WeChat\Exception\RequestError + */ + public function open($msgDataId, $index = 0) + { + return $this->send(ApiUrl::COMMENT_OPEN, [ + 'msg_data_id' => $msgDataId, + 'index' => $index + ]); + } + + /** + * @param $url + * @param $data + * + * @return array + * @throws OfficialAccountError + * @throws \EasySwoole\HttpClient\Exception\InvalidUrl + * @throws \EasySwoole\WeChat\Exception\RequestError + */ + protected function send($url, $data) + { + $url = ApiUrl::generateURL($url, [ + 'ACCESS_TOKEN' => $this->getOfficialAccount()->accessToken()->getToken() + ]); + + $response = NetWork::postJsonForJson($url, $data); + + $ex = OfficialAccountError::hasException($response); + + if ($ex) { + throw $ex; + } + + return $response; + } + +} \ No newline at end of file diff --git a/src/OfficialAccount/JsSdk/Auth.php b/src/OfficialAccount/JsSdk/Auth.php index 00bebad..5d94c61 100644 --- a/src/OfficialAccount/JsSdk/Auth.php +++ b/src/OfficialAccount/JsSdk/Auth.php @@ -130,7 +130,7 @@ function refreshToken($refreshToken) */ function authCheck(SnsAuthBean $authBean) { - $response = NetWork::getForJson(ApiUrl::generateURL(ApiUrl::JSAPI_REFRESH_TOKEN, [ + $response = NetWork::getForJson(ApiUrl::generateURL(ApiUrl::JSAPI_SNS_AUTH_CHECK, [ 'OPENID' => $authBean->getOpenid(), 'ACCESS_TOKEN' => $authBean->getAccessToken(), ])); diff --git a/src/OfficialAccount/Material.php b/src/OfficialAccount/Material.php index ab59938..3089239 100644 --- a/src/OfficialAccount/Material.php +++ b/src/OfficialAccount/Material.php @@ -142,7 +142,7 @@ public function list(string $mediaType, int $offset = 0, int $count = 20) } /** - * 更新多个永久素材 + * 上传多个永久图文素材 * @param MediaArticle ...$articles * @return array * @throws InvalidUrl diff --git a/src/OfficialAccount/Media.php b/src/OfficialAccount/Media.php index 4a10eb2..4204d22 100644 --- a/src/OfficialAccount/Media.php +++ b/src/OfficialAccount/Media.php @@ -99,6 +99,24 @@ public function get($mediaId) return $this->getMedia($url); } + /** + * 获取JSSDK上传的高清临时语音素材 + * @param $mediaId + * + * @return Response|MediaResponse + * @throws InvalidUrl + * @throws OfficialAccountError + */ + public function getHdVoice($mediaId) + { + $url = ApiUrl::generateURL(ApiUrl::MEDIA_HD_GET, [ + 'ACCESS_TOKEN' => $this->getOfficialAccount()->accessToken()->getToken(), + 'MEDIA_ID' => $mediaId + ]); + + return $this->getMedia($url); + } + /** * 执行素材上传 * diff --git a/src/OfficialAccount/Menu.php b/src/OfficialAccount/Menu.php index 52f9289..7bad29f 100644 --- a/src/OfficialAccount/Menu.php +++ b/src/OfficialAccount/Menu.php @@ -85,6 +85,26 @@ public function query() return $json; } + /** + * @return array 菜单配置 + * @throws OfficialAccountError + * @throws \EasySwoole\WeChat\Exception\RequestError + */ + public function queryConfig() + { + $url = ApiUrl::generateURL(ApiUrl::GET_CURRENT_SELFMENU_INFO, [ + 'ACCESS_TOKEN'=> $this->getOfficialAccount()->accessToken()->getToken() + ]); + + $json = NetWork::getForJson($url); + $ex = OfficialAccountError::hasException($json); + if($ex){ + throw $ex; + } + return $json; + } + + /** * delete * diff --git a/src/OfficialAccount/OfficialAccount.php b/src/OfficialAccount/OfficialAccount.php index aa7b21b..e146e7d 100644 --- a/src/OfficialAccount/OfficialAccount.php +++ b/src/OfficialAccount/OfficialAccount.php @@ -29,6 +29,7 @@ class OfficialAccount private $templateMsg; private $service; private $groupSending; + private $comment; public function onError(callable $onError) { @@ -147,6 +148,14 @@ public function user(): User return $this->user; } + public function comment(): Comment + { + if (!isset($this->comment)) { + $this->comment = new Comment($this); + } + return $this->comment; + } + public function getConfig(): OfficialAccountConfig { return $this->config; diff --git a/src/OfficialAccount/TemplateMsg.php b/src/OfficialAccount/TemplateMsg.php index ace67f6..2c930e5 100644 --- a/src/OfficialAccount/TemplateMsg.php +++ b/src/OfficialAccount/TemplateMsg.php @@ -131,7 +131,6 @@ public function sendSubscription(MsgBean $templateMsg) 'ACCESS_TOKEN'=> $this->getOfficialAccount()->accessToken()->getToken() ]); - $templateMsg->setAppid($this->getOfficialAccount()->getConfig()->getAppId()); $response = NetWork::postJsonForJson($url, $templateMsg->getSendMessage()); return $this->hasException($response); }