1 /*
  2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 package org.openjdk.skara.bots.mlbridge;
 24 
 25 import org.openjdk.skara.bot.WorkItem;
 26 import org.openjdk.skara.email.*;
 27 import org.openjdk.skara.forge.PullRequest;
 28 
 29 import java.nio.charset.StandardCharsets;
 30 import java.nio.file.Path;
 31 import java.util.*;
 32 import java.util.logging.Logger;
 33 import java.util.regex.Pattern;
 34 
 35 public class CommentPosterWorkItem implements WorkItem {
 36     private final PullRequest pr;
 37     private final List<Email> newMessages;
 38     private final Logger log = Logger.getLogger("org.openjdk.skara.bots.mlbridge");
 39 
 40     private final String bridgedMailMarker = "<!-- Bridged id (%s) -->";
 41     private final Pattern bridgedMailId = Pattern.compile("^<!-- Bridged id \\(([=\\w]+)\\) -->");
 42 
 43     CommentPosterWorkItem(PullRequest pr, List<Email> newMessages) {
 44         this.pr = pr;
 45         this.newMessages = newMessages;
 46     }
 47 
 48     @Override
 49     public String toString() {
 50         return "CommentPosterWorkItem@" + pr.toString();
 51     }
 52 
 53     @Override
 54     public boolean concurrentWith(WorkItem other) {
 55         if (!(other instanceof CommentPosterWorkItem)) {
 56             return true;
 57         }
 58         CommentPosterWorkItem otherItem = (CommentPosterWorkItem) other;
 59         if (!pr.equals(otherItem.pr)) {
 60             return true;
 61         }
 62         return false;
 63     }
 64 
 65     private void postNewMessage(Email email) {
 66         var marker = String.format(bridgedMailMarker,
 67                                  Base64.getEncoder().encodeToString(email.id().address().getBytes(StandardCharsets.UTF_8)));
 68 
 69         var body = marker + "\n" +
 70                 "*Mailing list message from [" + email.author().fullName().orElse(email.author().localPart()) +
 71                 "](mailto:" + email.author().address() + ") on [" + email.sender().localPart() +
 72                 "](mailto:" + email.sender().address() + "):*\n\n" +
 73                 email.body();
 74         pr.addComment(body);
 75     }
 76 
 77     @Override
 78     public void run(Path scratchPath) {
 79         var comments = pr.comments();
 80 
 81         var alreadyBridged = new HashSet<EmailAddress>();
 82         for (var comment : comments) {
 83             if (!comment.author().equals(pr.repository().forge().currentUser())) {
 84                 continue;
 85             }
 86             var matcher = bridgedMailId.matcher(comment.body());
 87             if (!matcher.find()) {
 88                 continue;
 89             }
 90             var id = new String(Base64.getDecoder().decode(matcher.group(1)), StandardCharsets.UTF_8);
 91             alreadyBridged.add(EmailAddress.from(id));
 92         }
 93 
 94         for (var message : newMessages) {
 95             if (alreadyBridged.contains(message.id())) {
 96                 log.fine("Message from " + message.author() + " to " + pr + " has already been bridged - skipping!");
 97                 continue;
 98             }
 99 
100             log.info("Bridging new message from " + message.author() + " to " + pr);
101             postNewMessage(message);
102         }
103     }
104 }