PHP code example of jx3dev / onebot-v11

1. Go to this page and download the library: Download jx3dev/onebot-v11 library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

jx3dev / onebot-v11 example snippets


use OneBot\Annotation\BotCommand;
use OneBot\Context\CommandContext;

class Ping
{
    #[BotCommand('ping')]
    public function ping(CommandContext $ctx): void
    {
        $ctx->reply('pong');
    }
}

[
    'name' => 'onebot',
    'type' => Hyperf\Server\ServerInterface::SERVER_WEBSOCKET,
    'host' => '0.0.0.0',
    'port' => 9502,
    'sock_type' => SWOOLE_SOCK_TCP,
    'callbacks' => [
        Hyperf\Server\Event::ON_HAND_SHAKE => [Hyperf\WebSocketServer\Server::class, 'onHandShake'],
        Hyperf\Server\Event::ON_MESSAGE    => [Hyperf\WebSocketServer\Server::class, 'onMessage'],
        Hyperf\Server\Event::ON_CLOSE      => [Hyperf\WebSocketServer\Server::class, 'onClose'],
    ],
],

Router::addServer('onebot', function () {
    Router::get('/onebot/v11/ws', OneBot\WebSocketController::class);
});

Router::get('/onebot/v11/ws', OneBot\WebSocketController::class, [
    'middleware' => [OneBot\Middleware\AccessTokenMiddleware::class],
]);

'name' => 'http',
'type' => Hyperf\Server\ServerInterface::SERVER_WEBSOCKET,
'callbacks' => [
    Hyperf\Server\Event::ON_REQUEST     => [Hyperf\HttpServer\Server::class, 'onRequest'],
    Hyperf\Server\Event::ON_HAND_SHAKE  => [Hyperf\WebSocketServer\Server::class, 'onHandShake'],
    Hyperf\Server\Event::ON_MESSAGE     => [Hyperf\WebSocketServer\Server::class, 'onMessage'],
    Hyperf\Server\Event::ON_CLOSE       => [Hyperf\WebSocketServer\Server::class, 'onClose'],
],

Router::addRoute(['GET'], '/onebot/v11/ws', OneBot\WebSocketController::class);

use OneBot\Annotation\{BotCommand, BotMessage};
use OneBot\Context\{BotContext, CommandContext};

class Hello
{
    #[BotCommand('天气')]                              // "/天气 北京" 命中,前缀见 command_prefixes
    public function weather(CommandContext $ctx): void
    {
        $city = $ctx->args()->get(0, '本地');          // 第 0 个参数,越界给默认
        $ctx->reply("{$city}:晴");
    }

    #[BotMessage('group')]                             // 每条群消息都跑(命令之后)
    public function log(BotContext $ctx): void
    {
        // $ctx->userId() / $ctx->groupId() / $ctx->text() ...
    }
}

#[BotNotice('notify.poke')]        // 只在戳一戳时跑
#[BotNotice('group_ban')]          // 群禁言(不限 sub_type)
#[BotNotice]                       // 所有通知

#[BotCommand('echo')]
public function echo(CommandContext $ctx): void
{
    $args = $ctx->args();     // Args 值对象
    $args->get(0, 'def');     // 第 N 个位置参数,越界给默认(不抛)
    $args->int(0);            // 取整,非整 / 越界返回 null
    $args->rest(1);           // 从第 1 个参数起的剩余原文(保留中间空白)
    $args->all();             // 全部位置参数 list<string>
    $args->param('name');     // 正则具名捕获
    count($args);             // 位置参数个数;foreach ($args as $a) 迭代
}

use OneBot\Permission\Permission;
use OneBot\Contract\PermissionInterface;

#[BotCommand('ban', permission: [Permission::SUPER, Permission::ADMIN])]   // 超管或群管
public function ban(BotContext $ctx): void { /* ... */ }

class InWhitelist implements PermissionInterface {
    public function check(BotContext $ctx): bool { return true; /* 你的逻辑 */ }
}
#[BotCommand('x', permission: [Permission::SUPER, InWhitelist::class])]

#[BotMessage]                                   // 判个格式就转给命令
public function onAnswer(CommandContext $ctx): void
{
    if ($this->isIdiom($ctx->text())) {
        $ctx->runCommand('接龙答案', $ctx->text());
    }
}

#[BotCommand('天气预报')]                        // 别名 / 同义词转发
public function alias(CommandContext $ctx): void
{
    $ctx->runCommand('天气', $ctx->args()->rest());
}

if ($ctx->hasCommand('天气')) {                  // 装了某插件才转、没装自己处理:先探避免 warning
    $ctx->runCommand('天气', $city);
}

#[BotCommandBefore]                 // 默认 guarded=true:权限**之后**,只有过了权限的命令才过这里
public function prepare(CommandContext $ctx): void { /* 统一准备;stop()=拦截 */ }

#[BotCommandBefore(guarded: false)] // 权限**之前**、命中就过
public function guard(CommandContext $ctx): void { /* 想拦就 stop() */ }

#[BotCommandAfter]                  // 命令执行后(handler 抛异常也照跑)
public function done(CommandContext $ctx): void { /* ... */ }

use OneBot\Context\ActionContext;

#[BotActionBefore('send_group_msg')]          // 限速拒绝:给 failed 响应,call() 抛 ApiCallException
public function rateLimit(ActionContext $ctx): void
{
    if ($this->overLimit($ctx->selfId())) {
        $ctx->abort(['status' => 'failed', 'retcode' => 1400, 'message' => 'rate limited']);
    }
}

#[BotActionBefore('get_group_member_list')]   // 缓存命中:给 ok 响应,call() 直接返回其中 data、不发请求
public function cache(ActionContext $ctx): void
{
    if ($hit = $this->cacheGet($ctx->getParams())) {
        $ctx->abort(['status' => 'ok', 'retcode' => 0, 'data' => $hit]);
    }
}

use OneBot\Context\ExceptionContext;
use OneBot\Exception\ApiTimeoutException;

#[BotException(ApiTimeoutException::class)]
public function onTimeout(ExceptionContext $ctx): void { /* ... */ }

#[BotException]                                        // 兜底全部
public function onAny(ExceptionContext $ctx): void { /* ... */ }

use Hyperf\Framework\Event\AfterWorkerStart;
use OneBot\Dispatch\HandlerRegistry;

class Setup
{
    #[BotServerStart]
    public function boot(HandlerRegistry $registry): void
    {
        $registry->addAlias('天气', ['weather', 'tq']);   // fork 前加一次,各 Worker 继承
    }

    #[BotWorkerStart]
    public function warm(AfterWorkerStart $e): void
    {
        if ($e->workerId === 0) { /* 想「只做一次」判 workerId===0 */ }
    }
}

#[BotCommand('foo')]
#[BotMiddleware(MyMiddleware::class, args: ['limit' => 5])]
public function foo(CommandContext $ctx): void { /* ... */ }

use OneBot\Middleware\Cooldown;

#[BotCommand('查询')]
#[Cooldown(seconds: 60, reply: '手速太快,{remain}秒后再来')]   // 固定 60s;{remain}=剩余秒,reply 留空静默
public function query(CommandContext $ctx): void { /* ... */ }

#[Cooldown(seconds: 3, parallel: 2)]        // 窗口内允许 2 次(突发额度)
#[Cooldown(seconds: [10, 30, 120])]         // 递增:冷却中再犯逐级 10→30→120(封顶末级),熬过则复位

use OneBot\Middleware\Exclusive;

#[BotCommand('加入')]  #[Exclusive(key: 'seats', reply: '有人在操作,请稍候')]
#[BotCommand('退出')]  #[Exclusive(key: 'seats', reply: '有人在操作,请稍候')]

use OneBot\BotManager;

class Foo
{
    public function __construct(private BotManager $bots) {}

    public function bar(): void
    {
        $bot = $this->bots->get('123456789');   // 不在线抛 BotNotFoundException;find() 则返回 null
        // ...
    }
}

$bot->call('send_private_msg', ['user_id' => 10001, 'message' => 'hello']);
$bot->sendPrivateMsg(['user_id' => 10001, 'message' => 'hello']);        // 等价,驼峰转 send_private_msg
$bot->sendPrivateMsgAsync(['user_id' => 10001, 'message' => 'hi']);      // 后缀同理:send_private_msg_async
$bot->call('get_group_member_list', ['group_id' => 233], timeout: 60.0); // 单次覆盖超时

$bot->send('send_group_msg', ['group_id' => 233, 'message' => '公告']);

foreach ($this->bots->all() as $bot) {
    $bot->send('send_group_msg', ['group_id' => 233, 'message' => '公告']);
}

$bot = $this->bots->get('123456789');
foreach ($bot->call('get_group_list') as $g) {          // call 要等响应
    $bot->send('send_group_msg', ['group_id' => $g['group_id'], 'message' => '公告']);
}

use Hyperf\Crontab\Annotation\Crontab;
use OneBot\BotManager;

#[Crontab(rule: '0 9 * * *', name: 'morning', callback: 'run', memo: '每早 9 点问好')]
class MorningPush
{
    public function __construct(private BotManager $bots) {}

    public function run(): void
    {
        foreach ($this->bots->all() as $bot) {
            $bot->send('send_group_msg', ['group_id' => 233, 'message' => '早安']);
        }
    }
}

$ctx->selfId();        // 机器人 QQ(恒为字符串)
$ctx->userId();        // 发送者,可能 null
$ctx->groupId();       // 群号,可能 null
$ctx->messageId();
$ctx->text();          // 纯文本,已剥昵称前缀、归一空格
$ctx->rawText();       // 未剥昵称的纯文本
$ctx->message();       // Message 对象
$ctx->senderRole();    // owner / admin / member(群消息)
$ctx->isSuper(); $ctx->isOwner(); $ctx->isAdmin();
$ctx->isGroup(); $ctx->isPrivate(); $ctx->isToMe(); $ctx->isAtMe();
$ctx->field('sender.nickname');   // 读 payload 原始字段(支持点路径),取不到给默认
$ctx->identify();      // 一行事件标识,如 "Message 10050 from 12345@67890"
$ctx->describe();      // identify + 正文摘要(长内容截断),日志用

$ctx->reply('文本');                          // 回复;支持 string / Message / Segment / 段数组
$ctx->reply('内容', at: true, quote: true);   // 群里 @ 发送者、引用原消息
$ctx->finish('收工');                          // = reply + stop(),回一句并停掉整条事件的后续分发

#[BotCommand('删除')]
public function del(CommandContext $ctx): void
{
    $reply = $ctx->prompt('确定删除?回复 y 确认', timeout: 30.0);  // 挂起等下一条,超时返回 null
    if ($reply?->extractPlainText() === 'y') {
        $ctx->reply('已删除');
    }
}

$ctx->setTemp('key', $value);   // 可链式
$ctx->getTemp('key', $default);
$ctx->hasTemp('key');
$ctx->delTemp('key');

$ctx->stop();       // 停掉本条事件剩余的全部 handler(任意 handler 里都能调)
$ctx->stopTier();   // 只停当前这一层、继续下一层(仅事件分层内有效)
$ctx->bot();                 // 当前机器人
$ctx->bot('987654321');      // 换一个机器人来发

// 上下文:继承 CommandContext(这样命令 handler 也能拿 args()),写工厂实现 BotContextFactoryInterface
class MyContext extends OneBot\Context\CommandContext { public function tag(): string { return '...'; } }

// 机器人:继承 Bot,实现 BotFactoryInterface(构造参数照抄默认工厂)
class MyBot extends OneBot\Bot
{
    public function call(string $action, array $params = [], ?float $timeout = null): mixed
    {
        return parent::call($action, $params, $timeout);   // 前后包你的逻辑
    }
}

// config/autoload/dependencies.php
return [
    OneBot\Contract\BotContextFactoryInterface::class => MyContextFactory::class,
    OneBot\Contract\BotFactoryInterface::class        => MyBotFactory::class,
];

use OneBot\Message\{Segment, Message};

Segment::text('你好'); Segment::at('123'); Segment::atAll();
Segment::image('a.jpg'); Segment::face(1); Segment::reply(10050);
Segment::record('a.amr'); Segment::poke(1, 2); Segment::music('163', 126);

Message::text('hi')->append(Segment::at('123'));   // 不可变,append/merge 返回新对象
Message::parse('hi[CQ:at,qq=9]');                  // CQ 码字符串 → Message
(string) $msg;                                     // Message → CQ 码字符串
bash
composer rf.php vendor:publish jx3dev/onebot-v11

Worker 3  call()  ──push──> Sender ──> 机器人
                                        │
Worker 1  onMessage <──响应帧───────────┘
   │ 解出 echo 前缀 = 3
   └─ sendMessage ──> Worker 3 的 onPipeMessage ──> 唤醒挂起的协程