Skip to content
This repository has been archived by the owner on May 13, 2024. It is now read-only.

WIP:Inject acronyms from IRC acrobot database into GChat response #12

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
*.iml
target
.secret
dependency-reduced-pom.xml
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "src/main/resources/AcroBot"]
path = src/main/resources/AcroBot
url = https://github.com/theacrobot/AcroBot.git
9 changes: 8 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<jackson.version>2.9.10.3</jackson.version>
<jackson.version>2.9.10.4</jackson.version>
<google.api.client.version>1.25.0</google.api.client.version>
<google.cloud.pubsub.version>1.70.0</google.cloud.pubsub.version>
<hibernate.version>4.3.6.Final</hibernate.version>
<snakeyaml.version>1.26</snakeyaml.version>
</properties>

<dependencies>
Expand All @@ -38,6 +39,12 @@
<version>${jackson.version}</version>
</dependency>

<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
</dependency>

<!-- JPA dependencies -->
<!-- Hibernate entity manager-->
<dependency>
Expand Down
Binary file added sec
Binary file not shown.
19 changes: 16 additions & 3 deletions src/main/java/com/redhat/constants/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ public class Constants {
public static final String CREDENTIALS_PATH_ENV_PROPERTY = "GOOGLE_APPLICATION_CREDENTIALS";
public static final String PROJECT_ID = System.getenv("PROJECT_ID");
public static final String SUBSCRIPTION_ID = System.getenv("SUBSCRIPTION_ID");
public static final String SUDO_PASSWORD = System.getenv("SUDO_PASSWORD");
public static final String HANGOUTS_CHAT_API_SCOPE = "https://www.googleapis.com/auth/chat.bot";

public static final String YAML_SOURCE = "AcroBot/data/abbrev.yaml"; // ClassLoader path
// Response templates
public static final String RESPONSE_URL_TEMPLATE = "https://chat.googleapis.com/v1/__SPACE_ID__/messages";
public static final String ADDED_RESPONSE = "Thank you for adding me! Send `@Acrobot help` for more information about me.";
Expand All @@ -30,5 +29,19 @@ public class Constants {
"All of the actions work in a direct message without tagging `@Acrobot`. Whitespaces shouldn't matter," +
"and you can input acronym in both lower- and uppercase; it will be matched regardless of the capitalisation. \n\n" +
"Acrobot is implemented by Marek Czernek. You can find documentation and file issues or suggest improvements at " +
"https://github.com/m-czernek/acrobot";
"https://github.com/m-czernek/acrobot \n\n" +
"Currently, Acrobot is testing injecting IRC acronym database into the results. For feedback, comments, or suggestions "+
"about the function, visit https://github.com/m-czernek/acrobot/pull/12";

public static final String FOUND_YAML_TEXT = "\n\n_Found requested acronym in the IRC database. " +
"Send @Acrobot help for more information about this feature._\n";

// we should not return null for sudo password
private static final String SUDO_PASSWORD = System.getenv("SUDO_PASSWORD");
public static String getSudoPassword(String defaultValue) {
if(SUDO_PASSWORD == null) {
return defaultValue;
}
return SUDO_PASSWORD;
}
}
17 changes: 17 additions & 0 deletions src/main/java/com/redhat/constants/MessageType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.redhat.constants;

public enum MessageType {
ADDED_TO_ROOM(Constants.ADDED_RESPONSE),
SUDO_RESPONSE("SUDO_PASSWORD"),
INVALID_MESSAGE(Constants.INCORRECT_FORMAT_FOR_SAVING_ACRONYM),
HELP(Constants.HELP_TEXT),
UPDATE_OR_REMOVE("UPDATE_REMOVE"),
SAVE_OR_MERGE("SAVE_MERGE"),
GET_ACRONYM("GET_ACRONYM");

public final String eventType;

private MessageType(String eventType) {
this.eventType = eventType;
}
}
11 changes: 10 additions & 1 deletion src/main/java/com/redhat/messages/AcroBot.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.google.cloud.pubsub.v1.MessageReceiver;
import com.google.pubsub.v1.PubsubMessage;
import com.redhat.constants.Constants;
import com.redhat.constants.MessageType;

import java.io.FileInputStream;
import java.util.Collections;
Expand All @@ -27,6 +28,7 @@ public class AcroBot implements MessageReceiver {
private HttpTransport httpTransport;
private HttpRequestFactory requestFactory;
private MessageHelper helper;
private YamlAcrobotInjector yamlAcrobotInjector;

public AcroBot() throws Exception {
credential = GoogleCredential
Expand All @@ -35,6 +37,7 @@ public AcroBot() throws Exception {
httpTransport = GoogleNetHttpTransport.newTrustedTransport();
requestFactory = httpTransport.createRequestFactory(credential);
helper = new MessageHelper();
yamlAcrobotInjector = new YamlAcrobotInjector();
}

// Called when a message is received by the subscriber.
Expand Down Expand Up @@ -68,7 +71,13 @@ public void handle(JsonNode eventJson) throws Exception {
break;
}
case "MESSAGE":
responseNode.put("text", helper.handleMessageAction(eventJson));
String response = helper.handleMessageAction(eventJson);

if(MessageTypeHelper.determineMessageAction(eventJson) == MessageType.GET_ACRONYM) {
response = yamlAcrobotInjector.injectYamlAcronyms(eventJson, response);
}

responseNode.put("text", response);

// In case of message, post the response in the same thread.
ObjectNode threadNode = jsonNodeFactory.objectNode();
Expand Down
53 changes: 19 additions & 34 deletions src/main/java/com/redhat/messages/MessageHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.databind.JsonNode;
import com.redhat.constants.Constants;
import com.redhat.constants.MessageType;
import com.redhat.entities.Acronym;
import com.redhat.entities.Explanation;
import com.redhat.persistence.AcronymExplanationDal;
Expand All @@ -15,41 +16,29 @@ public class MessageHelper {
private AcronymExplanationDal acronymExplanationDal = new AcronymExplanationDal();

public String handleMessageAction(JsonNode eventJson) {
String resp;
String message;
String authorEmail = eventJson.get("user").get("email").asText();
MessageType type = MessageTypeHelper.determineMessageAction(eventJson);

try {
message = eventJson.get("message").get("argumentText").asText().trim();
} catch (NullPointerException e) {
// Acrobot was added via mention to a room, and has no argument text
return Constants.ADDED_RESPONSE;
}

if(message.startsWith(Constants.SUDO_PASSWORD)) {
return AdministrativeMessageHelper.handleAdminMessage(message);
if(type == MessageType.ADDED_TO_ROOM) {
return type.eventType;
}

if(message.startsWith("!")) {
message = message.substring(1);

// Validate message
if (!isMessageValid(message)) {
return Constants.INCORRECT_FORMAT_FOR_SAVING_ACRONYM;
}

if(message.contains("=>")) {
resp = updateOrRemoveExplanation(message, authorEmail);
} else {
resp = saveOrMergeAcronym(message, authorEmail);
}

} else if (message.equals("help")) {
resp = Constants.HELP_TEXT;
} else {
resp = getAcronymAsString(message);
String message = eventJson.get("message").get("argumentText").asText().trim();

switch (type){
case SUDO_RESPONSE:
return AdministrativeMessageHelper.handleAdminMessage(message);
case UPDATE_OR_REMOVE:
// Message starts with "!"
return updateOrRemoveExplanation(message.substring(1), authorEmail);
case SAVE_OR_MERGE:
// Message starts with "!"
return saveOrMergeAcronym(message.substring(1), authorEmail);
case GET_ACRONYM:
return getAcronymAsString(message);
default:
return type.eventType;
}
return resp;
}

private String updateOrRemoveExplanation(String message, String authorEmail) {
Expand Down Expand Up @@ -102,10 +91,6 @@ private String saveOrMergeAcronym(String message, String authorEmail) {
return resp;
}

private boolean isMessageValid(String message) {
return message.contains("=") && (!message.trim().endsWith("="));
}

private String[] splitMessageToSaveAndTrim(String message) {
return trimArray(message.split("=", 2));
}
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/com/redhat/messages/MessageTypeHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.redhat.messages;

import com.fasterxml.jackson.databind.JsonNode;
import com.redhat.constants.Constants;
import com.redhat.constants.MessageType;

public class MessageTypeHelper {

public static MessageType determineMessageAction(JsonNode eventJson) {
final JsonNode msgNode = eventJson.get("message").get("argumentText");

if(msgNode == null) {
// Acrobot was added via mention to a room, and has no argument text
return MessageType.ADDED_TO_ROOM;
}

String message = msgNode.asText();

if(message.startsWith(Constants.getSudoPassword("PW does not exist"))) {
return MessageType.SUDO_RESPONSE;
}

if(message.startsWith("!")) {
message = message.substring(1);

// Validate message
if (!isMessageValid(message)) {
return MessageType.INVALID_MESSAGE;
}

if(message.contains("=>")) {
return MessageType.UPDATE_OR_REMOVE;
} else {
return MessageType.SAVE_OR_MERGE;
}
}

if (message.equals("help")) {
return MessageType.HELP;
} else {
return MessageType.GET_ACRONYM;
}
}

private static boolean isMessageValid(String message) {
return message.contains("=") && (!message.trim().endsWith("="));
}
}
32 changes: 32 additions & 0 deletions src/main/java/com/redhat/messages/YamlAcrobotInjector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.redhat.messages;

import com.fasterxml.jackson.databind.JsonNode;
import com.redhat.constants.Constants;
import com.redhat.entities.Acronym;
import com.redhat.entities.Explanation;
import com.redhat.persistence.YamlDal;

import java.util.Set;

public class YamlAcrobotInjector {
private YamlDal dal = new YamlDal();

public String injectYamlAcronyms(JsonNode eventJson, String originalMessage) {
String res = Constants.FOUND_YAML_TEXT;
String requestedAcronym = eventJson.get("message").get("argumentText").asText().trim();
Set<Acronym> yamlAcronyms = dal.getAcronymsByName(requestedAcronym);

if(yamlAcronyms.isEmpty()) {
return originalMessage;
}


for(Acronym a : yamlAcronyms) {
for(Explanation e : a.getExplanations()) {
res += e.getExplanation() + "\n";
}
}

return originalMessage + res;
}
}
56 changes: 56 additions & 0 deletions src/main/java/com/redhat/persistence/YamlDal.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.redhat.persistence;

import com.redhat.constants.Constants;
import com.redhat.entities.Acronym;
import com.redhat.entities.Explanation;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.representer.Representer;
import org.yaml.snakeyaml.resolver.Resolver;

import java.io.InputStream;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class YamlDal {
private Map<String, Map<String, String>> yamlDatabase;


public YamlDal() {
InputStream is = this.getClass()
.getClassLoader()
.getResourceAsStream(Constants.YAML_SOURCE);
this.yamlDatabase = new Yaml(new Constructor(), new Representer(),
new DumperOptions(), new CustomResolver())
.load(is);
}

public Set<Acronym> getAcronymsByName(String name) {
Set<Acronym> res = new HashSet<>();
for(String key : yamlDatabase.keySet()) {
Map<String, String> databaseSection = yamlDatabase.get(key);
if(databaseSection.containsKey(name)) {
Acronym a = new Acronym(name);
Set<Explanation> e = new HashSet<>();
e.add(new Explanation(databaseSection.get(name)));
a.setExplanations(e);
res.add(a);
}
}
return res;
}

// Custom resolver such that we don't have implicit number -> Integer or
// number -> Float typing. We want String or null values only.
private class CustomResolver extends Resolver {
protected void addImplicitResolvers() {
addImplicitResolver(Tag.MERGE, MERGE, "<");
addImplicitResolver(Tag.NULL, EMPTY, null);
}

}
}

1 change: 1 addition & 0 deletions src/main/resources/AcroBot
Submodule AcroBot added at 3eb87d
34 changes: 34 additions & 0 deletions src/test/java/com/redhat/YmlLayerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.redhat;

import com.redhat.persistence.YamlDal;
import org.assertj.core.api.Assertions;
import org.junit.Test;


public class YmlLayerTest {
private YamlDal dal = new YamlDal();
// Corner cases
// Acro is a number
private static String numberAcro = "82576";
private static String numberAcroExplanation = "Intel 82576 Gigabit Ethernet Controller";
// Acro has starts with a lowercase char
private static String lowerCaseAcro = "dNAT";
private static String lowerCaseAcroExplanation = "Dynamic Network Address Translation @networking";
// Acro contains special chars
private static String specialCharAcro = "SMI-S";
private static String specialCharAcroExplanation = "Storage Management Initiative Specification";

@Test
public void testCornerCases() {
Assertions.assertThat(dal.getAcronymsByName(numberAcro))
.hasSize(1)
.filteredOn(acronym -> acronym.getExplanations().contains(numberAcroExplanation));
Assertions.assertThat(dal.getAcronymsByName(lowerCaseAcro))
.hasSize(1)
.filteredOn(acronym -> acronym.getExplanations().contains(lowerCaseAcroExplanation));
Assertions.assertThat(dal.getAcronymsByName(specialCharAcro))
.hasSize(1)
.filteredOn(acronym -> acronym.getExplanations().contains(specialCharAcroExplanation));
Assertions.assertThat(dal.getAcronymsByName("NON_EXISTENT")).isEmpty();
}
}