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?} {--agent= : Agent user id}', function (?string $url = null) { $agentId = $this->option('agent'); if ($agentId !== null && $agentId !== '') { $agent = \App\Models\User::query()->find((int) $agentId); if ($agent === null || ! $agent->hasTelegramBot()) { $this->error('Agent not found or telegram bot_token/chat_id missing'); return 1; } $token = (string) $agent->bot_token; $url = $url ?: rtrim((string) config('app.url'), '/').'/hooks/telegram/agent/'.$agent->id; $secret = \App\Telegram\TelegramBotContext::webhookSecretFor($agent); } else { $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]; 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 Telegram webhook (official or --agent=ID)'); 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(\App\Services\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('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)');