Initial completion of scanning of qr code from gmail

This commit is contained in:
heyethereum
2024-07-28 03:07:23 +08:00
parent 20c9473bd3
commit 3567457026
7 changed files with 206 additions and 60 deletions

12
pom.xml
View File

@@ -109,6 +109,18 @@
<artifactId>jsoup</artifactId> <artifactId>jsoup</artifactId>
<version>1.18.1</version> <version>1.18.1</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.17.2</version>
</dependency>
</dependencies> </dependencies>

View File

@@ -1,6 +1,7 @@
package com.safeqr.app.gmail.controller; package com.safeqr.app.gmail.controller;
import com.google.api.services.gmail.model.*; import com.google.api.services.gmail.model.*;
import com.safeqr.app.gmail.dto.ScannedGmailResponseDto;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.json.JSONObject; import org.json.JSONObject;
import com.google.api.client.auth.oauth2.AuthorizationCodeRequestUrl; import com.google.api.client.auth.oauth2.AuthorizationCodeRequestUrl;
@@ -106,7 +107,7 @@ public class GmailController {
return new ResponseEntity<>("Access token is missing", HttpStatus.BAD_REQUEST); return new ResponseEntity<>("Access token is missing", HttpStatus.BAD_REQUEST);
} }
return new ResponseEntity<>(gmailService.getEmail(accessToken).toString(), HttpStatus.OK); return new ResponseEntity<>(gmailService.getEmail(accessToken), HttpStatus.OK);
} }
} }

View File

@@ -0,0 +1,15 @@
package com.safeqr.app.gmail.dto;
import com.safeqr.app.gmail.model.EmailMessage;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.util.List;
@Builder
@Data
@AllArgsConstructor
public class ScannedGmailResponseDto {
List<EmailMessage> messages;
}

View File

@@ -0,0 +1,38 @@
package com.safeqr.app.gmail.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class EmailMessage {
private String messageId;
private String subject;
private String historyId;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
List<QRCodeByContentId> qrCodeByContentId;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
List<QRCodeByURL> qrCodeByURL;
public EmailMessage(String messageId, String subject, String historyId) {
this.messageId = messageId;
this.subject = subject;
this.historyId = historyId;
this.qrCodeByContentId = new ArrayList<>();
this.qrCodeByURL = new ArrayList<>();
}
public void addQRCodeByContentId(QRCodeByContentId qrCode) {
this.qrCodeByContentId.add(qrCode);
}
public void addQRCodeByURL(QRCodeByURL qrCode) {
this.qrCodeByURL.add(qrCode);
}
public boolean hasQRCodes() {
return !qrCodeByContentId.isEmpty() || !qrCodeByURL.isEmpty();
}
}

View File

@@ -0,0 +1,17 @@
package com.safeqr.app.gmail.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.util.List;
@Data
@Builder
@AllArgsConstructor
public class QRCodeByContentId {
private String cid;
private String attachmentId;
private List<String> decodedContent;
private int totalQRCodeFound;
}

View File

@@ -0,0 +1,17 @@
package com.safeqr.app.gmail.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.util.List;
@Data
@Builder
@AllArgsConstructor
public class QRCodeByURL {
private String url;
private List<String> decodedContent;
private int totalQRCodeFound;
}

View File

@@ -7,9 +7,11 @@ import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer; import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.multi.qrcode.QRCodeMultiReader; import com.google.zxing.multi.qrcode.QRCodeMultiReader;
import com.safeqr.app.gmail.dto.ScannedGmailResponseDto;
import com.safeqr.app.gmail.model.EmailMessage;
import com.safeqr.app.gmail.model.QRCodeByContentId;
import com.safeqr.app.gmail.model.QRCodeByURL;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.json.JSONArray;
import org.json.JSONObject;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
@@ -25,11 +27,14 @@ import com.google.api.services.gmail.Gmail;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.*; import java.io.*;
import java.lang.Thread;
import java.net.ConnectException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.http.HttpClient; import java.net.http.HttpClient;
import java.net.http.HttpRequest; import java.net.http.HttpRequest;
import java.net.http.HttpResponse; import java.net.http.HttpResponse;
import java.net.http.HttpTimeoutException;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -49,55 +54,82 @@ public class GmailService {
.build(); .build();
} }
public JSONObject getEmail(String accessToken) throws IOException, InterruptedException { private static final long MAX_RESULTS = 100L;
JSONObject json = new JSONObject();
JSONArray emailArray = new JSONArray();
// Build the Gmail service public ScannedGmailResponseDto getEmail(String accessToken) throws IOException, InterruptedException {
Gmail service = getGmailService(accessToken); Gmail service = getGmailService(accessToken);
logger.info("service-> {}", service); logger.info("Gmail service initialized: {}", service);
// Get the list of messages List<EmailMessage> emailMessagesList = new ArrayList<>();
ListMessagesResponse listResponse = service.users().messages().list("me").execute(); String userId = "me";
List<Message> messages = listResponse.getMessages(); String nextPageToken = null;
for (Message message : messages) { do {
message = service.users().messages().get("me", message.getId()).setFormat("full").execute(); ListMessagesResponse listResponse = fetchMessages(service, userId, nextPageToken);
List<MessagePart> parts = message.getPayload().getParts(); List<Message> messages = listResponse.getMessages();
Set<String> attachmentIds = new HashSet<>(); nextPageToken = listResponse.getNextPageToken();
Set<String> imageUrls = new HashSet<>();
processPartsRecursively(parts, attachmentIds, imageUrls);
// Extract and log the email subject for (Message message : messages) {
String subject = getSubject(message); EmailMessage emailMessage = processMessage(service, userId, message);
logger.info("Email Subject-> {}", subject); if (emailMessage != null) {
emailMessagesList.add(emailMessage);
if (attachmentIds.isEmpty() && imageUrls.isEmpty())
continue;
String messageId = message.getId();
logger.info("messageId-> {}", messageId);
String historyId = String.valueOf(message.getHistoryId());
logger.info("historyId-> {}", historyId);
for (String attachmentId : attachmentIds) {
Optional<String> attachment = findAttachmentIdByCid(parts, attachmentId);
logger.info("attachment-> {}", attachment);
if (attachment.isPresent()) {
List<String> qrCodeValue = processAttachment(service, messageId, attachment.get());
emailArray.put(qrCodeValue);
} }
} }
for (String imageUrl : imageUrls) { } while (nextPageToken != null);
List<String> qrCodeValue = scanQRCodeFromUrl(imageUrl);
if (qrCodeValue != null) { return new ScannedGmailResponseDto(emailMessagesList);
emailArray.put(qrCodeValue); }
private ListMessagesResponse fetchMessages(Gmail service, String userId, String pageToken) throws IOException {
return service.users().messages().list(userId)
.setPageToken(pageToken)
.setMaxResults(MAX_RESULTS)
.execute();
}
private EmailMessage processMessage(Gmail service, String userId, Message message) throws IOException, InterruptedException {
message = service.users().messages().get(userId, message.getId()).setFormat("full").execute();
List<MessagePart> parts = message.getPayload().getParts();
Set<String> attachmentIds = new HashSet<>();
Set<String> imageUrls = new HashSet<>();
processPartsRecursively(parts, attachmentIds, imageUrls);
if (attachmentIds.isEmpty() && imageUrls.isEmpty()) {
return null;
}
String subject = getSubject(message);
logger.info("Email Subject: {}", subject);
logger.info("Message ID: {}", message.getId());
logger.info("History ID: {}", message.getHistoryId());
EmailMessage emailMessage = new EmailMessage(message.getId(), subject, String.valueOf(message.getHistoryId()));
processAttachments(service, message.getId(), parts, attachmentIds, emailMessage);
processImageUrls(imageUrls, emailMessage);
return emailMessage.hasQRCodes() ? emailMessage : null;
}
private void processAttachments(Gmail service, String messageId, List<MessagePart> parts, Set<String> attachmentIds, EmailMessage emailMessage) throws IOException {
for (String attachmentId : attachmentIds) {
Optional<String> attachment = findAttachmentIdByCid(parts, attachmentId);
if (attachment.isPresent()) {
List<String> qrCodeValue = processAttachment(service, messageId, attachment.get());
if (!qrCodeValue.isEmpty()) {
emailMessage.addQRCodeByContentId(new QRCodeByContentId(attachmentId, attachment.get(), qrCodeValue, qrCodeValue.size()));
} }
} }
} }
logger.info("Total Emails-> {}", messages.size()); }
json.put("qr_codes", emailArray);
return json; private void processImageUrls(Set<String> imageUrls, EmailMessage emailMessage) throws IOException {
for (String imageUrl : imageUrls) {
List<String> qrCodeValue = scanQRCodeFromUrl(imageUrl);
if (!qrCodeValue.isEmpty()) {
emailMessage.addQRCodeByURL(new QRCodeByURL(imageUrl, qrCodeValue, qrCodeValue.size()));
}
}
} }
private String getSubject(Message message) { private String getSubject(Message message) {
@@ -140,35 +172,49 @@ public class GmailService {
} }
} }
} }
private List<String> scanQRCodeFromUrl(String imageUrl) throws IOException, InterruptedException { private List<String> scanQRCodeFromUrl(String imageUrl) {
try { try {
BufferedImage image = downloadImageFromUrl(imageUrl); BufferedImage image = downloadImageFromUrl(imageUrl);
if (image != null) { if (image != null) {
return decodeQRCodes(image); return decodeQRCodes(image);
} }
} catch (IllegalArgumentException e) {
logger.error("Invalid URI scheme for URL: {} -> {}", imageUrl, e.getMessage());
} catch(URISyntaxException e) { } catch(URISyntaxException e) {
logger.error("Error while scanning QR code from URL", e); logger.error("Error while scanning QR code from URL", e);
} }
return null; return Collections.emptyList();
} }
// Download the image from the given URL // Download the image from the given URL
private BufferedImage downloadImageFromUrl(String imageUrl) throws IOException, InterruptedException, URISyntaxException { private BufferedImage downloadImageFromUrl(String imageUrl) throws URISyntaxException {
HttpClient client = HttpClient.newBuilder() try {
.followRedirects(HttpClient.Redirect.ALWAYS) imageUrl = imageUrl.replace(" ", "%20");
.build(); HttpClient client = HttpClient.newBuilder()
logger.info("imageUrl-> {}", imageUrl); .followRedirects(HttpClient.Redirect.ALWAYS)
// Encode the URL .build();
HttpRequest request = HttpRequest.newBuilder() logger.info("Downloading image from URL: {}", imageUrl);
.uri(URI.create(imageUrl.replace(" ", "%20"))) HttpRequest request = HttpRequest.newBuilder()
.GET() .uri(new URI(imageUrl))
.build(); .GET()
.build();
HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray()); HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() == 200) {
byte[] imageBytes = response.body(); if (response.statusCode() == 200) {
return ImageIO.read(new ByteArrayInputStream(imageBytes)); byte[] imageBytes = response.body();
} else { return ImageIO.read(new ByteArrayInputStream(imageBytes));
logger.error("Failed to download image. HTTP response code: {}", response.statusCode()); } else {
logger.warn("Failed to download image. HTTP response code: {}", response.statusCode());
}
} catch (URISyntaxException e) {
logger.warn("Invalid URL: {} -> {}", imageUrl, e.getMessage());
} catch (HttpTimeoutException e) {
logger.warn("Request timed out for URL: {} -> {}", imageUrl, e.getMessage());
} catch (ConnectException e) {
logger.warn("Failed to connect to URL: {} -> {}", imageUrl, e.getMessage());
} catch (IOException | InterruptedException e) {
logger.warn("Error downloading image from URL: {} -> {}", imageUrl, e.getMessage());
Thread.currentThread().interrupt();
} }
return null; return null;
} }