Gitea-Helpdesk/src/main/java/io/gitlab/jfronny/gitea/helpdesk/UpdateTask.java

136 lines
7.7 KiB
Java

package io.gitlab.jfronny.gitea.helpdesk;
import io.gitlab.jfronny.commons.StringFormatter;
import io.gitlab.jfronny.commons.throwable.ThrowingBiConsumer;
import io.gitlab.jfronny.gitea.helpdesk.db.DBInterface;
import io.gitlab.jfronny.gitea.helpdesk.db.Subscription;
import io.gitlab.jfronny.gitea.helpdesk.gitea.*;
import io.gitlab.jfronny.gitea.helpdesk.mail.MailInterface;
import io.gitlab.jfronny.gitea.helpdesk.mail.WrappedMessage;
import io.gitlab.jfronny.gitea.helpdesk.web.WebInterface;
import jakarta.mail.MessagingException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
public record UpdateTask(DBInterface db, MailInterface mail, GiteaInterface gitea, WebInterface web) implements Runnable {
private static final String TEMPLATE = Main.getResource("mail/template.html").formatted(WebInterface.THEME, "%s");
private static final String MAIL_ERROR = mail("error.html");
private static final String MAIL_UNEXPECTED = mail("unexpected.html");
private static final String MAIL_CREATE = mail("create.html");
private static final String MAIL_COMMENT = mail("comment.html");
private static final String MAIL_COMMENT_CLOSED = mail("comment_closed.html");
private static final String MAIL_ISSUE_DELETED = mail("issue_deleted.html");
private static final String MAIL_ISSUE_CLOSED = mail("issue_closed.html");
private static String mail(String path) {
return TEMPLATE.formatted(Main.getResource("mail/" + path));
}
@Override
public void run() {
try {
updateSubscriptions();
processEmails();
} catch (Exception e) {
Main.LOG.error("Could not run update task", e);
}
}
private void updateSubscriptions() throws Exception {
String[] addressParts = mail.getAddress().split("@");
db.forEachSubscription(subscription -> {
ThrowingBiConsumer<String, String, Exception> reply = (content, subject) -> {
String[] previousMessages = subscription.referenceChain().split(" ");
String previousMessageId = previousMessages[previousMessages.length - 1];
//TODO test
mail.reply(addressParts[0] + "+reply+" + subscription.id(), subscription.email(), content, subject, previousMessageId, subscription.referenceChain(), null);
};
GiteaIssue issue;
try {
issue = gitea.getIssue(subscription.repoOwner(), subscription.repo(), subscription.issue());
} catch (FileNotFoundException fe) {
reply.accept(MAIL_ISSUE_DELETED, "Issue deleted");
db.removeSubscription(subscription.id());
return;
}
if (issue.state.equals("closed")) { //TODO test
reply.accept(MAIL_ISSUE_CLOSED, "Issue closed");
db.removeSubscription(subscription.id());
}
for (GiteaIssueComment comment : gitea.getComments(subscription.repoOwner(), subscription.repo(), subscription.issue())) {
if (comment.id > subscription.issueComment()) {
reply.accept(comment.body, issue.title);
db.updateSubscriptionIssueComment(subscription.id(), comment.id);
}
}
});
}
private void processEmails() throws SQLException, MessagingException, IOException {
String[] addressParts = mail.getAddress().split("@");
for (WrappedMessage message : mail.getInbox()) {
try {
String[] args = message.getRecipientSubAddress().split("\\+");
switch (args[0]) {
case "create" -> {
if (args.length != 3) throw new UnexpectedMailException("Create classifier only allows two parameters");
String owner = args[1];
String repo = args[2];
checkArgs(owner, repo);
GiteaIssue issue = gitea.createIssue(owner, repo, message.getSubject(), formatBody(message));
String id = db.addSubscription(message.getSender(), owner, repo, issue.id, 0, message.getSender());
String unsubscribeUrl = web.getAddress() + "/unsubscribe?id=" + id; //TODO test
message.reply(addressParts[0] + "+reply+" + owner + "+" + repo + "+" + issue.id, MAIL_CREATE.formatted(issue.url, unsubscribeUrl));
}
case "reply" -> {
if (args.length != 2) throw new UnexpectedMailException("Reply classifier only allows one parameter");
Subscription sub = db.getSubscription(args[1]).orElseThrow(() -> new UnexpectedMailException("Reply classifier does not represent an active issue"));
GiteaIssueComment commentId = gitea.addComment(sub.repoOwner(), sub.repo(), sub.issue(), formatBody(message));
db.updateSubscriptionIssueComment(sub.id(), commentId.id);
db.updateSubscriptionReferenceChain(sub.id(), sub.referenceChain() + " " + message.getSender());
}
case "comment" -> {
if (args.length == 4) throw new UnexpectedMailException("Comment classifier requires four parameters");
String owner = args[1];
String repo = args[2];
long issueId = Long.parseLong(args[3]);
checkArgs(owner, repo);
GiteaIssue issue;
try {
issue = gitea.getIssue(owner, repo, issueId);
} catch (FileNotFoundException fe) {
throw new UnexpectedMailException("This issue does not exist");
}
gitea.addComment(owner, repo, issueId, formatBody(message));
if (issue.state.equals("closed")) {
message.reply(addressParts[0] + "+reply+" + owner + "+" + repo + "+" + issue.id, MAIL_COMMENT_CLOSED.formatted(issue.url));
} else {
String id = db.addSubscription(message.getSender(), owner, repo, issue.id, 0, message.getSender());
String unsubscribeUrl = web.getAddress() + "/unsubscribe?id=" + id; //TODO test
message.reply(addressParts[0] + "+reply+" + owner + "+" + repo + "+" + issue.id, MAIL_COMMENT.formatted(issue.url, unsubscribeUrl));
}
}
default -> throw new UnexpectedMailException("Did not expect classifier " + args[0]);
}
} catch (UnexpectedMailException | NumberFormatException t) {
message.reply(mail.getAddress(), MAIL_UNEXPECTED.formatted(WebInterface.escapeHTML(StringFormatter.toString(t))));
} catch (Throwable t) {
Main.LOG.error("Could not parse mail", t);
message.reply(mail.getAddress(), MAIL_ERROR.formatted(WebInterface.escapeHTML(StringFormatter.toString(t))));
}
}
}
private void checkArgs(String owner, String repo) throws UnexpectedMailException {
//TODO document this limitation
if (!Main.ALPHANUMERIC_PATTERN.matcher(owner).matches()) throw new UnexpectedMailException("Unsupported owner string");
if (!Main.ALPHANUMERIC_PATTERN.matcher(repo).matches()) throw new UnexpectedMailException("Unsupported repo string");
}
private String formatBody(WrappedMessage message) throws UnexpectedMailException, MessagingException, IOException {
return "Submitted by " + message.getSenderName() + ":\n\n" + message.getText();
}
}