Compare commits

..

23 Commits

Author SHA1 Message Date
Deon George
1f4c5c9774 Users created shouldnt be enabled by default, a user update job should enable users after checking guest status 2022-09-09 15:58:28 +10:00
Deon George
67f78b2f5d Enhancements to Message::class making it more similar to Modal::class in terms of methods 2022-09-09 14:55:19 +10:00
Deon George
6c6d32b4eb Fix hash conflict dump error 2022-09-08 17:43:17 +10:00
Deon George
1923f4486b Modal clear_on_close/notify_on_close defaults to TRUE, change MultiExternalSelect initial_options to be a collection 2022-09-08 13:50:48 +10:00
Deon George
b289f29948 Change elements that require plain_text to only accept string , make public the consts that describe element limits 2022-09-07 19:27:30 +10:00
Deon George
e9980ff9fd Get value() by block_id when it isnt a form submission 2022-09-06 20:08:04 +10:00
Deon George
145e322317 Fixes for interactive messages, we use block_id to determine what needs to be done 2022-09-06 17:23:54 +10:00
Deon George
1b55d7ab52 Standardisation of callback_id/key/value and action_id/key/value, Message can only have 50 blocks 2022-09-06 11:31:30 +10:00
Deon George
be26c3c0a3 Consistent handling of Listeners methods (the should be overwritten in the application) 2022-09-06 10:19:56 +10:00
Deon George
521de13d92 BlockKit classes is now countable 2022-09-05 23:13:44 +10:00
Deon George
a1be3ccd09 Switch Message::respond() to use Http::class instead of curl directly 2022-09-05 22:26:08 +10:00
Deon George
2c791ccead Minor fixes 2022-09-05 17:17:43 +10:00
Deon George
1529f470fa Add __set method to Job and auto-initialize _data, update Message to check for a Team if the message is being used in a slack API 2022-09-05 17:17:43 +10:00
Deon George
905c207956 Switch API to use Http::class instead of curl directly 2022-09-05 17:17:43 +10:00
Deon George
15a6933026 Enable __set() in Jobs, so all data is stored in _data 2022-09-05 11:43:38 +10:00
Deon George
4ff944cb3a Implemented SlackSyntaxException and removed some deprecated functions 2022-09-05 11:43:38 +10:00
Deon George
4c7d18c6b0 App::environment is now 'local' not 'dev', consistent use of slack queue 'slack' 2022-09-05 11:43:38 +10:00
Deon George
ff00e88417 Options code tidyup 2022-09-05 11:43:38 +10:00
Deon George
b6dc14971f Listener code tidyup - consistent use of slack queue "slack" 2022-09-05 11:43:38 +10:00
Deon George
89c13bcb73 API code tidyup 2022-09-04 10:21:01 +10:00
Deon George
a68c7936a6 Add active to Team fillable 2022-09-02 23:49:44 +10:00
Deon George
3bac7dcf6b More functionality with emphemeral messages 2022-09-02 23:08:55 +10:00
Deon George
e9a4eae7d1 Enable sending ephemeral messages to Slack 2022-09-02 17:39:49 +10:00
56 changed files with 759 additions and 841 deletions

View File

@ -4,6 +4,7 @@ namespace Slack;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Slack\Blockkit\Modal; use Slack\Blockkit\Modal;
@ -19,7 +20,7 @@ use Slack\Exceptions\{SlackAlreadyPinnedException,
SlackNotInChannelException, SlackNotInChannelException,
SlackThreadNotFoundException, SlackThreadNotFoundException,
SlackTokenScopeException}; SlackTokenScopeException};
use Slack\Models\{Team,User}; use Slack\Models\{Team,Token,User};
use Slack\Response\ChannelList; use Slack\Response\ChannelList;
use Slack\Response\Generic; use Slack\Response\Generic;
use Slack\Response\User as ResponseUser; use Slack\Response\User as ResponseUser;
@ -33,6 +34,7 @@ final class API
private const scopes = [ private const scopes = [
'auth.test'=>'', // No scope required 'auth.test'=>'', // No scope required
'chat.delete'=>'chat:write', 'chat.delete'=>'chat:write',
'chat.postEphemeral'=>'chat:write',
'chat.postMessage'=>'chat:write', 'chat.postMessage'=>'chat:write',
'chat.update'=>'chat:write', 'chat.update'=>'chat:write',
'conversations.history'=>'channels:history', // Also need groups:history for Private Channels and im:history for messages to the bot. 'conversations.history'=>'channels:history', // Also need groups:history for Private Channels and im:history for messages to the bot.
@ -54,7 +56,7 @@ final class API
]; ];
// Our slack token to use // Our slack token to use
private $_token; private Token $_token;
public function __construct(Team $o) public function __construct(Team $o)
{ {
@ -73,228 +75,128 @@ final class API
/** /**
* Delete a message in a channel * Delete a message in a channel
* *
* @param $channel * @param string $channel
* @param $timestamp * @param string $timestamp
* @return Generic * @return Generic
* @throws \Exception * @throws SlackException
*/ */
public function deleteChat($channel,$timestamp): Generic public function deleteChat(string $channel,string $timestamp): Generic
{ {
Log::debug(sprintf('%s:Delete Message [%s] in [%s]',static::LOGKEY,$timestamp,$channel),['m'=>__METHOD__]); Log::debug(sprintf('%s:Delete Message [%s] in [%s]',static::LOGKEY,$timestamp,$channel),['m'=>__METHOD__]);
return new Generic($this->execute('chat.delete',['channel'=>$channel,'ts'=>$timestamp])); return new Generic($this->execute('chat.delete',['channel'=>$channel,'ts'=>$timestamp]));
} }
/**
* Get Messages on a channel from a specific timestamp
*
* @param $channel
* @param $timestamp
* @param int $limit
* @return Generic
* @throws \Exception
*/
public function getChannelHistory($channel,$timestamp,$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]));
}
/**
* Get information on a channel.
*
* @param $channel
* @return Generic
* @throws \Exception
*/
public function getChannelInfo($channel): Generic
{
Log::debug(sprintf('%s:Channel Information [%s]',static::LOGKEY,$channel),['m'=>__METHOD__]);
return new Generic($this->execute('conversations.info',['channel'=>$channel]));
}
/**
* Get a list of channels.
*
* @param int $limit
* @return Generic
* @throws \Exception
*/
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]));
}
/**
* Get all messages from a thread
*
* @param string $channel
* @param string $thread_ts
* @return Chat
* @throws \Exception
*/
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]));
}
/**
* Get information on a user
*
* @param string $team_id
* @return ResponseTeam
* @throws \Exception
*/
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]));
}
/**
* Get information on a user
*
* @param $user_id
* @return ResponseUser
* @throws \Exception
*/
public function getUser($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]));
}
/** /**
* Open a dialogue with the user * Open a dialogue with the user
* *
* @param string $trigger * @param string $trigger
* @param string $dialog * @param string $dialog
* @return Generic * @return Generic
* @throws \Exception * @throws SlackException
*/ */
public function dialogOpen(string $trigger,string $dialog): Generic public function dialogOpen(string $trigger,string $dialog): Generic
{ {
Log::debug(sprintf('%s:Open a Dialog',static::LOGKEY),['m'=>__METHOD__,'d'=>$dialog,'t'=>$trigger]); 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]));
} }
/** /**
* Migrate users to Enterprise IDs * Get Messages on a channel from a specific timestamp
* *
* @param array $users * @param string $channel
* @param string $timestamp
* @param int $limit
* @return Generic * @return Generic
* @throws \Exception * @throws SlackException
*/ */
public function migrationExchange(array $users): Generic public function getChannelHistory(string $channel,string $timestamp,int $limit=20): Generic
{ {
Log::debug(sprintf('%s:Migrate Exchange [%s] users',static::LOGKEY,count($users)),['m'=>__METHOD__]); Log::debug(sprintf('%s:Message History for Channel [%s] from Timestamp [%s]',static::LOGKEY,$channel,$timestamp),['m'=>__METHOD__]);
return new Generic($this->execute('migration.exchange',['users'=>join(',',$users)])); return new Generic($this->execute('conversations.history',['channel'=>$channel,'oldest'=>$timestamp,'limit'=>$limit],TRUE));
} }
/** /**
* Pin a message in a channel * Get information on a channel.
* *
* @param $channel * @param string $channel
* @param $timestamp
* @return Generic * @return Generic
* @throws \Exception * @throws SlackException
*/ */
public function pinMessage(string $channel,string $timestamp): Generic public function getChannelInfo(string $channel): Generic
{ {
Log::debug(sprintf('%s:Pin Message [%s|%s]',static::LOGKEY,$channel,$timestamp),['m'=>__METHOD__]); Log::debug(sprintf('%s:Channel Information [%s]',static::LOGKEY,$channel),['m'=>__METHOD__]);
return new Generic($this->execute('pins.add',json_encode(['channel'=>$channel,'timestamp'=>$timestamp]))); return new Generic($this->execute('conversations.info',['channel'=>$channel],TRUE));
} }
/** /**
* Post a Slack Message * Get a list of channels.
* *
* @param Message $request * @param int $limit
* @return Generic * @return Generic
* @throws \Exception * @throws SlackException
*/ */
public function postMessage(Message $request): Generic public function getChannelList(int $limit=100): Generic
{ {
Log::debug(sprintf('%s:Post a Slack Message',static::LOGKEY),['m'=>__METHOD__,'r'=>$request]); Log::debug(sprintf('%s:Channel List',static::LOGKEY),['m'=>__METHOD__]);
return new Generic($this->execute('chat.postMessage',json_encode($request))); return new Generic($this->execute('conversations.list',['limit'=>$limit],TRUE));
} }
/** /**
* Schedule a slack message * Get all messages from a thread
* *
* @param Message $request * @param string $channel
* @return Generic * @param string $thread_ts
* @throws \Exception * @return Chat
* @throws SlackException
*/ */
public function scheduleMessage(Message $request): Generic public function getMessageHistory(string $channel,string $thread_ts): Chat
{ {
Log::debug(sprintf('%s:Scheduling a Slack Message',static::LOGKEY),['m'=>__METHOD__,'r'=>$request]); Log::debug(sprintf('%s:Get Message Threads for Message [%s] on Channel [%s]',static::LOGKEY,$thread_ts,$channel),['m'=>__METHOD__]);
return new Generic($this->execute('chat.scheduleMessage',json_encode($request))); return new Chat($this->execute('conversations.replies',['channel'=>$channel,'ts'=>$thread_ts],TRUE));
} }
/** /**
* Get the scheduled messages * Get information on a user
* *
* @param Message $request * @param string $team_id
* @return Generic * @return ResponseTeam
* @throws \Exception * @throws SlackException
*/ */
public function scheduleMessagesList(string $request=NULL): Generic public function getTeam(string $team_id): ResponseTeam
{ {
Log::debug(sprintf('%s:Get the Scheduled Messages in Slack',static::LOGKEY),['m'=>__METHOD__,'r'=>$request]); Log::debug(sprintf('%s:Team Info [%s]',static::LOGKEY,$team_id),['m'=>__METHOD__]);
return new Generic($this->execute('chat.scheduledMessages.list',$request ? ['channel'=>$request] : [])); return new ResponseTeam($this->execute('team.info',['team'=>$team_id],TRUE));
} }
/** /**
* Remove a Pin from a message * Get information on a user
* *
* @param $channel * @param string $user_id
* @param $timestamp * @return ResponseUser
* @return Generic * @throws SlackException
* @throws \Exception
*/ */
public function unpinMessage($channel,$timestamp): Generic public function getUser(string $user_id): ResponseUser
{ {
Log::debug(sprintf('%s:Remove Pin from Message [%s|%s]',static::LOGKEY,$channel,$timestamp),['m'=>__METHOD__]); Log::debug(sprintf('%s:User Info [%s]',static::LOGKEY,$user_id),['m'=>__METHOD__]);
return new Generic($this->execute('pins.remove',json_encode(['channel'=>$channel,'timestamp'=>$timestamp]))); return new ResponseUser($this->execute('users.info',['user'=>$user_id],TRUE));
}
/**
* Update a Slack Message
*
* @param Message $request
* @return Generic
* @throws \Exception
*/
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)));
} }
/** /**
* Get the list of channels for a user (the bot normally) * Get the list of channels for a user (the bot normally)
* *
* @param User $uo * @param User $uo
* @param int $limit * @param int $limit
* @param string|null $cursor * @param string|null $cursor
* @return ChannelList * @return ChannelList
* @throws \Exception * @throws SlackException
*/ */
public function getUserChannels(User $uo,int $limit=100,string $cursor=NULL): ChannelList public function getUserChannels(User $uo,int $limit=100,string $cursor=NULL): ChannelList
{ {
@ -310,105 +212,205 @@ final class API
if ($cursor) if ($cursor)
$args->put('cursor',$cursor); $args->put('cursor',$cursor);
return new ChannelList($this->execute('users.conversations',$args->toArray())); return new ChannelList($this->execute('users.conversations',$args->toArray(),TRUE));
}
/**
* Migrate users to Enterprise IDs
*
* @param array $users
* @return Generic
* @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)],TRUE));
}
/**
* Pin a message in a channel
*
* @param string $channel
* @param string $timestamp
* @return Generic
* @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',['channel'=>$channel,'timestamp'=>$timestamp]));
}
/**
* Post a Slack Message to a user as an ephemeral message
*
* @param Message $request
* @return Generic
* @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',$request));
}
/**
* Post a Slack Message
*
* @param Message $request
* @return Generic
* @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',$request));
}
/**
* Schedule a slack message
*
* @param Message $request
* @return Generic
* @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',$request));
}
/**
* Get the scheduled messages
*
* @param string|null $request
* @return Generic
* @throws SlackException
*/
public function scheduleMessagesList(string $request=NULL): Generic
{
Log::debug(sprintf('%s:Get the Scheduled Messages in Slack',static::LOGKEY),['m'=>__METHOD__,'r'=>$request]);
return new Generic($this->execute('chat.scheduledMessages.list',$request ? ['channel'=>$request] : []));
}
/**
* Remove a Pin from a message
*
* @param string $channel
* @param string $timestamp
* @return Generic
* @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',['channel'=>$channel,'timestamp'=>$timestamp]));
}
/**
* Update a Slack Message
*
* @param Message $request
* @return Generic
* @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',$request));
} }
public function viewOpen(string $trigger,Modal $view): Generic public function viewOpen(string $trigger,Modal $view): Generic
{ {
Log::debug(sprintf('%s:Open a view',static::LOGKEY),['m'=>__METHOD__,'t'=>$trigger]); 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]));
} }
/** /**
* Publish a view * Publish a view
* *
* @param string $user * @param string $user
* @param string $view * @param Modal $view
* @param string $hash * @param string $hash
* @return Generic * @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. * @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 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]); 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 public function viewPush(string $trigger,Modal $view): Generic
{ {
Log::debug(sprintf('%s:Push a view',static::LOGKEY),['m'=>__METHOD__,'t'=>$trigger]); 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 public function viewUpdate(string $view_id,Modal $view): Generic
{ {
Log::debug(sprintf('%s:Update a view',static::LOGKEY),['m'=>__METHOD__,'id'=>$view_id]); 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 * Call the Slack API
* *
* @param string $method * @param string $method
* @param null $parameters * @param mixed $parameters
* @param bool $asForm
* @return object * @return object
* @throws \Exception * @throws \Exception
* @throws SlackException
*/ */
private function execute(string $method,$parameters = NULL): object private function execute(string $method,mixed $parameters,bool $asForm=FALSE): object
{ {
switch (config('app.env')) { switch (config('app.env')) {
case 'dev': $url = 'http://steno:3000'; case 'steno': $url = 'http://steno:3000';
break; break;
case 'testing': $url = 'http://localhost:3000'; case 'replay': $url = 'http://steno_replay:3000';
break;
case 'testing-l': $url = 'http://steno_replay:3000';
break; break;
default: default:
$url = 'https://slack.com'; $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 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))) { 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)); 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. $http = Http::baseUrl($url);
if (is_array($parameters)) { $http
$parameters['token'] = $this->_token->token; ->withToken($this->_token->token)
$request = $this->prepareRequest( ->acceptJson();
$url,
$parameters
);
// If we are json, then we'll do an application/json post if ($asForm) {
} elseif (is_json($parameters)) { if (! is_array($parameters))
$request = $this->prepareRequest( throw new SlackException('Parameters are not an array for a form submission');
$url,
$parameters,
[
'Content-Type: application/json; charset=utf-8',
'Content-Length: '.strlen($parameters),
'Authorization: Bearer '.$this->_token->token,
]
);
} else { $http->asForm();
throw new \Exception('Parameters unknown');
} elseif ($parameters) {
$http->withBody((is_array($parameters) || ($parameters instanceof BlockKit)) ? json_encode($parameters) : $parameters,'application/json');
} }
try { try {
$response = curl_exec($request); $request = $http->post(sprintf('/api/%s',$method),$asForm ? $parameters : NULL)->throw();
if (! $response) $response = $request->object();
throw new \Exception('CURL exec returned an empty response: '.serialize(curl_getinfo($request)));
$result = json_decode($response);
} catch (\Exception $e) { } catch (\Exception $e) {
Log::error(sprintf('%s:Got an error while posting to [%s] (%s)',static::LOGKEY,$url,$e->getMessage()),['m'=>__METHOD__]); Log::error(sprintf('%s:Got an error while posting to [%s] (%s)',static::LOGKEY,$url,$e->getMessage()),['m'=>__METHOD__]);
@ -416,73 +418,49 @@ final class API
throw new \Exception($e->getMessage()); throw new \Exception($e->getMessage());
} }
if (! $result) { if ($response->ok)
Log::error(sprintf('%s:Our result shouldnt be empty',static::LOGKEY),['m'=>__METHOD__,'r'=>$request,'R'=>$response]); return $response;
throw new SlackException('Slack Result is Empty'); else
} switch ($response->error) {
if (! $result->ok) {
switch ($result->error) {
case 'already_pinned': case 'already_pinned':
throw new SlackAlreadyPinnedException('Already Pinned',curl_getinfo($request,CURLINFO_HTTP_CODE)); throw new SlackAlreadyPinnedException('Already Pinned',$request->status());
case 'not_authed':
throw new SlackNoAuthException('No Auth Token',curl_getinfo($request,CURLINFO_HTTP_CODE));
case 'channel_not_found': 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': case 'hash_conflict':
if (App::environment() == 'dev') if (App::environment() == 'local')
file_put_contents('/tmp/hash_conflict.'.$method,print_r(json_decode(json_decode($parameters)->view),TRUE)); file_put_contents('/tmp/hash_conflict.'.$method,print_r($response,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': 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': 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': case 'not_authed':
throw new SlackNotInChannelException('Not In Channel',curl_getinfo($request,CURLINFO_HTTP_CODE)); throw new SlackNoAuthException('No Auth Token',$request->status());
case 'not_found': case 'not_found':
file_put_contents('/tmp/method.'.$method,print_r(['request'=>is_json($parameters) ? json_decode($parameters,TRUE) : $parameters,'response'=>$result],TRUE)); 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',curl_getinfo($request,CURLINFO_HTTP_CODE)); throw new SlackNotFoundException('Not Found',$request->status());
case 'not_in_channel':
throw new SlackNotInChannelException('Not In Channel',$request->status());
case 'thread_not_found': case 'thread_not_found':
throw new SlackThreadNotFoundException('Thread Not Found',curl_getinfo($request,CURLINFO_HTTP_CODE)); throw new SlackThreadNotFoundException('Thread Not Found',$request->status());
case 'account_inactive':
// @todo Mark the token/team as inactive
default: default:
Log::error(sprintf('%s:Generic Error',static::LOGKEY),['m'=>__METHOD__,'t'=>$this->_token->team_id,'r'=>$result]); Log::error(sprintf('%s:Generic Error',static::LOGKEY),['m'=>__METHOD__,'t'=>$this->_token->team_id,'r'=>$response]);
throw new SlackException($result->error,curl_getinfo($request,CURLINFO_HTTP_CODE)); throw new SlackException($response->error,$request->status());
} }
}
curl_close($request);
return $result;
}
/**
* Setup the API call
*
* @param $url
* @param string $parameters
* @param array $headers
* @return resource
*/
private function prepareRequest($url,$parameters='',$headers = [])
{
$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;
} }
} }

View File

@ -44,6 +44,7 @@ abstract class Base
* *
* @param bool $create * @param bool $create
* @return Channel|null * @return Channel|null
* @todo Enable simulating an existing channel, using FALSE (dont create), 0 (create dont save), 1 (create and save)
*/ */
final public function channel(bool $create=FALSE): ?Channel final public function channel(bool $create=FALSE): ?Channel
{ {
@ -63,6 +64,44 @@ abstract class Base
return $o->exists ? $o : NULL; return $o->exists ? $o : NULL;
} }
/**
* Separate out a callback command to the id that the command relates to
*
* @param string $key
* @param string|null $item
* @return string|null
* @throws \Exception
*/
final protected function keyitem(string $key,string $item=NULL): ?string
{
if (! $item)
return $item;
$regex = '/^([a-z_]+)\|([0-9]+)$/';
$id = NULL;
$value = NULL;
if (preg_match($regex,$item)) {
$id = preg_replace($regex,'$1',$item);
$value = preg_replace($regex,'$2',$item);
}
switch ($key) {
case 'id':
return $id ?: $item;
case 'value':
return $value;
default:
throw new \Exception('Unknown key: '.$key);
}
}
/**
* Return the Eneterprise object that a Response is related to
*
* @return Enterprise
*/
final public function enterprise(): Enterprise final public function enterprise(): Enterprise
{ {
$class = class_exists(AppEnterprise::class) ? AppEnterprise::class : Enterprise::class; $class = class_exists(AppEnterprise::class) ? AppEnterprise::class : Enterprise::class;
@ -113,7 +152,7 @@ abstract class Base
if (! $o->exists) { if (! $o->exists) {
$o->team_id = $this->enterprise_id ? NULL : $this->team()->id; $o->team_id = $this->enterprise_id ? NULL : $this->team()->id;
$o->enterprise_id = ($x=$this->enterprise())->exists ? $x->id : NULL; $o->enterprise_id = ($x=$this->enterprise())->exists ? $x->id : NULL;
$o->active = TRUE; $o->active = FALSE;
$o->save(); $o->save();
Log::debug(sprintf('%s: User Created in DB [%s] (%s)',self::LOGKEY,$this->user_id,$o->id)); Log::debug(sprintf('%s: User Created in DB [%s] (%s)',self::LOGKEY,$this->user_id,$o->id));

View File

@ -5,12 +5,24 @@ namespace Slack;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Slack\Exceptions\SlackSyntaxException;
/** /**
* Class BlockKit - Slack Blockit Objects * Class BlockKit - Slack Blockit Objects
* *
* @notes
* + callback_id is used to identify the source of any action (modal). (Message blocks do not have a callback_id, accept in legacy attachments).
* eg: hometab, add_product, ask_modal
* + block_id is used to identify the sub action(s) of any action (modal). (Messages with blocks can have a block_id, we need to use this to determine what to do.)
* eg: multiple blocks (list of something)
* + action_id is used to identify the action that was initiated
* eg: view, edit, review
* + value is the value of the action_id
* eg: 5 (question #), yes, no, skip, abort
*
* @package Slack * @package Slack
*/ */
abstract class BlockKit implements \JsonSerializable abstract class BlockKit implements \JsonSerializable,\Countable
{ {
protected Collection $_data; protected Collection $_data;
@ -40,7 +52,7 @@ abstract class BlockKit implements \JsonSerializable
protected function validate(string $key,$value) protected function validate(string $key,$value)
{ {
if (Arr::get(static::LIMITS,$key) && (strlen($value) > static::LIMITS[$key])) if (Arr::get(static::LIMITS,$key) && (strlen($value) > static::LIMITS[$key]))
throw new Exception(sprintf('%s must be %d chars or less for buttons %s',$key,self::LIMITS[$key],get_class($this))); throw new SlackSyntaxException(sprintf('%s must be %d chars or less for buttons %s',$key,static::LIMITS[$key],get_class($this)));
return $value; return $value;
} }

View File

@ -2,26 +2,26 @@
namespace Slack\Blockkit\Blocks\Accessories; namespace Slack\Blockkit\Blocks\Accessories;
use \Exception;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Slack\Blockkit\Blocks\Elements\{Confirm,Text}; use Slack\Blockkit\Blocks\Elements\{Confirm,Text};
use Slack\Blockkit\Element; use Slack\Blockkit\Element;
use Slack\Exceptions\SlackSyntaxException;
final class Overflow extends Element final class Overflow extends Element
{ {
protected const LIMITS = [ public const LIMITS = [
'action_id' => 255, 'action_id' => 255,
]; ];
private const MIN_OPTIONS = 1; public const MIN_OPTIONS = 1;
private const MAX_OPTIONS = 5; public const MAX_OPTIONS = 5;
/** /**
* @param string $action_id * @param string $action_id
* @param Collection $options * @param Collection $options
* @throws Exception * @throws SlackSyntaxException
* @todo We dont handle option_groups yet. * @todo We dont handle option_groups yet.
*/ */
public function __construct(string $action_id,Collection $options) public function __construct(string $action_id,Collection $options)
@ -34,10 +34,10 @@ final class Overflow extends Element
$this->action_id = $this->validate('action_id',$action_id); $this->action_id = $this->validate('action_id',$action_id);
if (count($options) < self::MIN_OPTIONS) if (count($options) < self::MIN_OPTIONS)
throw new Exception(sprintf('Must have atleast %d options',self::MIN_OPTIONS)); throw new SlackSyntaxException(sprintf('Must have atleast %d options',self::MIN_OPTIONS));
if (count($options) > self::MAX_OPTIONS) if (count($options) > self::MAX_OPTIONS)
throw new Exception(sprintf('Can only have maximum %d options',self::MAX_OPTIONS)); throw new SlackSyntaxException(sprintf('Can only have maximum %d options',self::MAX_OPTIONS));
$this->options = $options->transform(function($item) { $this->options = $options->transform(function($item) {
return ['text'=>Text::item(Arr::get($item,'name'),'plain_text'),'value'=>(string)Arr::get($item,'id')]; return ['text'=>Text::item(Arr::get($item,'name'),'plain_text'),'value'=>(string)Arr::get($item,'id')];

View File

@ -2,21 +2,21 @@
namespace Slack\Blockkit\Blocks; namespace Slack\Blockkit\Blocks;
use \Exception;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Slack\Blockkit\Blocks; use Slack\Blockkit\Blocks;
use Slack\Blockkit\Blocks\Elements\{Button,MultiStaticSelect}; use Slack\Blockkit\Blocks\Elements\{Button,MultiStaticSelect};
use Slack\Exceptions\SlackSyntaxException;
final class Actions extends Blocks final class Actions extends Blocks
{ {
protected const LIMITS = [ public const LIMITS = [
'block_id' => 255, // @todo Should be unique for each message 'block_id' => 255, // @todo Should be unique for each message
]; ];
private const MAX_ELEMENTS = 5; public const MAX_ELEMENTS = 5;
private const VALID_ELEMENTS = [ public const VALID_ELEMENTS = [
Button::class, Button::class,
MultiStaticSelect::class, MultiStaticSelect::class,
Blocks\Accessories\Overflow::class, Blocks\Accessories\Overflow::class,
@ -38,7 +38,7 @@ final class Actions extends Blocks
public function jsonSerialize() public function jsonSerialize()
{ {
if (! $this->elements) if (! $this->elements)
throw new Exception('Must define at least 1 element'); throw new SlackSyntaxException('Must define at least 1 element');
return parent::jsonSerialize(); return parent::jsonSerialize();
} }
@ -55,7 +55,7 @@ final class Actions extends Blocks
public function elements(Collection $collection): self public function elements(Collection $collection): self
{ {
if (count($collection) > self::MAX_ELEMENTS) if (count($collection) > self::MAX_ELEMENTS)
throw new Exception(sprintf('Can only have maximum %d elements',self::MAX_ELEMENTS)); throw new SlackSyntaxException(sprintf('Can only have maximum %d elements',self::MAX_ELEMENTS));
// @todo Check that a valid element is added. https://api.slack.com/reference/block-kit/blocks#actions // @todo Check that a valid element is added. https://api.slack.com/reference/block-kit/blocks#actions

View File

@ -2,22 +2,22 @@
namespace Slack\Blockkit\Blocks; namespace Slack\Blockkit\Blocks;
use \Exception;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Slack\Blockkit\Blocks; use Slack\Blockkit\Blocks;
use Slack\Exceptions\SlackSyntaxException;
final class Context extends Blocks final class Context extends Blocks
{ {
protected const LIMITS = [ public const LIMITS = [
'block_id' => 255, // @todo Should be unique for each message 'block_id' => 255, // @todo Should be unique for each message
]; ];
private const MAX_ELEMENTS = 10; public const MAX_ELEMENTS = 10;
/** /**
* @param Collection $collection * @param Collection $collection
* @throws Exception * @throws SlackSyntaxException
* @todo Collection can only be image or text elements * @todo Collection can only be image or text elements
*/ */
public function __construct(Collection $collection) public function __construct(Collection $collection)
@ -28,7 +28,7 @@ final class Context extends Blocks
$this->type = 'context'; $this->type = 'context';
if (count($collection) > self::MAX_ELEMENTS) if (count($collection) > self::MAX_ELEMENTS)
throw new Exception(sprintf('Can only have maximum %d elements',self::MAX_ELEMENTS)); throw new SlackSyntaxException(sprintf('Can only have maximum %d elements',self::MAX_ELEMENTS));
$this->elements = $collection; $this->elements = $collection;
} }

View File

@ -2,9 +2,8 @@
namespace Slack\Blockkit\Blocks\Elements; namespace Slack\Blockkit\Blocks\Elements;
use \Exception;
use Slack\Blockkit\Element; use Slack\Blockkit\Element;
use Slack\Exceptions\SlackSyntaxException;
final class Button extends Element final class Button extends Element
{ {
@ -16,26 +15,19 @@ final class Button extends Element
'value' => 2000, 'value' => 2000,
]; ];
public function __construct(Text $text,string $value,string $action_id) public function __construct(string $text,string $value,string $action_id)
{ {
parent::__construct(); parent::__construct();
// Defaults // Defaults
$this->type = 'button'; $this->type = 'button';
if ($text->type != 'plain_text') $this->text = Text::item($this->validate('text',$text),'plain_text');
throw new Exception(sprintf('Text must be plain_text not %s',$text->type));
if (strlen($text->text) > self::LIMITS['text'])
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['text']));
$this->text = $text;
$this->value = $this->validate('value',$value); $this->value = $this->validate('value',$value);
$this->action_id = $this->validate('action_id',$action_id); $this->action_id = $this->validate('action_id',$action_id);
} }
public static function item(Text $text,string $value,string $action_id): self public static function item(string $text,string $value,string $action_id): self
{ {
return new self($text,$value,$action_id); return new self($text,$value,$action_id);
} }
@ -52,7 +44,7 @@ final class Button extends Element
public function style(string $string): self public function style(string $string): self
{ {
if (! in_array($string,['default','primary','danger'])) if (! in_array($string,['default','primary','danger']))
throw new Exception(sprintf('Unknown style %s',$string)); throw new SlackSyntaxException(sprintf('Unknown style %s',$string));
$this->style = $string; $this->style = $string;

View File

@ -3,18 +3,39 @@
namespace Slack\Blockkit\Blocks\Elements; namespace Slack\Blockkit\Blocks\Elements;
use Slack\Blockkit\Element; use Slack\Blockkit\Element;
use Slack\Exceptions\SlackSyntaxException;
final class Confirm extends Element final class Confirm extends Element
{ {
public function __construct() protected const LIMITS = [
'title' => 100,
'text' => 300,
'confirm' => 30,
'deny' => 30,
];
public function __construct(string $title,Text $text,string $confirm,string $deny)
{ {
parent::__construct(); parent::__construct();
abort(500,'Not Implememted'); $this->title = Text::item($this->validate('title',$title),'plain_text');
$this->text = $this->validate('text',$text->text) ? $text : NULL;
$this->confirm = Text::item($this->validate('confirm',$confirm),'plain_text');
$this->deny = Text::item($this->validate('deny',$deny),'plain_text');
} }
public static function item(): self public static function item(string $title,Text $text,string $confirm,string $deny): self
{ {
return new self(); return new self($title,$text,$confirm,$deny);
}
public function style(string $string): self
{
if (! in_array($string,['default','primary','danger']))
throw new SlackSyntaxException(sprintf('Unknown style %s',$string));
$this->style = $string;
return $this;
} }
} }

View File

@ -2,9 +2,8 @@
namespace Slack\Blockkit\Blocks\Elements; namespace Slack\Blockkit\Blocks\Elements;
use \Exception;
use Slack\Blockkit\Element; use Slack\Blockkit\Element;
use Slack\Exceptions\SlackSyntaxException;
final class ExternalSelect extends Element final class ExternalSelect extends Element
{ {
@ -14,29 +13,22 @@ final class ExternalSelect extends Element
]; ];
/** /**
* @param Text $placeholder * @param string $placeholder
* @param string $action_id * @param string $action_id
* @throws Exception * @throws SlackSyntaxException
*/ */
public function __construct(Text $placeholder,string $action_id) public function __construct(string $placeholder,string $action_id)
{ {
parent::__construct(); parent::__construct();
// Defaults // Defaults
$this->type = 'external_select'; $this->type = 'external_select';
if ($placeholder->type != 'plain_text') $this->placeholder = Text::item($this->validate('placeholder',$placeholder),'plain_text');
throw new Exception(sprintf('Text must be plain_text not %s',$placeholder->type));
if (strlen($placeholder->text) > self::LIMITS['placeholder'])
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['placeholder']));
$this->placeholder = $placeholder;
$this->action_id = $this->validate('action_id',$action_id); $this->action_id = $this->validate('action_id',$action_id);
} }
public static function item(Text $placeholder,string $action_id): self public static function item(string $placeholder,string $action_id): self
{ {
return new self($placeholder,$action_id); return new self($placeholder,$action_id);
} }
@ -68,7 +60,7 @@ final class ExternalSelect extends Element
public function min_query_length(int $int): self public function min_query_length(int $int): self
{ {
if ($int < 1) if ($int < 1)
throw new Exception('Minimum 1 options must be configured'); throw new SlackSyntaxException('Minimum 1 options must be configured');
$this->min_query_length = $int; $this->min_query_length = $int;

View File

@ -2,47 +2,39 @@
namespace Slack\Blockkit\Blocks\Elements; namespace Slack\Blockkit\Blocks\Elements;
use \Exception;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Slack\Blockkit\Element; use Slack\Blockkit\Element;
use Slack\Exceptions\SlackSyntaxException;
final class MultiExternalSelect extends Element final class MultiExternalSelect extends Element
{ {
protected const LIMITS = [ public const LIMITS = [
'action_id' => 255, 'action_id' => 255,
'placeholder' => 150, 'placeholder' => 150,
]; ];
private const MAX_OPTIONS = 100; public const MAX_OPTIONS = 100;
// @todo option_group? (double check it is applicable to this item) // @todo option_group? (double check it is applicable to this item)
/** /**
* @param Text $placeholder * @param string $placeholder
* @param string $action_id * @param string $action_id
* @throws Exception * @throws SlackSyntaxException
* @todo We dont handle option_groups yet.
*/ */
public function __construct(Text $placeholder,string $action_id) public function __construct(string $placeholder,string $action_id)
{ {
parent::__construct(); parent::__construct();
// Defaults // Defaults
$this->type = 'multi_external_select'; $this->type = 'multi_external_select';
if ($placeholder->type != 'plain_text') $this->placeholder = Text::item($this->validate('placeholder',$placeholder),'plain_text');
throw new Exception(sprintf('Text must be plain_text not %s',$placeholder->type));
if (strlen($placeholder->text) > self::LIMITS['placeholder'])
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['placeholder']));
$this->placeholder = $placeholder;
$this->action_id = $this->validate('action_id',$action_id); $this->action_id = $this->validate('action_id',$action_id);
} }
public static function item(Text $placeholder,string $action_id): self public static function item(string $placeholder,string $action_id): self
{ {
return new self($placeholder,$action_id); return new self($placeholder,$action_id);
} }
@ -74,7 +66,7 @@ final class MultiExternalSelect extends Element
public function min_query_length(int $int): self public function min_query_length(int $int): self
{ {
if ($int < 1) if ($int < 1)
throw new Exception('Minimum 1 options must be configured'); throw new SlackSyntaxException('Minimum 1 options must be configured');
$this->min_query_length = $int; $this->min_query_length = $int;
@ -84,7 +76,7 @@ final class MultiExternalSelect extends Element
public function max_selected_items(int $int): self public function max_selected_items(int $int): self
{ {
if ($int < 1) if ($int < 1)
throw new Exception('Minimum 1 options must be configured'); throw new SlackSyntaxException('Minimum 1 options must be configured');
$this->max_selected_items = $int; $this->max_selected_items = $int;

View File

@ -2,58 +2,51 @@
namespace Slack\Blockkit\Blocks\Elements; namespace Slack\Blockkit\Blocks\Elements;
use \Exception;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Slack\Blockkit\Element; use Slack\Blockkit\Element;
use Slack\Exceptions\SlackSyntaxException;
final class MultiStaticSelect extends Element final class MultiStaticSelect extends Element
{ {
protected const LIMITS = [ public const LIMITS = [
'action_id' => 255, 'action_id' => 255,
'placeholder' => 150, 'placeholder' => 150,
]; ];
private const MAX_OPTIONS = 100; public const MAX_OPTIONS = 100;
// @todo option_group? (double check it is applicable to this item) // @todo option_group? (double check it is applicable to this item)
/** /**
* @param Text $placeholder * @param string $placeholder
* @param string $action_id * @param string $action_id
* @param Collection $options * @param Collection $options
* @throws Exception * @throws SlackSyntaxException
* @todo We dont handle option_groups yet. * @todo We dont handle option_groups yet.
*/ */
public function __construct(Text $placeholder,string $action_id,Collection $options) public function __construct(string $placeholder,string $action_id,Collection $options)
{ {
parent::__construct(); parent::__construct();
// Defaults // Defaults
$this->type = 'multi_static_select'; $this->type = 'multi_static_select';
if ($placeholder->type != 'plain_text') $this->placeholder = Text::item($this->validate('placeholder',$placeholder),'plain_text');
throw new Exception(sprintf('Text must be plain_text not %s',$placeholder->type));
if (strlen($placeholder->text) > self::LIMITS['placeholder'])
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['placeholder']));
$this->placeholder = $placeholder;
$this->action_id = $this->validate('action_id',$action_id); $this->action_id = $this->validate('action_id',$action_id);
if (! $options->count()) if (! $options->count())
throw new Exception('There are no options?'); throw new SlackSyntaxException('There are no options?');
if ($options->count() > self::MAX_OPTIONS) if ($options->count() > self::MAX_OPTIONS)
throw new Exception(sprintf('Can only have maximum %d options',self::MAX_OPTIONS)); throw new SlackSyntaxException(sprintf('Can only have maximum %d options',self::MAX_OPTIONS));
$this->options = $options->transform(function($item) { $this->options = $options->transform(function($item) {
return ['text'=>Text::item($item->name,'plain_text'),'value'=>(string)$item->value]; return ['text'=>Text::item($item->name,'plain_text'),'value'=>(string)$item->value];
}); });
} }
public static function item(Text $placeholder,string $action_id,Collection $options): self public static function item(string $placeholder,string $action_id,Collection $options): self
{ {
return new self($placeholder,$action_id,$options); return new self($placeholder,$action_id,$options);
} }
@ -74,17 +67,17 @@ final class MultiStaticSelect extends Element
return $this; return $this;
} }
public function initial_options(array $initial=NULL): self public function initial_options(Collection $initial=NULL): self
{ {
// No initial options. // No initial options.
if (count($initial)) { if (count($initial)) {
if (! $this->options) if (! $this->options)
throw new Exception('Cannot set an initial value without options defined first'); throw new SlackSyntaxException('Cannot set an initial value without options defined first');
if (count($initial) > self::MAX_OPTIONS) if (count($initial) > self::MAX_OPTIONS)
throw new Exception(sprintf('Can only have maximum %d options',self::MAX_OPTIONS)); throw new SlackSyntaxException(sprintf('Can only have maximum %d options',self::MAX_OPTIONS));
$this->initial_options = $this->options->filter(function($item) use ($initial) { return in_array($item['value'],$initial); }); $this->initial_options = $this->options->filter(function($item) use ($initial) { return $initial->contains($item['value']); });
} }
return $this; return $this;
@ -93,7 +86,7 @@ final class MultiStaticSelect extends Element
public function max_selected_items(int $int): self public function max_selected_items(int $int): self
{ {
if ($int < 1) if ($int < 1)
throw new Exception('Minimum 1 options must be configured'); throw new SlackSyntaxException('Minimum 1 options must be configured');
$this->max_selected_items = $int; $this->max_selected_items = $int;

View File

@ -2,9 +2,8 @@
namespace Slack\Blockkit\Blocks\Elements; namespace Slack\Blockkit\Blocks\Elements;
use \Exception;
use Slack\Blockkit\Element; use Slack\Blockkit\Element;
use Slack\Exceptions\SlackSyntaxException;
/** /**
* @note Overflow, select, and multi-select menus can only use plain_text objects, * @note Overflow, select, and multi-select menus can only use plain_text objects,
@ -24,7 +23,7 @@ final class Options extends Element
parent::__construct(); parent::__construct();
if (strlen($text->text) > self::LIMITS['text']) if (strlen($text->text) > self::LIMITS['text'])
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['text'])); throw new SlackSyntaxException(sprintf('Text must be %d chars or less',self::LIMITS['text']));
$this->text = $text; $this->text = $text;
$this->value = $this->validate('value',$value); $this->value = $this->validate('value',$value);
@ -37,15 +36,9 @@ final class Options extends Element
/* OPTIONAL ITEMS */ /* OPTIONAL ITEMS */
public function description(Text $text): self public function description(string $text): self
{ {
if ($text->type != 'plain_text') $this->description = Text::item($this->validate('description',$text),'plain_text');
throw new Exception(sprintf('Text must be plain_text not %s',$text->type));
if (strlen($text->text) > self::LIMITS['description'])
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['description']));
$this->description = $text;
return $this; return $this;
} }

View File

@ -3,18 +3,19 @@
namespace Slack\Blockkit\Blocks\Elements; namespace Slack\Blockkit\Blocks\Elements;
use Slack\Blockkit\Element; use Slack\Blockkit\Element;
use Slack\Exceptions\SlackSyntaxException;
/** /**
* This is an element of an input dialog * This is an element of an input dialog
*/ */
final class PlainTextInput extends Element final class PlainTextInput extends Element
{ {
protected const LIMITS = [ public const LIMITS = [
'action_id' => 255, // @todo Should be unique for each message 'action_id' => 255, // @todo Should be unique for each message
'placeholder' => 150, 'placeholder' => 150,
]; ];
private const MAX_MIN_LENGTH = 3000; public const MAX_MIN_LENGTH = 3000;
// @todo dispatch_action_config // @todo dispatch_action_config
// @todo focus_on_load // @todo focus_on_load
@ -45,7 +46,7 @@ final class PlainTextInput extends Element
public function min_length(int $int): self public function min_length(int $int): self
{ {
if ($int > self::MAX_MIN_LENGTH) if ($int > self::MAX_MIN_LENGTH)
throw new Exception(sprintf('min_length must be less than %d',self::MAX_MIN_LENGTH)); throw new SlackSyntaxException(sprintf('min_length must be less than %d',self::MAX_MIN_LENGTH));
$this->min_length = $int; $this->min_length = $int;
@ -55,7 +56,7 @@ final class PlainTextInput extends Element
public function max_length(int $int): self public function max_length(int $int): self
{ {
if ($this->min_length && ($int < $this->min_length)) if ($this->min_length && ($int < $this->min_length))
throw new Exception('max_length must be greater than min_length'); throw new SlackSyntaxException('max_length must be greater than min_length');
$this->max_length = $int; $this->max_length = $int;
@ -72,7 +73,7 @@ final class PlainTextInput extends Element
public function placeholder(Text $text): self public function placeholder(Text $text): self
{ {
if (strlen($text->text) > self::LIMITS['placeholder']) if (strlen($text->text) > self::LIMITS['placeholder'])
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['placeholder'])); throw new SlackSyntaxException(sprintf('Text must be %d chars or less',self::LIMITS['placeholder']));
$this->placeholder = $text; $this->placeholder = $text;

View File

@ -2,58 +2,51 @@
namespace Slack\Blockkit\Blocks\Elements; namespace Slack\Blockkit\Blocks\Elements;
use \Exception;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Slack\Blockkit\Element; use Slack\Blockkit\Element;
use Slack\Exceptions\SlackSyntaxException;
final class StaticSelect extends Element final class StaticSelect extends Element
{ {
protected const LIMITS = [ public const LIMITS = [
'action_id' => 255, 'action_id' => 255,
'placeholder' => 150, 'placeholder' => 150,
]; ];
private const MAX_OPTIONS = 100; public const MAX_OPTIONS = 100;
// @todo option_group // @todo option_group
/** /**
* @param Text $placeholder * @param string $placeholder
* @param string $action_id * @param string $action_id
* @param Collection $options * @param Collection $options
* @throws Exception * @throws SlackSyntaxException
* @todo We dont handle option_groups yet. * @todo We dont handle option_groups yet.
*/ */
public function __construct(Text $placeholder,string $action_id,Collection $options) public function __construct(string $placeholder,string $action_id,Collection $options)
{ {
parent::__construct(); parent::__construct();
// Defaults // Defaults
$this->type = 'static_select'; $this->type = 'static_select';
if ($placeholder->type != 'plain_text') $this->placeholder = Text::item($this->validate('placeholder',$placeholder),'plain_text');
throw new Exception(sprintf('Text must be plain_text not %s',$placeholder->type));
if (strlen($placeholder->text) > self::LIMITS['placeholder'])
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['placeholder']));
$this->placeholder = $placeholder;
$this->action_id = $this->validate('action_id',$action_id); $this->action_id = $this->validate('action_id',$action_id);
if (! $options->count()) if (! $options->count())
throw new Exception('There are no options?'); throw new SlackSyntaxException('There are no options?');
if ($options->count() > self::MAX_OPTIONS) if ($options->count() > self::MAX_OPTIONS)
throw new Exception(sprintf('Can only have maximum %d options',self::MAX_OPTIONS)); throw new SlackSyntaxException(sprintf('Can only have maximum %d options',self::MAX_OPTIONS));
$this->options = $options->transform(function($item) { $this->options = $options->transform(function($item) {
return ['text'=>Text::item($item->name,'plain_text'),'value'=>(string)$item->value]; return ['text'=>Text::item($item->name,'plain_text'),'value'=>(string)$item->value];
}); });
} }
public static function item(Text $placeholder,string $action_id,Collection $options): self public static function item(string $placeholder,string $action_id,Collection $options): self
{ {
return new self($placeholder,$action_id,$options); return new self($placeholder,$action_id,$options);
} }
@ -77,7 +70,7 @@ final class StaticSelect extends Element
public function initial_option(string $string=NULL): self public function initial_option(string $string=NULL): self
{ {
if (! $this->options) if (! $this->options)
throw new Exception('Cannot set an initial value without options defined first'); throw new SlackSyntaxException('Cannot set an initial value without options defined first');
if ($string) if ($string)
$this->initial_option = $this->options->first(function($item) use ($string) { return $item['value'] == $string; }); $this->initial_option = $this->options->first(function($item) use ($string) { return $item['value'] == $string; });

View File

@ -2,23 +2,22 @@
namespace Slack\Blockkit\Blocks\Elements; namespace Slack\Blockkit\Blocks\Elements;
use \Exception;
use Slack\Blockkit\Element; use Slack\Blockkit\Element;
use Slack\Exceptions\SlackSyntaxException;
final class Text extends Element final class Text extends Element
{ {
private const TYPES = ['mrkdwn','plain_text']; public const TYPES = ['mrkdwn','plain_text'];
public function __construct(string $text,string $type) public function __construct(string $text,string $type)
{ {
parent::__construct(); parent::__construct();
if (! in_array($type,self::TYPES)) if (! in_array($type,self::TYPES))
throw new Exception(sprintf('Type [%s] not valid',$type)); throw new SlackSyntaxException(sprintf('Type [%s] not valid',$type));
$this->type = $type;
$this->text = $text; $this->text = $text;
$this->type = $type;
} }
public static function item(string $text,string $type='mrkdwn'): self public static function item(string $text,string $type='mrkdwn'): self
@ -30,8 +29,8 @@ final class Text extends Element
public function emoji(bool $bool): self public function emoji(bool $bool): self
{ {
if ($x=$this->type != 'plain_text') if ($this->type != 'plain_text')
throw new Exception(sprintf('Cannnot use emoji when type is [%s]',$x)); throw new SlackSyntaxException(sprintf('Cannnot use emoji when type is [%s]',$this->type));
$this->emoji = $bool; $this->emoji = $bool;

View File

@ -2,10 +2,9 @@
namespace Slack\Blockkit\Blocks; namespace Slack\Blockkit\Blocks;
use \Exception;
use Slack\Blockkit\Blocks; use Slack\Blockkit\Blocks;
use Slack\Blockkit\Blocks\Elements\Text; use Slack\Blockkit\Blocks\Elements\Text;
use Slack\Exceptions\SlackSyntaxException;
final class Header extends Blocks final class Header extends Blocks
{ {
@ -15,26 +14,20 @@ final class Header extends Blocks
]; ];
/** /**
* @param Text $text * @param string $text
* @throws Exception * @throws SlackSyntaxException
*/ */
public function __construct(Text $text) public function __construct(string $text)
{ {
parent::__construct(); parent::__construct();
// Defaults // Defaults
$this->type = 'header'; $this->type = 'header';
if ($text->type != 'plain_text') $this->text = Text::item($this->validate('text',$text),'plain_text');
throw new Exception(sprintf('Text must be plain_text not %s',$text->type));
if (strlen($text->text) > self::LIMITS['text'])
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['text']));
$this->text = $text;
} }
public static function item(Text $text): self public static function item(string $text): self
{ {
return new self($text); return new self($text);
} }

View File

@ -2,19 +2,18 @@
namespace Slack\Blockkit\Blocks; namespace Slack\Blockkit\Blocks;
use \Exception; use Slack\Blockkit\{Blocks,Blocks\Elements\Text,Element};
use Slack\Exceptions\SlackSyntaxException;
use Slack\Blockkit\{Blocks,Element};
final class Input extends Blocks final class Input extends Blocks
{ {
protected const LIMITS = [ public const LIMITS = [
'block_id' => 255, // @todo Should be unique for each message 'block_id' => 255, // @todo Should be unique for each message
'hint' => 2000, 'hint' => 2000,
'label' => 2000, 'label' => 2000,
]; ];
private const VALID_ELEMENTS = [ public const VALID_ELEMENTS = [
Elements\PlainTextInput::class, Elements\PlainTextInput::class,
Elements\MultiStaticSelect::class Elements\MultiStaticSelect::class
]; ];
@ -22,26 +21,20 @@ final class Input extends Blocks
// @todo dispatch_action // @todo dispatch_action
/** /**
* @param Elements\Text|null $label * @param string|null $label
* @throws Exception * @throws SlackSyntaxException
*/ */
public function __construct(Elements\Text $label=NULL) public function __construct(string $label=NULL)
{ {
parent::__construct(); parent::__construct();
if ($label->type != 'plain_text')
throw new Exception(sprintf('Text must be plain_text not %s',$label->type));
// Defaults // Defaults
$this->type = 'input'; $this->type = 'input';
if (strlen($label->text) > self::LIMITS['label']) $this->label = Text::item($this->validate('label',$label),'plain_text');
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['label']));
$this->label = $label;
} }
public static function item(Elements\Text $label=NULL): self public static function item(string $label=NULL): self
{ {
return new self($label); return new self($label);
} }
@ -49,7 +42,7 @@ final class Input extends Blocks
public function jsonSerialize() public function jsonSerialize()
{ {
if (! $this->element) if (! $this->element)
throw new Exception('Must define an element'); throw new SlackSyntaxException('Must define an element');
return parent::jsonSerialize(); return parent::jsonSerialize();
} }
@ -66,7 +59,7 @@ final class Input extends Blocks
public function element(Element $object): self public function element(Element $object): self
{ {
if (! in_array(get_class($object),self::VALID_ELEMENTS)) if (! in_array(get_class($object),self::VALID_ELEMENTS))
throw new Exception(sprintf('Invalid element [%s] added to input',get_class($object))); throw new SlackSyntaxException(sprintf('Invalid element [%s] added to input',get_class($object)));
$this->element = $object; $this->element = $object;
@ -76,7 +69,7 @@ final class Input extends Blocks
public function hint(Elements\Text $text): self public function hint(Elements\Text $text): self
{ {
if (strlen($text->text) > self::LIMITS['hint']) if (strlen($text->text) > self::LIMITS['hint'])
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['hint'])); throw new SlackSyntaxException(sprintf('Text must be %d chars or less',self::LIMITS['hint']));
$this->hint = $text; $this->hint = $text;

View File

@ -2,25 +2,25 @@
namespace Slack\Blockkit\Blocks; namespace Slack\Blockkit\Blocks;
use \Exception;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Slack\Blockkit\{Blocks,Element}; use Slack\Blockkit\{Blocks,Element};
use Slack\BLockKit\Blocks\Elements\Text; use Slack\BLockKit\Blocks\Elements\Text;
use Slack\Exceptions\SlackSyntaxException;
final class Section extends Blocks final class Section extends Blocks
{ {
protected const LIMITS = [ public const LIMITS = [
'block_id' => 255, // @todo Should be unique for each message 'block_id' => 255, // @todo Should be unique for each message
'text' => 3000, 'text' => 3000,
]; ];
private const MAX_FIELDS = 10; public const MAX_FIELDS = 10;
private const MAX_FIELDS_TEXT = 2000; public const MAX_FIELDS_TEXT = 2000;
/** /**
* @param Text|NULL $text not required if fields is provided * @param Text|NULL $text not required if fields is provided
* @throws Exception * @throws SlackSyntaxException
*/ */
public function __construct(Text $text=NULL) public function __construct(Text $text=NULL)
{ {
@ -31,7 +31,7 @@ final class Section extends Blocks
if ($text) { if ($text) {
if (strlen($text->text) > self::LIMITS['text']) if (strlen($text->text) > self::LIMITS['text'])
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['text'])); throw new SlackSyntaxException(sprintf('Text must be %d chars or less',self::LIMITS['text']));
$this->text = $text; $this->text = $text;
} }
@ -45,7 +45,7 @@ final class Section extends Blocks
public function jsonSerialize() public function jsonSerialize()
{ {
if (! $this->text && ! $this->fields && ! $this->accessory) if (! $this->text && ! $this->fields && ! $this->accessory)
throw new Exception('Must define text, accessory or fields'); throw new SlackSyntaxException('Must define text, accessory or fields');
return parent::jsonSerialize(); return parent::jsonSerialize();
} }
@ -69,10 +69,10 @@ final class Section extends Blocks
public function fields(Collection $collection): self public function fields(Collection $collection): self
{ {
if (count($collection) > self::MAX_FIELDS) if (count($collection) > self::MAX_FIELDS)
throw new Exception(sprintf('Can only have maximum %d fields',self::MAX_FIELDS)); throw new SlackSyntaxException(sprintf('Can only have maximum %d fields',self::MAX_FIELDS));
if (($x=$collection->map(function($item) { return strlen($item->text); })->max()) > self::MAX_FIELDS_TEXT) if (($x=$collection->map(function($item) { return strlen($item->text); })->max()) > self::MAX_FIELDS_TEXT)
throw new Exception(sprintf('The maximum size of the text in a field is %d (%d)',self::MAX_FIELDS_TEXT,$x)); throw new SlackSyntaxException(sprintf('The maximum size of the text in a field is %d (%d)',self::MAX_FIELDS_TEXT,$x));
$this->fields = $collection; $this->fields = $collection;

View File

@ -2,52 +2,44 @@
namespace Slack\Blockkit; namespace Slack\Blockkit;
use \Exception;
use Slack\BlockKit; use Slack\BlockKit;
use Slack\Blockkit\Blocks\Divider; use Slack\Blockkit\Blocks\Divider;
use Slack\Blockkit\Blocks\Elements\Text; use Slack\Blockkit\Blocks\Elements\Text;
use Slack\Blockkit\Blocks\Section; use Slack\Blockkit\Blocks\Section;
use Slack\Exceptions\SlackSyntaxException;
/** /**
* This class creates a slack Modal Response * This class creates a slack Modal Response
*/ */
final class Modal extends BlockKit final class Modal extends BlockKit
{ {
protected const LIMITS = [ public const LIMITS = [
'callback_id' => 255, 'callback_id' => 255,
'close' => 24, 'close' => 24,
'private_metadata' => 3000, 'private_metadata' => 3000,
'submit' => 24, 'submit' => 24,
'text' => 24, 'title' => 24,
]; ];
private const MAX_BLOCKS = 100; public const MAX_BLOCKS = 100;
private $action = NULL; private $action = NULL;
public function __construct(Text $title=NULL,string $type='modal') public function __construct(string $title=NULL,string $type='modal')
{ {
parent::__construct(); parent::__construct();
if (! in_array($type,['modal','home'])) if (! in_array($type,['modal','home']))
throw new Exception(sprintf('Unknown type %s',$type)); throw new SlackSyntaxException(sprintf('Unknown type %s',$type));
if ($type != 'modal' && $title)
throw new Exception(sprintf('Titles are not required for %s',$type));
if ($title) { if ($title) {
if ($title->type != 'plain_text') if ($type != 'modal')
throw new Exception(sprintf('Text must be plain_text not %s',$title->type)); throw new SlackSyntaxException(sprintf('Titles are not required for %s',$type));
if (strlen($title->text) > self::LIMITS['text']) $this->title = Text::item($this->validate('title',$title),'plain_text');
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['text']));
$this->title = $title;
} }
$this->type = $type; $this->type = $type;
$this->blocks = collect(); $this->blocks = collect();
} }
@ -56,14 +48,14 @@ final class Modal extends BlockKit
* *
* @param Blocks $block * @param Blocks $block
* @return $this * @return $this
* @throws Exception * @throws SlackSyntaxException
*/ */
public function addBlock(Blocks $block): self public function addBlock(Blocks $block): self
{ {
$this->blocks->push($block); $this->blocks->push($block);
if ($x=$this->blocks->count() > self::MAX_BLOCKS) if ($this->blocks->count() > self::MAX_BLOCKS)
throw new Exception(sprintf('Modal can only have %d blocks',self::MAX_BLOCKS)); throw new SlackSyntaxException(sprintf('Modal can only have %d blocks',self::MAX_BLOCKS));
return $this; return $this;
} }
@ -90,17 +82,17 @@ final class Modal extends BlockKit
public function action(string $string) public function action(string $string)
{ {
if (! in_array($string,['clear','update'])) if (! in_array($string,['clear','update']))
throw new Exception(sprintf('Unknown action %s',$string)); throw new SlackSyntaxException(sprintf('Unknown action %s',$string));
$this->action = $string; $this->action = $string;
return $this; return $this;
} }
public function clear_on_close(bool $bool): self public function clear_on_close(bool $bool=TRUE): self
{ {
if ($this->type != 'modal') if ($this->type != 'modal')
throw new Exception(sprintf('clear_on_close is not required for %s',$type)); throw new SlackSyntaxException(sprintf('clear_on_close is not required for %s',$this->type));
$this->clear_on_close = $bool; $this->clear_on_close = $bool;
@ -114,18 +106,12 @@ final class Modal extends BlockKit
return $this; return $this;
} }
public function close(Text $text): self public function close(string $text): self
{ {
if ($this->type != 'modal') if ($this->type != 'modal')
throw new Exception(sprintf('Close is not required for %s',$type)); throw new SlackSyntaxException(sprintf('Close is not required for %s',$this->type));
if ($text->type != 'plain_text') $this->close = Text::item($this->validate('close',$text),'plain_text');
throw new Exception(sprintf('Text must be plain_text not %s',$text->type));
if (strlen($text->text) > self::LIMITS['close'])
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['close']));
$this->close = $text;
return $this; return $this;
} }
@ -150,10 +136,10 @@ final class Modal extends BlockKit
return $this; return $this;
} }
public function notify_on_close(bool $bool): self public function notify_on_close(bool $bool=TRUE): self
{ {
if ($this->type != 'modal') if ($this->type != 'modal')
throw new Exception(sprintf('notify_on_close is not required for %s',$type)); throw new SlackSyntaxException(sprintf('notify_on_close is not required for %s',$this->type));
$this->notify_on_close = $bool; $this->notify_on_close = $bool;
@ -175,18 +161,12 @@ final class Modal extends BlockKit
return $this; return $this;
} }
public function submit(Text $text): self public function submit(string $text): self
{ {
if ($this->type != 'modal') if ($this->type != 'modal')
throw new Exception(sprintf('Submit is not required for %s',$type)); throw new SlackSyntaxException(sprintf('Submit is not required for %s',$this->type));
if ($text->type != 'plain_text') $this->submit = Text::item($this->validate('submit',$text),'plain_text');
throw new Exception(sprintf('Text must be plain_text not %s',$text->type));
if (strlen($text->text) > self::LIMITS['submit'])
throw new Exception(sprintf('Text must be %d chars or less',self::LIMITS['submit']));
$this->submit = $text;
return $this; return $this;
} }
@ -194,24 +174,10 @@ final class Modal extends BlockKit
public function submit_disabled(bool $bool): self public function submit_disabled(bool $bool): self
{ {
if ($this->type != 'modal') if ($this->type != 'modal')
throw new Exception(sprintf('submit_disabled is not required for %s',$type)); throw new SlackSyntaxException(sprintf('submit_disabled is not required for %s',$this->type));
$this->submit_disabled = $bool; $this->submit_disabled = $bool;
return $this; return $this;
} }
/**
* Add a block to the modal
*
* @param Blocks $blocks
* @return $this
* @deprecated use addBlock() instead
*/
public function setBlocks(Blocks $blocks): self
{
$this->_data->put('blocks',$blocks);
return $this;
}
} }

View File

@ -9,6 +9,7 @@ use React\EventLoop\Loop;
use Slack\Client\SocketMode; use Slack\Client\SocketMode;
use Slack\Command\Factory as SlackCommandFactory; use Slack\Command\Factory as SlackCommandFactory;
use Slack\Event\Factory as SlackEventFactory; use Slack\Event\Factory as SlackEventFactory;
use Slack\Exceptions\SlackException;
use Slack\Interactive\Factory as SlackInteractiveFactory; use Slack\Interactive\Factory as SlackInteractiveFactory;
class SlackSocketClient extends Command class SlackSocketClient extends Command
@ -33,13 +34,13 @@ class SlackSocketClient extends Command
* Execute the console command. * Execute the console command.
* *
* @return void * @return void
* @throws \Exception * @throws SlackException
*/ */
public function handle() public function handle()
{ {
// Make sure our socket_token is defined // Make sure our socket_token is defined
if (! config('slack.socket_token')) if (! config('slack.socket_token'))
throw new \Exception('SocketMode Client Token not defined.'); throw new SlackException('SocketMode Client Token not defined.');
$loop = Loop::get(); $loop = Loop::get();

View File

@ -0,0 +1,7 @@
<?php
namespace Slack\Exceptions;
class SlackSyntaxException extends SlackException
{
}

View File

@ -18,7 +18,6 @@ final class InteractiveOptionsController extends Controller
* *
* @param Request $request * @param Request $request
* @return \Illuminate\Http\Response|\Laravel\Lumen\Http\ResponseFactory * @return \Illuminate\Http\Response|\Laravel\Lumen\Http\ResponseFactory
* @throws \Exception
*/ */
public function fire(Request $request) public function fire(Request $request)
{ {

View File

@ -73,7 +73,7 @@ final class CheckRequest
return response('',200); return response('',200);
} else { } else {
Log::debug(sprintf('%s:Incoming Request Allowed',static::LOGKEY),['m'=>__METHOD__,'e'=>$event->enterprise_id,'t'=>$event->team_id,'eo'=>$event->enterprise()->id,'to'=>$event->team()]); Log::debug(sprintf('%s:Incoming Request Allowed [%s/%s]',static::LOGKEY,$event->enterprise_id,$event->team_id),['m'=>__METHOD__]);
return $next($request); return $next($request);
} }

View File

@ -35,6 +35,7 @@ abstract class Base extends SlackBase
* + user_id * + user_id
* @param string $key * @param string $key
* @return mixed|object * @return mixed|object
* @throws \Exception
*/ */
public function __get(string $key) public function __get(string $key)
{ {
@ -46,6 +47,12 @@ abstract class Base extends SlackBase
case 'user_id': case 'user_id':
return object_get($this->_data,'user.id'); return object_get($this->_data,'user.id');
case 'callback_key':
return $this->keyitem('id',$this->callback_id);
case 'callback_value':
return $this->keyitem('value',$this->callback_id);
case 'callback_id': case 'callback_id':
case 'trigger_id': case 'trigger_id':
case 'type': case 'type':

View File

@ -5,7 +5,6 @@ namespace Slack\Interactive;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Slack\Exceptions\SlackException;
use Slack\Models\Channel; use Slack\Models\Channel;
/** /**
@ -65,19 +64,25 @@ final class BlockActions extends Base
case 'action': case 'action':
return Arr::get(object_get($this->_data,'actions'),$this->index); return Arr::get(object_get($this->_data,'actions'),$this->index);
case 'action_id':
return object_get(Arr::get($this->actions,$this->index),$key);
// An event can have more than 1 action, each action can have 1 value. // An event can have more than 1 action, each action can have 1 value.
case 'action_key': case 'action_key':
return $this->action('action'); return $this->keyitem('id',$this->action_id);
case 'action_value': case 'action_value':
return $this->action('value'); return $this->keyitem('value',$this->action_id);
// Interactive Messages have a block_id.
case 'block_key':
return $this->keyitem('id',$this->block_id);
case 'block_value':
return $this->keyitem('value',$this->block_id);
case 'actions': case 'actions':
case 'response_url':
return object_get($this->_data,$key); return object_get($this->_data,$key);
case 'action_id':
case 'block_id': case 'block_id':
return object_get($this->action,$key); return object_get($this->action,$key);
@ -87,25 +92,33 @@ final class BlockActions extends Base
// For app hometab, the callback is in the view->callback_id array. // For app hometab, the callback is in the view->callback_id array.
return object_get($this->_data,sprintf('%s.%s',$this->container_type,$key)); return object_get($this->_data,sprintf('%s.%s',$this->container_type,$key));
case 'message':
case 'message_attachment': case 'message_attachment':
return NULL; return NULL;
default: default:
throw new SlackException('Unknown container type: '.$this->container_type); throw new \Exception('Unknown container type: '.$this->container_type);
} }
case 'container_type': case 'container_type':
return object_get($this->_data,'container.type'); return object_get($this->_data,'container.type');
case 'channel_id': case 'channel_id':
return object_get($this->_data,'channel.id') ?: Channel::findOrFail($this->action('value'))->channel_id; return object_get($this->_data,'channel.id') ?: Channel::findOrFail($this->keyitem('value',object_get($this->action,'action_id')))->channel_id;
case 'keys': case 'keys':
return collect(object_get($this->_data,'view.blocks'))->pluck('accessory.action_id'); return collect(object_get($this->_data,'view.blocks'))->pluck('accessory.action_id');
// For Block Actions that are messages // For Block Actions that are messages
case 'message_ts': case 'message_ts':
return object_get($this->_data,'message.ts'); switch ($this->container_type) {
// Ephemeral messages
case 'message':
return object_get($this->_data,'container.message_ts');
default:
return object_get($this->_data,'message.ts');
}
case 'team_id': // view.team_id represent workspace publishing view case 'team_id': // view.team_id represent workspace publishing view
return object_get($this->_data,'user.team_id'); return object_get($this->_data,'user.team_id');
@ -134,7 +147,7 @@ final class BlockActions extends Base
// @todo To Check // @todo To Check
case 'overflow': case 'overflow':
// @todo To Check // @todo To Check
throw new SlackException('To be implemented: ',$x); throw new \Exception('To be implemented: ',$x);
case 'static_select': case 'static_select':
return count(object_get($this->action,'selected_option')); return count(object_get($this->action,'selected_option'));
case 'multi_static_select': case 'multi_static_select':
@ -148,38 +161,6 @@ final class BlockActions extends Base
} }
} }
/**
* Separate out an action command to the id that the command relates to
*
* @param string $key
* @return string|null
* @throws SlackException
*/
private function action(string $key): ?string
{
$regex = '/^([a-z_]+)\|([0-9]+)$/';
$action = NULL;
$value = NULL;
// We only take the action up to the pipe symbol
$action_key = object_get($this->action,'action_id');
if (preg_match($regex,$action_key)) {
$action = preg_replace($regex,'$1',$action_key);
$value = preg_replace($regex,'$2',$action_key);
}
switch ($key) {
case 'action':
return $action ?: $action_key;
case 'value':
return $value;
default:
throw new SlackException('Unknown key: '.$key);
}
}
/** /**
* Some block actions are triggered by messages, and thus dont have a callback_id * Some block actions are triggered by messages, and thus dont have a callback_id
* *

View File

@ -99,26 +99,8 @@ class InteractiveMessage extends Base
public function respond(): Message public function respond(): Message
{ {
Log::info(sprintf('%s:Interactive Message - Callback [%s] Name [%s] Type [%s]',static::LOGKEY,$this->callback_id,$this->name,$this->type),['m'=>__METHOD__]); Log::info(sprintf('%s:Interactive Message - Callback [%s] Name [%s] Type [%s]',static::LOGKEY,$this->callback_id,$this->name,$this->type),['m'=>__METHOD__]);
Log::notice(sprintf('%s:Unhandled action [%s]',static::LOGKEY,$this->callback_id),['m'=>__METHOD__]);
$action = NULL; return (new Message)->text('That didnt work, I didnt know what to do with your button - you might like to tell '.$this->team()->owner->slack_user);
$id = NULL;
if (preg_match('/^(.*)\|([0-9]+)/',$this->callback_id)) {
[$action,$id] = explode('|',$this->callback_id,2);
} elseif (preg_match('/^[a-z_]+$/',$this->callback_id)) {
$id = $this->name;
$action = $this->callback_id;
} else {
// If we get here, its an action that we dont know about.
Log::notice(sprintf('%s:Unhandled CALLBACK [%s]',static::LOGKEY,$this->callback_id),['m'=>__METHOD__]);
}
switch ($action) {
default:
Log::notice(sprintf('%s:Unhandled ACTION [%s]',static::LOGKEY,$action),['m'=>__METHOD__]);
return (new Message)->text('That didnt work, I didnt know what to do with your button - you might like to tell '.$this->team()->owner->slack_user);
}
} }
} }

View File

@ -2,13 +2,9 @@
namespace Slack\Interactive; namespace Slack\Interactive;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Slack\Blockkit\Modal; use Slack\Blockkit\Modal;
use Slack\Exceptions\SlackException;
use Slack\Models\Team;
/** /**
* Incoming modal view submission event. * Incoming modal view submission event.
@ -28,14 +24,14 @@ class ViewSubmission extends Base
case 'blocks': case 'blocks':
return collect(object_get($this->_data,'view.'.$key)); return collect(object_get($this->_data,'view.'.$key));
case 'callback': case 'callback_id':
return object_get($this->_data,'view.callback_id'); return object_get($this->_data,'view.callback_id');
case 'callback_id': case 'callback_key':
return $this->callback('id'); return $this->keyitem('id',$this->callback_id);
case 'callback_value': case 'callback_value':
return $this->callback('value'); return $this->keyitem('value',$this->callback_id);
case 'meta': case 'meta':
return object_get($this->_data,'view.private_metadata'); return object_get($this->_data,'view.private_metadata');
@ -51,70 +47,29 @@ class ViewSubmission extends Base
} }
} }
/**
* Separate out an callback command to the id that the command relates to
*
* @param string $key
* @return string|null
* @throws SlackException
*/
private function callback(string $key): ?string
{
$regex = '/^([a-z_]+)\|([0-9]+)$/';
$id = NULL;
$value = NULL;
if (preg_match($regex,$this->callback)) {
$id = preg_replace($regex,'$1',$this->callback);
$value = preg_replace($regex,'$2',$this->callback);
}
switch ($key) {
case 'id':
return $id ?: $this->callback;
case 'value':
return $value;
default:
throw new SlackException('Unknown key: '.$key);
}
}
/** /**
* This method should be overridden by a local implementation * This method should be overridden by a local implementation
* *
* @return Modal * @return Modal
* @throws \Exception
*/ */
public function respond(): Modal public function respond(): Modal
{ {
// Do some magic with event data // Do some magic with event data
Log::info(sprintf('%s:View Submission for Callback [%s] User [%s] in [%s]',self::LOGKEY,$this->callback_id,$this->user_id,$this->team_id),['m'=>__METHOD__]); Log::info(sprintf('%s:View Submission for Callback [%s] User [%s] in [%s]',self::LOGKEY,$this->callback_id,$this->user_id,$this->team_id),['m'=>__METHOD__]);
Log::notice(sprintf('%s:Unhandled action [%s]',self::LOGKEY,$this->callback_key),['m'=>__METHOD__]);
$action = NULL;
$id = NULL;
if (preg_match('/^(.*)\|([0-9]+)/',$this->callback_id)) {
[$action,$cid] = explode('|',$this->callback_id,2);
} elseif (preg_match('/^[a-z_]+$/',$this->callback_id)) {
$action = $this->callback_id;
} else {
// If we get here, its an action that we dont know about.
Log::notice(sprintf('%s:Unhandled CALLBACK [%s]',static::LOGKEY,$this->callback_id),['m'=>__METHOD__]);
}
switch ($action) {
default:
Log::notice(sprintf('%s:Unhandled ACTION [%s]',self::LOGKEY,$action),['m'=>__METHOD__]);
}
return new Modal; return new Modal;
} }
public function value(string $block_id,string $action_id): ?string public function value(string $block_id,string $action_id=NULL): ?string
{ {
return object_get($this->state->get($block_id),$action_id.'.value'); // If there is no state we need to search out blocks for the block_id
if (! $this->state->count()) {
$block = $this->blocks->search(function($item) use ($block_id) { return $item->block_id == $block_id; });
return $block !== FALSE ? object_get($this->blocks->get($block),$action_id) : NULL;
} else {
return object_get($this->state->get($block_id),$action_id.'.value');
}
} }
} }

View File

@ -22,16 +22,16 @@ final class DeleteChat extends Job
public function __construct(Model $o,string $ts) public function __construct(Model $o,string $ts)
{ {
if ($o instanceof Channel) { if ($o instanceof Channel) {
$this->_data['cid'] = $o->channel_id; $this->cid = $o->channel_id;
} elseif ($o instanceof User) { } elseif ($o instanceof User) {
$this->_data['cid'] = $o->user_id; $this->cid = $o->user_id;
} else } else
throw new \Exception('Invalid Model: '.get_class($o)); throw new \Exception('Invalid Model: '.get_class($o));
$this->_data['to'] = $o->team; $this->to = $o->team;
$this->_data['ts'] = $ts; $this->ts = $ts;
} }
/** /**

View File

@ -0,0 +1,46 @@
<?php
namespace Slack\Jobs;
use Illuminate\Support\Facades\Log;
use Slack\Exceptions\SlackException;
use Slack\Message;
final class DeleteResponse extends Job
{
private const LOGKEY = 'JDC';
/**
* Create a new job instance.
*
* @param string $url
*/
public function __construct(string $url)
{
$this->url = $url;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Log::info(sprintf('%s:Start - Delete Response [%s]',static::LOGKEY,$this->url),['m'=>__METHOD__]);
$o = new Message;
$o->text('')
->delete_original();
try {
$o->respond($this->url);
Log::debug(sprintf('%s:Deleted Slack Message: %s',static::LOGKEY,$this->url),['m'=>__METHOD__]);
} catch (SlackException $e) {
Log::error(sprintf('%s:Failed to delete slack message [%s] [%s]',static::LOGKEY,$this->url,$e->getMessage()),['m'=>__METHOD__]);
}
}
}

View File

@ -7,6 +7,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
abstract class Job implements ShouldQueue abstract class Job implements ShouldQueue
{ {
@ -23,10 +24,20 @@ abstract class Job implements ShouldQueue
use InteractsWithQueue, Queueable, SerializesModels; use InteractsWithQueue, Queueable, SerializesModels;
protected $_data = []; protected Collection $_data;
public function __get($key) public function __get($key)
{ {
if (! isset($this->_data))
$this->_data = collect();
return Arr::get($this->_data,$key); return Arr::get($this->_data,$key);
} }
public function __set(string $key,$value) {
if (! isset($this->_data))
$this->_data = collect();
return $this->_data->put($key,$value);
}
} }

View File

@ -18,7 +18,7 @@ class TeamUpdate extends Job
*/ */
public function __construct(Team $to) public function __construct(Team $to)
{ {
$this->_data['to'] = $to; $this->to = $to;
} }
public function handle() public function handle()

View File

@ -20,11 +20,11 @@ class AppHomeOpenedListener implements ShouldQueue
* @param AppHomeOpened $event * @param AppHomeOpened $event
* @return void * @return void
*/ */
public function handle(AppHomeOpened $event) public function handle(AppHomeOpened $event): void
{ {
// Do some magic with event data // Do some magic with event data
Log::info(sprintf('%s:App Home Page open for [%s] in team [%s]',self::LOGKEY,$event->user_id,$event->team_id),['m'=>__METHOD__]); Log::info(sprintf('%s:App Home Page open for [%s] in team [%s]',self::LOGKEY,$event->user_id,$event->team_id),['m'=>__METHOD__]);
dispatch((new SlackHomeTabUpdate($event->user(),$event->team(TRUE),$event->tab,$event->view))->onQueue('high')); dispatch((new SlackHomeTabUpdate($event->user(),$event->team(TRUE),$event->tab,$event->view))->onQueue('slack'));
} }
} }

View File

@ -2,6 +2,7 @@
namespace Slack\Listeners; namespace Slack\Listeners;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Slack\Jobs\DeleteChat; use Slack\Jobs\DeleteChat;
@ -10,13 +11,16 @@ use Slack\Interactive\BlockActions;
/** /**
* This class handles BlockActions events. * This class handles BlockActions events.
* It's expected that the local application would implement this class completely, rather than using this * It's expected that the local application would implement this class completely, rather than using this
* modules implementation. * module's implementation.
*
* @note Since block actions contain a trigger_id, we shouldnt queue this as the trigger_id may have expired by the time
* the queue runs the job. (trigger_id's only last 3 seconds)
*/ */
class BlockActionListener class BlockActionListener //implements ShouldQueue
{ {
protected const LOGKEY = 'LBA'; protected const LOGKEY = 'LBA';
public $queue = 'slack'; // public $queue = 'slack';
/** /**
* Handle the event. * Handle the event.
@ -60,7 +64,7 @@ class BlockActionListener
switch ($event->action_id) { switch ($event->action_id) {
case 'self_destruct': case 'self_destruct':
// Queue the delete of the message // Queue the delete of the message
dispatch((new DeleteChat($event->user(),$event->message_ts))->onQueue('low')); dispatch((new DeleteChat($event->user(),$event->message_ts))->onQueue('slack'));
// @todo If this message is on integrations messages channel, which is not the user_id() - need to use the user's integration direct channel ID // @todo If this message is on integrations messages channel, which is not the user_id() - need to use the user's integration direct channel ID
break; break;

View File

@ -2,10 +2,12 @@
namespace Slack\Listeners; namespace Slack\Listeners;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Slack\Options\BlockSuggestion; use Slack\Options\BlockSuggestion;
class BlockSuggestionListener class BlockSuggestionListener implements ShouldQueue
{ {
private const LOGKEY = 'LBS'; private const LOGKEY = 'LBS';
@ -22,6 +24,7 @@ class BlockSuggestionListener
public function handle(BlockSuggestion $event): void public function handle(BlockSuggestion $event): void
{ {
// Do some magic with event data // Do some magic with event data
Log::info(sprintf('%s:Block Suggestion for Callback [%s] User [%s] in [%s]',self::LOGKEY,$event->callback_id,$event->user_id,$event->team_id),['e'=>$event]); Log::info(sprintf('%s:Block Suggestion Callback [%s] User [%s] in [%s]',self::LOGKEY,$event->callback_id,$event->user_id,$event->team_id),['e'=>$event]);
Log::notice(sprintf('%s:Ignoring Block Suggestion [%s]',static::LOGKEY,$event->callback_id),['m'=>__METHOD__]);
} }
} }

View File

@ -19,7 +19,7 @@ class ChannelJoinListener implements ShouldQueue
* @param MemberJoinedChannel $event * @param MemberJoinedChannel $event
* @return void * @return void
*/ */
public function handle(MemberJoinedChannel $event) public function handle(MemberJoinedChannel $event): void
{ {
// Do some magic with event data // Do some magic with event data
Log::info(sprintf('%s:User [%s] joined Channel [%s]',self::LOGKEY,$event->invited,$event->channel_id),['m'=>__METHOD__]); Log::info(sprintf('%s:User [%s] joined Channel [%s]',self::LOGKEY,$event->invited,$event->channel_id),['m'=>__METHOD__]);

View File

@ -19,7 +19,7 @@ class ChannelLeftListener implements ShouldQueue
* @param Base $event * @param Base $event
* @return void * @return void
*/ */
public function handle(Base $event) public function handle(Base $event): void
{ {
if (! $event instanceof ChannelLeft AND ! $event instanceof GroupLeft) if (! $event instanceof ChannelLeft AND ! $event instanceof GroupLeft)
abort(500,'Wrong class calling this listener? '.get_class($event)); abort(500,'Wrong class calling this listener? '.get_class($event));

View File

@ -19,14 +19,10 @@ class InteractiveMessageListener implements ShouldQueue
* @param InteractiveMessage $event * @param InteractiveMessage $event
* @return void * @return void
*/ */
public function handle(InteractiveMessage $event) public function handle(InteractiveMessage $event): void
{ {
// Do some magic with event data // Do some magic with event data
Log::info(sprintf('%s:Interactive Message for Callback [%s] User [%s] in [%s]',self::LOGKEY,$event->callback_id,$event->user_id,$event->team_id),['m'=>__METHOD__]); Log::info(sprintf('%s:Interactive Message Callback [%s] User [%s] in [%s]',self::LOGKEY,$event->callback_id,$event->user_id,$event->team_id),['m'=>__METHOD__]);
Log::notice(sprintf('%s:Ignoring Interactive Message [%s]',static::LOGKEY,$event->callback_id),['m'=>__METHOD__]);
switch ($event->callback_id) {
default:
Log::notice(sprintf('%s:Unhandled CALLBACK [%s]',self::LOGKEY,$event->callback_id),['m'=>__METHOD__]);
}
} }
} }

View File

@ -19,7 +19,7 @@ class MessageListener implements ShouldQueue
* @param Message $event * @param Message $event
* @return void * @return void
*/ */
public function handle(Message $event) public function handle(Message $event): void
{ {
// Do some magic with event data // Do some magic with event data
Log::info(sprintf('%s:Message event [%s] - subtype [%s]',self::LOGKEY,$event->ts,$event->type),['m'=>__METHOD__]); Log::info(sprintf('%s:Message event [%s] - subtype [%s]',self::LOGKEY,$event->ts,$event->type),['m'=>__METHOD__]);

View File

@ -19,7 +19,7 @@ class PinAddedListener implements ShouldQueue
* @param PinAdded $event * @param PinAdded $event
* @return void * @return void
*/ */
public function handle(PinAdded $event) public function handle(PinAdded $event): void
{ {
// Do some magic with event data // Do some magic with event data
Log::info(sprintf('%s:Pin Added to message [%s] in [%s]',self::LOGKEY,$event->ts,$event->channel_id),['m'=>__METHOD__]); Log::info(sprintf('%s:Pin Added to message [%s] in [%s]',self::LOGKEY,$event->ts,$event->channel_id),['m'=>__METHOD__]);

View File

@ -19,7 +19,7 @@ class PinRemovedListener implements ShouldQueue
* @param PinRemoved $event * @param PinRemoved $event
* @return void * @return void
*/ */
public function handle(PinRemoved $event) public function handle(PinRemoved $event): void
{ {
// Do some magic with event data // Do some magic with event data
Log::info(sprintf('%s:Pin Removed from message [%s] in [%s]',self::LOGKEY,$event->ts,$event->channel_id),['m'=>__METHOD__]); Log::info(sprintf('%s:Pin Removed from message [%s] in [%s]',self::LOGKEY,$event->ts,$event->channel_id),['m'=>__METHOD__]);

View File

@ -19,11 +19,10 @@ class ReactionAddedListener implements ShouldQueue
* @param ReactionAdded $event * @param ReactionAdded $event
* @return void * @return void
*/ */
public function handle(ReactionAdded $event) public function handle(ReactionAdded $event): void
{ {
// Do some magic with event data // Do some magic with event data
Log::info(sprintf('%s:Reaction [%s] added to message in [%s]',self::LOGKEY,$event->reaction,$event->team_id),['m'=>__METHOD__]); Log::info(sprintf('%s:Reaction [%s] added to message in [%s]',self::LOGKEY,$event->reaction,$event->team_id),['m'=>__METHOD__]);
Log::notice(sprintf('%s:Ignoring Reaction Add [%s] on [%s]',static::LOGKEY,$event->reaction,$event->ts),['m'=>__METHOD__]);
Log::debug(sprintf('%s:Ignoring Reaction Add [%s] on [%s]',static::LOGKEY,$event->reaction,$event->ts),['m'=>__METHOD__]);
} }
} }

View File

@ -2,13 +2,13 @@
namespace Slack\Listeners; namespace Slack\Listeners;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Slack\Blockkit\Blocks\Elements\Text; use Slack\Blockkit\Blocks\Elements\Text;
use Slack\Blockkit\Blocks\{Header,Section}; use Slack\Blockkit\Blocks\{Header,Section};
use Slack\Blockkit\Modal; use Slack\Blockkit\Modal;
use Slack\Interactive\Shortcut; use Slack\Interactive\Shortcut;
use Slack\Message;
class ShortcutListener //implements ShouldQueue class ShortcutListener //implements ShouldQueue
{ {
@ -20,38 +20,28 @@ class ShortcutListener //implements ShouldQueue
* Handle the event. * Handle the event.
* *
* @param Shortcut $event * @param Shortcut $event
* @return Message|null * @return void
* @throws \Exception * @throws \Exception
* @todo To Test * @todo To Test
*/ */
public function handle(Shortcut $event): ?Message public function handle(Shortcut $event): void
{ {
if (! $event->channel() || ! $event->channel()->active) { if (! $event->channel() || ! $event->channel()->active) {
$modal = new Modal(Text::item(config('app.name'),'plain_text')); $modal = new Modal(config('app.name'));
$modal->addBlock( $modal->addBlock(Header::item(':robot_face: Bot not in this channel'))
Header::item(Text::item(':robot_face: Bot not in this channel','plain_text')) ->addBlock(Section::item(Text::item('Please add the BOT to this channel and try this again.')));
)
->addBlock(
Section::item(Text::item('Please add the BOT to this channel and try this again.'))
);
try { try {
$event->team()->slackAPI()->viewOpen($event->trigger_id,json_encode($modal)); $event->team()->slackAPI()->viewOpen($event->trigger_id,$modal);
} catch (\Exception $e) { } catch (\Exception $e) {
Log::error(sprintf('%s:Got an error posting view to slack: %s',static::LOGKEY,$e->getMessage()),['m'=>__METHOD__]); Log::error(sprintf('%s:Got an error posting view to slack: %s',static::LOGKEY,$e->getMessage()),['m'=>__METHOD__]);
} }
return (new Message)->blank();
} }
// Do some magic with event data // Do some magic with event data
Log::info(sprintf('%s:Shortcut [%s] triggered for: [%s]',self::LOGKEY,$event->callback_id,$event->team_id),['m'=>__METHOD__]); Log::info(sprintf('%s:Shortcut [%s] triggered for: [%s]',self::LOGKEY,$event->callback_id,$event->team_id),['m'=>__METHOD__]);
Log::notice(sprintf('%s:Ignoring Shortcut [%s] in team [%s]',static::LOGKEY,$event->callback_id,$event->team_id),['m'=>__METHOD__]);
switch ($event->callback_id) {
default:
Log::notice(sprintf('%s:Unhandled CALLBACK [%s]',self::LOGKEY,$event->callback_id),['m'=>__METHOD__]);
}
} }
} }

View File

@ -19,14 +19,10 @@ class ViewClosedListener implements ShouldQueue
* @param ViewClosed $event * @param ViewClosed $event
* @return void * @return void
*/ */
public function handle(ViewClosed $event) public function handle(ViewClosed $event): void
{ {
// Do some magic with event data // Do some magic with event data
Log::info(sprintf('%s:Block Action for Callback [%s] User [%s] in [%s]',self::LOGKEY,$event->callback_id,$event->user_id,$event->team_id),['m'=>__METHOD__]); Log::info(sprintf('%s:View Closed Callback [%s] User [%s] in [%s]',self::LOGKEY,$event->callback_id,$event->user_id,$event->team_id),['m'=>__METHOD__]);
Log::notice(sprintf('%s:Ignoring View Closed [%s]',static::LOGKEY,$event->callback_id),['m'=>__METHOD__]);
switch ($event->callback_id) {
default:
Log::notice(sprintf('%s:Unhandled CALLBACK [%s]',self::LOGKEY,$event->callback_id),['m'=>__METHOD__]);
}
} }
} }

View File

@ -19,14 +19,10 @@ class ViewSubmissionListener implements ShouldQueue
* @param ViewSubmission $event * @param ViewSubmission $event
* @return void * @return void
*/ */
public function handle(ViewSubmission $event) public function handle(ViewSubmission $event): void
{ {
// Do some magic with event data // Do some magic with event data
Log::info(sprintf('%s:View Submission for Callback [%s] User [%s] in [%s]',self::LOGKEY,$event->callback_id,$event->user_id,$event->team_id),['m'=>__METHOD__]); Log::info(sprintf('%s:View Submission Callback [%s] User [%s] in [%s]',self::LOGKEY,$event->callback_id,$event->user_id,$event->team_id),['m'=>__METHOD__]);
Log::notice(sprintf('%s:Ignoring View Closed [%s]',static::LOGKEY,$event->callback_id),['m'=>__METHOD__]);
switch ($event->callback_id) {
default:
Log::notice(sprintf('%s:Unhandled CALLBACK [%s]',self::LOGKEY,$event->callback_id),['m'=>__METHOD__]);
}
} }
} }

View File

@ -7,13 +7,14 @@ use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Slack\Blockkit\Blocks; use Slack\Blockkit\Blocks;
use Slack\Blockkit\Blocks\Context; use Slack\Blockkit\Blocks\{Context,Divider,Section};
use Slack\Blockkit\Blocks\Elements\Text; use Slack\Blockkit\Blocks\Elements\Text;
use Slack\Exceptions\SlackException; use Slack\Exceptions\{SlackException,SlackSyntaxException};
use Slack\Jobs\DeleteChat; use Slack\Jobs\{DeleteChat,DeleteResponse};
use Slack\Message\Attachment; use Slack\Message\Attachment;
use Slack\Models\{Channel,User}; use Slack\Models\{Channel,User};
use Slack\Response\Generic; use Slack\Response\Generic;
@ -25,7 +26,8 @@ final class Message extends BlockKit
{ {
private const LOGKEY = 'SM-'; private const LOGKEY = 'SM-';
private const MAX_ATTACHMENTS = 20; public const MAX_ATTACHMENTS = 20;
public const MAX_BLOCKS = 50;
private Model $o; private Model $o;
private ?Carbon $selfdestruct = NULL; private ?Carbon $selfdestruct = NULL;
@ -34,7 +36,7 @@ final class Message extends BlockKit
* Message constructor. * Message constructor.
* *
* @param Model|null $o Who the message will be to - Channel or User * @param Model|null $o Who the message will be to - Channel or User
* @throws SlackException * @throws \Exception
*/ */
public function __construct(Model $o=NULL) public function __construct(Model $o=NULL)
{ {
@ -50,7 +52,7 @@ final class Message extends BlockKit
$this->setUser($o); $this->setUser($o);
} else { } else {
throw new SlackException('Model not handled: '.get_class($o)); throw new \Exception('Model not handled: '.get_class($o));
} }
$this->o = $o; $this->o = $o;
@ -72,9 +74,9 @@ final class Message extends BlockKit
/** /**
* Add a block to the message * Add a block to the message
* *
* @param Blocks $blocks * @param Attachment $attachment
* @return Message * @return Message
* @todo to test * @throws SlackSyntaxException
*/ */
public function addAttachment(Attachment $attachment): self public function addAttachment(Attachment $attachment): self
{ {
@ -84,7 +86,7 @@ final class Message extends BlockKit
$this->attachments->push($attachment); $this->attachments->push($attachment);
if (count($this->attachments) > self::MAX_ATTACHMENTS) if (count($this->attachments) > self::MAX_ATTACHMENTS)
throw new Exception(sprintf('Messages should not have more than %d attachments',self::MAX_ATTACHMENTS)); throw new SlackSyntaxException(sprintf('Messages should not have more than %d attachments',self::MAX_ATTACHMENTS));
return $this; return $this;
} }
@ -96,6 +98,9 @@ final class Message extends BlockKit
$this->blocks->push($block); $this->blocks->push($block);
if (count($this->blocks) > self::MAX_BLOCKS)
throw new SlackSyntaxException(sprintf('Messages should not have more than %d blocks',self::MAX_BLOCKS));
return $this; return $this;
} }
@ -106,6 +111,27 @@ final class Message extends BlockKit
return $this; return $this;
} }
/**
* For messages that we interact with via a response_url
*
* @param bool $bool
* @return Message
*/
public function delete_original(bool $bool=TRUE): self
{
$this->delete_original = $bool;
return $this;
}
// This is a helper method
public function divider(): self
{
$this->blocks->push(Divider::item());
return $this;
}
public function forgetTS(): self public function forgetTS(): self
{ {
$this->_data->forget('ts'); $this->_data->forget('ts');
@ -120,7 +146,7 @@ final class Message extends BlockKit
*/ */
public function isEmpty(): bool public function isEmpty(): bool
{ {
return $this->jsonSerialize() ? FALSE : TRUE; return ! $this->jsonSerialize();
} }
/** /**
@ -129,7 +155,7 @@ final class Message extends BlockKit
public function jsonSerialize() public function jsonSerialize()
{ {
// For interactive messages that generate a dialog, we need to return NULL // For interactive messages that generate a dialog, we need to return NULL
return $this->_data->count() ? parent::jsonSerialize() : NULL; return count($this) ? parent::jsonSerialize() : NULL;
} }
/** /**
@ -144,20 +170,31 @@ final class Message extends BlockKit
if (! $delete && $this->selfdestruct) if (! $delete && $this->selfdestruct)
$delete = $this->selfdestruct; $delete = $this->selfdestruct;
if ($this->_data->has('ephemeral')) if ((! isset($this->o)) || ((! $this->o->team) && (! $this->o->user_team)))
abort('500','Cannot post ephemeral messages.'); throw new SlackSyntaxException('Message needs to have a user or a channel to work out the team.');
if ($this->_data->get('blocks') && $this->_data->get('attachments')) $api = $this->o->team ? $this->o->team->slackAPI() : $this->o->user_team->slackAPI();
throw new SlackException('Message cannot have blocks and attachments.');
$api = $this->o->team->slackAPI(); if ($this->ephemeral) {
$response = $this->_data->has('ts') ? $api->updateMessage($this) : $api->postMessage($this); $response = $api->postEphemeral($this);
} else {
$response = $this->_data->has('ts') ? $api->updateMessage($this) : $api->postMessage($this);
}
if ($delete) { if ($delete) {
Log::debug(sprintf('%s:Scheduling Delete of [%s:%s] on [%s]',static::LOGKEY,object_get($this->o,'channel_id',$this->o->id),$response->ts,$delete->format('Y-m-d')),['m'=>__METHOD__]); Log::debug(sprintf('%s:Scheduling Delete of [%s:%s] on [%s]',static::LOGKEY,object_get($this->o,'channel_id',$this->o->id),$response->ts,$delete->format('Y-m-d H:i')),['m'=>__METHOD__,'r'=>$response,'message_ts'=>$response->ts]);
// Queue the delete of the message if requested if ($this->o instanceof User) {
dispatch((new DeleteChat($this->o,$response->ts))->onQueue('low')->delay($delete)); Log::error(sprintf('%s:Cannot schedule delete of [%s:%s] on user channels [%s]',static::LOGKEY,object_get($this->o,'channel_id',$this->o->id),$response->ts,$this->o->user_id),['m'=>__METHOD__]);
} elseif ($this->ephemeral) {
Log::error(sprintf('%s:Ignoring delete of [%s:%s] on ephemeral messages',static::LOGKEY,object_get($this->o,'channel_id',$this->o->id),$response->ts),['m'=>__METHOD__]);
} else {
// Queue the delete of the message if requested
dispatch((new DeleteChat($this->o,$response->ts))->onQueue('slack')->delay($delete));
}
} }
return $response; return $response;
@ -168,24 +205,21 @@ final class Message extends BlockKit
* *
* @note This URL can only be used 5 times in 30 minutes * @note This URL can only be used 5 times in 30 minutes
* @param string $url * @param string $url
* @return string * @param Carbon|null $delete
* @return object
* @throws SlackException * @throws SlackException
*/ */
public function respond(string $url) public function respond(string $url,Carbon $delete=NULL): object
{ {
$request = curl_init(); if (! $delete && $this->selfdestruct)
$delete = $this->selfdestruct;
curl_setopt($request,CURLOPT_URL,$url); $http = Http::acceptJson();
curl_setopt($request,CURLOPT_RETURNTRANSFER,TRUE); $http->withBody(json_encode($this),'application/json');
curl_setopt($request,CURLINFO_HEADER_OUT,TRUE);
curl_setopt($request,CURLOPT_HTTPHEADER,['Content-Type: application/json; charset=utf-8']);
curl_setopt($request,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($request,CURLOPT_POSTFIELDS,json_encode($this));
try { try {
$result = curl_exec($request); $request = $http->post($url)->throw();
if (! $result) $response = $request->object();
throw new \Exception('CURL exec returned an empty response: '.serialize(curl_getinfo($request)));
} catch (\Exception $e) { } catch (\Exception $e) {
Log::error(sprintf('%s:Got an error while posting to [%s] (%s)',static::LOGKEY,$url,$e->getMessage()),['m'=>__METHOD__]); Log::error(sprintf('%s:Got an error while posting to [%s] (%s)',static::LOGKEY,$url,$e->getMessage()),['m'=>__METHOD__]);
@ -193,16 +227,19 @@ final class Message extends BlockKit
throw new \Exception($e->getMessage()); throw new \Exception($e->getMessage());
} }
if ($result !== 'ok') { if (! $response->ok) {
switch ($result) { Log::critical(sprintf('%s:Generic Error',static::LOGKEY),['m'=>__METHOD__,'r'=>$response]);
default: throw new SlackException(serialize($response),$request->status());
Log::critical(sprintf('%s:Generic Error',static::LOGKEY),['m'=>__METHOD__,'r'=>$result]);
throw new SlackException($result,curl_getinfo($request,CURLINFO_HTTP_CODE));
}
} }
curl_close($request); if ($delete) {
return $result; Log::debug(sprintf('%s:Scheduling Delete of [%s:%s] on [%s]',static::LOGKEY,object_get($this->o,'channel_id',$this->o->id),$url,$delete->format('Y-m-d')),['m'=>__METHOD__]);
// Queue the delete of the response if requested
dispatch((new DeleteResponse($url))->onQueue('slack')->delay($delete));
}
return $response;
} }
/** /**
@ -226,6 +263,7 @@ final class Message extends BlockKit
* *
* @param Carbon $time * @param Carbon $time
* @return Message * @return Message
* @throws SlackSyntaxException
*/ */
public function selfdestruct(Carbon $time): self public function selfdestruct(Carbon $time): self
{ {
@ -236,6 +274,7 @@ final class Message extends BlockKit
); );
$this->selfdestruct = $time; $this->selfdestruct = $time;
return $this; return $this;
} }
@ -267,11 +306,18 @@ final class Message extends BlockKit
return $this; return $this;
} }
// This is a helper method
public function spacer(): self
{
$this->blocks->push(Section::item(Text::item(' ')));
return $this;
}
/* CONFIGURATION METHODS */ /* CONFIGURATION METHODS */
/** /**
* @return Message * @return Message
* @todo - Check this is a valid option
*/ */
public function ephemeral(): self public function ephemeral(): self
{ {
@ -308,8 +354,10 @@ final class Message extends BlockKit
} }
/** /**
* Replace the original message
*
* @param bool $bool
* @return Message * @return Message
* @todo - Check this is a valid option
*/ */
public function replace_original(bool $bool=TRUE): self public function replace_original(bool $bool=TRUE): self
{ {
@ -370,84 +418,29 @@ final class Message extends BlockKit
return $this; return $this;
} }
/**
* Post the message to user
*
* @param string $user
* @return $this
*/
public function user(string $user): self
{
$this->user = $user;
return $this;
}
/**
* Set bot username (used with as_user)
*
* @param string $user
* @return $this
*/
public function username(string $user): self public function username(string $user): self
{ {
$this->username = $user; $this->username = $user;
return $this; return $this;
} }
/**
* Add an attachment to a message
*
* @param Attachment $attachments
* @return Message
* @deprecated use addAttachment()
*/
public function setAttachments(Attachment $attachments): self
{
$this->_data->put('attachments',[$attachments]);
return $this;
}
/**
* Add blocks to the message
*
* @param Blocks $blocks
* @return Message
* @throws \Exception
* @deprecated use addBlocks()
*/
public function setBlocks(Blocks $blocks): self
{
if ($this->blocks->count())
throw new \Exception('Blocks already defined');
$this->blocks = $blocks;
return $this;
}
/**
* Message text
*
* @param string $string
* @return Message
* @deprecated use text()
*/
public function setText(string $string): self
{
$this->text = $string;
return $this;
}
/**
* Set the timestamp, used when replacing messages
*
* @param string $string
* @return Message
* @deprecated use ts()
*/
public function setTS(string $string): self
{
$this->_data->put('ts',$string);
return $this;
}
/**
* Set the thread timestamp, used when adding a threaded response
*
* @param string $string
* @return Message
* @deprecated use thead_ts()
*/
public function setThreadTS(string $string): self
{
$this->_data->put('thread_ts',$string);
return $this;
}
} }

View File

@ -2,7 +2,6 @@
namespace Slack\Message; namespace Slack\Message;
use \Exception;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Slack\BlockKit; use Slack\BlockKit;
@ -10,7 +9,7 @@ use Slack\Blockkit\Blocks;
final class Attachment extends BlockKit final class Attachment extends BlockKit
{ {
protected const LIMITS = [ public const LIMITS = [
'footer' => 300, 'footer' => 300,
]; ];

View File

@ -4,10 +4,11 @@ namespace Slack\Message;
use Slack\BlockKit; use Slack\BlockKit;
use Slack\Blockkit\Blocks\Elements\Text; use Slack\Blockkit\Blocks\Elements\Text;
use Slack\Exceptions\SlackSyntaxException;
/** /**
* Class MessageAttachmentAction - Slack Message Attachments Actions * Class MessageAttachmentAction - Slack Message Attachments Actions
* Represents an Single Action for a Slack Message Attachment * Represents a Single Action for a Slack Message Attachment
* *
* @package App\Slack\Message * @package App\Slack\Message
* @note These are now legacy - use blocks instead * @note These are now legacy - use blocks instead
@ -15,14 +16,14 @@ use Slack\Blockkit\Blocks\Elements\Text;
*/ */
final class AttachmentAction extends BlockKit final class AttachmentAction extends BlockKit
{ {
protected const LIMITS = [ public const LIMITS = [
'text' => 30, 'text' => 30,
'value' => 2000, 'value' => 2000,
]; ];
private const DATA_SOURCES = ['static','users','channels','conversastions','external']; public const DATA_SOURCES = ['static','users','channels','conversastions','external'];
private const STYLES = ['default','danger','primary']; public const STYLES = ['default','danger','primary'];
private const TYPES = ['button','select']; public const TYPES = ['button','select'];
// @todo options // @todo options
// @todo option_groups // @todo option_groups
@ -33,7 +34,7 @@ final class AttachmentAction extends BlockKit
parent::__construct(); parent::__construct();
if (! in_array($type,self::TYPES)) if (! in_array($type,self::TYPES))
throw new Exception(sprintf('Type [%s] not valid',$type)); throw new SlackSyntaxException(sprintf('Type [%s] not valid',$type));
$this->type = $type; $this->type = $type;
switch ($type) { switch ($type) {
@ -80,7 +81,7 @@ final class AttachmentAction extends BlockKit
public function data_source(string $string): self public function data_source(string $string): self
{ {
if (! in_array($string,self::DATA_SOURCES)) if (! in_array($string,self::DATA_SOURCES))
throw new Exception(sprintf('Type [%s] not valid',$string)); throw new SlackSyntaxException(sprintf('Type [%s] not valid',$string));
$this->data_source = $string; $this->data_source = $string;
@ -97,13 +98,14 @@ final class AttachmentAction extends BlockKit
/** /**
* Set the text displayed in the action * Set the text displayed in the action
* *
* @param string $type * @param string $string
* @return $this * @return $this
* @throws SlackSyntaxException
*/ */
public function style(string $string): self public function style(string $string): self
{ {
if (! in_array($string,self::STYLES)) if (! in_array($string,self::STYLES))
throw new Exception(sprintf('Type [%s] not valid',$string)); throw new SlackSyntaxException(sprintf('Type [%s] not valid',$string));
$this->style = $string; $this->style = $string;
@ -115,6 +117,7 @@ final class AttachmentAction extends BlockKit
* *
* @param string $string * @param string $string
* @return $this * @return $this
* @throws SlackSyntaxException
*/ */
public function value(string $string): self public function value(string $string): self
{ {

View File

@ -4,9 +4,9 @@ namespace Slack\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Slack\Traits\ScopeActive;
use App\Models\Slack\Team as AppTeam; use App\Models\Slack\Team as AppTeam;
use Slack\Traits\ScopeActive;
class Channel extends Model class Channel extends Model
{ {

View File

@ -14,7 +14,7 @@ class Team extends Model
{ {
use ScopeActive; use ScopeActive;
protected $fillable = ['team_id']; protected $fillable = ['team_id','active'];
protected $table = 'slack_teams'; protected $table = 'slack_teams';
/* RELATIONS */ /* RELATIONS */

View File

@ -61,8 +61,6 @@ class User extends Model
*/ */
public function getUserTeamAttribute(): ?Team public function getUserTeamAttribute(): ?Team
{ {
Log::debug(sprintf('%s:User [%s], Team [%s], Enterprise [%s]',self::LOGKEY,$this->id,$this->team_id,$this->enterprise_id),['m'=>__METHOD__]);
return $this->team_id ? $this->team : (($x=$this->enterprise->teams) ? $x->first() : NULL); return $this->team_id ? $this->team : (($x=$this->enterprise->teams) ? $x->first() : NULL);
} }

View File

@ -5,6 +5,7 @@ namespace Slack\Options;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Slack\Base as SlackBase; use Slack\Base as SlackBase;
use Slack\Exceptions\SlackException;
use Slack\Message; use Slack\Message;
abstract class Base extends SlackBase abstract class Base extends SlackBase
@ -35,47 +36,42 @@ abstract class Base extends SlackBase
* + user_id * + user_id
* @param string $key * @param string $key
* @return mixed|object * @return mixed|object
* @throws \Exception
*/ */
public function __get(string $key) public function __get(string $key)
{ {
switch ($key) { switch ($key) {
case 'team_id':
return object_get($this->_data,'team.id');
case 'channel_id': case 'channel_id':
return object_get($this->_data,'channel.id'); return object_get($this->_data,'channel.id');
case 'enterprise_id': case 'enterprise_id':
return object_get($this->_data,'team.'.$key); return object_get($this->_data,'team.'.$key);
case 'team_id':
return object_get($this->_data,'team.id');
case 'user_id': case 'user_id':
return object_get($this->_data,'user.id'); return object_get($this->_data,'user.id');
case 'callback_key':
return $this->keyitem('id',$this->callback_id);
case 'callback_value':
return $this->keyitem('value',$this->callback_id);
case 'callback_id': case 'callback_id':
//case 'action_ts': //case 'action_ts':
//case 'message_ts': //case 'message_ts':
case 'type': case 'type':
return object_get($this->_data,$key); return object_get($this->_data,$key);
default:
throw new SlackException('Unknown key: '.$key);
} }
} }
/** /**
* Interactive messages can return their output in the incoming HTTP post * Interactive messages can return their output in the incoming HTTP post.
* This function should be overwritten in the parent class, and finish by calling return Message::blank();
* *
* @return Message * @return Message
* @throws \Exception
*/ */
public function respond(): Message abstract public function respond(): Message;
{
Log::info(sprintf('%s:Interactive Option - Callback [%s] Name [%s] Value [%s]',self::LOGKEY,$this->callback_id,$this->name,$this->value),['m'=>__METHOD__]);
if (preg_match('/^(.*)\|([0-9]+)/',$this->callback_id)) {
[$action,$id] = explode('|',$this->callback_id,2);
} else {
// If we get here, its an action that we dont know about.
Log::notice(sprintf('%s:Unhandled CALLBACK [%s]',self::LOGKEY,$this->callback_id),['m'=>__METHOD__]);
}
return Message::blank();
}
} }

View File

@ -126,7 +126,7 @@ use Slack\Message;
) )
) )
*/ */
class BlockSuggestion extends Base abstract class BlockSuggestion extends Base
{ {
protected const LOGKEY = 'OIM'; protected const LOGKEY = 'OIM';

View File

@ -2,7 +2,6 @@
namespace Slack\Options; namespace Slack\Options;
use Illuminate\Http\Request;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@ -26,14 +25,16 @@ class Factory {
* @param string $type * @param string $type
* @param array $request * @param array $request
* @return Base * @return Base
* @note Options classes must be defined in the calling application and extend these self::map abstract classes
* otherwise errors will be thrown
*/ */
public static function create(string $type,array $request) public static function create(string $type,array $request)
{ {
$class = Arr::get(config('slack.options',self::map),$type,Unknown::class); $class = Arr::get(config('slack.options',self::map),$type,Unknown::class);
Log::debug(sprintf('%s:Working out Interactive Options Event Class for [%s] as [%s]',static::LOGKEY,$type,$class),['m'=>__METHOD__]); Log::debug(sprintf('%s:Working out Interactive Options Event Class for [%s] as [%s]',static::LOGKEY,$type,$class),['m'=>__METHOD__]);
if (App::environment() == 'dev') if (App::environment() == 'local')
file_put_contents('/tmp/option.'.$type,print_r(json_decode($request->input('payload')),TRUE)); file_put_contents('/tmp/option.'.$type,print_r($request,TRUE));
return new $class($request); return new $class($request);
} }

View File

@ -33,7 +33,7 @@ use Slack\Message;
* [attachment_id] => 3 * [attachment_id] => 3
* [token] => Oow8S2EFvrZoS9z8N4nwf9Jo * [token] => Oow8S2EFvrZoS9z8N4nwf9Jo
*/ */
class InteractiveMessage extends Base abstract class InteractiveMessage extends Base
{ {
protected const LOGKEY = 'OIM'; protected const LOGKEY = 'OIM';
@ -49,25 +49,4 @@ class InteractiveMessage extends Base
return parent::__get($key); return parent::__get($key);
} }
} }
/**
* Interactive messages can return their output in the incoming HTTP post
*
* @return Message
* @throws \Exception
*/
public function respond(): Message
{
Log::info(sprintf('%s:Interactive Option - Callback [%s] Name [%s] Value [%s]',static::LOGKEY,$this->callback_id,$this->name,$this->value),['m'=>__METHOD__]);
if (preg_match('/^(.*)\|([0-9]+)/',$this->callback_id)) {
[$action,$id] = explode('|',$this->callback_id,2);
} else {
// If we get here, its an action that we dont know about.
Log::notice(sprintf('%s:Unhandled CALLBACK [%s]',static::LOGKEY,$this->callback_id),['m'=>__METHOD__]);
}
return (new Message)->blank();
}
} }

View File

@ -4,6 +4,8 @@ namespace Slack\Options;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Slack\Message;
/** /**
* Catch all unknown slack event that we havent specifically programmed for. * Catch all unknown slack event that we havent specifically programmed for.
* *
@ -11,10 +13,26 @@ use Illuminate\Support\Facades\Log;
*/ */
final class Unknown extends Base final class Unknown extends Base
{ {
protected const LOGKEY = 'OU-';
public function __construct(array $request) public function __construct(array $request)
{ {
Log::notice(sprintf('SOU:UNKNOWN Slack Interaction Option received [%s]',get_class($this)),['m'=>__METHOD__]); Log::notice(sprintf('SOU:UNKNOWN Slack Interaction Option received [%s]',get_class($this)),['m'=>__METHOD__]);
parent::__construct($request); parent::__construct($request);
} }
/**
* Interactive messages can return their output in the incoming HTTP post
*
* @return Message
*/
public function respond(): Message
{
Log::info(sprintf('%s:Unknown Option - Callback [%s] Name [%s] Value [%s]',static::LOGKEY,$this->callback_id,$this->name,$this->value),['m'=>__METHOD__]);
Log::notice(sprintf('%s:Unhandled CALLBACK [%s]',static::LOGKEY,$this->callback_id),['m'=>__METHOD__]);
return Message::blank();
}
} }

View File

@ -29,7 +29,7 @@ abstract class Base extends SlackBase implements \JsonSerializable
if (get_class($this) == Base::class) { if (get_class($this) == Base::class) {
Log::debug(sprintf('%s:Slack RESPONSE Initialised [%s]',static::LOGKEY,get_class($this)),['m'=>__METHOD__]); Log::debug(sprintf('%s:Slack RESPONSE Initialised [%s]',static::LOGKEY,get_class($this)),['m'=>__METHOD__]);
if (App::environment() == 'dev') if (App::environment() == 'local')
file_put_contents('/tmp/response',print_r($this,TRUE),FILE_APPEND); file_put_contents('/tmp/response',print_r($this,TRUE),FILE_APPEND);
} }
} }
@ -55,8 +55,9 @@ abstract class Base extends SlackBase implements \JsonSerializable
case 'scheduled_messages': // Used by scheduledMessagesList() case 'scheduled_messages': // Used by scheduledMessagesList()
return collect(object_get($this->_data,$key)); return collect(object_get($this->_data,$key));
case 'team_id':
case 'ts': case 'ts':
return object_get($this->_data,$key) ?: object_get($this->_data,'message_ts');
case 'team_id':
case 'user_id': case 'user_id':
case 'type': // Needed by URL verification case 'type': // Needed by URL verification
return object_get($this->_data,$key); return object_get($this->_data,$key);