1
1
package com .annimon .similarimagesbot ;
2
2
3
+ import com .annimon .similarimagesbot .data .ImageResult ;
3
4
import com .annimon .similarimagesbot .data .Post ;
4
5
import com .annimon .similarimagesbot .data .SimilarImagesInfo ;
5
6
import com .pengrad .telegrambot .TelegramBot ;
6
7
import com .pengrad .telegrambot .UpdatesListener ;
7
8
import com .pengrad .telegrambot .model .Message ;
8
9
import com .pengrad .telegrambot .model .PhotoSize ;
9
10
import com .pengrad .telegrambot .model .Update ;
11
+ import com .pengrad .telegrambot .model .request .InputMediaPhoto ;
10
12
import com .pengrad .telegrambot .model .request .ParseMode ;
11
13
import com .pengrad .telegrambot .request .DeleteMessage ;
14
+ import com .pengrad .telegrambot .request .ForwardMessage ;
12
15
import com .pengrad .telegrambot .request .GetFile ;
13
16
import com .pengrad .telegrambot .request .GetUpdates ;
17
+ import com .pengrad .telegrambot .request .SendMediaGroup ;
14
18
import com .pengrad .telegrambot .request .SendMessage ;
19
+ import com .pengrad .telegrambot .response .SendResponse ;
15
20
import java .awt .image .BufferedImage ;
16
21
import java .io .IOException ;
17
22
import java .net .URL ;
21
26
import java .util .Comparator ;
22
27
import java .util .List ;
23
28
import java .util .Objects ;
29
+ import java .util .Optional ;
24
30
import java .util .Set ;
31
+ import java .util .function .Predicate ;
32
+ import java .util .regex .Matcher ;
25
33
import java .util .regex .Pattern ;
26
34
import java .util .stream .Collectors ;
27
35
import javax .imageio .ImageIO ;
28
36
29
37
public class BotHandler {
30
38
39
+ private final Comparator <PhotoSize > photoSizeComparator = Comparator
40
+ .comparingInt (ps -> ps .width () * ps .height ());
41
+ private final Pattern delPattern = Pattern .compile ("/del(\\ d+)m(\\ d+)" );
42
+ private final Pattern comparePattern = Pattern .compile ("/compare(\\ d+)m(\\ d+)x(\\ d+)" );
43
+
31
44
private final TelegramBot bot ;
32
45
private final ImageIndexer indexer ;
33
46
private long adminId ;
@@ -58,32 +71,64 @@ public void runOnce() {
58
71
}
59
72
60
73
private Set <Post > processAdminCommands (List <Update > updates ) {
61
- final var delPattern = Pattern .compile ("/del(\\ d+)m(\\ d+)" );
62
74
return updates .stream ()
63
75
.map (Update ::message )
64
76
.filter (Objects ::nonNull )
65
77
.filter (msg -> msg .chat ().id () == adminId )
66
78
.map (Message ::text )
67
79
.filter (Objects ::nonNull )
68
- .map (command -> {
69
- final var m = delPattern .matcher (command );
70
- if (m .find ()) {
71
- final var channelId = Long .parseLong ("-100" + m .group (1 ));
72
- final var messageId = Integer .parseInt (m .group (2 ));
73
- bot .execute (new DeleteMessage (channelId , messageId ));
74
- try {
75
- indexer .deleteImage (channelId , messageId );
76
- } catch (SQLException ex ) {
77
- System .err .println ("Cannot delete image in db" );
78
- }
79
- return new Post (channelId , messageId );
80
- }
81
- return null ;
82
- })
80
+ .map (command -> Optional .<Post >empty ()
81
+ .or (() -> processDelCommand (delPattern .matcher (command )))
82
+ .or (() -> processCompareCommand (comparePattern .matcher (command )))
83
+ .orElse (null ))
83
84
.filter (Objects ::nonNull )
84
85
.collect (Collectors .toSet ());
85
86
}
86
87
88
+ private Optional <Post > processDelCommand (Matcher m ) {
89
+ if (!m .find ()) {
90
+ return Optional .empty ();
91
+ }
92
+ final var channelId = Long .parseLong ("-100" + m .group (1 ));
93
+ final var messageId = Integer .parseInt (m .group (2 ));
94
+ bot .execute (new DeleteMessage (channelId , messageId ));
95
+ try {
96
+ indexer .deleteImage (channelId , messageId );
97
+ } catch (SQLException ex ) {
98
+ System .err .println ("Cannot delete image in db" );
99
+ }
100
+ return Optional .of (new Post (channelId , messageId ));
101
+ }
102
+
103
+ private Optional <Post > processCompareCommand (Matcher m ) {
104
+ if (!m .find ()) {
105
+ return Optional .empty ();
106
+ }
107
+ final var channelId = Long .parseLong ("-100" + m .group (1 ));
108
+ final var messageA = Integer .parseInt (m .group (2 ));
109
+ final var messageB = Integer .parseInt (m .group (3 ));
110
+
111
+ // Forward and get photo to compare
112
+ var sentA = bot .execute (new ForwardMessage (adminId , channelId , messageA ));
113
+ var sentB = bot .execute (new ForwardMessage (adminId , channelId , messageB ));
114
+ final Predicate <SendResponse > hasPhoto = (r ) -> r .isOk () && r .message ().photo () != null ;
115
+ if (hasPhoto .test (sentA ) && hasPhoto .test (sentB )) {
116
+ final var photoA = getBiggestPhoto (sentA .message ().photo ());
117
+ final var photoB = getBiggestPhoto (sentB .message ().photo ());
118
+ bot .execute (new SendMediaGroup (adminId ,
119
+ new InputMediaPhoto (photoA .fileId ()).caption ("Post " + messageA ),
120
+ new InputMediaPhoto (photoB .fileId ()).caption ("Post " + messageB ) ));
121
+ }
122
+ // Clean up if one of the images already deleted
123
+ if (sentA .message () != null ) {
124
+ bot .execute (new DeleteMessage (adminId , sentA .message ().messageId ()));
125
+ }
126
+ if (sentB .message () != null ) {
127
+ bot .execute (new DeleteMessage (adminId , sentB .message ().messageId ()));
128
+ }
129
+ return Optional .empty ();
130
+ }
131
+
87
132
private void processUpdates (List <Update > updates , Set <Post > ignoredPosts ) {
88
133
final List <Message > channelPosts = getChannelPostsWithPhotos (updates );
89
134
final var similarImagesInfos = new ArrayList <SimilarImagesInfo >();
@@ -120,13 +165,20 @@ private List<Message> getChannelPostsWithPhotos(List<Update> updates) {
120
165
private void sendReport (List <SimilarImagesInfo > infos ) {
121
166
String report = infos .stream ().map (info -> {
122
167
final var post = info .getOriginalPost ();
168
+ final var channelId = post .getChannelId ().toString ().replace ("-100" , "" );
123
169
String text = "For post " + formatPostLink (post ) + " found:\n " ;
170
+ // Matching results
124
171
text += info .getResults ().stream ()
125
172
.map (r -> String .format (" %s, dst: %.2f" , formatPostLink (r .getPost ()), r .getDistance ()))
126
173
.collect (Collectors .joining ("\n " ));
127
- text += String .format ("%n/del%sm%d" ,
128
- post .getChannelId ().toString ().replace ("-100" , "" ),
129
- post .getMessageId ());
174
+ // /compare command
175
+ text += info .getResults ().stream ()
176
+ .map (ImageResult ::getPost )
177
+ .map (p -> String .format ("/compare%sm%dx%d" ,
178
+ channelId , post .getMessageId (), p .getMessageId ()))
179
+ .collect (Collectors .joining ("\n " ));
180
+ // /del command
181
+ text += String .format ("%n/del%sm%d" , channelId , post .getMessageId ());
130
182
return text ;
131
183
}).collect (Collectors .joining ("\n \n " ));
132
184
@@ -152,7 +204,13 @@ private String linkToMessage(Long chatId, Integer messageId) {
152
204
153
205
private PhotoSize getSmallestPhoto (PhotoSize [] photoSizes ) {
154
206
return Arrays .stream (photoSizes )
155
- .min (Comparator .comparingInt (ps -> ps .width () * ps .height ()))
207
+ .min (photoSizeComparator )
208
+ .orElse (photoSizes [0 ]);
209
+ }
210
+
211
+ private PhotoSize getBiggestPhoto (PhotoSize [] photoSizes ) {
212
+ return Arrays .stream (photoSizes )
213
+ .max (photoSizeComparator )
156
214
.orElse (photoSizes [0 ]);
157
215
}
158
216
}
0 commit comments