Secret chat improvements
This commit is contained in:
parent
bfc8009778
commit
fb1df72a21
@ -134,7 +134,7 @@ if (\file_exists('.env')) {
|
||||
echo 'Loading settings...'.PHP_EOL;
|
||||
$settings = \json_decode(\getenv('MTPROTO_SETTINGS'), true) ?: [];
|
||||
|
||||
$MadelineProto = new \danog\MadelineProto\API('s.madeline', $settings);
|
||||
$MadelineProto = new \danog\MadelineProto\API('secret.madeline', $settings);
|
||||
|
||||
// Reduce boilerplate with new wrapper method
|
||||
$MadelineProto->startAndLoop(SecretHandler::class);
|
||||
|
@ -360,35 +360,94 @@ class Connection
|
||||
$this->pinger->start();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Apply method abstractions.
|
||||
*
|
||||
* @param string $method Method name
|
||||
* @param array $arguments Arguments
|
||||
*
|
||||
* @return \Generator Whether we need to resolve a queue promise
|
||||
*/
|
||||
private function methodAbstractions(string &$method, array &$arguments): \Generator
|
||||
{
|
||||
if ($method === 'messages.importChatInvite' && isset($arguments['hash']) && \is_string($arguments['hash']) && \preg_match('@(?:t|telegram)\\.(?:me|dog)/(joinchat/)?([a-z0-9_-]*)@i', $arguments['hash'], $matches)) {
|
||||
if ($matches[1] === '') {
|
||||
$method = 'channels.joinChannel';
|
||||
$arguments['channel'] = $matches[2];
|
||||
} else {
|
||||
$arguments['hash'] = $matches[2];
|
||||
}
|
||||
} elseif ($method === 'messages.checkChatInvite' && isset($arguments['hash']) && \is_string($arguments['hash']) && \preg_match('@(?:t|telegram)\\.(?:me|dog)/joinchat/([a-z0-9_-]*)@i', $arguments['hash'], $matches)) {
|
||||
$arguments['hash'] = $matches[1];
|
||||
} elseif ($method === 'channels.joinChannel' && isset($arguments['channel']) && \is_string($arguments['channel']) && \preg_match('@(?:t|telegram)\\.(?:me|dog)/(joinchat/)?([a-z0-9_-]*)@i', $arguments['channel'], $matches)) {
|
||||
if ($matches[1] !== '') {
|
||||
$method = 'messages.importChatInvite';
|
||||
$arguments['hash'] = $matches[2];
|
||||
}
|
||||
} elseif ($method === 'messages.sendMessage' && isset($arguments['peer']['_']) && \in_array($arguments['peer']['_'], ['inputEncryptedChat', 'updateEncryption', 'updateEncryptedChatTyping', 'updateEncryptedMessagesRead', 'updateNewEncryptedMessage', 'encryptedMessage', 'encryptedMessageService'])) {
|
||||
$method = 'messages.sendEncrypted';
|
||||
$arguments = ['peer' => $arguments['peer'], 'message' => $arguments];
|
||||
if (!isset($arguments['message']['_'])) {
|
||||
$arguments['message']['_'] = 'decryptedMessage';
|
||||
}
|
||||
if (!isset($arguments['message']['ttl'])) {
|
||||
$arguments['message']['ttl'] = 0;
|
||||
}
|
||||
if (isset($arguments['message']['reply_to_msg_id'])) {
|
||||
$arguments['message']['reply_to_random_id'] = $arguments['message']['reply_to_msg_id'];
|
||||
}
|
||||
} elseif ($method === 'messages.sendEncryptedFile') {
|
||||
if (isset($arguments['file'])) {
|
||||
if ((!\is_array($arguments['file']) || !(isset($arguments['file']['_']) && $this->API->getTL()->getConstructors()->findByPredicate($arguments['file']['_']) === 'InputEncryptedFile')) && $this->API->getSettings()->getFiles()->getAllowAutomaticUpload()) {
|
||||
$arguments['file'] = (yield from $this->API->uploadEncrypted($arguments['file']));
|
||||
}
|
||||
if (isset($arguments['file']['key'])) {
|
||||
$arguments['message']['media']['key'] = $arguments['file']['key'];
|
||||
}
|
||||
if (isset($arguments['file']['iv'])) {
|
||||
$arguments['message']['media']['iv'] = $arguments['file']['iv'];
|
||||
}
|
||||
}
|
||||
$arguments['queuePromise'] = new Deferred;
|
||||
return $arguments['queuePromise'];
|
||||
} elseif (\in_array($method, ['messages.addChatUser', 'messages.deleteChatUser', 'messages.editChatAdmin', 'messages.editChatPhoto', 'messages.editChatTitle', 'messages.getFullChat', 'messages.exportChatInvite', 'messages.editChatAdmin', 'messages.migrateChat']) && isset($arguments['chat_id']) && (!\is_numeric($arguments['chat_id']) || $arguments['chat_id'] < 0)) {
|
||||
$res = (yield from $this->API->getInfo($arguments['chat_id']));
|
||||
if ($res['type'] !== 'chat') {
|
||||
throw new \danog\MadelineProto\Exception('chat_id is not a chat id (only normal groups allowed, not supergroups)!');
|
||||
}
|
||||
$arguments['chat_id'] = $res['chat_id'];
|
||||
} elseif ($method === 'photos.updateProfilePhoto') {
|
||||
if (isset($arguments['id'])) {
|
||||
if (!\is_array($arguments['id'])) {
|
||||
$method = 'photos.uploadProfilePhoto';
|
||||
$arguments['file'] = $arguments['id'];
|
||||
}
|
||||
} elseif (isset($arguments['file'])) {
|
||||
$method = 'photos.uploadProfilePhoto';
|
||||
}
|
||||
} elseif ($method === 'photos.uploadProfilePhoto') {
|
||||
if (isset($arguments['file'])) {
|
||||
if (\is_array($arguments['file']) && !\in_array($arguments['file']['_'], ['inputFile', 'inputFileBig'])) {
|
||||
$method = 'photos.uploadProfilePhoto';
|
||||
$arguments['id'] = $arguments['file'];
|
||||
}
|
||||
} elseif (isset($arguments['id'])) {
|
||||
$method = 'photos.updateProfilePhoto';
|
||||
}
|
||||
} elseif ($method === 'messages.uploadMedia') {
|
||||
if (!isset($arguments['peer']) && !$this->API->getSelf()['bot']) {
|
||||
$arguments['peer'] = 'me';
|
||||
}
|
||||
}
|
||||
if ($method === 'messages.sendEncrypted' || $method === 'messages.sendEncryptedService') {
|
||||
$arguments['queuePromise'] = new Deferred;
|
||||
return $arguments['queuePromise'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Send an MTProto message.
|
||||
*
|
||||
* Structure of message array:
|
||||
* [
|
||||
* // only in outgoing messages
|
||||
* 'body' => deserialized body, (optional if container)
|
||||
* 'serialized_body' => 'serialized body', (optional if container)
|
||||
* 'contentRelated' => bool,
|
||||
* '_' => 'predicate',
|
||||
* 'promise' => deferred promise that gets resolved when a response to the message is received (optional),
|
||||
* 'send_promise' => deferred promise that gets resolved when the message is sent (optional),
|
||||
* 'file' => bool (optional),
|
||||
* 'type' => 'type' (optional),
|
||||
* 'queue' => queue ID (optional),
|
||||
* 'container' => [message ids] (optional),
|
||||
*
|
||||
* // only in incoming messages
|
||||
* 'content' => deserialized body,
|
||||
* 'seq_no' => number (optional),
|
||||
* 'from_container' => bool (optional),
|
||||
*
|
||||
* // can be present in both
|
||||
* 'response' => message id (optional),
|
||||
* 'msg_id' => message id (optional),
|
||||
* 'sent' => timestamp,
|
||||
* 'tries' => number
|
||||
* ]
|
||||
*
|
||||
* @param OutgoingMessage $message The message to send
|
||||
* @param boolean $flush Whether to flush the message right away
|
||||
*
|
||||
@ -404,7 +463,9 @@ class Connection
|
||||
$this->API->referenceDatabase->refreshNext(true);
|
||||
}
|
||||
if ($message->isMethod()) {
|
||||
$body = yield from $this->API->getTL()->serializeMethod($message->getConstructor(), $body);
|
||||
$method = $message->getConstructor();
|
||||
$queuePromise = yield from $this->methodAbstractions($method, $body);
|
||||
$body = yield from $this->API->getTL()->serializeMethod($method, $body);
|
||||
} else {
|
||||
$body['_'] = $message->getConstructor();
|
||||
$body = yield from $this->API->getTL()->serializeObject(['type' => ''], $body, $message->getConstructor());
|
||||
@ -416,6 +477,9 @@ class Connection
|
||||
unset($body);
|
||||
}
|
||||
$this->pendingOutgoing[$this->pendingOutgoingKey++] = $message;
|
||||
if (isset($queuePromise)) {
|
||||
$queuePromise->resolve();
|
||||
}
|
||||
if ($flush && isset($this->writer)) {
|
||||
$this->writer->resume();
|
||||
}
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
namespace danog\MadelineProto\SecretChats;
|
||||
|
||||
use Amp\Deferred;
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\MTProtoTools\Crypt;
|
||||
|
||||
/**
|
||||
@ -26,17 +28,24 @@ use danog\MadelineProto\MTProtoTools\Crypt;
|
||||
*/
|
||||
trait MessageHandler
|
||||
{
|
||||
/**
|
||||
* Secret queue.
|
||||
*
|
||||
* @var Promise[]
|
||||
*/
|
||||
private $secretQueue = [];
|
||||
/**
|
||||
* Encrypt secret chat message.
|
||||
*
|
||||
* @param integer $chat_id Chat ID
|
||||
* @param array $message Message to encrypt
|
||||
* @param integer $chat_id Chat ID
|
||||
* @param array $message Message to encrypt
|
||||
* @param Deferred $queuePromise Queue promise
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function encryptSecretMessage(int $chat_id, array $message): \Generator
|
||||
public function encryptSecretMessage(int $chat_id, array $message, Deferred $queuePromise): \Generator
|
||||
{
|
||||
if (!isset($this->secret_chats[$chat_id])) {
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $chat_id));
|
||||
@ -48,6 +57,13 @@ trait MessageHandler
|
||||
if (($this->secret_chats[$chat_id]['ttr'] <= 0 || \time() - $this->secret_chats[$chat_id]['updated'] > 7 * 24 * 60 * 60) && $this->secret_chats[$chat_id]['rekeying'][0] === 0) {
|
||||
yield from $this->rekey($chat_id);
|
||||
}
|
||||
if (isset($this->secretQueue[$chat_id])) {
|
||||
$promise = $this->secretQueue[$chat_id];
|
||||
$this->secretQueue[$chat_id] = $queuePromise->promise();
|
||||
yield $promise;
|
||||
} else {
|
||||
$this->secretQueue[$chat_id] = $queuePromise->promise();
|
||||
}
|
||||
$message = ['_' => 'decryptedMessageLayer', 'layer' => $this->secret_chats[$chat_id]['layer'], 'in_seq_no' => $this->generateSecretInSeqNo($chat_id), 'out_seq_no' => $this->generateSecretOutSeqNo($chat_id), 'message' => $message];
|
||||
$this->secret_chats[$chat_id]['out_seq_no']++;
|
||||
}
|
||||
|
@ -520,7 +520,7 @@ class TL
|
||||
$concat = $this->constructors->findByPredicate('vector')['id'];
|
||||
$concat .= \danog\MadelineProto\Tools::packUnsignedInt(\count($object));
|
||||
foreach ($object as $k => $current_object) {
|
||||
$concat .= (yield from $this->serializeObject(['type' => $type['subtype']], $current_object, $k));
|
||||
$concat .= (yield from $this->serializeObject(['type' => $type['subtype']], $current_object, $k, $layer));
|
||||
}
|
||||
return $concat;
|
||||
case 'vector':
|
||||
@ -529,7 +529,7 @@ class TL
|
||||
}
|
||||
$concat = \danog\MadelineProto\Tools::packUnsignedInt(\count($object));
|
||||
foreach ($object as $k => $current_object) {
|
||||
$concat .= (yield from $this->serializeObject(['type' => $type['subtype']], $current_object, $k));
|
||||
$concat .= (yield from $this->serializeObject(['type' => $type['subtype']], $current_object, $k, $layer));
|
||||
}
|
||||
return $concat;
|
||||
case 'Object':
|
||||
@ -574,7 +574,7 @@ class TL
|
||||
$constructorData = $this->constructors->findByPredicate('inputMessageEntityMentionName');
|
||||
}
|
||||
$concat = $bare ? '' : $constructorData['id'];
|
||||
return $concat.(yield from $this->serializeParams($constructorData, $object, '', $layer));
|
||||
return $concat.(yield from $this->serializeParams($constructorData, $object, '', $layer, null));
|
||||
}
|
||||
/**
|
||||
* Serialize method.
|
||||
@ -588,78 +588,11 @@ class TL
|
||||
*/
|
||||
public function serializeMethod(string $method, $arguments): \Generator
|
||||
{
|
||||
if ($method === 'messages.importChatInvite' && isset($arguments['hash']) && \is_string($arguments['hash']) && \preg_match('@(?:t|telegram)\\.(?:me|dog)/(joinchat/)?([a-z0-9_-]*)@i', $arguments['hash'], $matches)) {
|
||||
if ($matches[1] === '') {
|
||||
$method = 'channels.joinChannel';
|
||||
$arguments['channel'] = $matches[2];
|
||||
} else {
|
||||
$arguments['hash'] = $matches[2];
|
||||
}
|
||||
} elseif ($method === 'messages.checkChatInvite' && isset($arguments['hash']) && \is_string($arguments['hash']) && \preg_match('@(?:t|telegram)\\.(?:me|dog)/joinchat/([a-z0-9_-]*)@i', $arguments['hash'], $matches)) {
|
||||
$arguments['hash'] = $matches[1];
|
||||
} elseif ($method === 'channels.joinChannel' && isset($arguments['channel']) && \is_string($arguments['channel']) && \preg_match('@(?:t|telegram)\\.(?:me|dog)/(joinchat/)?([a-z0-9_-]*)@i', $arguments['channel'], $matches)) {
|
||||
if ($matches[1] !== '') {
|
||||
$method = 'messages.importChatInvite';
|
||||
$arguments['hash'] = $matches[2];
|
||||
}
|
||||
} elseif ($method === 'messages.sendMessage' && isset($arguments['peer']['_']) && \in_array($arguments['peer']['_'], ['inputEncryptedChat', 'updateEncryption', 'updateEncryptedChatTyping', 'updateEncryptedMessagesRead', 'updateNewEncryptedMessage', 'encryptedMessage', 'encryptedMessageService'])) {
|
||||
$method = 'messages.sendEncrypted';
|
||||
$arguments = ['peer' => $arguments['peer'], 'message' => $arguments];
|
||||
if (!isset($arguments['message']['_'])) {
|
||||
$arguments['message']['_'] = 'decryptedMessage';
|
||||
}
|
||||
if (!isset($arguments['message']['ttl'])) {
|
||||
$arguments['message']['ttl'] = 0;
|
||||
}
|
||||
if (isset($arguments['message']['reply_to_msg_id'])) {
|
||||
$arguments['message']['reply_to_random_id'] = $arguments['message']['reply_to_msg_id'];
|
||||
}
|
||||
} elseif ($method === 'messages.sendEncryptedFile') {
|
||||
if (isset($arguments['file'])) {
|
||||
if ((!\is_array($arguments['file']) || !(isset($arguments['file']['_']) && $this->constructors->findByPredicate($arguments['file']['_']) === 'InputEncryptedFile')) && $this->API->getSettings()->getFiles()->getAllowAutomaticUpload()) {
|
||||
$arguments['file'] = (yield from $this->API->uploadEncrypted($arguments['file']));
|
||||
}
|
||||
if (isset($arguments['file']['key'])) {
|
||||
$arguments['message']['media']['key'] = $arguments['file']['key'];
|
||||
}
|
||||
if (isset($arguments['file']['iv'])) {
|
||||
$arguments['message']['media']['iv'] = $arguments['file']['iv'];
|
||||
}
|
||||
}
|
||||
} elseif (\in_array($method, ['messages.addChatUser', 'messages.deleteChatUser', 'messages.editChatAdmin', 'messages.editChatPhoto', 'messages.editChatTitle', 'messages.getFullChat', 'messages.exportChatInvite', 'messages.editChatAdmin', 'messages.migrateChat']) && isset($arguments['chat_id']) && (!\is_numeric($arguments['chat_id']) || $arguments['chat_id'] < 0)) {
|
||||
$res = (yield from $this->API->getInfo($arguments['chat_id']));
|
||||
if ($res['type'] !== 'chat') {
|
||||
throw new \danog\MadelineProto\Exception('chat_id is not a chat id (only normal groups allowed, not supergroups)!');
|
||||
}
|
||||
$arguments['chat_id'] = $res['chat_id'];
|
||||
} elseif ($method === 'photos.updateProfilePhoto') {
|
||||
if (isset($arguments['id'])) {
|
||||
if (!\is_array($arguments['id'])) {
|
||||
$method = 'photos.uploadProfilePhoto';
|
||||
$arguments['file'] = $arguments['id'];
|
||||
}
|
||||
} elseif (isset($arguments['file'])) {
|
||||
$method = 'photos.uploadProfilePhoto';
|
||||
}
|
||||
} elseif ($method === 'photos.uploadProfilePhoto') {
|
||||
if (isset($arguments['file'])) {
|
||||
if (\is_array($arguments['file']) && !\in_array($arguments['file']['_'], ['inputFile', 'inputFileBig'])) {
|
||||
$method = 'photos.uploadProfilePhoto';
|
||||
$arguments['id'] = $arguments['file'];
|
||||
}
|
||||
} elseif (isset($arguments['id'])) {
|
||||
$method = 'photos.updateProfilePhoto';
|
||||
}
|
||||
} elseif ($method === 'messages.uploadMedia') {
|
||||
if (!isset($arguments['peer']) && !$this->API->getSelf()['bot']) {
|
||||
$arguments['peer'] = 'me';
|
||||
}
|
||||
}
|
||||
$tl = $this->methods->findByMethod($method);
|
||||
if ($tl === false) {
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['method_not_found'].$method);
|
||||
}
|
||||
return $tl['id'].(yield from $this->serializeParams($tl, $arguments, $method));
|
||||
return $tl['id'].(yield from $this->serializeParams($tl, $arguments, $method, -1, $arguments['queuePromise'] ?? null));
|
||||
}
|
||||
/**
|
||||
* Serialize parameters.
|
||||
@ -673,7 +606,7 @@ class TL
|
||||
*
|
||||
* @psalm-return \Generator<int|mixed, Promise|Promise<\Amp\File\File>|Promise<\Amp\Ipc\Sync\ChannelledSocket>|Promise<int>|Promise<mixed>|Promise<null|string>|\danog\MadelineProto\Stream\StreamInterface|array|int|mixed, mixed, string>
|
||||
*/
|
||||
private function serializeParams(array $tl, $arguments, $ctx, int $layer = -1): \Generator
|
||||
private function serializeParams(array $tl, $arguments, $ctx, int $layer, $promise): \Generator
|
||||
{
|
||||
$serialized = '';
|
||||
$arguments = (yield from $this->API->botAPIToMTProto($arguments));
|
||||
@ -706,11 +639,11 @@ class TL
|
||||
continue;
|
||||
}
|
||||
if ($current_argument['name'] === 'random_bytes') {
|
||||
$serialized .= (yield from $this->serializeObject(['type' => 'bytes'], \danog\MadelineProto\Tools::random(15 + 4 * \danog\MadelineProto\Tools::randomInt($modulus = 3)), 'random_bytes'));
|
||||
$serialized .= yield from $this->serializeObject(['type' => 'bytes'], \danog\MadelineProto\Tools::random(15 + 4 * \danog\MadelineProto\Tools::randomInt($modulus = 3)), 'random_bytes');
|
||||
continue;
|
||||
}
|
||||
if ($current_argument['name'] === 'data' && isset($tl['method']) && \in_array($tl['method'], ['messages.sendEncrypted', 'messages.sendEncryptedFile', 'messages.sendEncryptedService']) && isset($arguments['message'])) {
|
||||
$serialized .= (yield from $this->serializeObject($current_argument, yield from $this->API->encryptSecretMessage($arguments['peer']['chat_id'], $arguments['message']), 'data'));
|
||||
$serialized .= yield from $this->serializeObject($current_argument, yield from $this->API->encryptSecretMessage($arguments['peer']['chat_id'], $arguments['message'], $promise), 'data');
|
||||
continue;
|
||||
}
|
||||
if ($current_argument['name'] === 'random_id') {
|
||||
|
Loading…
Reference in New Issue
Block a user