diff --git a/src/API.php b/src/API.php index 81fe68c..849c5a9 100644 --- a/src/API.php +++ b/src/API.php @@ -4,6 +4,7 @@ namespace Slack; use Illuminate\Support\Arr; use Illuminate\Support\Facades\App; +use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; use Slack\Blockkit\Modal; @@ -77,7 +78,7 @@ final class API * @param string $channel * @param string $timestamp * @return Generic - * @throws \Exception + * @throws SlackException */ public function deleteChat(string $channel,string $timestamp): Generic { @@ -92,13 +93,13 @@ final class API * @param string $trigger * @param string $dialog * @return Generic - * @throws \Exception + * @throws SlackException */ public function dialogOpen(string $trigger,string $dialog): Generic { Log::debug(sprintf('%s:Open a Dialog',static::LOGKEY),['m'=>__METHOD__,'d'=>$dialog,'t'=>$trigger]); - return new Generic($this->execute('dialog.open',json_encode(['dialog'=>$dialog,'trigger_id'=>$trigger]))); + return new Generic($this->execute('dialog.open',['dialog'=>$dialog,'trigger_id'=>$trigger])); } /** @@ -108,13 +109,13 @@ final class API * @param string $timestamp * @param int $limit * @return Generic - * @throws \Exception + * @throws SlackException */ public function getChannelHistory(string $channel,string $timestamp,int $limit=20): Generic { Log::debug(sprintf('%s:Message History for Channel [%s] from Timestamp [%s]',static::LOGKEY,$channel,$timestamp),['m'=>__METHOD__]); - return new Generic($this->execute('conversations.history',['channel'=>$channel,'oldest'=>$timestamp,'limit'=>$limit])); + return new Generic($this->execute('conversations.history',['channel'=>$channel,'oldest'=>$timestamp,'limit'=>$limit],TRUE)); } /** @@ -122,13 +123,13 @@ final class API * * @param string $channel * @return Generic - * @throws \Exception + * @throws SlackException */ public function getChannelInfo(string $channel): Generic { Log::debug(sprintf('%s:Channel Information [%s]',static::LOGKEY,$channel),['m'=>__METHOD__]); - return new Generic($this->execute('conversations.info',['channel'=>$channel])); + return new Generic($this->execute('conversations.info',['channel'=>$channel],TRUE)); } /** @@ -136,13 +137,13 @@ final class API * * @param int $limit * @return Generic - * @throws \Exception + * @throws SlackException */ public function getChannelList(int $limit=100): Generic { Log::debug(sprintf('%s:Channel List',static::LOGKEY),['m'=>__METHOD__]); - return new Generic($this->execute('conversations.list',['limit'=>$limit])); + return new Generic($this->execute('conversations.list',['limit'=>$limit],TRUE)); } /** @@ -151,13 +152,13 @@ final class API * @param string $channel * @param string $thread_ts * @return Chat - * @throws \Exception + * @throws SlackException */ public function getMessageHistory(string $channel,string $thread_ts): Chat { Log::debug(sprintf('%s:Get Message Threads for Message [%s] on Channel [%s]',static::LOGKEY,$thread_ts,$channel),['m'=>__METHOD__]); - return new Chat($this->execute('conversations.replies',['channel'=>$channel,'ts'=>$thread_ts])); + return new Chat($this->execute('conversations.replies',['channel'=>$channel,'ts'=>$thread_ts],TRUE)); } /** @@ -165,13 +166,13 @@ final class API * * @param string $team_id * @return ResponseTeam - * @throws \Exception + * @throws SlackException */ public function getTeam(string $team_id): ResponseTeam { Log::debug(sprintf('%s:Team Info [%s]',static::LOGKEY,$team_id),['m'=>__METHOD__]); - return new ResponseTeam($this->execute('team.info',['team'=>$team_id])); + return new ResponseTeam($this->execute('team.info',['team'=>$team_id],TRUE)); } /** @@ -179,13 +180,13 @@ final class API * * @param string $user_id * @return ResponseUser - * @throws \Exception + * @throws SlackException */ public function getUser(string $user_id): ResponseUser { Log::debug(sprintf('%s:User Info [%s]',static::LOGKEY,$user_id),['m'=>__METHOD__]); - return new ResponseUser($this->execute('users.info',['user'=>$user_id])); + return new ResponseUser($this->execute('users.info',['user'=>$user_id],TRUE)); } /** @@ -195,7 +196,7 @@ final class API * @param int $limit * @param string|null $cursor * @return ChannelList - * @throws \Exception + * @throws SlackException */ public function getUserChannels(User $uo,int $limit=100,string $cursor=NULL): ChannelList { @@ -211,7 +212,7 @@ final class API if ($cursor) $args->put('cursor',$cursor); - return new ChannelList($this->execute('users.conversations',$args->toArray())); + return new ChannelList($this->execute('users.conversations',$args->toArray(),TRUE)); } /** @@ -219,13 +220,13 @@ final class API * * @param array $users * @return Generic - * @throws \Exception + * @throws SlackException */ public function migrationExchange(array $users): Generic { Log::debug(sprintf('%s:Migrate Exchange [%s] users',static::LOGKEY,count($users)),['m'=>__METHOD__]); - return new Generic($this->execute('migration.exchange',['users'=>join(',',$users)])); + return new Generic($this->execute('migration.exchange',['users'=>join(',',$users)],TRUE)); } /** @@ -234,13 +235,13 @@ final class API * @param string $channel * @param string $timestamp * @return Generic - * @throws \Exception + * @throws SlackException */ public function pinMessage(string $channel,string $timestamp): Generic { Log::debug(sprintf('%s:Pin Message [%s|%s]',static::LOGKEY,$channel,$timestamp),['m'=>__METHOD__]); - return new Generic($this->execute('pins.add',json_encode(['channel'=>$channel,'timestamp'=>$timestamp]))); + return new Generic($this->execute('pins.add',['channel'=>$channel,'timestamp'=>$timestamp])); } /** @@ -248,13 +249,13 @@ final class API * * @param Message $request * @return Generic - * @throws \Exception + * @throws SlackException */ public function postEphemeral(Message $request): Generic { Log::debug(sprintf('%s:Post a Slack Ephemeral Message',static::LOGKEY),['m'=>__METHOD__,'r'=>$request]); - return new Generic($this->execute('chat.postEphemeral',json_encode($request))); + return new Generic($this->execute('chat.postEphemeral',$request)); } /** @@ -262,13 +263,13 @@ final class API * * @param Message $request * @return Generic - * @throws \Exception + * @throws SlackException */ public function postMessage(Message $request): Generic { Log::debug(sprintf('%s:Post a Slack Message',static::LOGKEY),['m'=>__METHOD__,'r'=>$request]); - return new Generic($this->execute('chat.postMessage',json_encode($request))); + return new Generic($this->execute('chat.postMessage',$request)); } /** @@ -276,13 +277,13 @@ final class API * * @param Message $request * @return Generic - * @throws \Exception + * @throws SlackException */ public function scheduleMessage(Message $request): Generic { Log::debug(sprintf('%s:Scheduling a Slack Message',static::LOGKEY),['m'=>__METHOD__,'r'=>$request]); - return new Generic($this->execute('chat.scheduleMessage',json_encode($request))); + return new Generic($this->execute('chat.scheduleMessage',$request)); } /** @@ -290,7 +291,7 @@ final class API * * @param string|null $request * @return Generic - * @throws \Exception + * @throws SlackException */ public function scheduleMessagesList(string $request=NULL): Generic { @@ -305,13 +306,13 @@ final class API * @param string $channel * @param string $timestamp * @return Generic - * @throws \Exception + * @throws SlackException */ public function unpinMessage(string $channel,string $timestamp): Generic { Log::debug(sprintf('%s:Remove Pin from Message [%s|%s]',static::LOGKEY,$channel,$timestamp),['m'=>__METHOD__]); - return new Generic($this->execute('pins.remove',json_encode(['channel'=>$channel,'timestamp'=>$timestamp]))); + return new Generic($this->execute('pins.remove',['channel'=>$channel,'timestamp'=>$timestamp])); } /** @@ -319,20 +320,20 @@ final class API * * @param Message $request * @return Generic - * @throws \Exception + * @throws SlackException */ public function updateMessage(Message $request): Generic { Log::debug(sprintf('%s:Update a Slack Message',static::LOGKEY),['m'=>__METHOD__,'r'=>$request]); - return new Generic($this->execute('chat.update',json_encode($request))); + return new Generic($this->execute('chat.update',$request)); } public function viewOpen(string $trigger,Modal $view): Generic { Log::debug(sprintf('%s:Open a view',static::LOGKEY),['m'=>__METHOD__,'t'=>$trigger]); - return new Generic($this->execute('views.open',json_encode(['trigger_id'=>$trigger,'view'=>$view]))); + return new Generic($this->execute('views.open',['trigger_id'=>$trigger,'view'=>$view])); } /** @@ -342,49 +343,41 @@ final class API * @param Modal $view * @param string $hash * @return Generic - * @throws \Exception + * @throws SlackException * @todo Add some smarts to detect if the new view is the same as the current view, and thus no need to post. */ public function viewPublish(string $user,Modal $view,string $hash=''): Generic { Log::debug(sprintf('%s:Publish a view',static::LOGKEY),['m'=>__METHOD__,'u'=>$user,'h'=>$hash]); - return new Generic($this->execute('views.publish',json_encode($hash ? ['user_id'=>$user,'view'=>$view,'hash'=>$hash] : ['user_id'=>$user,'view'=>$view]))); + return new Generic($this->execute('views.publish',$hash ? ['user_id'=>$user,'view'=>$view,'hash'=>$hash] : ['user_id'=>$user,'view'=>$view])); } public function viewPush(string $trigger,Modal $view): Generic { Log::debug(sprintf('%s:Push a view',static::LOGKEY),['m'=>__METHOD__,'t'=>$trigger]); - return new Generic($this->execute('views.push',json_encode(['trigger_id'=>$trigger,'view'=>$view]))); + return new Generic($this->execute('views.push',['trigger_id'=>$trigger,'view'=>$view])); } public function viewUpdate(string $view_id,Modal $view): Generic { Log::debug(sprintf('%s:Update a view',static::LOGKEY),['m'=>__METHOD__,'id'=>$view_id]); - return new Generic($this->execute('views.update',json_encode(['view_id'=>$view_id,'view'=>$view]))); + return new Generic($this->execute('views.update',['view_id'=>$view_id,'view'=>$view])); } /** * Call the Slack API * * @param string $method - * @param null $parameters + * @param mixed $parameters + * @param bool $asForm * @return object - * @throws SlackAlreadyPinnedException - * @throws SlackChannelNotFoundException + * @throws \Exception * @throws SlackException - * @throws SlackHashConflictException - * @throws SlackMessageNotFoundException - * @throws SlackNoAuthException - * @throws SlackNoPinException - * @throws SlackNotFoundException - * @throws SlackNotInChannelException - * @throws SlackThreadNotFoundException - * @throws SlackTokenScopeException */ - private function execute(string $method,$parameters=NULL): object + private function execute(string $method,mixed $parameters,bool $asForm=FALSE): object { switch (config('app.env')) { case 'steno': $url = 'http://steno:3000'; @@ -395,43 +388,29 @@ final class API $url = 'https://slack.com'; } - $url .= '/api/'.$method; - // If we dont have a scope definition, or if the scope definition is not in the token if (is_null($x=Arr::get(self::scopes,$method)) OR (($x !== '') AND ! $this->_token->hasScope($x))) { throw new SlackTokenScopeException(sprintf('Token [%d:%s] doesnt have the required scope: [%s] for [%s]',$this->_token->id,$this->_token->token_hidden,serialize($x),$method)); } - // If we are passed an array, we'll do a normal post. - if (is_array($parameters)) { - $parameters['token'] = $this->_token->token; - $request = $this->prepareRequest( - $url, - json_encode($parameters) - ); + $http = Http::baseUrl($url); + $http + ->withToken($this->_token->token) + ->acceptJson(); - // If we are json, then we'll do an application/json post - } elseif (is_json($parameters)) { - $request = $this->prepareRequest( - $url, - $parameters, - [ - 'Content-Type: application/json; charset=utf-8', - 'Content-Length: '.strlen($parameters), - 'Authorization: Bearer '.$this->_token->token, - ] - ); + if ($asForm) { + if (! is_array($parameters)) + throw new SlackException('Parameters are not an array for a form submission'); - } else { - throw new SlackException('Parameters unknown'); + $http->asForm(); + + } elseif ($parameters) { + $http->withBody((is_array($parameters) || ($parameters instanceof BlockKit)) ? json_encode($parameters) : $parameters,'application/json'); } try { - $response = curl_exec($request); - if (! $response) - throw new \Exception('CURL exec returned an empty response: '.serialize(curl_getinfo($request))); - - $result = json_decode($response); + $request = $http->post(sprintf('/api/%s',$method),$asForm ? $parameters : NULL)->throw(); + $response = $request->object(); } catch (\Exception $e) { Log::error(sprintf('%s:Got an error while posting to [%s] (%s)',static::LOGKEY,$url,$e->getMessage()),['m'=>__METHOD__]); @@ -439,73 +418,47 @@ final class API throw new \Exception($e->getMessage()); } - if (! $result) { - Log::error(sprintf('%s:Our result shouldnt be empty',static::LOGKEY),['m'=>__METHOD__,'r'=>$request,'R'=>$response]); - throw new SlackException('Slack Result is Empty'); - } - - if (! $result->ok) { - switch ($result->error) { + if ($response->ok) + return $response; + else + switch ($response->error) { case 'already_pinned': - throw new SlackAlreadyPinnedException('Already Pinned',curl_getinfo($request,CURLINFO_HTTP_CODE)); - - case 'not_authed': - throw new SlackNoAuthException('No Auth Token',curl_getinfo($request,CURLINFO_HTTP_CODE)); + throw new SlackAlreadyPinnedException('Already Pinned',$request->status()); case 'channel_not_found': - throw new SlackChannelNotFoundException('Channel Not Found',curl_getinfo($request,CURLINFO_HTTP_CODE)); + throw new SlackChannelNotFoundException('Channel Not Found',$request->status()); case 'hash_conflict': if (App::environment() == 'local') file_put_contents('/tmp/hash_conflict.'.$method,print_r(json_decode(json_decode($parameters)->view),TRUE)); - throw new SlackHashConflictException('Hash Conflict',curl_getinfo($request,CURLINFO_HTTP_CODE)); + throw new SlackHashConflictException('Hash Conflict',$request->status()); + + case 'invalid_auth': + throw new SlackNoAuthException('Invalid Auth Token',$request->status()); case 'message_not_found': - throw new SlackMessageNotFoundException('Message Not Found',curl_getinfo($request,CURLINFO_HTTP_CODE)); + throw new SlackMessageNotFoundException('Message Not Found',$request->status()); case 'no_pin': - throw new SlackNoPinException('No Pin',curl_getinfo($request,CURLINFO_HTTP_CODE)); + throw new SlackNoPinException('No Pin',$request->status()); - case 'not_in_channel': - throw new SlackNotInChannelException('Not In Channel',curl_getinfo($request,CURLINFO_HTTP_CODE)); + case 'not_authed': + throw new SlackNoAuthException('No Auth Token',$request->status()); case 'not_found': - file_put_contents('/tmp/method.'.$method,print_r(['request'=>is_json($parameters) ? json_decode($parameters,TRUE) : $parameters,'response'=>$result],TRUE)); - throw new SlackNotFoundException('Not Found',curl_getinfo($request,CURLINFO_HTTP_CODE)); + file_put_contents('/tmp/method.'.$method,print_r(['request'=>is_json($parameters) ? json_decode($parameters,TRUE) : $parameters,'response'=>$response],TRUE)); + throw new SlackNotFoundException('Not Found',$request->status()); + + case 'not_in_channel': + throw new SlackNotInChannelException('Not In Channel',$request->status()); case 'thread_not_found': - throw new SlackThreadNotFoundException('Thread Not Found',curl_getinfo($request,CURLINFO_HTTP_CODE)); + throw new SlackThreadNotFoundException('Thread Not Found',$request->status()); default: - Log::error(sprintf('%s:Generic Error',static::LOGKEY),['m'=>__METHOD__,'t'=>$this->_token->team_id,'r'=>$result]); - throw new SlackException($result->error,curl_getinfo($request,CURLINFO_HTTP_CODE)); + Log::error(sprintf('%s:Generic Error',static::LOGKEY),['m'=>__METHOD__,'t'=>$this->_token->team_id,'r'=>$response]); + throw new SlackException($response->error,$request->status()); } - } - - curl_close($request); - return $result; - } - - /** - * Setup the API call - * - * @param string $url - * @param string $parameters - * @param array $headers - * @return \CurlHandle - */ - private function prepareRequest(string $url,string $parameters='',array $headers=[]): \CurlHandle - { - $request = curl_init(); - - curl_setopt($request,CURLOPT_URL,$url); - curl_setopt($request,CURLOPT_RETURNTRANSFER,TRUE); - curl_setopt($request,CURLOPT_HTTPHEADER,$headers); - curl_setopt($request,CURLINFO_HEADER_OUT,TRUE); - curl_setopt($request,CURLOPT_SSL_VERIFYPEER,FALSE); - curl_setopt($request,CURLOPT_POSTFIELDS,$parameters); - - return $request; } } \ No newline at end of file