在 Telegram 机器人开发中,获取群组内所有成员的列表是一个常见需求,例如用于数据分析、成员统计或精准运营。然而,Telegram Bot API 并未提供类似于 getChatMembers 的便捷接口,直接在协议层面阻止了开发者批量拉取成员信息。本文将深入解析这一限制,并给出一个基于消息监听与数据库存储的分页实现方案,帮助你在现有 API 下优雅地解决成员列表管理问题。
Telegram Bot API 的限制:为什么无法直接获取成员列表?
Telegram 官方 Bot API 是基于 HTTP 的接口,提供了一组有限但强大的方法。但当我们尝试获取群组成员列表时,会发现根本没有对应的方法。官方仅提供了以下相关接口:
getChat:获取群组基本信息,包含成员数量,但不包含成员明细。getChatMember:需要指定user_id才能查询单个成员,无法枚举。getChatAdministrators:仅返回管理员列表,不包含普通成员。getChatMemberCount:仅返回成员总数,不返回名单。
这种设计的核心原因是隐私保护。Telegram 高度重视用户隐私,不希望任何第三方(包括机器人)随意抓取群组成员信息。因此,纯 Bot API 方案下,获取完整成员列表在技术上基本不可能。
可行的替代方案概览
虽然 Bot API 有限制,但我们可以通过以下变通方式收集成员数据:
- 监听群组消息:当用户发消息时,从消息的
from字段中提取用户信息,存入数据库。这是最合规且简单的方法,但只能收集“有发言记录”的成员。 - 使用 MTProto 用户客户端:例如 Telethon / Pyrogram,通过用户账号登录后可获取完整成员列表。但这种方式必须使用用户账号而非常规 Bot Token,且存在封号风险,不适合作为长期机器人方案。
- 利用邀请链接或手动统计:通过机器人创建邀请链接,新用户加入时会产生服务消息,从中捕获用户。但只有新加入的成员能被记录。
对于大多数应用场景,方案一(消息监听)是最平衡的选择。下面我们详细讲解如何实现。
基于消息监听的成员数据采集
第一步:设计数据库表
我们使用 SQLite 或 MySQL 存储成员数据。一个简单的表结构如下:
CREATE TABLE members (
id INTEGER PRIMARY KEY,
user_id BIGINT UNIQUE NOT NULL,
username TEXT,
first_name TEXT,
last_name TEXT,
chat_id BIGINT NOT NULL,
first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
第二步:监听群组消息
以 Python 的 python-telegram-bot 为例(v20+),我们可以在消息处理器中提取用户信息,并写入数据库。关键代码如下:
from telegram.ext import Application, MessageHandler, filters
from datetime import datetime
import sqlite3
def save_member(chat_id, user):
conn = sqlite3.connect('telegram.db')
cur = conn.cursor()
cur.execute(
"""INSERT INTO members (user_id, username, first_name, last_name, chat_id, last_seen)
VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(user_id) DO UPDATE SET
username=excluded.username,
first_name=excluded.first_name,
last_name=excluded.last_name,
last_seen=excluded.last_seen""",
(user.id, user.username or '', user.first_name or '', user.last_name or '', chat_id, datetime.now())
)
conn.commit()
conn.close()
async def handle_message(update, context):
message = update.message
if message.chat.type in ('group', 'supergroup'):
save_member(message.chat.id, message.from_user)
app = Application.builder().token('YOUR_BOT_TOKEN').build()
app.add_handler(MessageHandler(filters.TEXT & filters.ChatType.GROUPS, handle_message))
app.run_polling()
这段代码会在每次群成员发消息时,将用户信息 upsert 到数据库中。注意 ON CONFLICT 用于避免重复插入,并更新最后活跃时间。
第三步:记录新成员加入事件
除了消息,机器人还可以监听 new_chat_members 服务消息,以便及时捕获新加入的成员。
async def handle_new_members(update, context):
for new_member in update.message.new_chat_members:
save_member(update.message.chat.id, new_member)
app.add_handler(MessageHandler(filters.StatusUpdate.NEW_CHAT_MEMBERS, handle_new_members))
分页查询接口实现
当数据库积累了成员数据后,我们就可以设计一个分页查询接口,供前端或管理后台调用。以下是一个简单的分页逻辑实现:
def get_members_paginated(chat_id, page=1, page_size=50):
offset = (page - 1) * page_size
conn = sqlite3.connect('telegram.db')
cur = conn.cursor()
total = cur.execute(
'SELECT COUNT(*) FROM members WHERE chat_id = ?', (chat_id,)
).fetchone()[0]
rows = cur.execute(
'SELECT * FROM members WHERE chat_id = ? LIMIT ? OFFSET ?',
(chat_id, page_size, offset)
).fetchall()
conn.close()
members = []
for row in rows:
members.append({
'user_id': row[1],
'username': row[2],
'first_name': row[3],
'last_name': row[4],
'last_seen': row[6]
})
return {
'total': total,
'page': page,
'page_size': page_size,
'total_pages': (total + page_size - 1) // page_size,
'members': members
}
该函数接收 chat_id 和分页参数,返回总记录数、当前页、每页大小、总页数和当前成员列表。你可以将其封装为 HTTP API,例如使用 Flask FastAPI,或直接在机器人中通过命令触发。
性能优化与注意事项
- 避免频繁 I/O:对于高并发群组,推荐使用 Redis 缓存热门群组的成员列表,或使用异步数据库驱动。
- 索引优化:在
chat_id和user_id上建立复合索引,可大幅提升查询速度。 - 隐私合规:切勿公开成员的用户 ID 或个人信息,遵守 Telegram 服务条款和本地法律法规。
- 数据不完整:此方案只能收集“发过言”的成员,对于潜水成员,可考虑结合邀请链接等其他方式补充。
- 处理成员退出:如需同步移除已退出的成员,可监听
left_chat_member事件,并在数据库中标记或删除。
总结
Telegram Bot API 未提供直接获取群组成员列表的方法,这是出于隐私保护的刻意设计。通过监听群组消息和服务消息,我们可以逐步构建成员数据库,并利用分页查询接口实现成员列表的浏览与管理。虽然该方法存在一定延迟和覆盖不完整的问题,但在合规性、易用性和安全性上是最理想的折中方案。未来如果 Telegram 官方开放更全面的成员接口,届时可以轻松切换至官方方案。