1
0
mirror of https://github.com/danog/MadelineProto.git synced 2025-01-11 13:28:17 +01:00
MadelineProto/src/EventHandler/Message.php

339 lines
12 KiB
PHP
Raw Normal View History

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;
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;
/** @var list<int|string> list of our message reactions */
protected array $reactions = [];
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-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 */
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'])
? $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'])
? $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
}
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 22:36:55 +02:00
* Pin a message.
*
* @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
{
$result = $this->getClient()->methodCallAsyncRead(
'messages.updatePinnedMessage',
[
'peer' => $this->chatId,
'id' => $this->id,
'pm_oneside' => $pmOneside,
'silent' => $silent,
'unpin' => false
]
);
return $this->getClient()->wrapMessage($this->getClient()->extractMessage($result));
}
/**
2023-07-25 22:36:55 +02:00
* Unpin a message.
*
* @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
{
$result = $this->getClient()->methodCallAsyncRead(
'messages.updatePinnedMessage',
[
'peer' => $this->chatId,
'id' => $this->id,
'pm_oneside' => $pmOneside,
'silent' => $silent,
'unpin' => true
]
);
return $this->getClient()->wrapUpdate($result);
}
/**
* Get our reactions on the message.
*
* @return list<string|int>
*/
public function getOurReactions(): array
{
return $this->reactions;
}
/**
2023-07-25 22:36:55 +02:00
* Add reaction to message.
*
2023-07-26 12:11:47 +02:00
* @param string|int $reaction reaction
* @param bool $big Whether a bigger and longer reaction should be shown
* @param bool $addToRecent Add this reaction to the recent reactions list.
*
* @return list<string|int>
*/
public function addReaction(int|string $reaction, bool $big = false, bool $addToRecent = true): array
2023-07-25 22:36:55 +02:00
{
if (\in_array($reaction, $this->reactions, true)) {
return $this->reactions;
}
$this->getClient()->methodCallAsyncRead(
'messages.sendReaction',
[
'peer' => $this->chatId,
'msg_id' => $this->id,
'reaction' => \is_int($reaction)
? [['_' => 'reactionCustomEmoji', 'document_id' => $reaction]]
: [['_' => 'reactionEmoji', 'emoticon' => $reaction]],
'big' => $big,
'add_to_recent' => $addToRecent
]
);
$this->reactions[] = $reaction;
return $this->reactions;
}
/**
2023-07-25 22:36:55 +02:00
* Delete reaction from message.
*
* @param string|int $reaction string or int Reaction
*
* @return list<string|int>
*/
public function delReaction(int|string $reaction): array
{
$key = \array_search($reaction, $this->reactions, true);
if ($key === false) {
return $this->reactions;
}
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;
}
/**
* 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(
string $toLang
): string {
if (empty($message = $this->message)) {
return $message;
}
$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
* @param array|null $replyMarkup Reply markup for inline keyboards
* @param int|null $scheduleDate Scheduled message date for scheduled messages
* @param bool $noWebpage Disable webpage preview
*
*/
public function edit(
string $message,
?array $replyMarkup = null,
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,
'reply_markup' => $replyMarkup,
'parse_mode' => $parseMode,
'schedule_date' => $scheduleDate,
'no_webpage' => $noWebpage
]
);
return $this->getClient()->wrapMessage($this->getClient()->extractMessage($result));
}
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.
*
* @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
}
}