diff --git a/Dockerfile b/Dockerfile index c4b60c5..c763bd7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,8 +13,8 @@ RUN GRADLE_OPTS="-Xmx256m" gradle shadowJar --build-cache --stacktrace --no-daem FROM openjdk:11 WORKDIR /app -COPY --from=builder /usr/src/java-code/build/libs/SimilarImagesBot-1.0.1-all.jar . +COPY --from=builder /usr/src/java-code/build/libs/SimilarImagesBot-1.0.2-all.jar . ENV BOT_TOKEN '' \ ADMIN_ID 0 \ MODE 'once' -ENTRYPOINT ["java", "-jar", "/app/SimilarImagesBot-1.0.1-all.jar"] +ENTRYPOINT ["java", "-jar", "/app/SimilarImagesBot-1.0.2-all.jar"] diff --git a/build.gradle b/build.gradle index 8f038a1..c702256 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ sourceCompatibility = 11 targetCompatibility = 11 mainClassName = 'com.annimon.similarimagesbot.Main' group 'com.annimon' -version '1.0.1' +version '1.0.2' repositories { jcenter() diff --git a/src/main/java/com/annimon/similarimagesbot/BotHandler.java b/src/main/java/com/annimon/similarimagesbot/BotHandler.java index f8d110b..be3a472 100644 --- a/src/main/java/com/annimon/similarimagesbot/BotHandler.java +++ b/src/main/java/com/annimon/similarimagesbot/BotHandler.java @@ -1,5 +1,6 @@ package com.annimon.similarimagesbot; +import com.annimon.similarimagesbot.data.ImageResult; import com.annimon.similarimagesbot.data.Post; import com.annimon.similarimagesbot.data.SimilarImagesInfo; import com.pengrad.telegrambot.TelegramBot; @@ -7,11 +8,15 @@ import com.pengrad.telegrambot.UpdatesListener; import com.pengrad.telegrambot.model.Message; import com.pengrad.telegrambot.model.PhotoSize; import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.model.request.InputMediaPhoto; import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.request.DeleteMessage; +import com.pengrad.telegrambot.request.ForwardMessage; import com.pengrad.telegrambot.request.GetFile; import com.pengrad.telegrambot.request.GetUpdates; +import com.pengrad.telegrambot.request.SendMediaGroup; import com.pengrad.telegrambot.request.SendMessage; +import com.pengrad.telegrambot.response.SendResponse; import java.awt.image.BufferedImage; import java.io.IOException; import java.net.URL; @@ -21,13 +26,21 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; +import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.imageio.ImageIO; public class BotHandler { + private final Comparator photoSizeComparator = Comparator + .comparingInt(ps -> ps.width() * ps.height()); + private final Pattern delPattern = Pattern.compile("/del(\\d+)m(\\d+)"); + private final Pattern comparePattern = Pattern.compile("/compare(\\d+)m(\\d+)x(\\d+)"); + private final TelegramBot bot; private final ImageIndexer indexer; private long adminId; @@ -58,32 +71,64 @@ public class BotHandler { } private Set processAdminCommands(List updates) { - final var delPattern = Pattern.compile("/del(\\d+)m(\\d+)"); return updates.stream() .map(Update::message) .filter(Objects::nonNull) .filter(msg -> msg.chat().id() == adminId) .map(Message::text) .filter(Objects::nonNull) - .map(command -> { - final var m = delPattern.matcher(command); - if (m.find()) { - final var channelId = Long.parseLong("-100" + m.group(1)); - final var messageId = Integer.parseInt(m.group(2)); - bot.execute(new DeleteMessage(channelId, messageId)); - try { - indexer.deleteImage(channelId, messageId); - } catch (SQLException ex) { - System.err.println("Cannot delete image in db"); - } - return new Post(channelId, messageId); - } - return null; - }) + .map(command -> Optional.empty() + .or(() -> processDelCommand(delPattern.matcher(command))) + .or(() -> processCompareCommand(comparePattern.matcher(command))) + .orElse(null)) .filter(Objects::nonNull) .collect(Collectors.toSet()); } + private Optional processDelCommand(Matcher m) { + if (!m.find()) { + return Optional.empty(); + } + final var channelId = Long.parseLong("-100" + m.group(1)); + final var messageId = Integer.parseInt(m.group(2)); + bot.execute(new DeleteMessage(channelId, messageId)); + try { + indexer.deleteImage(channelId, messageId); + } catch (SQLException ex) { + System.err.println("Cannot delete image in db"); + } + return Optional.of(new Post(channelId, messageId)); + } + + private Optional processCompareCommand(Matcher m) { + if (!m.find()) { + return Optional.empty(); + } + final var channelId = Long.parseLong("-100" + m.group(1)); + final var messageA = Integer.parseInt(m.group(2)); + final var messageB = Integer.parseInt(m.group(3)); + + // Forward and get photo to compare + var sentA = bot.execute(new ForwardMessage(adminId, channelId, messageA)); + var sentB = bot.execute(new ForwardMessage(adminId, channelId, messageB)); + final Predicate hasPhoto = (r) -> r.isOk() && r.message().photo() != null; + if (hasPhoto.test(sentA) && hasPhoto.test(sentB)) { + final var photoA = getBiggestPhoto(sentA.message().photo()); + final var photoB = getBiggestPhoto(sentB.message().photo()); + bot.execute(new SendMediaGroup(adminId, + new InputMediaPhoto(photoA.fileId()).caption("Post " + messageA), + new InputMediaPhoto(photoB.fileId()).caption("Post " + messageB) )); + } + // Clean up if one of the images already deleted + if (sentA.message() != null) { + bot.execute(new DeleteMessage(adminId, sentA.message().messageId())); + } + if (sentB.message() != null) { + bot.execute(new DeleteMessage(adminId, sentB.message().messageId())); + } + return Optional.empty(); + } + private void processUpdates(List updates, Set ignoredPosts) { final List channelPosts = getChannelPostsWithPhotos(updates); final var similarImagesInfos = new ArrayList(); @@ -120,13 +165,20 @@ public class BotHandler { private void sendReport(List infos) { String report = infos.stream().map(info -> { final var post = info.getOriginalPost(); + final var channelId = post.getChannelId().toString().replace("-100", ""); String text = "For post " + formatPostLink(post) + " found:\n"; + // Matching results text += info.getResults().stream() .map(r -> String.format(" %s, dst: %.2f", formatPostLink(r.getPost()), r.getDistance())) .collect(Collectors.joining("\n")); - text += String.format("%n/del%sm%d", - post.getChannelId().toString().replace("-100", ""), - post.getMessageId()); + // /compare command + text += info.getResults().stream() + .map(ImageResult::getPost) + .map(p -> String.format("/compare%sm%dx%d", + channelId, post.getMessageId(), p.getMessageId())) + .collect(Collectors.joining("\n")); + // /del command + text += String.format("%n/del%sm%d", channelId, post.getMessageId()); return text; }).collect(Collectors.joining("\n\n")); @@ -152,7 +204,13 @@ public class BotHandler { private PhotoSize getSmallestPhoto(PhotoSize[] photoSizes) { return Arrays.stream(photoSizes) - .min(Comparator.comparingInt(ps -> ps.width() * ps.height())) + .min(photoSizeComparator) + .orElse(photoSizes[0]); + } + + private PhotoSize getBiggestPhoto(PhotoSize[] photoSizes) { + return Arrays.stream(photoSizes) + .max(photoSizeComparator) .orElse(photoSizes[0]); } }