保存机器人消息
引言
在开发 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
或其他标志位判断)
- 消息 ID (
用户信息:
- 发送者的用户名、名字和姓氏
- 接收者(通常是机器人)的用户名、名字和姓氏
2. 数据库表设计
为了存储提取的信息,我们需要设计一个合适的数据库表。
2.1 表结构设计
创建一个名为 telegram_bot_messages
的表,包含以下字段:
字段名 | 数据类型 | 描述 | 约束 |
---|---|---|---|
id | BIGINT | 消息 ID | PRIMARY KEY |
from_id | BIGINT | 发送者的用户 ID | NOT NULL |
to_id | BIGINT | 接收者的用户 ID | NOT NULL |
sequence | INT | 消息序列 ID | NOT NULL |
message_text | TEXT | 消息内容 | NOT NULL |
message_date | TIMESTAMP | 消息发送时间 | NOT NULL |
message_type | VARCHAR(50) | 消息类型(如文本、命令等) | NOT NULL |
entities | TEXT | 消息中的实体(JSON 格式存储) | NULLABLE |
from_username | VARCHAR(100) | 发送者的用户名 | NULLABLE |
from_first_name | VARCHAR(100) | 发送者的名字 | NULLABLE |
from_last_name | VARCHAR(100) | 发送者的姓氏 | NULLABLE |
to_username | VARCHAR(100) | 接收者的用户名 | NULLABLE |
to_first_name | VARCHAR(100) | 接收者的名字 | NULLABLE |
to_last_name | VARCHAR(100) | 接收者的姓氏 | NULLABLE |
create_time | TIMESTAMP | 记录创建时间 | DEFAULT CURRENT_TIMESTAMP |
update_time | TIMESTAMP | 记录更新时间 | DEFAULT CURRENT_TIMESTAMP |
tenant_id | BIGINT | 租户 ID(默认为 0) | NOT NULL DEFAULT 0 |
2.2 创建表的 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
。
package com.litongjava.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
并保存数据。
package com.litongjava.gpt.translator.services;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Optional;
import com.litongjava.db.activerecord.Db;
import com.litongjava.db.activerecord.Row;
import com.litongjava.gpt.translator.vo.TelegramMessage;
import com.litongjava.tio.utils.json.JsonUtils;
import com.litongjava.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
。
- 将消息的 Unix 时间戳转换为
ID 生成:
- 使用雪花算法生成全局唯一的消息 ID,确保在分布式环境下的唯一性。
数据存储:
- 将
TelegramMessage
对象转换为数据库行,保存到telegram_bot_messages
表中。
- 将
3.3 在应用中调用保存方法
在消息处理的逻辑中,调用 SendMessageEventService
的 save
方法:
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 数据库中的存储结果
保存后的数据库记录如下:
id | from_id | to_id | sequence | message_text | message_date | message_type | entities | from_username | from_first_name | from_last_name | to_username | to_first_name | to_last_name | create_time | update_time | tenant_id |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
453879471025647616 | 6276672963 | 7847170133 | 1589 | what's your name | 2024-12-03 19:14:58.000 | PRIVATE | [] | litonglinux | 蕾娜斯 | NULL | litongjava_bot | my-translator | NULL | 2024-12-03 19:15:18.928963 | 2024-12-03 19:15:18.928963 | 0 |
可以看到,消息的各项信息都被正确地存储到了数据库中,包括发送者和接收者的信息、消息内容、发送时间等。
5. 结论
通过以上步骤,我们成功地实现了对 Telegram 机器人接收到的消息的存储。详细分析了消息的数据结构,设计了合理的数据库表结构,并实现了数据的提取和存储逻辑。这为后续的消息处理、分析和响应奠定了坚实的基础。
附录:关键代码解释
A.1 SendMessageEventService
类
该类的作用是处理 SendMessageEvent
,提取必要的信息,并将其保存到数据库中。
save
方法: 接收SendMessageEvent
,调用extract
方法提取信息,并保存到数据库。extract
方法: 从事件中提取消息和用户信息,处理实体和时间数据。
A.2 时间处理
将 Unix 时间戳转换为 LocalDateTime
:
LocalDateTime messageDateTime = LocalDateTime.ofInstant(
Instant.ofEpochSecond(message.getDate()),
ZoneId.systemDefault()
);
A.3 实体信息处理
将消息中的实体列表转换为 JSON 字符串:
List<MessageEntity> entities = message.getEntities();
String entitiesJson = JsonUtils.toJson(entities);
telegramMessage.setEntities(entitiesJson);
A.4 ID 生成
使用雪花算法生成全局唯一的 ID:
long id = SnowflakeIdUtils.id();
telegramMessage.setId(id);
备注: 在实际应用中,需要根据具体的业务需求和技术栈,选择合适的数据库、ORM 框架和 ID 生成策略。同时,要注意异常处理和日志记录,确保系统的稳定性和可维护性。