207 lines
7.5 KiB
Java
207 lines
7.5 KiB
Java
package de.ph87.kleinanzeigen.api;
|
|
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
|
|
import org.telegram.telegrambots.meta.TelegramBotsApi;
|
|
import org.telegram.telegrambots.meta.api.methods.send.SendPhoto;
|
|
import org.telegram.telegrambots.meta.api.methods.updatingmessages.DeleteMessages;
|
|
import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageCaption;
|
|
import org.telegram.telegrambots.meta.api.objects.*;
|
|
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
|
|
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
|
|
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
|
|
import org.telegram.telegrambots.updatesreceivers.DefaultBotSession;
|
|
|
|
import javax.imageio.ImageIO;
|
|
import java.awt.image.BufferedImage;
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.FileInputStream;
|
|
import java.io.IOException;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.*;
|
|
import java.util.function.BiFunction;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.Function;
|
|
|
|
import static de.ph87.kleinanzeigen.api.JSON.objectMapper;
|
|
|
|
@Slf4j
|
|
public class Bot extends TelegramLongPollingBot {
|
|
|
|
private static final long CHAT_ID = 101138682L;
|
|
|
|
private static final String ICON_CHECK = "✅";
|
|
|
|
private static final String ICON_REMOVE = "❌";
|
|
|
|
private final byte[] NO_IMAGE;
|
|
|
|
private final DefaultBotSession session;
|
|
|
|
private final Consumer<MaybeInaccessibleMessage> ignore;
|
|
|
|
private final Function<MaybeInaccessibleMessage, Optional<Offer>> find;
|
|
|
|
private final BiFunction<MaybeInaccessibleMessage, Boolean, Optional<Offer>> remember;
|
|
|
|
public Bot(final Consumer<MaybeInaccessibleMessage> ignore, final Function<MaybeInaccessibleMessage, Optional<Offer>> find, final BiFunction<MaybeInaccessibleMessage, Boolean, Optional<Offer>> remember) throws IOException, TelegramApiException {
|
|
super(readToken());
|
|
|
|
final BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
|
|
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
|
ImageIO.write(img, "PNG", stream);
|
|
NO_IMAGE = stream.toByteArray();
|
|
|
|
this.ignore = ignore;
|
|
this.find = find;
|
|
this.remember = remember;
|
|
log.info("Starting telegram bot...");
|
|
final TelegramBotsApi api = new TelegramBotsApi(DefaultBotSession.class);
|
|
session = (DefaultBotSession) api.registerBot(this);
|
|
log.info("Telegram bot registered.");
|
|
}
|
|
|
|
@Override
|
|
public String getBotUsername() {
|
|
return "BotKleinanzeigenBot";
|
|
}
|
|
|
|
@Override
|
|
public void onUpdateReceived(final Update update) {
|
|
if (update.hasMessage() && update.getMessage().hasText()) {
|
|
log.info("#{} \"{}\": {}", update.getMessage().getChat().getId(), update.getMessage().getChat().getUserName(), update.getMessage().getText());
|
|
} else if (update.hasCallbackQuery()) {
|
|
handleCallbackQuery(update.getCallbackQuery());
|
|
}
|
|
}
|
|
|
|
private void handleCallbackQuery(final CallbackQuery query) {
|
|
final MaybeInaccessibleMessage message = query.getMessage();
|
|
if (message.getChatId() != CHAT_ID) {
|
|
return;
|
|
}
|
|
try {
|
|
final InlineDto dto = objectMapper.readValue(query.getData(), InlineDto.class);
|
|
switch (dto.getCommand()) {
|
|
case IGNORE -> ignore(message);
|
|
case REMEMBER -> remember(message);
|
|
case UNREMEMBER -> unremember(message);
|
|
default -> updateMessage(message);
|
|
}
|
|
} catch (JsonProcessingException e) {
|
|
log.error("Failed to read InlineDto.", e);
|
|
}
|
|
}
|
|
|
|
private void ignore(final MaybeInaccessibleMessage message) {
|
|
ignore.accept(message);
|
|
remove(message);
|
|
}
|
|
|
|
private void remember(final MaybeInaccessibleMessage message) {
|
|
remember.apply(message, true).ifPresentOrElse(
|
|
offer -> updateMessage(message, offer),
|
|
() -> remove(message)
|
|
);
|
|
}
|
|
|
|
private void unremember(final MaybeInaccessibleMessage message) {
|
|
remember.apply(message, false).ifPresentOrElse(
|
|
offer -> updateMessage(message, offer),
|
|
() -> remove(message)
|
|
);
|
|
}
|
|
|
|
private void updateMessage(final MaybeInaccessibleMessage message) {
|
|
find.apply(message).ifPresentOrElse(
|
|
offer -> updateMessage(message, offer),
|
|
() -> remove(message)
|
|
);
|
|
}
|
|
|
|
private void updateMessage(final MaybeInaccessibleMessage message, final Offer offer) {
|
|
try {
|
|
final EditMessageCaption edit = new EditMessageCaption(message.getChatId() + "", message.getMessageId(), null, createText(offer), createKeyboard(offer), null, null);
|
|
edit.setParseMode("Markdown");
|
|
execute(edit);
|
|
} catch (TelegramApiException | JsonProcessingException e) {
|
|
log.error("Failed to edit Message to #{}.", message.getChatId(), e);
|
|
}
|
|
}
|
|
|
|
private static String readToken() throws IOException {
|
|
try (final FileInputStream stream = new FileInputStream("./telegram.token")) {
|
|
return new String(stream.readAllBytes(), StandardCharsets.UTF_8).trim();
|
|
}
|
|
}
|
|
|
|
public void send(final Offer offer) {
|
|
try {
|
|
final InputFile inputFile = offer.getImage().isEmpty() ? new InputFile(new ByteArrayInputStream(NO_IMAGE), "[Kein Bild]") : new InputFile(offer.getImage());
|
|
final SendPhoto send = new SendPhoto(CHAT_ID + "", inputFile);
|
|
send.setCaption(createText(offer));
|
|
send.setReplyMarkup(createKeyboard(offer));
|
|
final Message message = execute(send);
|
|
|
|
offer.setTelegramMessageId(message.getMessageId());
|
|
} catch (TelegramApiException | JsonProcessingException e) {
|
|
log.error("Failed to send Message to #{}.", CHAT_ID, e);
|
|
}
|
|
}
|
|
|
|
private String createText(final Offer offer) {
|
|
return "[%s](%s)\n%s\n%s".formatted(
|
|
offer.getTitle().replaceAll("\\[", "(").replaceAll("]", ")"),
|
|
offer.getHref(),
|
|
offer.calculateLocationString(),
|
|
offer.getDescription()
|
|
);
|
|
}
|
|
|
|
private InlineKeyboardMarkup createKeyboard(final Offer offer) throws JsonProcessingException {
|
|
final InlineKeyboardMarkup markup = new InlineKeyboardMarkup();
|
|
final ArrayList<List<InlineKeyboardButton>> keyboard = new ArrayList<>();
|
|
final ArrayList<InlineKeyboardButton> row = new ArrayList<>();
|
|
if (offer.isRemember()) {
|
|
addButton(row, ICON_CHECK + ICON_CHECK + ICON_CHECK + " Gemerkt " + ICON_CHECK + ICON_CHECK + ICON_CHECK, InlineCommand.UNREMEMBER);
|
|
} else {
|
|
addButton(row, ICON_REMOVE + " Entfernen", InlineCommand.IGNORE);
|
|
addButton(row, ICON_CHECK + " Merken", InlineCommand.REMEMBER);
|
|
}
|
|
keyboard.add(row);
|
|
markup.setKeyboard(keyboard);
|
|
return markup;
|
|
}
|
|
|
|
private void addButton(final ArrayList<InlineKeyboardButton> row, final String caption, final InlineCommand command) throws JsonProcessingException {
|
|
final String data = objectMapper.writeValueAsString(new InlineDto(command));
|
|
row.add(new InlineKeyboardButton(caption, null, data, null, null, null, null, null, null));
|
|
}
|
|
|
|
public void remove(final List<Offer> offers) {
|
|
_remove(offers.stream().map(Offer::getTelegramMessageId).filter(Objects::nonNull).toList());
|
|
}
|
|
|
|
private void remove(final MaybeInaccessibleMessage... messages) {
|
|
_remove(Arrays.stream(messages).map(MaybeInaccessibleMessage::getMessageId).toList());
|
|
}
|
|
|
|
private void _remove(final List<Integer> messageIds) {
|
|
if (messageIds.isEmpty()) {
|
|
return;
|
|
}
|
|
try {
|
|
execute(new DeleteMessages(CHAT_ID + "", messageIds));
|
|
} catch (TelegramApiException e) {
|
|
log.error("Failed to remove Message to #{}.", CHAT_ID, e);
|
|
}
|
|
}
|
|
|
|
public void stop() {
|
|
session.stop();
|
|
}
|
|
|
|
}
|