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-30 14:42:04 +02:00
|
|
|
use danog\MadelineProto\ParseMode;
|
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 19:07:27 +02:00
|
|
|
/** @var list<int|string> list of our message reactions */
|
2023-07-26 17:36:53 +02:00
|
|
|
protected 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;
|
2023-07-29 15:53:52 +02:00
|
|
|
$this->imported = $fwdFrom['imported'];
|
2023-07-01 19:06:30 +02:00
|
|
|
} else {
|
|
|
|
$this->fwdInfo = null;
|
|
|
|
$this->psaType = null;
|
2023-07-29 15:53:52 +02:00
|
|
|
$this->imported = false;
|
2023-07-01 19:06:30 +02:00
|
|
|
}
|
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-26 17:36:53 +02:00
|
|
|
|
|
|
|
foreach ($rawMessage['reactions']['results'] ?? [] as $r) {
|
|
|
|
if (isset($r['chosen_order'])) {
|
|
|
|
// Todo: live synchronization using a message database...
|
|
|
|
$this->reactions []= $r['reaction']['emoticon'] ?? $r['reaction']['document_id'];
|
|
|
|
}
|
|
|
|
}
|
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-26 17:36:53 +02:00
|
|
|
* Get our reactions on the message.
|
2023-07-25 10:43:56 +02:00
|
|
|
*
|
2023-07-26 17:36:53 +02:00
|
|
|
* @return list<string|int>
|
2023-07-25 19:07:27 +02:00
|
|
|
*/
|
2023-07-26 17:36:53 +02:00
|
|
|
public function getOurReactions(): array
|
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
|
|
|
*
|
2023-07-26 12:11:47 +02:00
|
|
|
* @param string|int $reaction 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-26 17:36:53 +02:00
|
|
|
*
|
|
|
|
* @return list<string|int>
|
2023-07-25 10:43:56 +02:00
|
|
|
*/
|
2023-07-26 17:36:53 +02:00
|
|
|
public function addReaction(int|string $reaction, bool $big = false, bool $addToRecent = true): array
|
2023-07-25 22:36:55 +02:00
|
|
|
{
|
2023-07-26 17:36:53 +02:00
|
|
|
if (\in_array($reaction, $this->reactions, true)) {
|
|
|
|
return $this->reactions;
|
|
|
|
}
|
|
|
|
$this->getClient()->methodCallAsyncRead(
|
2023-07-25 10:43:56 +02:00
|
|
|
'messages.sendReaction',
|
|
|
|
[
|
|
|
|
'peer' => $this->chatId,
|
|
|
|
'msg_id' => $this->id,
|
2023-07-26 17:36:53 +02:00
|
|
|
'reaction' => \is_int($reaction)
|
|
|
|
? [['_' => 'reactionCustomEmoji', 'document_id' => $reaction]]
|
|
|
|
: [['_' => 'reactionEmoji', 'emoticon' => $reaction]],
|
2023-07-25 10:43:56 +02:00
|
|
|
'big' => $big,
|
|
|
|
'add_to_recent' => $addToRecent
|
|
|
|
]
|
|
|
|
);
|
2023-07-26 11:02:17 +02:00
|
|
|
$this->reactions[] = $reaction;
|
2023-07-26 17:36:53 +02:00
|
|
|
return $this->reactions;
|
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
|
2023-07-26 17:36:53 +02:00
|
|
|
*
|
|
|
|
* @return list<string|int>
|
2023-07-25 19:07:27 +02:00
|
|
|
*/
|
2023-07-26 17:36:53 +02:00
|
|
|
public function delReaction(int|string $reaction): array
|
2023-07-25 19:07:27 +02:00
|
|
|
{
|
2023-07-26 17:36:53 +02:00
|
|
|
$key = \array_search($reaction, $this->reactions, true);
|
|
|
|
if ($key === false) {
|
|
|
|
return $this->reactions;
|
2023-07-26 11:02:17 +02:00
|
|
|
}
|
2023-07-26 17:36:53 +02:00
|
|
|
unset($this->reactions[$key]);
|
|
|
|
$this->reactions = \array_values($this->reactions);
|
|
|
|
$r = \array_map(fn (string|int $r): array => \is_int($r) ? ['_' => 'reactionCustomEmoji', 'document_id' => $r] : ['_' => 'reactionEmoji', 'emoticon' => $r], $this->reactions);
|
|
|
|
$r[]= ['_' => 'reactionEmpty'];
|
|
|
|
$this->getClient()->methodCallAsyncRead(
|
|
|
|
'messages.sendReaction',
|
|
|
|
[
|
|
|
|
'peer' => $this->chatId,
|
|
|
|
'msg_id' => $this->id,
|
|
|
|
'reaction' => $r,
|
|
|
|
]
|
|
|
|
);
|
|
|
|
return $this->reactions;
|
2023-07-25 19:07:27 +02:00
|
|
|
}
|
|
|
|
|
2023-07-30 14:42:04 +02:00
|
|
|
/**
|
|
|
|
* Translate text message(for media translate it caption).
|
|
|
|
*
|
|
|
|
* @param string $toLang Two-letter ISO 639-1 language code of the language to which the message is translated
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public function translate(
|
2023-08-03 23:43:42 +02:00
|
|
|
string $toLang
|
2023-07-30 14:42:04 +02:00
|
|
|
): string {
|
2023-08-03 17:44:57 +02:00
|
|
|
if (empty($message = $this->message)) {
|
|
|
|
return $message;
|
|
|
|
}
|
2023-07-30 14:42:04 +02:00
|
|
|
$result = $this->getClient()->methodCallAsyncRead(
|
|
|
|
'messages.translateText',
|
|
|
|
[
|
|
|
|
'peer' => $this->chatId,
|
|
|
|
'id' => [$this->id],
|
|
|
|
'text' => $this->message,
|
|
|
|
'to_lang' => $toLang
|
|
|
|
]
|
|
|
|
);
|
|
|
|
return $result[0]['text'];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Edit message text.
|
|
|
|
*
|
|
|
|
* @param string $message New message
|
|
|
|
* @param ParseMode $parseMode Whether to parse HTML or Markdown markup in the message
|
2023-08-04 10:22:37 +02:00
|
|
|
* @param array|null $replyMarkup Reply markup for inline keyboards
|
2023-07-30 14:42:04 +02:00
|
|
|
* @param int|null $scheduleDate Scheduled message date for scheduled messages
|
|
|
|
* @param bool $noWebpage Disable webpage preview
|
|
|
|
*
|
|
|
|
*/
|
2023-08-05 19:13:26 +02:00
|
|
|
public function editText(
|
2023-07-30 14:42:04 +02:00
|
|
|
string $message,
|
2023-08-04 10:22:37 +02:00
|
|
|
?array $replyMarkup = null,
|
2023-07-30 14:42:04 +02:00
|
|
|
ParseMode $parseMode = ParseMode::TEXT,
|
|
|
|
?int $scheduleDate = null,
|
|
|
|
bool $noWebpage = false
|
|
|
|
): Message {
|
|
|
|
$result = $this->getClient()->methodCallAsyncRead(
|
|
|
|
'messages.editMessage',
|
|
|
|
[
|
|
|
|
'peer' => $this->chatId,
|
|
|
|
'id' => $this->id,
|
|
|
|
'message' => $message,
|
2023-08-04 10:22:37 +02:00
|
|
|
'reply_markup' => $replyMarkup,
|
2023-07-30 14:42:04 +02:00
|
|
|
'parse_mode' => $parseMode,
|
|
|
|
'schedule_date' => $scheduleDate,
|
|
|
|
'no_webpage' => $noWebpage
|
|
|
|
]
|
|
|
|
);
|
2023-08-03 23:43:42 +02:00
|
|
|
return $this->getClient()->wrapMessage($this->getClient()->extractMessage($result));
|
2023-07-30 14:42:04 +02:00
|
|
|
}
|
|
|
|
|
2023-07-26 11:02:17 +02:00
|
|
|
protected readonly string $html;
|
|
|
|
protected readonly string $htmlTelegram;
|
|
|
|
protected 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.
|
2023-08-05 22:55:13 +02:00
|
|
|
*
|
2023-08-05 21:26:58 +02:00
|
|
|
* @psalm-suppress InaccessibleProperty
|
2023-07-01 14:39:33 +02:00
|
|
|
*
|
|
|
|
* @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
|
|
|
}
|
|
|
|
}
|