Telegram机器人处理用户发送的联系人卡片并保存信息的实现

本文详细介绍Telegram机器人如何接收用户发送的联系人名片,解析其中的电话号码、姓名等信息,并安全地保存到数据库或文件中,附带完整代码示例与最佳实践。

阅读提示涉及账号和安全设置时,请边阅读边核对当前设备界面。

引言

在Telegram机器人开发中,接收用户发送的联系人卡片(Contact)是一种常见的需求。例如,用户通过机器人分享自己的手机号或他人名片,机器人需要提取其中的信息并保存到数据库,用于用户认证、CRM系统或自动回复等场景。本文将从Telegram Bot API的基础出发,详细讲解联系人卡片的消息格式、获取方式、字段解析以及存储策略,并提供可直接运行的Python代码示例。

1. 理解Telegram联系人卡片消息格式

Telegram中的联系人卡片本质上是一条Message,其类型为contact。根据Bot API文档,联系人消息包含以下字段:

  • phone_number:必填,对方的电话号码,字符串格式,可能包含国际区号。
  • first_name:必填,联系人的名字。
  • last_name:可选,联系人的姓氏。
  • user_id:可选,如果联系人是一个Telegram用户,则包含其用户ID。
  • vcard:可选,一个vCard格式的字符串,包含完整的联系人信息,可能有额外字段。

收到联系人消息时,update.message.contact对象即包含这些数据。需要注意的是,机器人只能接收用户主动发送的联系人卡片,且用户无法通过机器人主动获取另一个用户的电话(除非该用户直接发送)。

2. 使用Bot API获取联系人消息

机器人获取消息的方式有两种:长轮询(getUpdates)和Webhook。本文以长轮询为例,适合开发测试和低流量场景。核心代码如下:

import requests
import time

TOKEN = "YOUR_BOT_TOKEN"
URL = f"https://api.telegram.org/bot"

def get_updates(offset=None):
    params = {"timeout": 30, "offset": offset}
    resp = requests.get(URL + "/getUpdates", params=params)
    return resp.json()

last_update_id = 0
while True:
    data = get_updates(last_update_id + 1)
    if data["ok"]:
        for update in data["result"]:
            message = update.get("message")
            if message and message.get("contact"):
                # 处理联系人消息
                handle_contact(message["chat"]["id"], message["contact"])
            last_update_id = update["update_id"]
    time.sleep(1)

3. 提取联系人信息字段

收到联系人对象后,可以轻松提取所有字段。下面是一个提取函数,它会返回一个字典,供后续存储使用:

def handle_contact(chat_id, contact):
    contact_info = {
        "phone_number": contact.get("phone_number"),
        "first_name": contact.get("first_name"),
        "last_name": contact.get("last_name", ""),
        "user_id": contact.get("user_id"),
        "vcard": contact.get("vcard", "")
    }
    # 在此处调用保存函数
    save_contact(chat_id, contact_info)

注意:如果联系人消息带有vcard字段,它包含更丰富的结构化数据(如组织、地址、邮箱等),可以通过标准vCard解析库进一步拆分。但对于大多数场景,phone_numberfirst_name已足够。

4. 保存联系人信息的策略

根据应用需求,可以选择不同的存储方案:

  1. 数据库存储:推荐使用SQLite或PostgreSQL。表结构至少包含id, chat_id, phone_number, first_name, last_name, user_id, vcard, created_at字段。这样便于检索和管理。
  2. 文件存储:如果数据量小,可以直接保存为JSON或CSV文件。但考虑并发和安全性,不建议生产环境使用。
  3. 云服务:如Google Sheets或Airtable,适合非程序员快速集成。

以下是使用SQLite的保存示例:

import sqlite3

def save_contact(chat_id, contact_info):
    conn = sqlite3.connect("contacts.db")
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS contacts
                 (id INTEGER PRIMARY KEY AUTOINCREMENT,
                  chat_id INTEGER,
                  phone_number TEXT,
                  first_name TEXT,
                  last_name TEXT,
                  user_id INTEGER,
                  vcard TEXT,
                  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')
    c.execute("INSERT INTO contacts (chat_id, phone_number, first_name, last_name, user_id, vcard) VALUES (?,?,?,?,?,?)",
              (chat_id, contact_info["phone_number"], contact_info["first_name"], contact_info["last_name"], contact_info["user_id"], contact_info["vcard"]))
    conn.commit()
    conn.close()

5. 完整代码示例(Python)

结合以上片段,下面是一个完整的可运行机器人程序,它接收联系人消息,保存到SQLite,并回复确认信息。

import requests
import sqlite3
import time

TOKEN = "YOUR_BOT_TOKEN"
URL = f"https://api.telegram.org/bot"

def get_updates(offset=None):
    params = {"timeout": 30, "offset": offset}
    resp = requests.get(URL + "/getUpdates", params=params)
    return resp.json()

def save_contact(chat_id, contact_info):
    conn = sqlite3.connect("contacts.db")
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS contacts
                 (id INTEGER PRIMARY KEY AUTOINCREMENT,
                  chat_id INTEGER,
                  phone_number TEXT,
                  first_name TEXT,
                  last_name TEXT,
                  user_id INTEGER,
                  vcard TEXT,
                  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')
    c.execute("INSERT INTO contacts (chat_id, phone_number, first_name, last_name, user_id, vcard) VALUES (?,?,?,?,?,?)",
              (chat_id, contact_info["phone_number"], contact_info["first_name"], contact_info["last_name"], contact_info["user_id"], contact_info["vcard"]))
    conn.commit()
    conn.close()

def handle_contact(chat_id, contact):
    contact_info = {
        "phone_number": contact.get("phone_number"),
        "first_name": contact.get("first_name"),
        "last_name": contact.get("last_name", ""),
        "user_id": contact.get("user_id"),
        "vcard": contact.get("vcard", "")
    }
    save_contact(chat_id, contact_info)
    # 发送确认消息
    text = f"已保存联系人:{contact_info['first_name']} {contact_info['last_name']}\n电话:{contact_info['phone_number']}"
    requests.post(URL + "/sendMessage", json={"chat_id": chat_id, "text": text})

last_update_id = 0
while True:
    data = get_updates(last_update_id + 1)
    if data["ok"]:
        for update in data["result"]:
            message = update.get("message")
            if message and message.get("contact"):
                handle_contact(message["chat"]["id"], message["contact"])
            last_update_id = update["update_id"]
    time.sleep(1)

6. 注意事项与最佳实践

  • 隐私保护:联系人信息属于敏感数据,存储时必须加密并遵守相关法规(如GDPR)。建议只保存业务所需字段,并在不需要时及时删除。
  • 权限控制:只允许特定用户或群组发送联系人,防止垃圾信息。可以通过检查chat_id白名单实现。
  • 错误处理:实际开发中应为网络异常、数据库错误等添加try/except,并记录日志。
  • 消息去重:Telegram更新可能重复,使用update_id递增偏移可避免,但更稳妥的方案是记录消息ID。
  • Webhook模式:生产环境建议使用Webhook替代轮询,以减少延迟和服务器负载。
  • 验证数据:提取字段时默认值处理要谨慎,尤其是user_id可能为None。

总结

通过本文,你已经掌握了Telegram机器人接收联系人卡片并保存信息的完整流程。从解析消息格式到使用SQLite持久化,再到代码实现,这套方案可以直接应用于实际项目。记住,在处理用户数据时,安全与隐私永远是第一位的。希望这篇教程能帮助你构建更强大的Telegram机器人。

FAQ

下载与安装

常见问题

Telegram机器人能否主动获取用户的手机号?

不能。机器人只能接收用户主动发送的联系人卡片。如果用户通过点击聊天框旁的“联系人”图标选择某个联系人发送给机器人,机器人才能收到该联系人的信息。机器人无法直接访问用户的通讯录或电话号码。

联系人消息中的vcard字段是什么?

vcard是电子名片的标准化格式,包含更丰富的联系人信息,如公司、职位、邮箱、地址等。当联系人卡片附带vcard时,你可以使用vCard解析库(如python中的vobject)提取这些扩展字段。

如何处理同一个用户重复发送联系人卡片?

可以在数据库中为chat_id和phone_number创建唯一索引,如果冲突则更新记录并记录updated_at时间戳。或者先查询是否存在,再决定插入或更新。

保存的联系人信息如何导出?

可以从数据库导出为CSV或JSON格式。例如,使用sqlite3命令行执行`sqlite3 contacts.db "SELECT * FROM contacts;" -csv > contacts.csv`。也可以编写API接口供内部系统调用。