2023-06-28 15:50:38 +02:00
|
|
|
<?php declare(strict_types=1);
|
|
|
|
|
|
|
|
namespace danog\MadelineProto\EventHandler;
|
|
|
|
|
2023-07-01 19:14:53 +02:00
|
|
|
use danog\MadelineProto\EventHandler\Keyboard\InlineKeyboard;
|
|
|
|
use danog\MadelineProto\EventHandler\Keyboard\ReplyKeyboard;
|
2023-07-05 21:28:17 +02:00
|
|
|
use danog\MadelineProto\EventHandler\Media\Audio;
|
|
|
|
use danog\MadelineProto\EventHandler\Media\Document;
|
|
|
|
use danog\MadelineProto\EventHandler\Media\DocumentPhoto;
|
|
|
|
use danog\MadelineProto\EventHandler\Media\Gif;
|
|
|
|
use danog\MadelineProto\EventHandler\Media\MaskSticker;
|
|
|
|
use danog\MadelineProto\EventHandler\Media\Photo;
|
|
|
|
use danog\MadelineProto\EventHandler\Media\RoundVideo;
|
|
|
|
use danog\MadelineProto\EventHandler\Media\Sticker;
|
|
|
|
use danog\MadelineProto\EventHandler\Media\Video;
|
|
|
|
use danog\MadelineProto\EventHandler\Media\Voice;
|
2023-06-28 15:50:38 +02:00
|
|
|
use danog\MadelineProto\MTProto;
|
2023-07-01 14:39:33 +02:00
|
|
|
use danog\MadelineProto\StrTools;
|
2023-06-28 15:50:38 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents an incoming or outgoing message.
|
|
|
|
*/
|
2023-07-07 22:06:32 +02:00
|
|
|
abstract class Message extends AbstractMessage
|
2023-06-28 15:50:38 +02:00
|
|
|
{
|
|
|
|
/** Content of the message */
|
|
|
|
public readonly string $message;
|
|
|
|
|
2023-07-25 22:36:55 +02:00
|
|
|
protected bool $reactionsCached = false;
|
2023-07-25 19:07:27 +02:00
|
|
|
|
|
|
|
/** @var list<int|string> list of our message reactions */
|
2023-07-25 22:36:55 +02:00
|
|
|
private ?array $reactions = [];
|
2023-07-25 19:07:27 +02:00
|
|
|
|
2023-07-01 19:06:30 +02:00
|
|
|
/** Info about a forwarded message */
|
|
|
|
public readonly ?ForwardedInfo $fwdInfo;
|
|
|
|
|
2023-07-05 22:00:57 +02:00
|
|
|
/** Bot command (if present) */
|
|
|
|
public readonly ?string $command;
|
2023-07-10 10:12:46 +02:00
|
|
|
/** Bot command type (if present) */
|
|
|
|
public readonly ?CommandType $commandType;
|
2023-07-05 22:00:57 +02:00
|
|
|
/** @var list<string> Bot command arguments (if present) */
|
|
|
|
public readonly ?array $commandArgs;
|
|
|
|
|
2023-07-11 19:43:31 +02:00
|
|
|
/** Whether this message is protected */
|
|
|
|
public readonly bool $protected;
|
|
|
|
|
2023-07-05 22:00:57 +02:00
|
|
|
/**
|
|
|
|
* @readonly
|
|
|
|
*
|
|
|
|
* @var list<string> Regex matches, if a filter regex is present
|
|
|
|
*/
|
|
|
|
public ?array $matches = null;
|
|
|
|
|
2023-07-05 21:28:17 +02:00
|
|
|
/**
|
|
|
|
* Attached media.
|
|
|
|
*/
|
2023-07-14 20:15:04 +02:00
|
|
|
public readonly Audio|Document|DocumentPhoto|Gif|MaskSticker|Photo|RoundVideo|Sticker|Video|Voice|null $media;
|
2023-07-04 19:48:23 +02:00
|
|
|
|
2023-06-28 15:50:38 +02:00
|
|
|
/** Whether this message is a sent scheduled message */
|
|
|
|
public readonly bool $fromScheduled;
|
2023-07-07 22:06:32 +02:00
|
|
|
|
2023-07-01 13:04:59 +02:00
|
|
|
/** If the message was generated by an inline query, ID of the bot that generated it */
|
2023-06-28 15:50:38 +02:00
|
|
|
public readonly ?int $viaBotId;
|
|
|
|
|
2023-07-01 13:04:59 +02:00
|
|
|
/** Last edit date of the message */
|
|
|
|
public readonly ?int $editDate;
|
|
|
|
|
2023-07-01 17:53:29 +02:00
|
|
|
/** Inline or reply keyboard. */
|
2023-07-01 19:14:53 +02:00
|
|
|
public readonly InlineKeyboard|ReplyKeyboard|null $keyboard;
|
2023-07-01 14:39:33 +02:00
|
|
|
|
2023-07-01 19:06:30 +02:00
|
|
|
/** Whether this message was [imported from a foreign chat service](https://core.telegram.org/api/import) */
|
|
|
|
public readonly bool $imported;
|
|
|
|
|
|
|
|
/** For Public Service Announcement messages, the PSA type */
|
2023-07-08 21:42:18 +02:00
|
|
|
public readonly ?string $psaType;
|
2023-07-01 19:06:30 +02:00
|
|
|
|
2023-07-14 20:51:06 +02:00
|
|
|
/** @readonly For sent messages, contains the next message in the chain if the original message had to be split. */
|
|
|
|
public ?self $nextSent = null;
|
|
|
|
// Todo media (photosizes, thumbs), albums, reactions, games eventually
|
2023-06-28 15:50:38 +02:00
|
|
|
|
|
|
|
/** @internal */
|
2023-07-04 19:48:23 +02:00
|
|
|
public function __construct(
|
2023-06-28 15:50:38 +02:00
|
|
|
MTProto $API,
|
2023-07-04 18:19:06 +02:00
|
|
|
array $rawMessage,
|
2023-07-13 16:54:57 +02:00
|
|
|
array $info,
|
2023-06-28 15:50:38 +02:00
|
|
|
) {
|
2023-07-13 16:54:57 +02:00
|
|
|
parent::__construct($API, $rawMessage, $info);
|
2023-06-28 15:50:38 +02:00
|
|
|
|
2023-07-01 19:06:30 +02:00
|
|
|
$this->entities = $rawMessage['entities'] ?? null;
|
2023-07-07 22:06:32 +02:00
|
|
|
$this->message = $rawMessage['message'];
|
2023-06-28 15:50:38 +02:00
|
|
|
$this->fromScheduled = $rawMessage['from_scheduled'];
|
|
|
|
$this->viaBotId = $rawMessage['via_bot_id'] ?? null;
|
2023-07-01 13:04:59 +02:00
|
|
|
$this->editDate = $rawMessage['edit_date'] ?? null;
|
2023-07-01 17:53:29 +02:00
|
|
|
|
|
|
|
$this->keyboard = isset($rawMessage['reply_markup'])
|
|
|
|
? Keyboard::fromRawReplyMarkup($rawMessage['reply_markup'])
|
|
|
|
: null;
|
2023-07-01 18:45:35 +02:00
|
|
|
|
2023-07-01 19:06:30 +02:00
|
|
|
if (isset($rawMessage['fwd_from'])) {
|
|
|
|
$fwdFrom = $rawMessage['fwd_from'];
|
|
|
|
$this->fwdInfo = new ForwardedInfo(
|
|
|
|
$fwdFrom['date'],
|
|
|
|
isset($fwdFrom['from_id'])
|
2023-07-25 18:44:30 +02:00
|
|
|
? $this->getClient()->getIdInternal($fwdFrom['from_id'])
|
2023-07-01 19:06:30 +02:00
|
|
|
: null,
|
|
|
|
$fwdFrom['from_name'] ?? null,
|
|
|
|
$fwdFrom['channel_post'] ?? null,
|
|
|
|
$fwdFrom['post_author'] ?? null,
|
|
|
|
isset($fwdFrom['saved_from_peer'])
|
2023-07-25 18:44:30 +02:00
|
|
|
? $this->getClient()->getIdInternal($fwdFrom['saved_from_peer'])
|
2023-07-01 19:06:30 +02:00
|
|
|
: null,
|
|
|
|
$fwdFrom['saved_from_msg_id'] ?? null
|
|
|
|
);
|
|
|
|
$this->psaType = $fwdFrom['psa_type'] ?? null;
|
|
|
|
} else {
|
|
|
|
$this->fwdInfo = null;
|
|
|
|
$this->psaType = null;
|
|
|
|
}
|
2023-07-05 21:28:17 +02:00
|
|
|
|
2023-07-11 19:43:31 +02:00
|
|
|
$this->protected = $rawMessage['noforwards'];
|
|
|
|
|
2023-07-05 21:28:17 +02:00
|
|
|
$this->media = isset($rawMessage['media'])
|
2023-07-11 19:43:31 +02:00
|
|
|
? $API->wrapMedia($rawMessage['media'], $this->protected)
|
2023-07-05 21:28:17 +02:00
|
|
|
: null;
|
2023-07-05 22:00:57 +02:00
|
|
|
|
2023-07-10 10:12:46 +02:00
|
|
|
if (\in_array($this->message[0] ?? '', ['/', '.', '!'], true)) {
|
|
|
|
$space = \strpos($this->message, ' ', 1) ?: \strlen($this->message);
|
|
|
|
$this->command = \substr($this->message, 1, $space-1);
|
2023-07-21 19:21:34 +02:00
|
|
|
$args = \explode(
|
2023-07-05 22:00:57 +02:00
|
|
|
' ',
|
2023-07-10 10:12:46 +02:00
|
|
|
\substr($this->message, $space+1)
|
2023-07-05 22:00:57 +02:00
|
|
|
);
|
2023-07-21 19:21:34 +02:00
|
|
|
$this->commandArgs = $args === [''] ? [] : $args;
|
2023-07-10 10:12:46 +02:00
|
|
|
$this->commandType = match ($this->message[0]) {
|
|
|
|
'.' => CommandType::DOT,
|
|
|
|
'/' => CommandType::SLASH,
|
|
|
|
'!' => CommandType::BANG,
|
|
|
|
};
|
2023-07-05 22:00:57 +02:00
|
|
|
} else {
|
|
|
|
$this->command = null;
|
|
|
|
$this->commandArgs = null;
|
2023-07-10 10:12:46 +02:00
|
|
|
$this->commandType = null;
|
2023-07-05 22:00:57 +02:00
|
|
|
}
|
2023-07-01 14:39:33 +02:00
|
|
|
}
|
|
|
|
|
2023-07-25 10:43:56 +02:00
|
|
|
/**
|
2023-07-25 22:36:55 +02:00
|
|
|
* Pin a message.
|
2023-07-25 10:43:56 +02:00
|
|
|
*
|
|
|
|
* @param bool $pmOneside Whether the message should only be pinned on the local side of a one-to-one chat
|
|
|
|
* @param bool $silent Pin the message silently, without triggering a notification
|
|
|
|
*/
|
2023-07-25 22:36:55 +02:00
|
|
|
public function pin(bool $pmOneside = false, bool $silent = false): ?AbstractMessage
|
2023-07-25 10:43:56 +02:00
|
|
|
{
|
2023-07-25 19:07:27 +02:00
|
|
|
$result = $this->getClient()->methodCallAsyncRead(
|
2023-07-25 10:43:56 +02:00
|
|
|
'messages.updatePinnedMessage',
|
|
|
|
[
|
|
|
|
'peer' => $this->chatId,
|
|
|
|
'id' => $this->id,
|
|
|
|
'pm_oneside' => $pmOneside,
|
|
|
|
'silent' => $silent,
|
|
|
|
'unpin' => false
|
|
|
|
]
|
|
|
|
);
|
2023-07-25 19:07:27 +02:00
|
|
|
return $this->getClient()->wrapMessage($this->getClient()->extractMessage($result));
|
2023-07-25 10:43:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-07-25 22:36:55 +02:00
|
|
|
* Unpin a message.
|
2023-07-25 10:43:56 +02:00
|
|
|
*
|
|
|
|
* @param bool $pmOneside Whether the message should only be pinned on the local side of a one-to-one chat
|
|
|
|
* @param bool $silent Pin the message silently, without triggering a notification
|
|
|
|
*/
|
2023-07-25 22:36:55 +02:00
|
|
|
public function unpin(bool $pmOneside = false, bool $silent = false): ?Update
|
2023-07-25 10:43:56 +02:00
|
|
|
{
|
2023-07-25 19:07:27 +02:00
|
|
|
$result = $this->getClient()->methodCallAsyncRead(
|
2023-07-25 10:43:56 +02:00
|
|
|
'messages.updatePinnedMessage',
|
|
|
|
[
|
|
|
|
'peer' => $this->chatId,
|
|
|
|
'id' => $this->id,
|
|
|
|
'pm_oneside' => $pmOneside,
|
|
|
|
'silent' => $silent,
|
|
|
|
'unpin' => true
|
|
|
|
]
|
|
|
|
);
|
2023-07-25 19:07:27 +02:00
|
|
|
return $this->getClient()->wrapUpdate($result);
|
2023-07-25 10:43:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-07-25 22:36:55 +02:00
|
|
|
* Get our reaction on message return null if message deleted.
|
2023-07-25 10:43:56 +02:00
|
|
|
*
|
2023-07-25 19:07:27 +02:00
|
|
|
* @return list<string|int>|null
|
|
|
|
*/
|
|
|
|
public function getReactions(): ?array
|
|
|
|
{
|
2023-07-25 22:36:55 +02:00
|
|
|
if (!$this->reactionsCached) {
|
2023-07-25 19:07:27 +02:00
|
|
|
$this->reactionsCached = true;
|
|
|
|
$me = $this->getClient()->getSelf()['id'];
|
2023-07-25 22:36:55 +02:00
|
|
|
$myReactions = \array_filter(
|
2023-07-25 19:07:27 +02:00
|
|
|
$this->getClient()->methodCallAsyncRead(
|
|
|
|
'messages.getMessageReactionsList',
|
|
|
|
[
|
|
|
|
'peer' => $this->chatId,
|
|
|
|
'id' => $this->id
|
|
|
|
]
|
|
|
|
)['reactions'],
|
2023-07-26 00:31:39 +02:00
|
|
|
fn (array $r): bool => \intval($r['peer_id']['user_id'] ?? $r['peer_id']['channel_id']) == $me
|
2023-07-25 19:07:27 +02:00
|
|
|
);
|
2023-07-25 22:36:55 +02:00
|
|
|
$this->reactions = \array_map(fn (array $r) => $r['reaction']['emoticon'] ?? $r['reaction']['document_id'], $myReactions);
|
2023-07-25 19:07:27 +02:00
|
|
|
}
|
|
|
|
return $this->reactions;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-07-25 22:36:55 +02:00
|
|
|
* Add reaction to message.
|
2023-07-25 19:07:27 +02:00
|
|
|
*
|
|
|
|
* @param list<string|int> $reaction Array of Reaction
|
2023-07-25 10:43:56 +02:00
|
|
|
* @param bool $big Whether a bigger and longer reaction should be shown
|
|
|
|
* @param bool $addToRecent Add this reaction to the recent reactions list.
|
|
|
|
*/
|
2023-07-25 22:36:55 +02:00
|
|
|
public function addReaction(array $reaction, bool $big = false, bool $addToRecent = true): ?Update
|
|
|
|
{
|
2023-07-25 19:07:27 +02:00
|
|
|
$result = $this->getClient()->methodCallAsyncRead(
|
2023-07-25 10:43:56 +02:00
|
|
|
'messages.sendReaction',
|
|
|
|
[
|
|
|
|
'peer' => $this->chatId,
|
|
|
|
'msg_id' => $this->id,
|
2023-07-25 22:36:55 +02:00
|
|
|
'reaction' => \array_map(fn ($reactions) => \is_int($reactions) ? ['_' => 'reactionCustomEmoji', 'document_id' => $reactions] : ['_' => 'reactionEmoji', 'emoticon' => $reactions], $reaction),
|
2023-07-25 10:43:56 +02:00
|
|
|
'big' => $big,
|
|
|
|
'add_to_recent' => $addToRecent
|
|
|
|
]
|
|
|
|
);
|
2023-07-25 19:07:27 +02:00
|
|
|
$this->reactions += $reaction;
|
|
|
|
return $this->getClient()->wrapUpdate($result);
|
2023-07-25 10:43:56 +02:00
|
|
|
}
|
|
|
|
|
2023-07-25 19:07:27 +02:00
|
|
|
/**
|
2023-07-25 22:36:55 +02:00
|
|
|
* Delete reaction from message.
|
2023-07-25 19:07:27 +02:00
|
|
|
*
|
|
|
|
* @param string|int $reaction string or int Reaction
|
|
|
|
*/
|
|
|
|
public function delReaction(int|string $reaction): ?Update
|
|
|
|
{
|
2023-07-26 00:18:38 +02:00
|
|
|
$this->getReactions();
|
|
|
|
unset($this->reactions[\array_search($reaction, $this->reactions)]);
|
|
|
|
$r = \array_map(fn ($reactions) => \is_int($reactions) ? ['_' => 'reactionCustomEmoji', 'document_id' => $reactions] : ['_' => 'reactionEmoji', 'emoticon' => $reactions], $this->reactions);
|
|
|
|
$r[]= ['_' => 'reactionEmpty'];
|
2023-07-25 19:07:27 +02:00
|
|
|
$result = $this->getClient()->methodCallAsyncRead(
|
|
|
|
'messages.sendReaction',
|
|
|
|
[
|
|
|
|
'peer' => $this->chatId,
|
|
|
|
'msg_id' => $this->id,
|
2023-07-26 00:18:38 +02:00
|
|
|
'reaction' => $r,
|
2023-07-25 19:07:27 +02:00
|
|
|
]
|
|
|
|
);
|
|
|
|
return $this->getClient()->wrapUpdate($result);
|
|
|
|
}
|
|
|
|
|
2023-07-01 17:53:29 +02:00
|
|
|
private readonly string $html;
|
|
|
|
private readonly string $htmlTelegram;
|
2023-07-01 19:06:30 +02:00
|
|
|
private readonly ?array $entities;
|
2023-07-25 19:09:29 +02:00
|
|
|
|
2023-07-01 14:39:33 +02:00
|
|
|
/**
|
|
|
|
* Get an HTML version of the message.
|
|
|
|
*
|
|
|
|
* @param bool $allowTelegramTags Whether to allow telegram-specific tags like tg-spoiler, tg-emoji, mention links and so on...
|
|
|
|
*/
|
|
|
|
public function getHTML(bool $allowTelegramTags = false): string
|
|
|
|
{
|
2023-07-01 19:06:30 +02:00
|
|
|
if (!$this->entities) {
|
2023-07-04 18:19:06 +02:00
|
|
|
return \htmlentities($this->message);
|
2023-07-01 19:06:30 +02:00
|
|
|
}
|
2023-07-01 17:53:29 +02:00
|
|
|
if ($allowTelegramTags) {
|
2023-07-07 16:56:03 +02:00
|
|
|
return $this->htmlTelegram ??= StrTools::entitiesToHtml($this->message, $this->entities, $allowTelegramTags);
|
2023-07-01 17:53:29 +02:00
|
|
|
}
|
2023-07-07 16:56:03 +02:00
|
|
|
return $this->html ??= StrTools::entitiesToHtml($this->message, $this->entities, $allowTelegramTags);
|
2023-06-28 15:50:38 +02:00
|
|
|
}
|
|
|
|
}
|