309 lines
6.3 KiB
PHP
309 lines
6.3 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace Slack;
|
||
|
|
||
|
use Carbon\Carbon;
|
||
|
use Carbon\CarbonInterface;
|
||
|
use Illuminate\Database\Eloquent\Model;
|
||
|
use Illuminate\Support\Facades\Log;
|
||
|
use Slack\Jobs\DeleteChat;
|
||
|
use Slack\Models\{Channel,User};
|
||
|
use Slack\Blockkit\Block;
|
||
|
use Slack\Exceptions\SlackException;
|
||
|
use Slack\Message\Attachment;
|
||
|
use Slack\Response\Generic;
|
||
|
|
||
|
/**
|
||
|
* This class is used when composing a message to send to Slack.
|
||
|
*/
|
||
|
class Message implements \JsonSerializable
|
||
|
{
|
||
|
protected const LOGKEY = 'SM-';
|
||
|
|
||
|
private $o;
|
||
|
private $attachments;
|
||
|
private $blocks;
|
||
|
|
||
|
/**
|
||
|
* Message constructor.
|
||
|
*
|
||
|
* @param Model|null $o Who the message will be to - Channel or User
|
||
|
*/
|
||
|
public function __construct(Model $o=NULL)
|
||
|
{
|
||
|
$this->_data = collect();
|
||
|
|
||
|
// Message is to a channel
|
||
|
if ($o instanceof Channel) {
|
||
|
$this->setChannel($o);
|
||
|
|
||
|
// Message is to a user
|
||
|
} elseif ($o instanceof User) {
|
||
|
$this->setUser($o);
|
||
|
}
|
||
|
|
||
|
$this->o = $o;
|
||
|
$this->attachments = collect();
|
||
|
$this->blocks = collect();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add an attachment to a message
|
||
|
*
|
||
|
* @param Attachment $attachment
|
||
|
* @return Message
|
||
|
*/
|
||
|
public function addAttachment(Attachment $attachment): self
|
||
|
{
|
||
|
$this->attachments->push($attachment);
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add a block to the message
|
||
|
*
|
||
|
* @param BlockKit $block
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function addBlock(BlockKit $block): self
|
||
|
{
|
||
|
$this->blocks->push($block);
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Empty the message
|
||
|
*
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function blank(): self
|
||
|
{
|
||
|
$this->_data = collect();
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* @todo This doesnt appear to work
|
||
|
public function ephemeral(): self
|
||
|
{
|
||
|
$this->_data->put('ephemeral',TRUE);
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
public function forgetTS(): self
|
||
|
{
|
||
|
$this->_data->forget('ts');
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return if this is an empty message
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function isEmpty(): bool
|
||
|
{
|
||
|
return $this->jsonSerialize() ? FALSE : TRUE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* When we json_encode this object, this is the data that will be returned
|
||
|
*/
|
||
|
public function jsonSerialize()
|
||
|
{
|
||
|
if ($this->blocks->count()) {
|
||
|
if ($this->_data->has('text'))
|
||
|
throw new \Exception('Messages cannot have text and blocks!');
|
||
|
|
||
|
$this->_data->put('blocks',$this->blocks);
|
||
|
}
|
||
|
|
||
|
if ($this->attachments->count())
|
||
|
$this->_data->put('attachments',$this->attachments);
|
||
|
|
||
|
// For interactive messages that generate a dialog, we need to return NULL
|
||
|
return $this->_data->count() ? $this->_data : NULL;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Post this message to slack
|
||
|
*
|
||
|
* @param Carbon|null $delete
|
||
|
* @return Generic
|
||
|
* @throws \Exception
|
||
|
*/
|
||
|
public function post(Carbon $delete=NULL): Generic
|
||
|
{
|
||
|
if ($this->_data->has('ephemeral'))
|
||
|
abort('500','Cannot post ephemeral messages.');
|
||
|
|
||
|
$api = $this->o->team->slackAPI();
|
||
|
$response = $this->_data->has('ts') ? $api->updateMessage($this) : $api->postMessage($this);
|
||
|
|
||
|
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__]);
|
||
|
|
||
|
// Queue the delete of the message if requested
|
||
|
dispatch((new DeleteChat($this->o,$response->ts))->onQueue('low')->delay($delete));
|
||
|
}
|
||
|
|
||
|
return $response;
|
||
|
}
|
||
|
|
||
|
public function replace(bool $replace=TRUE): self
|
||
|
{
|
||
|
$this->_data['replace_original'] = $replace ? 'true' : 'false';
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Post a message to slack using the respond_url
|
||
|
* @note This URL can only be used 5 times in 30 minutes
|
||
|
*
|
||
|
* @param string $url
|
||
|
*/
|
||
|
public function respond(string $url)
|
||
|
{
|
||
|
$request = curl_init();
|
||
|
|
||
|
curl_setopt($request,CURLOPT_URL,$url);
|
||
|
curl_setopt($request,CURLOPT_RETURNTRANSFER,TRUE);
|
||
|
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 {
|
||
|
$result = curl_exec($request);
|
||
|
if (! $result)
|
||
|
throw new \Exception('CURL exec returned an empty response: '.serialize(curl_getinfo($request)));
|
||
|
|
||
|
} catch (\Exception $e) {
|
||
|
Log::error(sprintf('%s:Got an error while posting to [%s] (%s)',static::LOGKEY,$url,$e->getMessage()),['m'=>__METHOD__]);
|
||
|
|
||
|
throw new \Exception($e->getMessage());
|
||
|
}
|
||
|
|
||
|
if ($result !== 'ok') {
|
||
|
switch ($result) {
|
||
|
default:
|
||
|
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);
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Make the message self destruct
|
||
|
*
|
||
|
* @param Carbon $time
|
||
|
* @return Generic
|
||
|
* @throws \Exception
|
||
|
*/
|
||
|
public function selfdestruct(Carbon $time): Generic
|
||
|
{
|
||
|
$this->addBlock(
|
||
|
(new Block)->addContext(
|
||
|
collect()
|
||
|
->push((new BlockKit)->text(sprintf('This message will self destruct in %s...',$time->diffForHumans(Carbon::now(),['syntax' => CarbonInterface::DIFF_RELATIVE_TO_NOW]))))));
|
||
|
|
||
|
return $this->post($time);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set our channel
|
||
|
*
|
||
|
* @param Channel $o
|
||
|
* @return Message
|
||
|
*/
|
||
|
public function setChannel(Channel $o)
|
||
|
{
|
||
|
$this->_data['channel'] = $o->channel_id;
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the icon next to the message
|
||
|
*
|
||
|
* @param string $icon
|
||
|
* @return $this
|
||
|
* @deprecated
|
||
|
*/
|
||
|
public function setIcon(string $icon): self
|
||
|
{
|
||
|
$this->_data->put('icon_emoji',$icon);
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Option groups are used by the interactive Options controller and hold no other attributes
|
||
|
*
|
||
|
* @param array $array
|
||
|
* @return void
|
||
|
*/
|
||
|
public function setOptionGroup(array $array): void
|
||
|
{
|
||
|
$this->_data = collect();
|
||
|
$this->_data->put('option_groups',$array);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Message text
|
||
|
*
|
||
|
* @param string $string
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function setText(string $string): self
|
||
|
{
|
||
|
$this->_data->put('text',$string);
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function setTS(string $string): self
|
||
|
{
|
||
|
$this->_data->put('ts',$string);
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function setThreadTS(string $string): self
|
||
|
{
|
||
|
$this->_data->put('thread_ts',$string);
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set our channel
|
||
|
*
|
||
|
* @param User $o
|
||
|
* @return Message
|
||
|
*/
|
||
|
public function setUser(User $o)
|
||
|
{
|
||
|
$this->_data['channel'] = $o->user_id;
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function setUserName(string $user)
|
||
|
{
|
||
|
$this->_data['username'] = $user;
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
}
|