Доработки для загрузки фото

This commit is contained in:
Struchkov Mark 2022-07-19 20:37:29 +03:00
parent fb240a7b36
commit bf8ba844cf
18 changed files with 610 additions and 260 deletions

View File

@ -5,7 +5,7 @@
<groupId>dev.struchkov.godfather</groupId>
<artifactId>telegram-bot</artifactId>
<version>0.0.15</version>
<version>0.0.17</version>
<packaging>pom</packaging>
<modules>
@ -33,7 +33,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<godfather.core.ver>0.0.12</godfather.core.ver>
<godfather.core.ver>0.0.14</godfather.core.ver>
<telegrambots.ver>6.1.0</telegrambots.ver>
<plugin.maven.compiler.ver>3.10.1</plugin.maven.compiler.ver>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>dev.struchkov.godfather</groupId>
<artifactId>telegram-bot</artifactId>
<version>0.0.15</version>
<version>0.0.17</version>
</parent>
<artifactId>telegram-core</artifactId>

View File

@ -2,10 +2,15 @@ package dev.struchkov.godfather.telegram.convert;
import dev.struchkov.godfather.context.domain.content.Mail;
import dev.struchkov.godfather.context.domain.content.attachment.Attachment;
import dev.struchkov.godfather.telegram.domain.attachment.ContactAttachment;
import dev.struchkov.godfather.telegram.domain.attachment.DocumentAttachment;
import dev.struchkov.godfather.telegram.domain.attachment.Picture;
import dev.struchkov.godfather.telegram.domain.attachment.PictureGroupAttachment;
import org.telegram.telegrambots.meta.api.objects.Contact;
import org.telegram.telegrambots.meta.api.objects.Document;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.MessageEntity;
import org.telegram.telegrambots.meta.api.objects.PhotoSize;
import java.time.Instant;
import java.time.LocalDateTime;
@ -38,6 +43,8 @@ public final class MessageMailConvert {
mail.setLastName(message.getChat().getLastName());
convertDocument(message.getDocument()).ifPresent(mail::addAttachment);
convertContact(message.getContact()).ifPresent(mail::addAttachment);
convertPhoto(message.getPhoto()).ifPresent(mail::addAttachment);
final List<MessageEntity> entities = message.getEntities();
if (entities != null) {
@ -51,6 +58,46 @@ public final class MessageMailConvert {
return mail;
}
private static Optional<Attachment> convertPhoto(List<PhotoSize> photoSizes) {
if (photoSizes != null && !photoSizes.isEmpty()) {
final PictureGroupAttachment attachment = new PictureGroupAttachment();
final List<Picture> pictures = photoSizes.stream()
.map(photoSize -> {
final Picture picture = new Picture();
picture.setFileSize(photoSize.getFileSize());
picture.setFileId(photoSize.getFileId());
picture.setHeight(photoSize.getHeight());
picture.setWeight(photoSize.getWidth());
picture.setFileUniqueId(photoSize.getFileUniqueId());
return picture;
}).toList();
attachment.setPictureSizes(pictures);
return Optional.of(attachment);
}
return Optional.empty();
}
private static Optional<ContactAttachment> convertContact(Contact contact) {
if (contact != null) {
final ContactAttachment attachment = new ContactAttachment();
attachment.setPhoneNumber(contact.getPhoneNumber());
attachment.setUserId(contact.getUserId());
attachment.setFirstName(contact.getFirstName());
attachment.setLastName(contact.getLastName());
if (contact.getVCard() != null) {
attachment.setOwner(false);
attachment.setVCard(contact.getVCard());
} else {
attachment.setOwner(true);
}
return Optional.of(attachment);
}
return Optional.empty();
}
private static Optional<DocumentAttachment> convertDocument(Document document) {
if (document != null) {
final DocumentAttachment attachment = new DocumentAttachment();

View File

@ -0,0 +1,71 @@
package dev.struchkov.godfather.telegram.domain.attachment;
import dev.struchkov.godfather.context.domain.content.attachment.Attachment;
public class ContactAttachment extends Attachment {
private String phoneNumber;
private String firstName;
private String lastName;
private Long userId;
private String vCard;
/**
* если true, то контакт принадлежит отправившему телеграм аккаунту.
*/
private boolean owner;
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public boolean isOwner() {
return owner;
}
public void setOwner(boolean owner) {
this.owner = owner;
}
public String getVCard() {
return vCard;
}
public void setVCard(String vCard) {
this.vCard = vCard;
}
@Override
public String getType() {
return TelegramAttachmentType.CONTACT.name();
}
}

View File

@ -0,0 +1,51 @@
package dev.struchkov.godfather.telegram.domain.attachment;
public class Picture {
private String fileId;
private String fileUniqueId;
private Integer fileSize;
private Integer weight;
private Integer height;
public String getFileId() {
return fileId;
}
public void setFileId(String fileId) {
this.fileId = fileId;
}
public String getFileUniqueId() {
return fileUniqueId;
}
public void setFileUniqueId(String fileUniqueId) {
this.fileUniqueId = fileUniqueId;
}
public Integer getFileSize() {
return fileSize;
}
public void setFileSize(Integer fileSize) {
this.fileSize = fileSize;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
public Integer getHeight() {
return height;
}
public void setHeight(Integer height) {
this.height = height;
}
}

View File

@ -0,0 +1,31 @@
package dev.struchkov.godfather.telegram.domain.attachment;
import dev.struchkov.godfather.context.domain.content.attachment.Attachment;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class PictureGroupAttachment extends Attachment {
private List<Picture> pictures;
public void setPictureSizes(List<Picture> pictures) {
this.pictures = pictures;
}
public List<Picture> getPictureSizes() {
return pictures;
}
public Optional<Picture> getLargePicture() {
return pictures.stream()
.max(Comparator.comparingInt(Picture::getFileSize));
}
@Override
public String getType() {
return TelegramAttachmentType.PICTURE.name();
}
}

View File

@ -2,6 +2,8 @@ package dev.struchkov.godfather.telegram.domain.attachment;
public enum TelegramAttachmentType {
DOCUMENT
DOCUMENT,
CONTACT,
PICTURE
}

View File

@ -1,15 +1,19 @@
package dev.struchkov.godfather.telegram.domain.keyboard;
import dev.struchkov.godfather.context.domain.keyboard.KeyBoard;
import dev.struchkov.godfather.context.domain.keyboard.KeyBoardButton;
import dev.struchkov.godfather.context.domain.keyboard.KeyBoardLine;
import java.util.ArrayList;
import java.util.List;
import static dev.struchkov.godfather.context.domain.keyboard.simple.SimpleKeyBoardLine.simpleLine;
public class InlineKeyBoard implements KeyBoard {
public static final String TYPE = "INLINE";
protected List<KeyBoardLine> lines = new ArrayList<>();
protected List<KeyBoardLine> lines;
public InlineKeyBoard(List<KeyBoardLine> lines) {
this.lines = lines;
@ -23,8 +27,16 @@ public class InlineKeyBoard implements KeyBoard {
return new Builder();
}
public static InlineKeyBoard inlineKeyBoard(KeyBoardLine keyBoardLine) {
return builder().line(keyBoardLine).build();
public static InlineKeyBoard inlineKeyBoard(KeyBoardLine... keyBoardLine) {
final Builder builder = builder();
for (KeyBoardLine boardLine : keyBoardLine) {
builder.line(boardLine);
}
return builder.build();
}
public static InlineKeyBoard inlineKeyBoard(KeyBoardButton... buttons) {
return builder().line(simpleLine(buttons)).build();
}
public List<KeyBoardLine> getLines() {

View File

@ -4,16 +4,17 @@ import dev.struchkov.godfather.context.domain.keyboard.KeyBoardLine;
import dev.struchkov.godfather.context.domain.keyboard.simple.SimpleKeyBoard;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MarkupKeyBoard extends SimpleKeyBoard {
public static final String TYPE = "MARKUP";
private static final MarkupKeyBoard EMPTY = new MarkupKeyBoard();
/**
* Скрыть меню после ответа или нет.
*/
private boolean oneTime = true;
private boolean oneTime;
/**
* Изменяет размер клавиатуры по вертикали для оптимального соответствия (например, сделать клавиатуру меньше, если есть только два ряда кнопок).
@ -22,6 +23,10 @@ public class MarkupKeyBoard extends SimpleKeyBoard {
private String inputFieldPlaceholder;
public MarkupKeyBoard() {
super(Collections.emptyList());
}
private MarkupKeyBoard(Builder builder) {
super(builder.lines);
oneTime = builder.oneTime;
@ -29,10 +34,14 @@ public class MarkupKeyBoard extends SimpleKeyBoard {
inputFieldPlaceholder = builder.inputFieldPlaceholder;
}
public static Builder newBuilder() {
public static Builder markupBuilder() {
return new Builder();
}
public static MarkupKeyBoard empty() {
return EMPTY;
}
public boolean isResizeKeyboard() {
return resizeKeyboard;
}
@ -50,6 +59,14 @@ public class MarkupKeyBoard extends SimpleKeyBoard {
return TYPE;
}
public boolean isEmpty() {
return lines.isEmpty();
}
public boolean isNotEmpty() {
return !lines.isEmpty();
}
public static final class Builder {
private List<KeyBoardLine> lines = new ArrayList<>();
private boolean oneTime = true;

View File

@ -0,0 +1,27 @@
package dev.struchkov.godfather.telegram.domain.keyboard.button;
import dev.struchkov.godfather.context.domain.keyboard.KeyBoardButton;
/**
* Запрашивает у пользователя его контактный номер.
*/
public class ContactButton implements KeyBoardButton {
public static final String TYPE = "CONTACT";
private final String label;
public ContactButton(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
@Override
public String getType() {
return TYPE;
}
}

View File

@ -2,20 +2,20 @@ package dev.struchkov.godfather.telegram.domain.keyboard.button;
import dev.struchkov.godfather.context.domain.keyboard.KeyBoardButton;
public class ButtonUrl implements KeyBoardButton {
public class UrlButton implements KeyBoardButton {
public static final String TYPE = "URL";
private String label;
private String url;
public ButtonUrl(String label, String url) {
public UrlButton(String label, String url) {
this.label = label;
this.url = url;
}
public static ButtonUrl buttonUrl(String label, String url) {
return new ButtonUrl(label, url);
public static UrlButton buttonUrl(String label, String url) {
return new UrlButton(label, url);
}
@Override

View File

@ -2,20 +2,20 @@ package dev.struchkov.godfather.telegram.domain.keyboard.button;
import dev.struchkov.godfather.context.domain.keyboard.KeyBoardButton;
public class ButtonWebApp implements KeyBoardButton {
public class WebAppButton implements KeyBoardButton {
public static final String TYPE = "WEB_APP";
private final String label;
private final String url;
public ButtonWebApp(String label, String url) {
public WebAppButton(String label, String url) {
this.label = label;
this.url = url;
}
public static ButtonWebApp buttonWebApp(String label, String url) {
return new ButtonWebApp(label, url);
public static WebAppButton buttonWebApp(String label, String url) {
return new WebAppButton(label, url);
}
public String getUrl() {

View File

@ -1,226 +0,0 @@
package dev.struchkov.godfather.telegram.listen;
import dev.struchkov.godfather.context.domain.BoxAnswer;
import dev.struchkov.godfather.context.domain.keyboard.KeyBoard;
import dev.struchkov.godfather.context.domain.keyboard.KeyBoardButton;
import dev.struchkov.godfather.context.domain.keyboard.KeyBoardLine;
import dev.struchkov.godfather.context.domain.keyboard.button.SimpleButton;
import dev.struchkov.godfather.context.domain.keyboard.simple.SimpleKeyBoard;
import dev.struchkov.godfather.context.service.sender.SendType;
import dev.struchkov.godfather.context.service.sender.Sending;
import dev.struchkov.godfather.telegram.TelegramConnect;
import dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard;
import dev.struchkov.godfather.telegram.domain.keyboard.MarkupKeyBoard;
import dev.struchkov.godfather.telegram.domain.keyboard.button.ButtonUrl;
import dev.struchkov.godfather.telegram.domain.keyboard.button.ButtonWebApp;
import dev.struchkov.godfather.telegram.service.SendPreProcessing;
import dev.struchkov.haiti.context.exception.ConvertException;
import dev.struchkov.haiti.utils.Inspector;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageText;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboard;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardButton;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow;
import org.telegram.telegrambots.meta.api.objects.webapp.WebAppInfo;
import org.telegram.telegrambots.meta.bots.AbsSender;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static dev.struchkov.haiti.utils.Inspector.isNotNull;
/**
* TODO: Добавить описание класса.
*
* @author upagge [15/07/2019]
*/
public class TelegramSender implements Sending {
private static final Logger log = LoggerFactory.getLogger(TelegramSender.class);
private static final String ERROR_REPLACE_MESSAGE = "Bad Request: message to edit not found";
private final AbsSender absSender;
private Map<Long, Integer> map = new HashMap<>();
private SendPreProcessing sendPreProcessing;
public TelegramSender(TelegramConnect telegramConnect) {
this.absSender = telegramConnect.getAdsSender();
}
public void setSendPreProcessing(SendPreProcessing sendPreProcessing) {
this.sendPreProcessing = sendPreProcessing;
}
public void send(@NotNull Long telegramId, @NotNull BoxAnswer boxAnswer) {
isNotNull(telegramId, boxAnswer);
try {
if (boxAnswer.isReplace() && map.containsKey(telegramId)) {
replaceMessage(telegramId, boxAnswer);
} else {
sendMessage(telegramId, boxAnswer);
}
} catch (TelegramApiRequestException e) {
log.error(e.getApiResponse());
if (ERROR_REPLACE_MESSAGE.equals(e.getApiResponse())) {
sendMessage(telegramId, boxAnswer);
}
} catch (TelegramApiException e) {
log.error(e.getMessage());
}
}
private void replaceMessage(@NotNull Long telegramId, @NotNull BoxAnswer boxAnswer) throws TelegramApiException {
final EditMessageText editMessageText = new EditMessageText();
editMessageText.setChatId(String.valueOf(telegramId));
editMessageText.setMessageId(map.get(telegramId));
editMessageText.enableMarkdown(true);
editMessageText.setText(boxAnswer.getMessage());
editMessageText.setReplyMarkup(convertInlineKeyBoard((InlineKeyBoard) boxAnswer.getKeyBoard()));
absSender.execute(editMessageText);
}
private void sendMessage(@NotNull Long telegramId, @NotNull BoxAnswer boxAnswer) {
final SendMessage sendMessage = new SendMessage();
sendMessage.enableMarkdown(true);
sendMessage.setChatId(String.valueOf(telegramId));
sendMessage.setText(
sendPreProcessing != null
? sendPreProcessing.pretreatment(boxAnswer.getMessage())
: boxAnswer.getMessage()
);
sendMessage.setReplyMarkup(convertKeyBoard(boxAnswer.getKeyBoard()));
try {
final Message execute = absSender.execute(sendMessage);
map.put(telegramId, execute.getMessageId());
} catch (TelegramApiRequestException e) {
log.error(e.getApiResponse());
} catch (TelegramApiException e) {
log.error(e.getMessage());
}
}
private ReplyKeyboard convertKeyBoard(KeyBoard keyBoard) {
if (keyBoard != null) {
switch (keyBoard.getType()) {
case InlineKeyBoard.TYPE:
return convertInlineKeyBoard((InlineKeyBoard) keyBoard);
case MarkupKeyBoard.TYPE:
return convertMarkupKeyBoard((MarkupKeyBoard) keyBoard);
case SimpleKeyBoard.TYPE:
return convertSimpleKeyBoard((SimpleKeyBoard) keyBoard);
}
}
return null;
}
private ReplyKeyboard convertSimpleKeyBoard(SimpleKeyBoard keyBoard) {
final ReplyKeyboardMarkup keyboardMarkup = new ReplyKeyboardMarkup();
keyboardMarkup.setKeyboard(
keyBoard.getLines().stream()
.map(this::convertMarkupLine)
.toList()
);
return keyboardMarkup;
}
private ReplyKeyboard convertMarkupKeyBoard(MarkupKeyBoard keyBoard) {
if (keyBoard != null) {
final ReplyKeyboardMarkup keyboardMarkup = new ReplyKeyboardMarkup();
keyboardMarkup.setOneTimeKeyboard(keyBoard.isOneTime());
keyboardMarkup.setInputFieldPlaceholder(keyBoard.getInputFieldPlaceholder());
keyboardMarkup.setResizeKeyboard(keyBoard.isResizeKeyboard());
keyboardMarkup.setKeyboard(
keyBoard.getLines().stream()
.map(this::convertMarkupLine)
.toList()
);
return keyboardMarkup;
}
return null;
}
private InlineKeyboardMarkup convertInlineKeyBoard(InlineKeyBoard keyBoard) {
if (keyBoard != null) {
final InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
inlineKeyboardMarkup.setKeyboard(
keyBoard.getLines().stream()
.map(this::convertInlineLine)
.toList()
);
return inlineKeyboardMarkup;
}
return null;
}
private List<InlineKeyboardButton> convertInlineLine(KeyBoardLine line) {
return line.getButtons().stream().map(this::convertInlineButton).toList();
}
private KeyboardRow convertMarkupLine(KeyBoardLine line) {
final List<KeyboardButton> buttons = line.getButtons().stream().map(this::convertMarkupButton).toList();
return new KeyboardRow(buttons);
}
private InlineKeyboardButton convertInlineButton(KeyBoardButton keyBoardButton) {
final InlineKeyboardButton button = new InlineKeyboardButton();
switch (keyBoardButton.getType()) {
case SimpleButton.TYPE -> {
final SimpleButton simpleButton = (SimpleButton) keyBoardButton;
final String callbackData = simpleButton.getCallbackData();
final String label = simpleButton.getLabel();
button.setText(label);
button.setCallbackData(callbackData != null ? callbackData : label);
}
case ButtonUrl.TYPE -> {
final ButtonUrl buttonUrl = (ButtonUrl) keyBoardButton;
button.setUrl(buttonUrl.getUrl());
button.setText(buttonUrl.getLabel());
}
case ButtonWebApp.TYPE -> {
final ButtonWebApp buttonWebApp = (ButtonWebApp) keyBoardButton;
final WebAppInfo webAppInfo = WebAppInfo.builder().url(buttonWebApp.getUrl()).build();
button.setWebApp(webAppInfo);
button.setText(buttonWebApp.getLabel());
}
default -> throw new ConvertException("Ошибка преобразования кнопки");
}
return button;
}
private KeyboardButton convertMarkupButton(KeyBoardButton keyBoardButton) {
final KeyboardButton button = new KeyboardButton();
switch (keyBoardButton.getType()) {
case SimpleButton.TYPE -> {
final SimpleButton simpleButton = (SimpleButton) keyBoardButton;
button.setText(simpleButton.getLabel());
Inspector.isNull(simpleButton.getCallbackData(), ConvertException.supplier("CallbackData поддерживает только Inline клавитаура"));
}
case ButtonWebApp.TYPE -> {
final ButtonWebApp buttonWebApp = (ButtonWebApp) keyBoardButton;
final WebAppInfo webAppInfo = WebAppInfo.builder().url(buttonWebApp.getUrl()).build();
button.setText(buttonWebApp.getLabel());
button.setWebApp(webAppInfo);
}
default -> throw new ConvertException("Ошибка преобразования кнопки");
}
return button;
}
public SendType getType() {
return SendType.PRIVATE;
}
}

View File

@ -2,6 +2,7 @@ package dev.struchkov.godfather.telegram.service;
import dev.struchkov.godfather.telegram.TelegramConnect;
import dev.struchkov.godfather.telegram.domain.attachment.DocumentAttachment;
import dev.struchkov.godfather.telegram.domain.attachment.Picture;
import dev.struchkov.godfather.telegram.domain.files.ByteContainer;
import dev.struchkov.godfather.telegram.domain.files.FileContainer;
import org.apache.commons.io.FileUtils;
@ -78,14 +79,32 @@ public class AttachmentServiceImpl {
return ByteContainer.empty();
}
public ByteContainer uploadBytes(@NotNull Picture picture) {
isNotNull(picture);
try {
final byte[] bytes = downloadBytes(picture);
return new ByteContainer(null, bytes);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return ByteContainer.empty();
}
private byte[] downloadBytes(Picture picture) throws TelegramApiException, IOException {
return telegramDownloadBytes(picture.getFileId());
}
private byte[] downloadBytes(DocumentAttachment documentAttachment) throws TelegramApiException, IOException {
final org.telegram.telegrambots.meta.api.objects.File file = getFilePath(documentAttachment);
final URL url = new URL(file.getFileUrl(botToken));
return IOUtils.toByteArray(url);
return telegramDownloadBytes(documentAttachment.getFileId());
}
private byte[] telegramDownloadBytes(String fileId) throws TelegramApiException, IOException {
final String fileUrl = getFileUrl(fileId);
return IOUtils.toByteArray(new URL(fileUrl));
}
private File downloadFile(DocumentAttachment documentAttachment) throws IOException, TelegramApiException {
final org.telegram.telegrambots.meta.api.objects.File file = getFilePath(documentAttachment);
final String fileUrl = getFileUrl(documentAttachment.getFileId());
final StringBuilder filePath = new StringBuilder();
if (folderPathForFiles != null) {
@ -96,15 +115,15 @@ public class AttachmentServiceImpl {
filePath.append(documentAttachment.getFileName());
final java.io.File localFile = new java.io.File(filePath.toString());
final InputStream is = new URL(file.getFileUrl(botToken)).openStream();
final InputStream is = new URL(fileUrl).openStream();
FileUtils.copyInputStreamToFile(is, localFile);
return localFile;
}
private org.telegram.telegrambots.meta.api.objects.File getFilePath(DocumentAttachment documentAttachment) throws TelegramApiException {
private String getFileUrl(String fileId) throws TelegramApiException {
final GetFile getFile = new GetFile();
getFile.setFileId(documentAttachment.getFileId());
return absSender.execute(getFile);
getFile.setFileId(fileId);
return absSender.execute(getFile).getFileUrl(botToken);
}
}

View File

@ -0,0 +1,102 @@
package dev.struchkov.godfather.telegram.service;
import dev.struchkov.godfather.context.domain.BoxAnswer;
import dev.struchkov.godfather.context.service.sender.SendType;
import dev.struchkov.godfather.context.service.sender.Sending;
import dev.struchkov.godfather.telegram.TelegramConnect;
import dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageText;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.bots.AbsSender;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException;
import java.util.HashMap;
import java.util.Map;
import static dev.struchkov.godfather.telegram.utils.KeyBoardConvert.convertInlineKeyBoard;
import static dev.struchkov.godfather.telegram.utils.KeyBoardConvert.convertKeyBoard;
import static dev.struchkov.haiti.utils.Inspector.isNotNull;
public class TelegramSender implements Sending {
private static final Logger log = LoggerFactory.getLogger(TelegramSender.class);
private static final String ERROR_REPLACE_MESSAGE = "Bad Request: message to edit not found";
private final AbsSender absSender;
private Map<Long, Integer> map = new HashMap<>();
private SendPreProcessing sendPreProcessing;
public TelegramSender(TelegramConnect telegramConnect) {
this.absSender = telegramConnect.getAdsSender();
}
public void setSendPreProcessing(SendPreProcessing sendPreProcessing) {
this.sendPreProcessing = sendPreProcessing;
}
public void send(@NotNull Long telegramId, @NotNull BoxAnswer boxAnswer) {
isNotNull(telegramId, boxAnswer);
if (boxAnswer.getMessage() != null && !boxAnswer.getMessage().isBlank()) {
try {
if (boxAnswer.isReplace() && map.containsKey(telegramId)) {
replaceMessage(telegramId, boxAnswer);
} else {
sendMessage(telegramId, boxAnswer);
}
} catch (TelegramApiRequestException e) {
log.error(e.getApiResponse());
if (ERROR_REPLACE_MESSAGE.equals(e.getApiResponse())) {
sendMessage(telegramId, boxAnswer);
}
} catch (TelegramApiException e) {
log.error(e.getMessage());
}
} else {
log.warn("Сообщение не было отправлено, так как текст сообщения был пустым");
}
}
private void replaceMessage(@NotNull Long telegramId, @NotNull BoxAnswer boxAnswer) throws TelegramApiException {
final EditMessageText editMessageText = new EditMessageText();
editMessageText.setChatId(String.valueOf(telegramId));
editMessageText.setMessageId(map.get(telegramId));
editMessageText.enableMarkdown(true);
editMessageText.setText(boxAnswer.getMessage());
editMessageText.setReplyMarkup(convertInlineKeyBoard((InlineKeyBoard) boxAnswer.getKeyBoard()));
absSender.execute(editMessageText);
}
private void sendMessage(@NotNull Long telegramId, @NotNull BoxAnswer boxAnswer) {
final SendMessage sendMessage = new SendMessage();
sendMessage.enableMarkdown(true);
sendMessage.setChatId(String.valueOf(telegramId));
sendMessage.setText(
sendPreProcessing != null
? sendPreProcessing.pretreatment(boxAnswer.getMessage())
: boxAnswer.getMessage()
);
sendMessage.setReplyMarkup(convertKeyBoard(boxAnswer.getKeyBoard()));
try {
final Message execute = absSender.execute(sendMessage);
map.put(telegramId, execute.getMessageId());
} catch (TelegramApiRequestException e) {
log.error(e.getApiResponse());
} catch (TelegramApiException e) {
log.error(e.getMessage());
}
}
public SendType getType() {
return SendType.PRIVATE;
}
}

View File

@ -1,7 +1,10 @@
package dev.struchkov.godfather.telegram.utils;
import dev.struchkov.godfather.context.domain.content.attachment.Attachment;
import dev.struchkov.godfather.telegram.domain.attachment.ContactAttachment;
import dev.struchkov.godfather.telegram.domain.attachment.DocumentAttachment;
import dev.struchkov.godfather.telegram.domain.attachment.Picture;
import dev.struchkov.godfather.telegram.domain.attachment.PictureGroupAttachment;
import dev.struchkov.godfather.telegram.domain.attachment.TelegramAttachmentType;
import dev.struchkov.haiti.utils.Inspector;
@ -16,6 +19,29 @@ public final class Attachments {
utilityClass();
}
public static Optional<PictureGroupAttachment> findFirstPictureGroup(Collection<Attachment> attachments) {
if (attachments != null) {
for (Attachment attachment : attachments) {
if (isPictureGroup(attachment)) {
return Optional.ofNullable((PictureGroupAttachment) attachment);
}
}
}
return Optional.empty();
}
public static Optional<Picture> findFirstLargePicture(Collection<Attachment> attachments) {
if (attachments != null) {
for (Attachment attachment : attachments) {
if (isPictureGroup(attachment)) {
final PictureGroupAttachment pictureGroup = (PictureGroupAttachment) attachment;
return pictureGroup.getLargePicture();
}
}
}
return Optional.empty();
}
public static Optional<DocumentAttachment> findFirstDocument(Collection<Attachment> attachments) {
if (attachments != null) {
for (Attachment attachment : attachments) {
@ -27,6 +53,17 @@ public final class Attachments {
return Optional.empty();
}
public static Optional<ContactAttachment> findFirstContact(Collection<Attachment> attachments) {
if (attachments != null) {
for (Attachment attachment : attachments) {
if (isContact(attachment)) {
return Optional.ofNullable((ContactAttachment) attachment);
}
}
}
return Optional.empty();
}
public static boolean hasDocument(Collection<Attachment> attachments) {
Inspector.isNotNull(attachments);
for (Attachment attachment : attachments) {
@ -42,4 +79,14 @@ public final class Attachments {
return TelegramAttachmentType.DOCUMENT.name().equals(attachment.getType());
}
private static boolean isContact(Attachment attachment) {
Inspector.isNotNull(attachment);
return TelegramAttachmentType.CONTACT.name().equals(attachment.getType());
}
private static boolean isPictureGroup(Attachment attachment) {
Inspector.isNotNull(attachment);
return TelegramAttachmentType.PICTURE.name().equals(attachment.getType());
}
}

View File

@ -15,10 +15,10 @@ import static dev.struchkov.haiti.utils.Exceptions.utilityClass;
public final class InlineKeyBoards {
public static final SimpleButton YES_BUTTON = simpleButton("Да", "{\"button\": \"yes\"}");
public static final SimpleButton NO_BUTTON = simpleButton("Нет", "{\"button\": \"no\"}");
public static final SimpleButton YES_BUTTON = simpleButton("Да", "Да");
public static final SimpleButton NO_BUTTON = simpleButton("Нет", "Нет");
public InlineKeyBoards() {
private InlineKeyBoards() {
utilityClass();
}
@ -27,10 +27,8 @@ public final class InlineKeyBoards {
*
* @return {@link SimpleKeyBoard}
*/
public static InlineKeyBoard keyBoardYesNo() {
return InlineKeyBoard.inlineKeyBoard(
SimpleKeyBoardLine.builder().button(YES_BUTTON).button(NO_BUTTON).build()
);
public static SimpleKeyBoardLine lineYesOrNo() {
return simpleLine(YES_BUTTON, NO_BUTTON);
}
/**
@ -122,7 +120,7 @@ public final class InlineKeyBoards {
* @param buttons Список кнопок
* @return {@link SimpleKeyBoard}
*/
public static InlineKeyBoard verticalMenuButton(List<KeyBoardButton> buttons) {
public static InlineKeyBoard verticalMenuButton(KeyBoardButton... buttons) {
final InlineKeyBoard.Builder keyBoard = InlineKeyBoard.builder();
for (KeyBoardButton simpleButton : buttons) {
keyBoard.line(simpleLine(simpleButton));

View File

@ -0,0 +1,152 @@
package dev.struchkov.godfather.telegram.utils;
import dev.struchkov.godfather.context.domain.keyboard.KeyBoard;
import dev.struchkov.godfather.context.domain.keyboard.KeyBoardButton;
import dev.struchkov.godfather.context.domain.keyboard.KeyBoardLine;
import dev.struchkov.godfather.context.domain.keyboard.button.SimpleButton;
import dev.struchkov.godfather.context.domain.keyboard.simple.SimpleKeyBoard;
import dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard;
import dev.struchkov.godfather.telegram.domain.keyboard.MarkupKeyBoard;
import dev.struchkov.godfather.telegram.domain.keyboard.button.ContactButton;
import dev.struchkov.godfather.telegram.domain.keyboard.button.UrlButton;
import dev.struchkov.godfather.telegram.domain.keyboard.button.WebAppButton;
import dev.struchkov.haiti.context.exception.ConvertException;
import dev.struchkov.haiti.utils.Exceptions;
import dev.struchkov.haiti.utils.Inspector;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboard;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardRemove;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardButton;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow;
import org.telegram.telegrambots.meta.api.objects.webapp.WebAppInfo;
import java.util.List;
public final class KeyBoardConvert {
private KeyBoardConvert() {
Exceptions.utilityClass();
}
public static ReplyKeyboard convertKeyBoard(KeyBoard keyBoard) {
if (keyBoard != null) {
switch (keyBoard.getType()) {
case InlineKeyBoard.TYPE:
return convertInlineKeyBoard((InlineKeyBoard) keyBoard);
case MarkupKeyBoard.TYPE:
return convertMarkupKeyBoard((MarkupKeyBoard) keyBoard);
case SimpleKeyBoard.TYPE:
return convertSimpleKeyBoard((SimpleKeyBoard) keyBoard);
}
}
return null;
}
public static ReplyKeyboard convertSimpleKeyBoard(SimpleKeyBoard keyBoard) {
final ReplyKeyboardMarkup keyboardMarkup = new ReplyKeyboardMarkup();
keyboardMarkup.setKeyboard(
keyBoard.getLines().stream()
.map(KeyBoardConvert::convertMarkupLine)
.toList()
);
return keyboardMarkup;
}
public static ReplyKeyboard convertMarkupKeyBoard(MarkupKeyBoard keyBoard) {
if (keyBoard != null) {
if (keyBoard.isNotEmpty()) {
final ReplyKeyboardMarkup keyboardMarkup = new ReplyKeyboardMarkup();
keyboardMarkup.setOneTimeKeyboard(keyBoard.isOneTime());
keyboardMarkup.setInputFieldPlaceholder(keyBoard.getInputFieldPlaceholder());
keyboardMarkup.setResizeKeyboard(keyBoard.isResizeKeyboard());
keyboardMarkup.setKeyboard(
keyBoard.getLines().stream()
.map(KeyBoardConvert::convertMarkupLine)
.toList()
);
return keyboardMarkup;
} else {
final ReplyKeyboardRemove replyKeyboardRemove = new ReplyKeyboardRemove();
replyKeyboardRemove.setRemoveKeyboard(true);
return replyKeyboardRemove;
}
}
return null;
}
public static InlineKeyboardMarkup convertInlineKeyBoard(InlineKeyBoard keyBoard) {
if (keyBoard != null) {
final InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
inlineKeyboardMarkup.setKeyboard(
keyBoard.getLines().stream()
.map(KeyBoardConvert::convertInlineLine)
.toList()
);
return inlineKeyboardMarkup;
}
return null;
}
private static List<InlineKeyboardButton> convertInlineLine(KeyBoardLine line) {
return line.getButtons().stream().map(KeyBoardConvert::convertInlineButton).toList();
}
private static KeyboardRow convertMarkupLine(KeyBoardLine line) {
final List<KeyboardButton> buttons = line.getButtons().stream().map(KeyBoardConvert::convertMarkupButton).toList();
return new KeyboardRow(buttons);
}
private static InlineKeyboardButton convertInlineButton(KeyBoardButton keyBoardButton) {
final InlineKeyboardButton button = new InlineKeyboardButton();
switch (keyBoardButton.getType()) {
case SimpleButton.TYPE -> {
final SimpleButton simpleButton = (SimpleButton) keyBoardButton;
final String callbackData = simpleButton.getCallbackData();
final String label = simpleButton.getLabel();
button.setText(label);
button.setCallbackData(callbackData != null ? callbackData : label);
}
case UrlButton.TYPE -> {
final UrlButton urlButton = (UrlButton) keyBoardButton;
button.setUrl(urlButton.getUrl());
button.setText(urlButton.getLabel());
}
case WebAppButton.TYPE -> {
final WebAppButton webAppButton = (WebAppButton) keyBoardButton;
final WebAppInfo webAppInfo = WebAppInfo.builder().url(webAppButton.getUrl()).build();
button.setWebApp(webAppInfo);
button.setText(webAppButton.getLabel());
}
default -> throw new ConvertException("Ошибка преобразования кнопки");
}
return button;
}
private static KeyboardButton convertMarkupButton(KeyBoardButton keyBoardButton) {
final KeyboardButton button = new KeyboardButton();
switch (keyBoardButton.getType()) {
case SimpleButton.TYPE -> {
final SimpleButton simpleButton = (SimpleButton) keyBoardButton;
button.setText(simpleButton.getLabel());
Inspector.isNull(simpleButton.getCallbackData(), ConvertException.supplier("CallbackData поддерживает только Inline клавитаура"));
}
case WebAppButton.TYPE -> {
final WebAppButton webAppButton = (WebAppButton) keyBoardButton;
final WebAppInfo webAppInfo = WebAppInfo.builder().url(webAppButton.getUrl()).build();
button.setText(webAppButton.getLabel());
button.setWebApp(webAppInfo);
}
case ContactButton.TYPE -> {
final ContactButton contactButton = (ContactButton) keyBoardButton;
button.setText(contactButton.getLabel());
button.setRequestContact(true);
}
default -> throw new ConvertException("Ошибка преобразования кнопки");
}
return button;
}
}