Skip to content

保存机器人消息 ​

引言 ​

在开发 Telegram 机器人时,经常需要对接收到的消息进行存储和处理,以便于后续的分析和响应。为了高效地实现这一需求,我们需要深入理解消息的数据结构,提取关键信息,并设计合理的数据库表结构。本文将详细介绍如何从 SendMessageEvent 中提取必要的数据,设计数据库表,并实现消息的存储。

1. SendMessageEvent 数据分析 ​

SendMessageEvent 是 Telegram 机器人接收到消息时触发的事件,其中包含了丰富的消息和用户信息。为了有效地存储这些信息,我们需要明确需要提取和保存的关键字段。

1.1 关键字段 ​

在 SendMessageEvent 中,以下字段对于消息处理和存储至关重要:

  • message.Message.data.Variant2.t1.BaseMessage:

    • id: 消息的唯一标识符。
    • fromId.PeerUser.userId: 发送者的用户 ID。
    • peerId.PeerUser.userId: 接收者的用户 ID(通常是机器人自身)。
    • date: 消息发送的时间戳。
    • message: 消息的文本内容。
    • entities: 消息中的实体(如命令、链接等)。
  • chat.PrivateChat.user.User.minData.BaseUser:

    • id: 聊天中的另一位用户的 ID。
    • firstName: 用户的名字。
    • lastName: 用户的姓氏。
    • username: 用户的用户名。
  • chat.PrivateChat.selfUser.User.minData.BaseUser:

    • id: 机器人的用户 ID。
    • firstName: 机器人的名字。
    • username: 机器人的用户名。
  • author.User.minData.BaseUser:

    • id: 消息作者的 ID。
    • firstName: 消息作者的名字。
    • username: 消息作者的用户名。

1.2 数据提取 ​

基于以上字段,我们可以提取以下关键信息:

  • 消息基本信息:

    • 消息 ID (id)
    • 发送者 ID (fromId)
    • 接收者 ID (toId)
    • 消息内容 (message)
    • 消息发送时间 (date)
    • 消息类型(可根据 entities 或其他标志位判断)
  • 用户信息:

    • 发送者的用户名、名字和姓氏
    • 接收者(通常是机器人)的用户名、名字和姓氏

2. 数据库表设计 ​

为了存储提取的信息,我们需要设计一个合适的数据库表。

2.1 表结构设计 ​

创建一个名为 telegram_bot_messages 的表,包含以下字段:

字段名数据类型描述约束
idBIGINT消息 IDPRIMARY KEY
from_idBIGINT发送者的用户 IDNOT NULL
to_idBIGINT接收者的用户 IDNOT NULL
sequenceINT消息序列 IDNOT NULL
message_textTEXT消息内容NOT NULL
message_dateTIMESTAMP消息发送时间NOT NULL
message_typeVARCHAR(50)消息类型(如文本、命令等)NOT NULL
entitiesTEXT消息中的实体(JSON 格式存储)NULLABLE
from_usernameVARCHAR(100)发送者的用户名NULLABLE
from_first_nameVARCHAR(100)发送者的名字NULLABLE
from_last_nameVARCHAR(100)发送者的姓氏NULLABLE
to_usernameVARCHAR(100)接收者的用户名NULLABLE
to_first_nameVARCHAR(100)接收者的名字NULLABLE
to_last_nameVARCHAR(100)接收者的姓氏NULLABLE
create_timeTIMESTAMP记录创建时间DEFAULT CURRENT_TIMESTAMP
update_timeTIMESTAMP记录更新时间DEFAULT CURRENT_TIMESTAMP
tenant_idBIGINT租户 ID(默认为 0)NOT NULL DEFAULT 0

2.2 创建表的 SQL 语句 ​

sql
DROP TABLE IF EXISTS telegram_bot_messages;
CREATE TABLE telegram_bot_messages (
    id BIGINT PRIMARY KEY,
    from_id BIGINT NOT NULL,
    to_id BIGINT NOT NULL,
    sequence INT NOT NULL,
    message_text TEXT NOT NULL,
    message_date TIMESTAMP NOT NULL,
    message_type VARCHAR(50) NOT NULL,
    entities TEXT,
    from_username VARCHAR(100),
    from_first_name VARCHAR(100),
    from_last_name VARCHAR(100),
    to_username VARCHAR(100),
    to_first_name VARCHAR(100),
    to_last_name VARCHAR(100),
    create_time TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
    update_time TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
    tenant_id BIGINT NOT NULL DEFAULT 0
);

2.3 字段说明 ​

  • id: 消息的唯一标识符,用于唯一标识每条消息。
  • from_id: 发送者的用户 ID,用于关联用户信息。
  • to_id: 接收者的用户 ID,可以是另一个用户或机器人自身。
  • sequence: 消息的序列 ID,用于表示消息的顺序。
  • message_text: 存储消息的具体内容。
  • message_date: 消息发送的时间,便于按时间排序和查询。
  • message_type: 消息的类型,如 Private,GROUP,SUPERGROUP,CHANNEL。
  • entities: 以 JSON 字符串格式存储消息中的实体信息,如命令、链接等,便于后续解析和处理。
  • from_username、from_first_name、from_last_name: 发送者的基本信息,便于快速查看和展示。
  • to_username、to_first_name、to_last_name: 接收者的基本信息,便于快速查看和展示。
  • create_time: 记录数据插入的时间,便于审计和数据管理。
  • update_time: 记录数据更新时间。
  • tenant_id: 租户 ID,便于多租户系统的数据隔离。

3. 数据存储实现 ​

完成数据库表设计后,需要在代码中实现将接收到的 SendMessageEvent 数据存储到数据库的逻辑。

3.1 数据模型定义 ​

首先,定义一个与数据库表对应的实体类 TelegramMessage。

java
package nexus.io.gpt.translator.vo;

import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class TelegramMessage {
  private Long id;
  private Long fromId;
  private Long toId;
  private Integer sequence;
  private String messageText;
  private LocalDateTime messageDate;
  private String messageType;
  private String entities;
  private String fromUsername;
  private String fromFirstName;
  private String fromLastName;
  private String toUsername;
  private String toFirstName;
  private String toLastName;
  private LocalDateTime createTime;
  private LocalDateTime updateTime;
  private Long tenantId;
}

3.2 数据存储逻辑实现 ​

创建一个服务类 SendMessageEventService,用于处理 SendMessageEvent 并保存数据。

java
package nexus.io.gpt.translator.services;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Optional;
import nexus.io.db.activerecord.Db;
import nexus.io.db.activerecord.Row;
import nexus.io.gpt.translator.vo.TelegramMessage;
import nexus.io.tio.utils.json.JsonUtils;
import nexus.io.tio.utils.snowflake.SnowflakeIdUtils;
import telegram4j.core.event.domain.message.SendMessageEvent;
import telegram4j.core.object.Message;
import telegram4j.core.object.MessageEntity;
import telegram4j.core.object.User;
import telegram4j.core.object.chat.Chat;
import telegram4j.core.object.chat.PrivateChat;

public class SendMessageEventService {

  public void save(SendMessageEvent event) {
    TelegramMessage telegramMessage = extract(event);
    if (telegramMessage != null) {
      Db.save("telegram_bot_messages", Row.fromBean(telegramMessage));
    }
  }

  private TelegramMessage extract(SendMessageEvent event) {
    Optional<Chat> chatOptional = event.getChat();

    if (chatOptional.isEmpty()) {
      return null;
    }

    Chat chat = chatOptional.get();
    String fromFirstName = null;
    String fromLastName = null;
    String fromUsername = null;
    long toUserId = 0;
    String toFirstName = null;
    String toLastName = null;
    String toUsername = null;

    if (chat instanceof PrivateChat) {
      PrivateChat privateChat = (PrivateChat) chat;
      User user = privateChat.getUser();
      fromFirstName = user.getFirstName().orElse(null);
      fromLastName = user.getLastName().orElse(null);
      fromUsername = user.getUsername().orElse(null);

      Optional<User> selfUserOptional = privateChat.getSelfUser();
      if (selfUserOptional.isPresent()) {
        User selfUser = selfUserOptional.get();
        toUserId = selfUser.getId().asLong();
        toUsername = selfUser.getUsername().orElse(null);
        toFirstName = selfUser.getFirstName().orElse(null);
        toLastName = selfUser.getLastName().orElse(null);
      }
    }

    // 提取消息基本信息
    Message message = event.getMessage();

    int sequence = message.getId();
    // 提取实体信息
    List<MessageEntity> entities = message.getEntities();
    String entitiesJson = JsonUtils.toJson(entities);

    // 消息发送时间
    LocalDateTime messageDateTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(message.getDate()), ZoneId.systemDefault());

    // 生成唯一 ID
    long id = SnowflakeIdUtils.id();

    TelegramMessage telegramMessage = new TelegramMessage();
    telegramMessage.setId(id);
    telegramMessage.setFromId(event.getAuthor().get().getId().asLong());
    telegramMessage.setFromUsername(fromUsername);
    telegramMessage.setFromFirstName(fromFirstName);
    telegramMessage.setFromLastName(fromLastName);

    telegramMessage.setToId(toUserId);
    telegramMessage.setToUsername(toUsername);
    telegramMessage.setToFirstName(toFirstName);
    telegramMessage.setToLastName(toLastName);

    telegramMessage.setSequence(sequence);
    telegramMessage.setMessageText(message.getContent());
    telegramMessage.setMessageDate(messageDateTime);
    telegramMessage.setMessageType(chat.getType().toString());
    telegramMessage.setEntities(entitiesJson);

    // 设置创建和更新时间
    telegramMessage.setCreateTime(LocalDateTime.now());
    telegramMessage.setUpdateTime(LocalDateTime.now());

    // 设置租户 ID(如有需要)
    telegramMessage.setTenantId(0L);

    return telegramMessage;
  }
}

3.2.1 代码说明 ​

  • 消息提取:

    • 从 SendMessageEvent 中获取 Chat 对象,判断是否为 PrivateChat 类型。
    • 提取发送者和接收者的用户信息,包括 ID、用户名、名字和姓氏。
  • 实体信息处理:

    • 获取消息中的实体列表,将其转换为 JSON 字符串,便于存储。
  • 时间处理:

    • 将消息的 Unix 时间戳转换为 LocalDateTime。
  • ID 生成:

    • 使用雪花算法生成全局唯一的消息 ID,确保在分布式环境下的唯一性。
  • 数据存储:

    • 将 TelegramMessage 对象转换为数据库行,保存到 telegram_bot_messages 表中。

3.3 在应用中调用保存方法 ​

在消息处理的逻辑中,调用 SendMessageEventService 的 save 方法:

java
Aop.get(SendMessageEventService.class).save(event);

4. 测试与验证 ​

在完成上述实现后,我们可以对系统进行测试,确保消息数据被正确地存储到了数据库中。

4.1 示例数据 ​

假设接收到以下消息:

  • 消息内容: "what's your name"
  • 发送者 ID: 6276672963
  • 接收者(机器人)ID: 7847170133
  • 消息序列: 1589
  • 消息日期: 2024-12-03 19:14:58
  • 消息类型: PRIVATE
  • 发送者用户名: litonglinux
  • 发送者名字: 蕾娜斯
  • 接收者用户名: litongjava_bot
  • 接收者名字: my-translator

4.2 数据库中的存储结果 ​

保存后的数据库记录如下:

idfrom_idto_idsequencemessage_textmessage_datemessage_typeentitiesfrom_usernamefrom_first_namefrom_last_nameto_usernameto_first_nameto_last_namecreate_timeupdate_timetenant_id
453879471025647616627667296378471701331589what's your name2024-12-03 19:14:58.000PRIVATE[]litonglinux蕾娜斯NULLlitongjava_botmy-translatorNULL2024-12-03 19:15:18.9289632024-12-03 19:15:18.9289630

可以看到,消息的各项信息都被正确地存储到了数据库中,包括发送者和接收者的信息、消息内容、发送时间等。

5. 结论 ​

通过以上步骤,我们成功地实现了对 Telegram 机器人接收到的消息的存储。详细分析了消息的数据结构,设计了合理的数据库表结构,并实现了数据的提取和存储逻辑。这为后续的消息处理、分析和响应奠定了坚实的基础。

附录:关键代码解释 ​

A.1 SendMessageEventService 类 ​

该类的作用是处理 SendMessageEvent,提取必要的信息,并将其保存到数据库中。

  • save 方法: 接收 SendMessageEvent,调用 extract 方法提取信息,并保存到数据库。
  • extract 方法: 从事件中提取消息和用户信息,处理实体和时间数据。

A.2 时间处理 ​

将 Unix 时间戳转换为 LocalDateTime:

java
LocalDateTime messageDateTime = LocalDateTime.ofInstant(
    Instant.ofEpochSecond(message.getDate()),
    ZoneId.systemDefault()
);

A.3 实体信息处理 ​

将消息中的实体列表转换为 JSON 字符串:

java
List<MessageEntity> entities = message.getEntities();
String entitiesJson = JsonUtils.toJson(entities);
telegramMessage.setEntities(entitiesJson);

A.4 ID 生成 ​

使用雪花算法生成全局唯一的 ID:

java
long id = SnowflakeIdUtils.id();
telegramMessage.setId(id);

备注: 在实际应用中,需要根据具体的业务需求和技术栈,选择合适的数据库、ORM 框架和 ID 生成策略。同时,要注意异常处理和日志记录,确保系统的稳定性和可维护性。