1
0
mirror of https://github.com/danog/MadelineProto.git synced 2024-11-27 07:14:39 +01:00
This commit is contained in:
Daniil Gentili 2023-06-18 21:18:38 +02:00
parent ac06acf704
commit bd8602c57d
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
6 changed files with 209 additions and 5 deletions

View File

@ -29,7 +29,7 @@ use function Amp\ByteStream\getOutputBufferStream;
*/
trait Templates
{
private const TEMPLATE = '<!DOCTYPE html><html><head><title>MadelineProto</title></head><body><h1>MadelineProto</h1><p>%s</p><form method="POST">%s<button type="submit"/>%s</button></form>%s</body></html>';
private const TEMPLATE = '<!DOCTYPE html><html><head><title>MadelineProto</title></head><body><h1>MadelineProto</h1><p>%s</p><form method="POST">%s<button type="submit"/>%s</button></form></body></html>';
/**
* Generate page from template.
*

View File

@ -0,0 +1,52 @@
<?php declare(strict_types=1);
namespace danog\MadelineProto\Ipc\Wrapper;
use Amp\Cancellation as AmpCancellation;
use danog\MadelineProto\FileCallbackInterface;
/**
* @internal
*/
final class Cancellation extends Obj implements AmpCancellation
{
/**
* Subscribes a new handler to be invoked on a cancellation request.
*
* This handler might be invoked immediately in case the cancellation has already been requested. Any unhandled
* exceptions will be thrown into the event loop.
*
* @param \Closure(CancelledException) $callback Callback to be invoked on a cancellation request. Will receive a
* `CancelledException` as first argument that may be used to fail the operation.
*
* @return string Identifier that can be used to cancel the subscription.
*/
public function subscribe(\Closure $callback): string {
return $this->__call('unsubscribe', [$callback]);
}
/**
* Unsubscribes a previously registered handler.
*
* The handler will no longer be called as long as this method isn't invoked from a subscribed callback.
*/
public function unsubscribe(string $id): void {
return $this->__call('unsubscribe', [$id]);
}
/**
* Returns whether cancellation has been requested yet.
*/
public function isRequested(): bool {
return $this->__call('isRequested');
}
/**
* Throws the `CancelledException` if cancellation has been requested, otherwise does nothing.
*
* @throws CancelledException
*/
public function throwIfRequested(): void {
$this->__call('throwIfRequested');
}
}

View File

@ -174,6 +174,9 @@ final class Lang
'en' =>
[
'go' => 'Go',
'loginChoosePromptWeb' => 'Do you want to login as a user or as a bot?',
'loginOptionBot' => 'Bot',
'loginOptionUser' => 'User',
'apiChooseManualAutoTip' => 'Note that you can also provide the API ID/hash directly in the code using the settings: %s',
'apiChooseManualAutoTipWeb' => 'Note that you can also provide the API ID/hash directly in the code using the <a target="_blank" href="%s">settings</a>.',
'apiChoosePrompt' => 'Your choice (m/a): ',

View File

@ -715,9 +715,6 @@ final class MTProto implements TLCallback, LoggerGetter
'updateHandlerType',
// Web login template
'webTemplate',
// Settings
'settings',
'config',

View File

@ -0,0 +1,152 @@
<?php
declare(strict_types=1);
/**
* Login QR code.
*
* This file is part of MadelineProto.
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU General Public License along with MadelineProto.
* If not, see <http://www.gnu.org/licenses/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto\TL\Types;
use Amp\Cancellation;
use Amp\CancelledException;
use Amp\CompositeCancellation;
use Amp\DeferredFuture;
use Amp\TimeoutCancellation;
use Amp\TimeoutException;
use AssertionError;
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\PlainTextRenderer;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
use danog\MadelineProto\Ipc\Client;
use danog\MadelineProto\MTProto;
use JsonSerializable;
use Webmozart\Assert\Assert;
/**
* Represents a login QR code.
*/
final class LoginQrCode implements JsonSerializable {
private string $session;
/** @internal */
public function __construct(
private MTProto|Client $API,
/** @var non-empty-string The [QR code login link](https://core.telegram.org/api/links#qr-code-login-links) */
public readonly string $link,
/** @var positive-int The expiry date of the link */
public readonly int $expiry
) {
$this->session = $API->getWrapper()->getSession()->getSessionDirectoryPath();
}
/**
* @internal
*/
public function __sleep(): array
{
return ['link', 'expiry', 'session'];
}
/** @internal */
public function jsonSerialize(): mixed
{
return [
'link' => $this->link,
'expiry' => $this->expiry,
];
}
/**
* Returns true if the QR code has expired and a new one should be fetched.
*/
public function isExpired(): bool {
return $this->expiry <= time();
}
/**
* Returns the number of seconds until the QR code expires.
*
* @return non-negative-int
*/
public function expiresIn(): int {
return max(0, $this->expiry - time());
}
public function getExpirationCancellation(): Cancellation {
return new TimeoutCancellation((float) $this->expiresIn(), "The QR code expired!");
}
public function getLoginCancellation(): Cancellation {
$this->API ??= Client::giveInstanceBySession($this->session);
return $this->API->getQrLoginCancellation();
}
/**
* Waits for the user to login or for the QR code to expire.
*
* If the user logins, null is returned.
*
* If the QR code expires, the new QR code is returned.
*
* If cancellation is requested externally through $cancellation, a CancelledException is thrown.
*
* @throws CancelledException
*
* @param Cancellation|null $customCancellation Optional additional cancellation
*/
public function waitForLoginOrQrCodeExpiration(?Cancellation $customCancellation = null): ?self {
$expire = $this->getExpirationCancellation();
if ($customCancellation) {
$cancellation = new CompositeCancellation($expire, $customCancellation);
} else {
$cancellation = $expire;
}
$login = $this->getLoginCancellation();
$cancellation = new CompositeCancellation($login, $cancellation);
try {
(new DeferredFuture)->getFuture()->await($cancellation);
} catch (CancelledException) {
$customCancellation?->throwIfRequested();
return $this->API->qrLogin();
}
throw new AssertionError("Unreachable!");
}
/**
* Render and return SVG version of QR code.
*/
public function getQRSvg(): string {
$writer = new Writer(new ImageRenderer(
new RendererStyle(400),
new SvgImageBackEnd
));
return $writer->writeString($this->link);
}
/**
* Render and return plain text version of QR code.
*
* @param non-negative-int $margin Text margin
*/
public function getQRText(int $margin = 2): string {
$writer = new Writer(new PlainTextRenderer($margin));
return $writer->writeString($this->link);
}
}

View File

@ -54,7 +54,7 @@ trait Templates
$token = \htmlentities(Lang::$current_lang['loginBotTokenWeb']);
$form = "<input type='text' name='token' placeholder='$token' required/>";
}
} elseif (isset($_POST['waitQrCodeOrLogin'])) {
} elseif (isset($_GET['waitQrCodeOrLogin'])) {
header('Content-type: application/json');
try {
/** @var ?LoginQrCode */