使用MadelineProto获取Telegram多个会话消息无返回问题求助
问题根源分析
- 异步模式无返回:MadelineProto的异步基于AMP协程框架,你定义的
getMessagesById是生成器函数,直接调用不会触发执行,必须将协程挂载到事件循环中调度才会运行。 - 同步模式仅单会话返回:你在
getMessagesById中写了死循环while(true),第一个会话的调用进入循环后会一直阻塞,永远不会执行后续两个会话的调用逻辑。 - ajax轮询返回空:每次ajax请求都会重新初始化MadelineProto实例,会话文件序列化/反序列化存在锁机制,频繁启动进程会导致读取会话失败;同时你每次拉取都从offset_id=0开始,无新消息时自然返回空结果。
修复方案
异步模式最优实现(支持多会话并行拉取)
异步模式是MadelineProto官方推荐的生产环境使用方式,可实现多会话并行拉取无阻塞:
<?php require 'vendor/autoload.php'; use Amp\Promise; $settings['logger']['logger'] = 0; $settings['serialization']['serialization_interval'] = 30; $MadelineProto = new \danog\MadelineProto\API('session.madeline', $settings); $MadelineProto->async(true); $MadelineProto->start(); $chatList = [1234567, 3254682, 154789965]; function getMessagesById(int $u_id, \danog\MadelineProto\API $MadelineProto): \Generator { $offset_id = 0; $limit = 100; do { // 先转换为标准InputPeer格式,避免ID类型不匹配导致拉取失败 $peer = yield $MadelineProto->getInfo($u_id)['InputPeer']; $messages_Messages = yield $MadelineProto->messages->getHistory([ 'peer' => $peer, 'offset_id' => $offset_id, 'offset_date' => 0, 'add_offset' => 0, 'limit' => $limit, 'max_id' => 0, 'min_id' => 0, 'hash' => 0 ]); if (count($messages_Messages['messages']) == 0) { break; } print_r($messages_Messages); $offset_id = end($messages_Messages['messages'])['id']; // 拉取完所有历史消息后自动终止,增量拉取可配合更新钩子实现 if (count($messages_Messages['messages']) < $limit) { break; } yield $MadelineProto->sleep(3); } while (true); } // 将多个会话的拉取协程合并,挂载到事件循环执行 $MadelineProto->loop(function () use ($chatList, $MadelineProto) { $promises = []; foreach ($chatList as $chatId) { $promises[] = \Amp\call(fn() => getMessagesById($chatId, $MadelineProto)); } yield Promise\all($promises); });
异步方案说明
- 多会话拉取并行执行,效率远高于同步模式
- 新增InputPeer格式转换,兼容用户ID、群组ID、频道ID等多种peer类型,避免拉取失败
- 移除无意义的死循环,如需增量拉取可配合MadelineProto的
setUpdateHandler更新钩子实现,无需轮询
同步模式适配修改
如果必须使用同步模式,修改死循环逻辑即可支持多会话顺序拉取:
<?php require 'vendor/autoload.php'; $settings['logger']['logger'] = 0; $settings['serialization']['serialization_interval'] = 30; $MadelineProto = new \danog\MadelineProto\API('session.madeline', $settings); $MadelineProto->async(false); $MadelineProto->start(); $chatList = [1234567, 3254682, 154789965]; function getMessagesById(int $u_id, \danog\MadelineProto\API $MadelineProto): void { $offset_id = 0; $limit = 100; do { $peer = $MadelineProto->getInfo($u_id)['InputPeer']; $messages_Messages = $MadelineProto->messages->getHistory([ 'peer' => $peer, 'offset_id' => $offset_id, 'offset_date' => 0, 'add_offset' => 0, 'limit' => $limit, 'max_id' => 0, 'min_id' => 0, 'hash' => 0 ]); if (count($messages_Messages['messages']) == 0) { break; } print_r($messages_Messages); $offset_id = end($messages_Messages['messages'])['id']; if (count($messages_Messages['messages']) < $limit) { break; } sleep(3); } while (true); } // 循环调用多个会话拉取 foreach ($chatList as $chatId) { getMessagesById($chatId, $MadelineProto); }
轮询场景优化建议
- 不要通过ajax频繁启动新进程拉取消息,改为使用长驻进程运行MadelineProto,通过
setUpdateHandler监听新消息,将消息存入数据库/缓存,ajax直接从缓存读数据,可完全避免空返回问题 - 如果必须使用轮询,将每个会话的最新
offset_id存入缓存,下次拉取时传入缓存的offset,仅拉取更新的消息,减少无效请求 - 增加会话文件锁判断,避免多个进程同时读写同一个session.madeline文件导致读取失败
内容的提问来源于stack exchange,提问作者sqaderi
相关产品推荐
相关产品推荐

