在Telegram群组或频道中,随着消息数量增长,快速找到特定内容成为刚需。然而,Telegram Bot API并不提供直接搜索聊天历史的接口。本文将带你通过消息记录与数据库索引,开发一个能够实现聊天内搜索的机器人,并提供完整的Python代码与进阶优化方案。
为什么Telegram机器人需要聊天搜索功能?
无论是管理一个活跃的社群,还是维护知识型频道,用户经常需要回溯历史消息。机器人作为自动化助手,若能提供关键词搜索,将大幅提升效率。但受限于Bot API,无法读取历史消息,只能通过实时记录积累数据,再基于数据库实现自定义搜索。
核心思路:消息记录与数据库索引
机器人在加入群组后,通过监听所有消息(需关闭隐私模式)将消息内容、发送者、时间等信息写入数据库。当用户执行/search命令时,机器人从数据库检索关键词,并以分页形式返回结果。整个过程不依赖官方未开放的接口,完全合规。
环境准备与数据库设计
我们使用Python的python-telegram-bot(v20+)作为机器人框架,SQLite作为存储引擎。首先安装依赖:
pip install python-telegram-bot
数据库表设计如下:
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
chat_id INTEGER,
message_id INTEGER,
user_id INTEGER,
username TEXT,
text TEXT,
date INTEGER
);
字段包括聊天ID、消息ID、发送者ID、用户名、消息文本、时间戳。使用AUTOINCREMENT保证消息顺序。
实现消息监听与存储
创建一个异步处理器,对Message类型进行监听。注意在BotFather中通过/setprivacy将机器人设置为Disable,才能接收群组内所有消息。
import sqlite3
from telegram import Update
from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes
conn = sqlite3.connect('messages.db', check_same_thread=False)
cursor = conn.cursor()
cursor.execute('''CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
chat_id INTEGER,
message_id INTEGER,
user_id INTEGER,
username TEXT,
text TEXT,
date INTEGER
)''')
conn.commit()
async def record_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
msg = update.message
if not msg.text:
return
cursor.execute(
'INSERT INTO messages (chat_id, message_id, user_id, username, text, date) VALUES (?,?,?,?,?,?)',
(msg.chat.id, msg.message_id, msg.from_user.id, msg.from_user.username, msg.text, msg.date.timestamp())
)
conn.commit()
def main():
app = Application.builder().token('YOUR_BOT_TOKEN').build()
app.add_handler(MessageHandler(filters.TEXT & (~filters.COMMAND), record_message))
app.run_polling()
以上代码会将所有文本消息存入SQLite表。注意线程安全,使用check_same_thread=False。
实现关键词搜索命令
添加/search命令,用户输入关键词后,从数据库查询,并展示结果。为控制输出长度,我们分页显示,每次5条。
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import CommandHandler, ContextTypes
async def search(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not context.args:
await update.message.reply_text('用法:/search 关键词')
return
keyword = ' '.join(context.args)
cursor.execute(
'SELECT * FROM messages WHERE text LIKE ? ORDER BY id DESC LIMIT 50',
(f'%%',)
)
rows = cursor.fetchall()
if not rows:
await update.message.reply_text('未找到匹配消息。')
return
# 显示前5条
text = '\n\n'.join(
f"{r[4] or '未知用户'} ({r[3]}): {r[5]}" for r in rows[:5]
)
await update.message.reply_text(f'找到 {len(rows)} 条结果,显示前5条:\n\n')
def main():
app.add_handler(CommandHandler('search', search))
对于大量结果,可增加回调分页。以下是一个简单分页按钮示例:
async def page_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
# 解析页码,继续查询并渲染
# 实现略...
进阶优化:使用SQLite FTS5全文搜索
LIKE查询对中文支持较差,且全表扫描性能低下。SQLite的FTS5扩展支持全文搜索,如果数据源是纯文本,可以配合分词实现高效检索。创建FTS5虚拟表:
CREATE VIRTUAL TABLE messages_fts USING fts5(text, content='messages', content_rowid='id');
INSERT INTO messages_fts(rowid, text) SELECT id, text FROM messages;
查询时使用MATCH:
SELECT * FROM messages_fts WHERE messages_fts MATCH ?
中文搜索还需引入分词,例如使用simple分词器或提前用jieba切分后存储到独立字段。需要更全面的方案,可以参考相关分词库文档。
总结与部署建议
通过本文的代码,你可以快速实现一个具备聊天内搜索能力的Telegram机器人。关键点在于消息的实时记录与数据库的有效索引。建议在部署时考虑以下几点:
- 使用环境变量存储BOT_TOKEN,避免硬编码。
- 定期备份数据库文件,防止数据丢失。
- 若消息量极大,可迁移到PostgreSQL或MySQL,并使用Redis缓存搜索结果。
- 注意用户隐私,不要在搜索结果中暴露敏感信息,遵守平台规范。
希望本教程能帮助你打造更智能的Telegram机器人。如果你有更多优化思路,欢迎在实践中探索。