Java Telegram 机器人 API
用于与 Telegram Bot API 交互的 Java 库
下载
Gradle:
implementation 'com.github.pengrad:java-telegram-bot-api:7.8.0'
Maven:
<dependency>
<groupId>com.github.pengrad</groupId>
<artifactId>java-telegram-bot-api</artifactId>
<version>7.8.0</version>
</dependency>
使用方法
// 使用从 @BotFather 获得的令牌创建您的机器人
TelegramBot bot = new TelegramBot("BOT_TOKEN");
// 注册更新监听器
bot.setUpdatesListener(updates -> {
// ... 处理更新
// 返回最后处理的更新 ID 或确认所有更新
return UpdatesListener.CONFIRMED_UPDATES_ALL;
// 创建异常处理器
}, e -> {
if (e.response() != null) {
// 从 Telegram 收到错误响应
e.response().errorCode();
e.response().description();
} else {
// 可能是网络错误
e.printStackTrace();
}
});
// 发送消息
long chatId = update.message().chat().id();
SendResponse response = bot.execute(new SendMessage(chatId, "你好!"));
文档
创建您的机器人
TelegramBot bot = new TelegramBot("BOT_TOKEN");
网络操作基于 OkHttp 库。
您可以使用自定义 OkHttpClient 构建机器人,以设置特定的超时时间或拦截器。
TelegramBot bot = new TelegramBot.Builder("BOT_TOKEN").okHttpClient(client).build();
发送请求
同步
BaseResponse response = bot.execute(request);
异步
bot.execute(request, new Callback() {
@Override
public void onResponse(BaseRequest request, BaseResponse response) {
}
@Override
public void onFailure(BaseRequest request, IOException e) {
}
});
String response = request.toWebhookResponse();
获取更新
您可以使用 getUpdates 请求,解析传入的 Webhook 请求,或设置监听器来接收更新。
Update 对象只是复制 Telegram 的响应。
class Update {
Integer updateId();
Message message();
Message editedMessage();
InlineQuery inlineQuery();
ChosenInlineResult chosenInlineResult();
CallbackQuery callbackQuery();
}
获取更新
构建请求
GetUpdates getUpdates = new GetUpdates().limit(100).offset(0).timeout(0);
getUpdates 方法返回最早的 100 个未确认更新。要确认更新,在调用 getUpdates 时使用 offset 参数,如下所示:
offset = 最后处理的更新的 updateId + 1
所有 updateId 小于 offset 的更新将在服务器上被标记为已确认,不再返回。
执行
// 同步
GetUpdatesResponse updatesResponse = bot.execute(getUpdates);
List<Update> updates = updatesResponse.updates();
...
Message message = update.message()
// 异步
bot.execute(getUpdates, new Callback<GetUpdates, GetUpdatesResponse>() {
@Override
public void onResponse(GetUpdates request, GetUpdatesResponse response) {
List<Update> updates = response.updates();
}
@Override
public void onFailure(GetUpdates request, IOException e) {
}
});
Webhook
构建请求
SetWebhook request = new SetWebhook()
.url("url")
.certificate(new byte[]{}) // byte[]
.certificate(new File("path")); // 或文件
执行
// 同步
BaseResponse response = bot.execute(request);
boolean ok = response.isOk();
// 异步
bot.execute(request, new Callback<SetWebhook, BaseResponse>() {
@Override
public void onResponse(SetWebhook request, BaseResponse response) {
}
@Override
public void onFailure(SetWebhook request, IOException e) {
}
});
使用 Webhook 时,您可以将请求解析为 Update
Update update = BotUtils.parseUpdate(stringRequest); // 从字符串
Update update = BotUtils.parseUpdate(reader); // 或从 java.io.Reader
Message message = update.message();
更新监听器
您可以设置监听器来接收传入的更新,就像使用 Webhook 一样。
这将触发循环执行 getUpdates 请求。
bot.setUpdatesListener(new UpdatesListener() {
@Override
public int process(List<Update> updates) {
// 处理更新
return UpdatesListener.CONFIRMED_UPDATES_ALL;
}
// 创建异常处理器
}, new ExceptionHandler() {
@override
public void onException(TelegramException e)
{
if (e.response() != null) {
// 从 Telegram 收到错误响应
e.response().errorCode();
e.response().description();
} else {
// 可能是网络错误
e.printStackTrace();
}
}
});
监听器应返回最后一个已处理(已确认)更新的 id。
要确认所有更新,请返回 UpdatesListener.CONFIRMED_UPDATES_ALL
,这在大多数情况下应该足够。
要不确认任何更新,请返回 UpdatesListener.CONFIRMED_UPDATES_NONE
,这些更新将被重新传递。
要将特定更新设置为最后确认的更新,只需返回所需的 updateId。
要停止接收更新
bot.removeGetUpdatesListener();
可用类型
所有类型的名称与原始名称相同。 类型的字段是采用小驼峰命名法的方法。
响应中使用的类型(Update、Message、User、Document...)位于 com.pengrad.telegrambot.model
包中。
请求中使用的类型(Keyboard、InlineQueryResult、ParseMode、InputMessageContent...)位于 com.pengrad.telegrambot.model.request
包中。
创建请求类型时,必需的参数应在构造函数中传递,可选参数可以通过链式调用添加。
键盘
ForceReply, ReplyKeyboardRemove
Keyboard forceReply = new ForceReply(isSelective); // 或直接使用 new ForceReply();
Keyboard replyKeyboardRemove = new ReplyKeyboardRemove(); // new ReplyKeyboardRemove(isSelective)
ReplyKeyboardMarkup
Keyboard replyKeyboardMarkup = new ReplyKeyboardMarkup(
new String[]{"第一行按钮1", "第一行按钮2"},
new String[]{"第二行按钮1", "第二行按钮2"})
.oneTimeKeyboard(true) // 可选
.resizeKeyboard(true) // 可选
.selective(true); // 可选
KeyboardButton
Keyboard keyboard = new ReplyKeyboardMarkup(
new KeyboardButton[]{
new KeyboardButton("文本"),
new KeyboardButton("联系人").requestContact(true),
new KeyboardButton("位置").requestLocation(true)
}
);
InlineKeyboardMarkup
InlineKeyboardMarkup inlineKeyboard = new InlineKeyboardMarkup(
new InlineKeyboardButton[]{
new InlineKeyboardButton("url").url("www.google.com"),
new InlineKeyboardButton("callback_data").callbackData("callback_data"),
new InlineKeyboardButton("切换!").switchInlineQuery("switch_inline_query")
});
聊天动作
ChatAction action = ChatAction.typing;
ChatAction action = ChatAction.upload_photo;
ChatAction action = ChatAction.find_location;
可用方法
所有请求方法的名称与原始名称相同。 必需的参数应在构造函数中传递。 可选参数可以通过链式调用添加。
发送消息
所有发送请求(SendMessage、SendPhoto、SendLocation...)都返回包含 Message 的 SendResponse 对象。
SendMessage request = new SendMessage(chatId, "文本")
.parseMode(ParseMode.HTML)
.disableWebPagePreview(true)
.disableNotification(true)
.replyToMessageId(1)
.replyMarkup(new ForceReply());
// 同步
SendResponse sendResponse = bot.execute(request);
boolean ok = sendResponse.isOk();
Message message = sendResponse.message();
// 异步
bot.execute(request, new Callback<SendMessage, SendResponse>() {
@Override
public void onResponse(SendMessage request, SendResponse response) {
}
@Override
public void onFailure(SendMessage request, IOException e) {
}
});
格式化选项
ParseMode parseMode = ParseMode.Markdown;
ParseMode parseMode = ParseMode.HTML;
获取文件
GetFile request = new GetFile("fileId")
GetFileResponse getFileResponse = bot.execute(request);
File file = getFileResponse.file(); // com.pengrad.telegrambot.model.File
file.fileId();
file.filePath(); // 相对路径
file.fileSize();
要获取下载链接,格式为 https://api.telegram.org/file/<BOT_TOKEN>/<FILE_PATH>
String fullPath = bot.getFullFilePath(file); // com.pengrad.telegrambot.model.File
其他请求
如果这里没有提到,所有请求都返回 BaseResponse
class BaseResponse {
boolean isOk();
int errorCode();
String description();
}
GetMe 请求返回 GetMeResponse
class GetMeResponse {
User user();
}
GetChatAdministrators
class GetChatAdministratorsResponse {
List<ChatMember> administrators()
}
GetChatMembersCount
class GetChatMembersCountResponse {
int count()
}
GetChatMember
class GetChatMemberResponse {
ChatMember chatMember()
}
GetChat
class GetChatResponse {
Chat chat()
}
GetUserProfilePhotos
class GetUserProfilePhotosResponse {
UserProfilePhotos photos()
}
StopPoll
class PollResponse {
Poll poll()
}
更新消息
普通消息
EditMessageText editMessageText = new EditMessageText(chatId, messageId, "新测试")
.parseMode(ParseMode.HTML)
.disableWebPagePreview(true)
.replyMarkup(new ReplyKeyboardRemove());
BaseResponse response = bot.execute(editMessageText);
内联消息
EditMessageText editInlineMessageText = new EditMessageText(inlineMessageId, "新文本");
BaseResponse response = bot.execute(editInlineMessageText);
删除消息
DeleteMessage deleteMessage = new DeleteMessage(chatId, messageId);
BaseResponse response = bot.execute(deleteMessage);
贴纸
发送贴纸
// File 或 byte[] 或现有贴纸的 fileId 字符串或 URL 字符串
SendSticker sendSticker = new SendSticker(chatId, imageFile);
SendResponse response = bot.execute(sendSticker);
获取贴纸集
GetStickerSet getStickerSet = new GetStickerSet(stickerSet);
GetStickerSetResponse response = bot.execute(getStickerSet);
StickerSet stickerSet = response.stickerSet();
上传贴纸文件
// File 或 byte[] 或 URL 字符串
UploadStickerFile uploadStickerFile = new UploadStickerFile(chatId, stickerFile);
GetFileResponse response = bot.execute(uploadStickerFile);
内联模式
获取更新
GetUpdatesResponse updatesResponse = bot.execute(new GetUpdates());
List<Update> updates = updatesResponse.updates();
...
InlineQuery inlineQuery = update.inlineQuery();
ChosenInlineResult chosenInlineResult = update.chosenInlineResult();
CallbackQuery callbackQuery = update.callbackQuery();
如果使用 webhook,您可以将请求解析为 InlineQuery
Update update = BotUtils.parseUpdate(stringRequest); // 从 String
Update update = BotUtils.parseUpdate(reader); // 从 java.io.Reader
InlineQuery inlineQuery = update.inlineQuery();
内联查询结果
InlineQueryResult r1 = new InlineQueryResultPhoto("id", "photoUrl", "thumbUrl");
InlineQueryResult r2 = new InlineQueryResultArticle("id", "title", "message text").thumbUrl("url");
InlineQueryResult r3 = new InlineQueryResultGif("id", "gifUrl", "thumbUrl");
InlineQueryResult r4 = new InlineQueryResultMpeg4Gif("id", "mpeg4Url", "thumbUrl");
InlineQueryResult r5 = new InlineQueryResultVideo(
"id", "videoUrl", InlineQueryResultVideo.MIME_VIDEO_MP4, "message", "thumbUrl", "video title")
.inputMessageContent(new InputLocationMessageContent(21.03f, 105.83f));
回答内联查询
BaseResponse response = bot.execute(new AnswerInlineQuery(inlineQuery.id(), r1, r2, r3, r4, r5));
// 或完整版
bot.execute(
new AnswerInlineQuery(inlineQuery.id(), new InlineQueryResult[]{r1, r2, r3, r4, r5})
.cacheTime(cacheTime)
.isPersonal(isPersonal)
.nextOffset("offset")
.switchPmParameter("pmParam")
.switchPmText("pmText")
);
支付
发送账单
SendInvoice sendInvoice = new SendInvoice(chatId, "标题", "描述", "my_payload",
"providerToken", "my_start_param", "USD", new LabeledPrice("标签", 200))
.needPhoneNumber(true)
.needShippingAddress(true)
.isFlexible(true)
.replyMarkup(new InlineKeyboardMarkup(new InlineKeyboardButton[]{
new InlineKeyboardButton("直接支付").pay(),
new InlineKeyboardButton("谷歌一下").url("www.google.com")
}));
SendResponse response = bot.execute(sendInvoice);
回答配送查询
LabeledPrice[] prices = new LabeledPrice[]{
new LabeledPrice("配送", 100),
new LabeledPrice("小费", 50)
};
AnswerShippingQuery answerShippingQuery = new AnswerShippingQuery(shippingQueryId,
new ShippingOption("1", "VNPT", prices),
new ShippingOption("2", "免费", new LabeledPrice("免费配送", 0))
);
BaseResponse response = bot.execute(answerShippingQuery);
// 回答错误
AnswerShippingQuery answerShippingError = new AnswerShippingQuery(id, "无法配送到这里!");
BaseResponse response = bot.execute(answerShippingError);
回答预结账查询
AnswerPreCheckoutQuery answerCheckout = new AnswerPreCheckoutQuery(preCheckoutQueryId);
BaseResponse response = bot.execute(answerPreCheckoutQuery);
// 回答错误
AnswerPreCheckoutQuery answerCheckout = new AnswerPreCheckoutQuery(id, "抱歉,商品不可用");
BaseResponse response = bot.execute(answerPreCheckoutQuery);
Telegram Passport
当用户点击"授权"按钮确认您的请求时,Bot API会向机器人发送一个包含加密的Telegram Passport数据的更新,该更新包含passport_data
字段。Telegram Passport 手册
接收信息
您可以从更新中获取加密的Passport数据(通过UpdatesListener或Webhook)
PassportData passportData = update.message().passportData();
PassportData包含一组EncryptedPassportElement
和EncryptedCredentials
。
您需要使用私钥解密Credentials
(公钥已上传到@BotFather
)
String privateKey = "...";
EncryptedCredentials encryptedCredentials = passportData.credentials();
Credentials credentials = encryptedCredentials.decrypt(privateKey);
这些Credentials
可用于解密EncryptedPassportElement
中的加密数据。
EncryptedPassportElement[] encryptedPassportElements = passportData.data();
for (EncryptedPassportElement element : encryptedPassportElements) {
DecryptedData decryptedData = element.decryptData(credentials);
// DecryptedData可以通过检查instanceOf转换为特定类型
if (decryptedData instanceof PersonalDetails) {
PersonalDetails personalDetails = (PersonalDetails) decryptedData;
}
// 或者通过检查护照元素的类型
if (element.type() == EncryptedPassportElement.Type.address) {
ResidentialAddress address = (ResidentialAddress) decryptedData;
}
}
EncryptedPassportElement
还包含一组PassportFile
(上传到Telegram Passport的文件)。
您需要逐个下载并解密内容。
该库支持下载和解密,返回解密后的byte[]
EncryptedPassportElement element = ...
// 合并所有文件
List<PassportFile> files = new ArrayList<PassportFile>();
files.add(element.frontSide());
files.add(element.reverseSide());
files.add(element.selfie());
if (element.files() != null) {
files.addAll(Arrays.asList(element.files()));
}
if (element.translation() != null) {
files.addAll(Arrays.asList(element.translation()));
}
// 解密
for (PassportFile file : files) {
if (file == null) continue;
byte[] data = element.decryptFile(file, credentials, bot); // GetFile请求并解密内容
// 如果需要,保存到文件
new FileOutputStream("files/" + element.type()).write(data);
}
设置Passport数据错误
SetPassportDataErrors setPassportDataErrors = new SetPassportDataErrors(chatId,
new PassportElementErrorDataField("personal_details", "first_name", "dataHash",
"请输入有效的名字"),
new PassportElementErrorSelfie("driver_license", "fileHash",
"照片上看不清您的脸")
);
bot.execute(setPassportDataErrors);
游戏
发送游戏
SendResponse response = bot.execute(new SendGame(chatId, "my_super_game"));
设置游戏分数
BaseResponse response = bot.execute(new SetGameScore(userId, score, chatId, messageId));
获取游戏高分
GetGameHighScoresResponse response = bot.execute(new GetGameHighScores(userId, chatId, messageId));
GameHighScore[] scores = response.result();