<!-- Source: https://telegram-bot.io.nexus/telegram4j/10 -->

# 延迟测试

::: danger 不推荐使用
笔者不推荐使用 Telegram4J：它有并发问题，服务总是挂掉。新项目请使用 [telegram-bot-base](<https://telegram-bot.io.nexus/telegram-bot-base/>)。本章仅作为历史记录保留。
:::


[[toc]]

本文档详细介绍了一个用于测试服务器与 Telegram 服务器之间延迟的功能实现。该功能通过 Telegram 机器人发送消息并测量响应时间，以评估网络延迟情况。

## 功能概述

该功能通过 Telegram 机器人执行以下步骤：

1. **发送初始消息**：机器人向用户发送一条内容为 "Wait a second..." 的消息，提示用户等待。
2. **编辑消息内容**：在发送初始消息后，机器人计算从发送到编辑的时间差，并将消息内容更新为 "Pong! + 时间(ms)"，其中时间表示延迟毫秒数。

通过这种方式，用户可以直观地看到服务器与 Telegram 服务器之间的通信延迟。

## 代码结构

以下是实现该功能的 Java 代码：

```java
package nexus.io.telegram.bots.command;

import java.util.Optional;
import java.util.function.Function;

import org.reactivestreams.Publisher;

import reactor.core.publisher.Mono;
import telegram4j.core.event.domain.message.SendMessageEvent;
import telegram4j.core.object.Message;
import telegram4j.core.object.chat.Chat;
import telegram4j.core.spec.EditMessageSpec;
import telegram4j.core.spec.ReplyToMessageSpec;
import telegram4j.core.spec.SendMessageSpec;

public class PingCommandFunction {

  public Publisher<? extends Message> apply(SendMessageEvent event) {
    Optional<Chat> chat = event.getChat();
    Mono<Chat> monoChat = Mono.justOrEmpty(chat).switchIfEmpty(event.getMessage().getChat());
    Message message = event.getMessage();

    return monoChat.flatMap(c -> processMessage(c, message));
  }

  private Mono<Message> processMessage(Chat chat, Message message) {
    long pre = System.currentTimeMillis();
    SendMessageSpec sendMessage = SendMessageSpec.of("Wait a second...").withReplyTo(ReplyToMessageSpec.of(message));
    Mono<Message> moniMesage = chat.sendMessage(sendMessage);

    Function<? super Message, ? extends Mono<? extends Message>> transformer = (m) -> {
      EditMessageSpec build = EditMessageSpec.builder().message("Pong! " + (System.currentTimeMillis() - pre) + "ms.").build();
      return m.edit(build);
    };
    return moniMesage.flatMap(transformer);
  }

}
```

## 详细解析

### 1. 类与方法概述

- **PingCommandFunction**：主要负责处理延迟测试命令的类。
- **apply(SendMessageEvent event)**：入口方法，接收发送消息事件并返回处理后的消息发布者。
- **processMessage(Chat chat, Message message)**：辅助方法，负责发送初始消息并在之后编辑消息内容以显示延迟时间。

### 2. 方法实现详解

#### apply 方法

```java
public Publisher<? extends Message> apply(SendMessageEvent event) {
  Optional<Chat> chat = event.getChat();
  Mono<Chat> monoChat = Mono.justOrEmpty(chat).switchIfEmpty(event.getMessage().getChat());
  Message message = event.getMessage();

  return monoChat.flatMap(c -> processMessage(c, message));
}
```

- **功能**：获取当前聊天对象，并调用 `processMessage` 方法处理消息。
- **步骤**：
  1. 从事件中获取聊天对象 `chat`，可能为空。
  2. 使用 Reactor 的 `Mono` 类型封装聊天对象，若 `chat` 为空，则从消息中重新获取聊天对象。
  3. 获取触发事件的消息对象 `message`。
  4. 使用 `flatMap` 处理聊天对象，调用 `processMessage` 方法。

#### processMessage 方法

```java
private Mono<Message> processMessage(Chat chat, Message message) {
  long pre = System.currentTimeMillis();
  SendMessageSpec sendMessage = SendMessageSpec.of("Wait a second...").withReplyTo(ReplyToMessageSpec.of(message));
  Mono<Message> moniMesage = chat.sendMessage(sendMessage);

  Function<? super Message, ? extends Mono<? extends Message>> transformer = (m) -> {
    EditMessageSpec build = EditMessageSpec.builder().message("Pong! " + (System.currentTimeMillis() - pre) + "ms.").build();
    return m.edit(build);
  };
  return moniMesage.flatMap(transformer);
}
```

- **功能**：发送初始消息并编辑该消息以显示延迟时间。
- **步骤**：
  1. 记录当前时间 `pre`，用于计算延迟。
  2. 构建发送消息的规格 `SendMessageSpec`，内容为 "Wait a second..."，并设置为回复原始消息。
  3. 发送消息，返回一个 `Mono<Message>` 类型的消息发布者。
  4. 定义一个转换函数 `transformer`，用于编辑发送的消息内容，将其更新为 "Pong! + 延迟时间(ms)"。
  5. 使用 `flatMap` 应用转换函数，返回编辑后的消息发布者。

### 3. Reactor 异步处理

该实现利用 Reactor 库进行异步编程：

- **Mono**：代表一个可能包含或不包含值的异步序列。
- **flatMap**：用于在一个 Mono 上执行转换操作，生成新的 Mono。
- **Function**：定义了如何将初始发送的消息转换为编辑后的消息。

这种异步处理方式确保了在高并发环境下，延迟测试功能能够高效运行，不会阻塞主线程。

## 使用说明

1. **集成 Telegram4J**：确保项目中已集成 Telegram4J 库，用于与 Telegram API 交互。
2. **注册命令**：将 `PingCommandFunction` 注册为 Telegram 机器人的命令处理函数，例如 `/ping` 命令。
3. **运行机器人**：启动 Telegram 机器人，当用户发送 `/ping` 命令时，机器人将执行延迟测试功能，回复 "Wait a second..." 并随后更新为 "Pong! + 延迟时间(ms)"。

## 示例

用户在 Telegram 中发送 `/ping` 命令，机器人响应如下：

1. **初始消息**：
   ```
   Wait a second...
   ```
2. **编辑后的消息**：
   ```
   Pong! 123ms.
   ```

其中 `123ms` 表示从发送初始消息到编辑消息所经历的时间，即服务器与 Telegram 服务器之间的延迟。

## 总结

本文介绍了一个基于 Telegram4J 的延迟测试功能实现。通过发送和编辑消息的方式，机器人能够有效测量并反馈服务器与 Telegram 服务器之间的通信延迟。该实现利用 Reactor 的异步特性，确保高效和响应迅速，适用于需要实时性能监控的应用场景。
