registerShared($bot, allowTransfer: true); } /** Agent bot — no /transfer; binds request context to this agent. */ public function registerAgent(Nutgram $bot, User $agent): void { $this->context->setAgent($agent); $this->registerShared($bot, allowTransfer: false); } private function registerShared(Nutgram $bot, bool $allowTransfer): void { // HandlerGroup::middleware() unshifts — register GroupAdminOnly first so // AuthorizedChat ends up outermost (chat allowlist before admin check). $bot->group(function (Nutgram $bot) use ($allowTransfer) { $bot->onCommand('help', HelpCommand::class) ->description('List available commands'); $bot->onCommand('ping', PingCommand::class) ->description('Health check'); $bot->onCommand('data', DataCommand::class) ->description('Visit stats (today). Optional: /data 1|7|30 [channelId]'); $bot->onCommand('data {args}', DataCommand::class) ->where('args', '.+') ->description('Visit stats for 1/7/30 days, optional channelId'); $bot->onCommand('channel', ChannelCommand::class) ->description('List channels with copyable support links'); if ($allowTransfer) { $bot->onCommand('transfer {args}', TransferCommand::class) ->where('args', '.+') ->middleware(OfficialOnly::class) ->description('Transfer TRX or USDT from a device address (omit amount = all)'); } }) ->middleware(GroupAdminOnly::class) ->middleware(AuthorizedChat::class); } }