Merge pull request #2 from setiawanhu/dev

Added Support for Multi Account Login
This commit is contained in:
Setiawan 2020-08-13 03:32:59 +07:00 committed by GitHub
commit 87d42730c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 364 additions and 32 deletions

View File

@ -5,7 +5,7 @@
A third party Telegram client library [danog/MadelineProto](https://github.com/danog/MadelineProto) wrapper for Laravel. A third party Telegram client library [danog/MadelineProto](https://github.com/danog/MadelineProto) wrapper for Laravel.
# Usage # Getting Started
Add the laravel-madeline-proto to the project dependency: Add the laravel-madeline-proto to the project dependency:
@ -13,7 +13,7 @@ Add the laravel-madeline-proto to the project dependency:
composer require setiawanhu/laravel-madeline-proto composer require setiawanhu/laravel-madeline-proto
``` ```
Then publish the telegram config file: Then publish the `telegram.php` config file:
```shell script ```shell script
php artisan vendor:publish --provider="Hu\MadelineProto\MadelineProtoServiceProvider" php artisan vendor:publish --provider="Hu\MadelineProto\MadelineProtoServiceProvider"
@ -26,14 +26,11 @@ MP_TELEGRAM_API_ID=... //your telegram api id here
MP_TELEGRAM_API_HASH=... //your telegram api hash here MP_TELEGRAM_API_HASH=... //your telegram api hash here
``` ```
To do a login: This wrapper package supports for running both [single](https://github.com/setiawanhu/laravel-madeline-proto/wiki/Single-Telegram-Account) / [multiple](https://github.com/setiawanhu/laravel-madeline-proto/wiki/Multiple-Telegram-Account) telegram account.
* call `MadelineProto::phoneLogin(string $phone)` method to send the phone code. ## Dig Deeper
* call `MadelineProto::completePhoneLogin(string $code)` to complete the phone login by providing the phone code sent by the telegram. Please check [wiki](https://github.com/setiawanhu/sanctum-auth/wiki) for more details about laravel-madeline-proto usage
* You're logged in! Now you can use the `Messages` api.
# Notes # Notes

View File

@ -2,7 +2,7 @@
"name": "setiawanhu/laravel-madeline-proto", "name": "setiawanhu/laravel-madeline-proto",
"description": "A third party Telegram client library danog/MadelineProto wrapper for Laravel", "description": "A third party Telegram client library danog/MadelineProto wrapper for Laravel",
"type": "library", "type": "library",
"license": "GPL-2.0-only", "license": "AGPL-3.0-only",
"authors": [ "authors": [
{ {
"name": "Setiawan Hu", "name": "Setiawan Hu",
@ -13,7 +13,8 @@
"php": "^7.4", "php": "^7.4",
"danog/madelineproto": "^5.1", "danog/madelineproto": "^5.1",
"illuminate/support": "^7.0", "illuminate/support": "^7.0",
"illuminate/console": "^7.0" "illuminate/console": "^7.0",
"illuminate/database": "^7.0"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View File

@ -6,7 +6,7 @@ return [
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Madeline Proto Session File Location | Madeline Proto Sessions
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| To store information about an account session and avoid re-logging in, serialization must be done. | To store information about an account session and avoid re-logging in, serialization must be done.
@ -15,9 +15,21 @@ return [
| and on shutdown. If the scripts shutdowns normally (without ctrl+c or fatal errors/exceptions), the | and on shutdown. If the scripts shutdowns normally (without ctrl+c or fatal errors/exceptions), the
| session will also be serialized automatically. | session will also be serialized automatically.
| |
| Types: "single", "multiple"
|
*/ */
'session_file' => env('MADELINE_PROTO_SESSION_FILE', 'session.madeline'), 'sessions' => [
'single' => [
'session_file' => env('MP_SESSION_FILE', 'session.madeline'),
],
'multiple' => [
'table' => 'telegram_sessions'
],
],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -37,15 +49,15 @@ return [
'logger' => Logger::FILE_LOGGER, 'logger' => Logger::FILE_LOGGER,
'logger_param' => storage_path('logs/madeline-proto.log'), 'logger_param' => env('MP_LOGGER_PATH', storage_path('logs/madeline-proto.log')),
], ],
'app_info' => [ 'app_info' => [
'api_id' => env('TELEGRAM_API_ID'), 'api_id' => env('MP_TELEGRAM_API_ID'),
'api_hash' => env('TELEGRAM_API_HASH'), 'api_hash' => env('MP_TELEGRAM_API_HASH'),
], ],
], ],

View File

@ -0,0 +1,141 @@
<?php
namespace Hu\MadelineProto\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
class MultiSessionCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'madeline-proto:multi-session
{ --model : Including the telegram_session model }
{ --force : Overwrite the existing migration file }';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Publishing the multi session telegram migration table';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
if ($this->option('model')) {
$user = $this->ask('Telegram user model (for relation)', 'App/User');
if (file_exists(app_path()) && !$this->option('force')) {
if (!$this->confirm("The App/TelegramSession model is already exist. Replace it?")) {
$this->info('Multi session export aborted.');
return;
}
}
$this->exportModel($user);
$this->info('TelegramSession model generated.');
}
$tableName = config('telegram.sessions.multiple.table');
$migration = "2020_08_12_000000_create_{$tableName}_table.php";
if (file_exists(database_path("migrations/$migration")) && !$this->option('force')) {
if (!$this->confirm("The {$migration} migration file is already exist. Replace it?")) {
$this->info('Multi session export aborted.');
return;
}
}
$this->exportMigration($tableName, $user ?? null);
$this->info('Migration file exported.');
}
/**
* Export the telegram_session migration file.
*
* @param string $tableName
* @param string|null $relation
*/
public function exportMigration(string $tableName, string $relation = null)
{
if ($relation == null) {
$relation = 'App/User';
}
file_put_contents(
database_path("migrations/2020_08_12_000000_create_{$tableName}_table.php"),
$this->compileMigrationStub($tableName, $relation)
);
}
/**
* Export the TelegramSession model file.
*
* @param string $relation
*/
public function exportModel(string $relation)
{
file_put_contents(
app_path('TelegramSession.php'),
$this->compileModelStub($relation)
);
}
/**
* Compile the TelegramSession stub.
*
* @param string $user
* @return string
*/
public function compileModelStub(string $user)
{
$stub = file_get_contents(__DIR__ . '/stubs/telegram_session.stub');
$namespace = str_replace(
"/" . class_basename($user),
"",
$user
);
return str_replace(
['{{user}}', '{{package}}'],
[Str::snake(class_basename($user)), $namespace],
$stub
);
}
/**
* Compile the TelegramSession migration stub.
*
* @param string $tableName
* @param string $user
* @return string
*/
public function compileMigrationStub(string $tableName, string $user)
{
return str_replace(
['{{table}}', '{{user}}'],
[$tableName, Str::snake(class_basename($user))],
file_get_contents(__DIR__ . '/stubs/migration.stub')
);
}
}

View File

@ -34,12 +34,12 @@ class TelegramAccountLoginCommand extends Command
$phoneNumber = $this->ask('Phone number with country code (e.g: +6282112345678)?'); $phoneNumber = $this->ask('Phone number with country code (e.g: +6282112345678)?');
MadelineProto::sendPhoneCode($phoneNumber); MadelineProto::phoneLogin($phoneNumber);
$code = $this->ask('Phone code?'); $code = $this->ask('Phone code?');
try { try {
MadelineProto::signIn($code); MadelineProto::completePhoneLogin($code);
} catch (NeedTwoFactorAuthException $e) { } catch (NeedTwoFactorAuthException $e) {
$password = $this->ask("2FA Password (hint '{$e->account->hint}')"); $password = $this->ask("2FA Password (hint '{$e->account->hint}')");

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTelegramSessionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('{{table}}', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('{{user}}_id');
$table->string('session_file');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('{{table}}');
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace {{package}};
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
/**
* Class TelegramSession.
*
* @package {{package}}
* @mixin Builder
*
* @property int id
* @property int {{user}}_id
* @property string session_file
*/
class TelegramSession extends Model
{
/**
* @inheritdoc
*/
protected $fillable = [
'{{user}}_id', 'session_file'
];
}

25
src/Facades/Factory.php Normal file
View File

@ -0,0 +1,25 @@
<?php
namespace Hu\MadelineProto\Facades;
use Illuminate\Support\Facades\Facade;
use \Hu\MadelineProto\MadelineProto;
/**
* Facade for MadelineProtoFactory class.
*
* @package Hu\MadelineProto\Facades
*
* @method static MadelineProto get(mixed $session, array $config = null)
* @method static MadelineProto make(string $sessionFile, array $config)
*/
class Factory extends Facade
{
/**
* @inheritDoc
*/
protected static function getFacadeAccessor()
{
return 'madeline-proto-factory';
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace Hu\MadelineProto\Factories;
use danog\MadelineProto\API;
use Hu\MadelineProto\MadelineProto;
use Illuminate\Database\Connection;
use Illuminate\Database\DatabaseManager;
use Illuminate\Database\Eloquent\Model;
class MadelineProtoFactory
{
/**
* @var Connection
*/
private $database;
/**
* Table name.
*
* @var string
*/
private $table;
/**
* SessionFactory constructor.
*
* @param DatabaseManager $manager
* @param string $table
*/
public function __construct(DatabaseManager $manager, string $table)
{
$this->database = $manager->connection();
$this->table = $table;
}
/**
* Get the MadelineProto (session) instance from session table.
*
* @param int|Model $session can be either <b>id</b> or model instance of <b>TelegramSession</b> which
* generated from <u>madeline-proto:multi-session --model</u> command
* @param array|null $config if this parameter is null, then the config from <b>telegram.php</b>
* file will be used
* @return MadelineProto
*/
public function get($session, array $config = null)
{
if (is_null($config)) {
$config = config('telegram.settings');
}
if (is_int($session)) {
$session = $this->database->table($this->table)->find($session);
$sessionFile = $session->session_file;
} else {
$sessionFile = $session->session_file;
}
return $this->make($sessionFile, $config);
}
/**
* Generating MadelineProto (session) instance.
*
* @param string $sessionFile
* @param array $config
* @return MadelineProto
*/
public function make(string $sessionFile, array $config)
{
if (!file_exists(storage_path("app/telegram/"))) {
mkdir(storage_path("app/telegram"), 0755);
}
$client = new API(storage_path("app/telegram/$sessionFile"), $config);
return new MadelineProto($client);
}
}

View File

@ -40,8 +40,7 @@ class MadelineProto
* *
* @param string $code * @param string $code
* @return TelegramObject auth.Authorization * @return TelegramObject auth.Authorization
* @throws NeedTwoFactorAuthException * @throws NeedTwoFactorAuthException|SignUpNeededException
* @throws SignUpNeededException
*/ */
public function completePhoneLogin(string $code): TelegramObject public function completePhoneLogin(string $code): TelegramObject
{ {
@ -50,10 +49,8 @@ class MadelineProto
switch ($response->return_type) { switch ($response->return_type) {
case Account::PASSWORD: case Account::PASSWORD:
throw new NeedTwoFactorAuthException($response); throw new NeedTwoFactorAuthException($response);
break;
case Account::NEED_SIGN_UP: case Account::NEED_SIGN_UP:
throw new SignUpNeededException(); throw new SignUpNeededException();
break;
} }
return $response; return $response;
@ -142,4 +139,14 @@ class MadelineProto
{ {
return $this->fullGetSelf() !== false; return $this->fullGetSelf() !== false;
} }
/**
* Get MadelineProto Message API wrapper instance.
*
* @return ClientMessages
*/
public function messages()
{
return new ClientMessages($this->client->messages);
}
} }

View File

@ -2,8 +2,9 @@
namespace Hu\MadelineProto; namespace Hu\MadelineProto;
use danog\MadelineProto\API as Client; use Hu\MadelineProto\Commands\MultiSessionCommand;
use Hu\MadelineProto\Commands\TelegramAccountLoginCommand; use Hu\MadelineProto\Commands\TelegramAccountLoginCommand;
use Hu\MadelineProto\Factories\MadelineProtoFactory;
use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
@ -16,20 +17,27 @@ class MadelineProtoServiceProvider extends ServiceProvider
*/ */
public function register() public function register()
{ {
$this->app->singleton('madeline-proto-client', function () { $this->app->singleton('madeline-proto-factory', function (Application $app) {
return new Client(config('telegram.session_file'), config('telegram.settings')); return new MadelineProtoFactory($app->make('db'), config('telegram.sessions.multiple.table'));
}); });
$this->app->alias('madeline-proto-client', Client::class); $this->app->alias('madeline-proto-factory', MadelineProtoFactory::class);
$this->app->bind('madeline-proto', function (Application $app) { //Only for single Telegram session.
return new MadelineProto($app->make('madeline-proto-client'));
$this->app->singleton('madeline-proto', function (Application $app) {
$sessionFactory = $app->make('madeline-proto-factory');
return $sessionFactory->make(config('telegram.sessions.single.session_file'), config('telegram.settings'));
}); });
$this->app->alias('madeline-proto', MadelineProto::class); $this->app->alias('madeline-proto', MadelineProto::class);
$this->app->bind('madeline-proto-messages', function (Application $app) { $this->app->singleton('madeline-proto-messages', function (Application $app) {
$client = $app->make('madeline-proto-client'); $sessionFactory = $app->make('madeline-proto-factory');
return new ClientMessages($client->messages); return $sessionFactory->make(
config('telegram.sessions.single.session_file'),
config('telegram.settings')
)->messages();
}); });
$this->app->alias('madeline-proto-messages', ClientMessages::class); $this->app->alias('madeline-proto-messages', ClientMessages::class);
} }
@ -58,7 +66,8 @@ class MadelineProtoServiceProvider extends ServiceProvider
public function registerCommands() public function registerCommands()
{ {
$this->commands([ $this->commands([
TelegramAccountLoginCommand::class TelegramAccountLoginCommand::class,
MultiSessionCommand::class
]); ]);
} }
@ -69,7 +78,8 @@ class MadelineProtoServiceProvider extends ServiceProvider
{ {
return [ return [
'madeline-proto', 'madeline-proto',
'madeline-proto-client' 'madeline-proto-messages',
'madeline-proto-factory'
]; ];
} }
} }