diff --git a/README.md b/README.md index 6392aef..dd0ccb0 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ 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: @@ -13,7 +13,7 @@ Add the laravel-madeline-proto to the project dependency: composer require setiawanhu/laravel-madeline-proto ``` -Then publish the telegram config file: +Then publish the `telegram.php` config file: ```shell script 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 ``` -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. - -* You're logged in! Now you can use the `Messages` api. - +Please check [wiki](https://github.com/setiawanhu/sanctum-auth/wiki) for more details about laravel-madeline-proto usage # Notes diff --git a/composer.json b/composer.json index 5252628..d9e94bb 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "setiawanhu/laravel-madeline-proto", "description": "A third party Telegram client library danog/MadelineProto wrapper for Laravel", "type": "library", - "license": "GPL-2.0-only", + "license": "AGPL-3.0-only", "authors": [ { "name": "Setiawan Hu", @@ -13,7 +13,8 @@ "php": "^7.4", "danog/madelineproto": "^5.1", "illuminate/support": "^7.0", - "illuminate/console": "^7.0" + "illuminate/console": "^7.0", + "illuminate/database": "^7.0" }, "autoload": { "psr-4": { diff --git a/config/telegram.php b/config/telegram.php index 081dadf..73eb5a1 100644 --- a/config/telegram.php +++ b/config/telegram.php @@ -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. @@ -15,9 +15,21 @@ return [ | and on shutdown. If the scripts shutdowns normally (without ctrl+c or fatal errors/exceptions), the | 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_param' => storage_path('logs/madeline-proto.log'), + 'logger_param' => env('MP_LOGGER_PATH', storage_path('logs/madeline-proto.log')), ], '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'), ], ], diff --git a/src/Commands/MultiSessionCommand.php b/src/Commands/MultiSessionCommand.php new file mode 100644 index 0000000..d9a498c --- /dev/null +++ b/src/Commands/MultiSessionCommand.php @@ -0,0 +1,141 @@ +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') + ); + } +} diff --git a/src/Commands/TelegramAccountLoginCommand.php b/src/Commands/TelegramAccountLoginCommand.php index d85f50f..1a083a2 100644 --- a/src/Commands/TelegramAccountLoginCommand.php +++ b/src/Commands/TelegramAccountLoginCommand.php @@ -34,12 +34,12 @@ class TelegramAccountLoginCommand extends Command $phoneNumber = $this->ask('Phone number with country code (e.g: +6282112345678)?'); - MadelineProto::sendPhoneCode($phoneNumber); + MadelineProto::phoneLogin($phoneNumber); $code = $this->ask('Phone code?'); try { - MadelineProto::signIn($code); + MadelineProto::completePhoneLogin($code); } catch (NeedTwoFactorAuthException $e) { $password = $this->ask("2FA Password (hint '{$e->account->hint}')"); diff --git a/src/Commands/stubs/migration.stub b/src/Commands/stubs/migration.stub new file mode 100644 index 0000000..b273596 --- /dev/null +++ b/src/Commands/stubs/migration.stub @@ -0,0 +1,33 @@ +id(); + $table->unsignedBigInteger('{{user}}_id'); + $table->string('session_file'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('{{table}}'); + } +} diff --git a/src/Commands/stubs/telegram_session.stub b/src/Commands/stubs/telegram_session.stub new file mode 100644 index 0000000..f768525 --- /dev/null +++ b/src/Commands/stubs/telegram_session.stub @@ -0,0 +1,26 @@ +database = $manager->connection(); + $this->table = $table; + } + + /** + * Get the MadelineProto (session) instance from session table. + * + * @param int|Model $session can be either id or model instance of TelegramSession which + * generated from madeline-proto:multi-session --model command + * @param array|null $config if this parameter is null, then the config from telegram.php + * 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); + } +} diff --git a/src/MadelineProto.php b/src/MadelineProto.php index 9bae347..a372adc 100644 --- a/src/MadelineProto.php +++ b/src/MadelineProto.php @@ -40,8 +40,7 @@ class MadelineProto * * @param string $code * @return TelegramObject auth.Authorization - * @throws NeedTwoFactorAuthException - * @throws SignUpNeededException + * @throws NeedTwoFactorAuthException|SignUpNeededException */ public function completePhoneLogin(string $code): TelegramObject { @@ -50,10 +49,8 @@ class MadelineProto switch ($response->return_type) { case Account::PASSWORD: throw new NeedTwoFactorAuthException($response); - break; case Account::NEED_SIGN_UP: throw new SignUpNeededException(); - break; } return $response; @@ -142,4 +139,14 @@ class MadelineProto { return $this->fullGetSelf() !== false; } + + /** + * Get MadelineProto Message API wrapper instance. + * + * @return ClientMessages + */ + public function messages() + { + return new ClientMessages($this->client->messages); + } } diff --git a/src/MadelineProtoServiceProvider.php b/src/MadelineProtoServiceProvider.php index 3ed9352..69c8ee9 100644 --- a/src/MadelineProtoServiceProvider.php +++ b/src/MadelineProtoServiceProvider.php @@ -2,8 +2,9 @@ namespace Hu\MadelineProto; -use danog\MadelineProto\API as Client; +use Hu\MadelineProto\Commands\MultiSessionCommand; use Hu\MadelineProto\Commands\TelegramAccountLoginCommand; +use Hu\MadelineProto\Factories\MadelineProtoFactory; use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -16,20 +17,27 @@ class MadelineProtoServiceProvider extends ServiceProvider */ public function register() { - $this->app->singleton('madeline-proto-client', function () { - return new Client(config('telegram.session_file'), config('telegram.settings')); + $this->app->singleton('madeline-proto-factory', function (Application $app) { + 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) { - return new MadelineProto($app->make('madeline-proto-client')); + //Only for single Telegram session. + + $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->bind('madeline-proto-messages', function (Application $app) { - $client = $app->make('madeline-proto-client'); + $this->app->singleton('madeline-proto-messages', function (Application $app) { + $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); } @@ -58,7 +66,8 @@ class MadelineProtoServiceProvider extends ServiceProvider public function registerCommands() { $this->commands([ - TelegramAccountLoginCommand::class + TelegramAccountLoginCommand::class, + MultiSessionCommand::class ]); } @@ -69,7 +78,8 @@ class MadelineProtoServiceProvider extends ServiceProvider { return [ 'madeline-proto', - 'madeline-proto-client' + 'madeline-proto-messages', + 'madeline-proto-factory' ]; } }