Permalink
Browse files

Made the Command API delightful

  • Loading branch information...
s2team committed Jan 15, 2015
1 parent a2d967b commit da662945c78726107fc8456e43a8c02d96b04701
Showing with 720 additions and 941 deletions.
  1. +3 −1 build.gradle
  2. +6 −0 src/main/java/de/votesapp/client/GroupMessage.java
  3. +11 −0 src/main/java/de/votesapp/commands/CommandPlugin.java
  4. +3 −7 src/main/java/de/votesapp/{parser/plugins → commands}/Describable.java
  5. +45 −0 src/main/java/de/votesapp/commands/Description.java
  6. +16 −0 src/main/java/de/votesapp/commands/TextNormalizer.java
  7. +31 −0 src/main/java/de/votesapp/commands/plugins/Answer.java
  8. +1 −1 src/main/java/de/votesapp/{parser → commands/plugins}/Attitude.java
  9. +13 −17 src/main/java/de/votesapp/{parser → commands}/plugins/ChuckNorrisCommandPlugin.java
  10. +72 −0 src/main/java/de/votesapp/commands/plugins/HelpCommandPlugin.java
  11. +30 −0 src/main/java/de/votesapp/commands/plugins/PingCommandPlugin.java
  12. +43 −0 src/main/java/de/votesapp/commands/plugins/QuestionStoreCommandPlugin.java
  13. +41 −0 src/main/java/de/votesapp/commands/plugins/ResetCommandPlugin.java
  14. +42 −0 src/main/java/de/votesapp/commands/plugins/RollCommandPlugin.java
  15. +47 −0 src/main/java/de/votesapp/commands/plugins/SetAdditionalsCommandPlugin.java
  16. +72 −0 src/main/java/de/votesapp/commands/plugins/SetAttitudeCommandPlugin.java
  17. +105 −0 src/main/java/de/votesapp/commands/plugins/StatusCommandPlugin.java
  18. +31 −0 src/main/java/de/votesapp/commands/plugins/TextEqualsWordPlugin.java
  19. +1 −1 src/main/java/de/votesapp/groups/Group.java
  20. +25 −13 src/main/java/de/votesapp/groups/GroupMessageListener.java
  21. +0 −14 src/main/java/de/votesapp/parser/Command.java
  22. +0 −30 src/main/java/de/votesapp/parser/HumanMessageParser.java
  23. +0 −14 src/main/java/de/votesapp/parser/NoopCommand.java
  24. +0 −21 src/main/java/de/votesapp/parser/plugins/AbstractCommandPlugin.java
  25. +0 −76 src/main/java/de/votesapp/parser/plugins/HelpCommandPlugin.java
  26. +0 −27 src/main/java/de/votesapp/parser/plugins/PingCommandPlugin.java
  27. +0 −66 src/main/java/de/votesapp/parser/plugins/QuestionStoreCommandPlugin.java
  28. +0 −47 src/main/java/de/votesapp/parser/plugins/ResetCommandPlugin.java
  29. +0 −54 src/main/java/de/votesapp/parser/plugins/RollCommandPlugin.java
  30. +0 −74 src/main/java/de/votesapp/parser/plugins/SetAdditionalsCommandPlugin.java
  31. +0 −74 src/main/java/de/votesapp/parser/plugins/SetAttitudeCommandPlugin.java
  32. +0 −132 src/main/java/de/votesapp/parser/plugins/StatusCommandPlugin.java
  33. +0 −52 src/main/java/de/votesapp/parser/plugins/TextEqualsWordPlugin.java
  34. +1 −1 src/test/java/de/votesapp/VotesAppUserTests_Test.java
  35. +3 −3 src/test/java/de/votesapp/{parser → commands}/plugins/ChuckNorrisCommandPluginTest.java
  36. +74 −11 src/test/java/de/votesapp/groups/GroupMessageListenerTest.java
  37. +4 −4 src/test/java/de/votesapp/groups/GroupTest.java
  38. +0 −147 src/test/java/de/votesapp/parser/HumanMessageParserTest.java
  39. +0 −54 src/test/java/de/votesapp/parser/plugins/TextEqualsWordPluginTest.java
@@ -39,7 +39,9 @@ dependencies {
compile("org.apache.httpcomponents:httpclient:4.3.6")
compile("org.projectreactor:reactor-core:1.1.5.RELEASE")
compile("org.projectreactor:reactor-spring:1.0.1.RELEASE")
compile("com.github.fakemongo:fongo:1.5.9")
compile("com.github.fakemongo:fongo:1.5.9")
compile("com.google.guava:guava:18.0")
testCompile("org.springframework.boot:spring-boot-starter-test")
}
@@ -10,6 +10,8 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.votesapp.commands.TextNormalizer;
/**
* Represents a Messages that got sent inside a group.
*/
@@ -65,4 +67,8 @@ public User sender() {
// TODO: That should be done lazy
return new User(senderPhone, senderName);
}
public String normalizedText() {
return TextNormalizer.normalize(text);
}
}
@@ -0,0 +1,11 @@
package de.votesapp.commands;
import java.util.Optional;
import de.votesapp.client.GroupMessage;
import de.votesapp.commands.plugins.Answer;
import de.votesapp.groups.Group;
public interface CommandPlugin {
public abstract Optional<Answer> interpret(final GroupMessage message, final Group group);
}
@@ -1,4 +1,5 @@
package de.votesapp.parser.plugins;
package de.votesapp.commands;
public interface Describable {
@@ -8,11 +9,6 @@
public int EASTEREGGS = 3000;
public int BOTTOM = Integer.MAX_VALUE - 10000;
public int getPriority();
public String getName();
public String[] getTriggers();
public Description describe();
public String getDescription();
}
@@ -0,0 +1,45 @@
package de.votesapp.commands;
import lombok.Value;
@Value
public class Description {
int priority;
String name;
String[] trigger;
String description;
public static class DescriptionBuilder {
private final String name;
private String[] trigger;
private String description;
private int priority;
private DescriptionBuilder(final String name) {
this.name = name;
}
public static DescriptionBuilder describe(final String name) {
return new DescriptionBuilder(name);
}
public DescriptionBuilder withTrigger(final String... trigger) {
this.trigger = trigger;
return this;
}
public DescriptionBuilder withDescription(final String description) {
this.description = description;
return this;
}
public DescriptionBuilder onPosition(final int priority) {
this.priority = priority;
return this;
}
public Description done() {
return new Description(priority, name, trigger, description);
}
}
}
@@ -0,0 +1,16 @@
package de.votesapp.commands;
import static org.apache.commons.lang3.StringUtils.lowerCase;
import static org.apache.commons.lang3.StringUtils.normalizeSpace;
public final class TextNormalizer {
private TextNormalizer() {
// utility
}
public static String normalize(final String text) {
return normalizeSpace(lowerCase(text));
}
}
@@ -0,0 +1,31 @@
package de.votesapp.commands.plugins;
import reactor.core.Reactor;
import reactor.event.Event;
import de.votesapp.client.GroupMessage;
import de.votesapp.groups.Group;
/**
* An {@link Answer} that gets created for and received {@link GroupMessage}.
*
* {@link AbstractCommandPlugin}s are using those as Returntype. Currently only
* answers into Groups are supported. But it's also possible to override the
* invoke message and send messages to individuals.
*/
public abstract class Answer {
public abstract void invoke(final Group group, final Reactor reactor, final GroupMessage message);
/**
* Creates an answer that sends the given message into the given group.
*/
public static Answer intoGroup(final Group group, final String message) {
return new Answer() {
@Override
public void invoke(final Group group, final Reactor reactor, final GroupMessage groupMessage) {
reactor.notify("group.outbox", Event.wrap(GroupMessage.of(group.getGroupId(), message)));
}
};
}
}
@@ -1,4 +1,4 @@
package de.votesapp.parser;
package de.votesapp.commands.plugins;
/**
* The "Attitude" of a vote describes whether its more {@value #POSITIVE},
@@ -1,39 +1,35 @@
package de.votesapp.parser.plugins;
package de.votesapp.commands.plugins;
import java.util.Optional;
import lombok.Data;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import reactor.core.Reactor;
import reactor.event.Event;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import de.votesapp.client.GroupMessage;
import de.votesapp.groups.Group;
import de.votesapp.parser.Command;
@Service
public class ChuckNorrisCommandPlugin extends TextEqualsWordPlugin {
public static final String[] DEFAULT_CHUCKS = { "chuck", "chucknorris", "chuck norris", "icndb" };
public ChuckNorrisCommandPlugin() {
super(new ChuckNorrisCommand(), DEFAULT_CHUCKS);
super(DEFAULT_CHUCKS);
}
@Override
public Optional<Answer> matches(final GroupMessage message, final Group group) {
return Optional.of(Answer.intoGroup(group, fetchNewJoke()));
}
public static class ChuckNorrisCommand extends Command {
@Override
public void execute(final GroupMessage message, final Group group, final Reactor reactor) {
reactor.notify("group.outbox", Event.wrap(GroupMessage.of(group.getGroupId(), fetchNewJoke())));
}
public String fetchNewJoke() {
final RestTemplate restTemplate = new RestTemplate();
final ChuckNorrisJoke root = restTemplate.getForObject("http://api.icndb.com/jokes/random/", ChuckNorrisJoke.class);
return root.getValue().getJoke();
}
public String fetchNewJoke() {
final RestTemplate restTemplate = new RestTemplate();
final ChuckNorrisJoke root = restTemplate.getForObject("http://api.icndb.com/jokes/random/", ChuckNorrisJoke.class);
return root.getValue().getJoke();
}
@Data
@@ -0,0 +1,72 @@
package de.votesapp.commands.plugins;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.Reactor;
import de.votesapp.client.GroupMessage;
import de.votesapp.commands.Describable;
import de.votesapp.commands.Description;
import de.votesapp.commands.Description.DescriptionBuilder;
import de.votesapp.groups.Group;
@Service
public class HelpCommandPlugin extends TextEqualsWordPlugin implements Describable {
public static final String[] DEFAULT_WORDS = { "help" };
private final List<Describable> describables;
@Autowired
Reactor reactor;
@Autowired
public HelpCommandPlugin(final List<Describable> describables) {
super(DEFAULT_WORDS);
this.describables = describables;
}
@Override
public Optional<Answer> matches(final GroupMessage message, final Group group) {
final StringBuilder sb = new StringBuilder();
sb.append("I react on your messages. You can use any of the following commands:\n");
// 1. convert Describables into Descriptions
// 2. sort them by priority
// 3. append them to the StringBuilder
describables.stream() //
.map(describable -> describable.describe()) //
.sorted((descriptionA, descriptionB) -> Integer.compare(descriptionA.getPriority(), descriptionB.getPriority())) //
.forEach(description -> appendToSb(description, sb));
sb.append("\nIf you like to stop using VotesApp, kick me out of the room.\n");
sb.append("\nIf you like to use VotesApp in another room, invite my there.");
// sb.append("\nTo report bugs, request features or get in touch, just message me directly. I'll forward it.");
return Optional.of(Answer.intoGroup(group, sb.toString()));
}
private void appendToSb(final Description description, final StringBuilder sb) {
sb.append("➡ " + description.getName() + ":\n");
sb.append(description.getDescription() + "\n");
if (description.getTrigger().length != 0) {
for (final String trigger : description.getTrigger()) {
sb.append("- " + trigger + "\n");
}
}
}
@Override
public Description describe() {
return DescriptionBuilder.describe("Help") //
.withTrigger(DEFAULT_WORDS) //
.withDescription("Shows this help") //
.onPosition(Integer.MIN_VALUE) //
.done();
}
}
@@ -0,0 +1,30 @@
package de.votesapp.commands.plugins;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.Reactor;
import de.votesapp.client.GroupMessage;
import de.votesapp.groups.Group;
@Service
public class PingCommandPlugin extends TextEqualsWordPlugin {
public static final String[] DEFAULT_RESETS = { "ping", "ping?" };
@Autowired
private Reactor reactor;
public PingCommandPlugin() {
super(DEFAULT_RESETS);
}
@Override
public Optional<Answer> matches(final GroupMessage message, final Group group) {
group.resetVotes();
return Optional.of(Answer.intoGroup(group, "Pong!"));
}
}
@@ -0,0 +1,43 @@
package de.votesapp.commands.plugins;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.stereotype.Service;
import de.votesapp.client.GroupMessage;
import de.votesapp.commands.CommandPlugin;
import de.votesapp.commands.Describable;
import de.votesapp.commands.Description;
import de.votesapp.commands.Description.DescriptionBuilder;
import de.votesapp.groups.Group;
@Service
public class QuestionStoreCommandPlugin implements CommandPlugin, Describable {
private static Pattern QUESTION = Pattern.compile(".+\\?", Pattern.CASE_INSENSITIVE);
public QuestionStoreCommandPlugin() {
}
@Override
public Optional<Answer> interpret(final GroupMessage message, final Group group) {
final Matcher statusMatcher = QUESTION.matcher(message.getText());
if (statusMatcher.matches()) {
group.setQuestion(message.getText());
}
return Optional.empty();
}
@Override
public Description describe() {
return DescriptionBuilder.describe("Last Question") //
.withTrigger("Every text that ends with \"?\"") //
.withDescription("The last question is displayed on vote status") //
.onPosition(VOTE + 4) //
.done();
}
}
Oops, something went wrong.

0 comments on commit da66294

Please sign in to comment.