Skip to content

VotesApp request - by s2team #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 79 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
2b20f52
initial commit
s2team Jan 1, 2015
28e0502
Change old name to current one
s2team Jan 1, 2015
d2a921c
Corrected the "Whoodle" "VotesApp" mythos
d0x Jan 3, 2015
ddf8960
Merge pull request #1 from d0x/patch-1
s2team Jan 3, 2015
1ce44b4
Added first Draft of YowsupClient
s2team Jan 4, 2015
66843fb
Added HumanMessageParser
s2team Jan 4, 2015
0d15661
Applied Formatting
s2team Jan 4, 2015
c47e8f3
Added Travis
s2team Jan 4, 2015
8e694f3
Added Travis to README
s2team Jan 4, 2015
bc1f271
Attached the VotesAppApi with the WhatsAppClient
s2team Jan 4, 2015
4d25bcf
Added diary entry for 4.1.
s2team Jan 5, 2015
c3d7b33
Made the first voting possible over the ConsoleClient
s2team Jan 8, 2015
82dbf27
Fixed typo
s2team Jan 8, 2015
274a26d
Fixed bug that Users without sending cmds are not counted
s2team Jan 8, 2015
dab4fb4
Added Reset command and put two integrationtests for the process
s2team Jan 8, 2015
9a61396
Added Fongo to made the integration test run without mongodb
s2team Jan 8, 2015
cfb11ee
Added Diary for 8.01.2015
s2team Jan 9, 2015
c7a5841
Removed the "-Team" because it doesn't fit in one row
s2team Jan 9, 2015
1465069
Split the command class with tons of members into single classes
s2team Jan 9, 2015
d651fa2
Replace S2 by s2
s2team Jan 9, 2015
0e8291e
Added diary entry for 10.1.
s2team Jan 10, 2015
90ac796
Adjust VotesApp to the new Yowsup API
s2team Jan 11, 2015
38503f9
Added ChuckNorris, Ping, Help and Roll Commands.
s2team Jan 11, 2015
ceba467
Updated Icons in tests
s2team Jan 11, 2015
a5c740a
Added gradlewrapper
s2team Jan 11, 2015
8cca529
Now the Yowsup-Rest Client ist configureable
s2team Jan 11, 2015
b0d34dc
Added Usernames to Group entity
s2team Jan 12, 2015
90d5ae2
Refactored names
s2team Jan 13, 2015
bf7a7dc
Now its possible to vote with the icons as well
s2team Jan 13, 2015
5102127
Show ICON instead of text
s2team Jan 13, 2015
bd093ac
Enhanced output. Added Calendar and Questions
s2team Jan 14, 2015
733af80
Added diry for 13.01.2014
s2team Jan 14, 2015
7bcfd63
Add .idea to gitignore
s2team Jan 14, 2015
80a5144
Add License (GPL v3)
s2team Jan 14, 2015
753fb5a
Add *.iml (idea modules) to gitignore
s2team Jan 14, 2015
a2d967b
Set Travis path to VotesApp
s2team Jan 15, 2015
da66294
Made the Command API delightful
s2team Jan 15, 2015
2f1a439
Deleted commented out code
s2team Jan 15, 2015
d99a60f
Added diary entry for 15.1.
s2team Jan 16, 2015
b0e79be
Added Mongo Configuration
s2team Jan 16, 2015
2f1f477
Added log config and disabled some gradle modules
s2team Jan 16, 2015
c7c43b6
Added the . by _ also in the Group constructor
s2team Jan 16, 2015
d8e15f7
Fixed mongo setup
s2team Jan 16, 2015
ef5cf64
Added todo
s2team Jan 16, 2015
e2d26cc
Enhanced log message if the rest server is down
s2team Jan 17, 2015
09d2230
Added some metrics about our messages
s2team Jan 17, 2015
875d8df
Removed debug messages in additionals and attitude plugins
s2team Jan 17, 2015
defbe43
Added an errorThrottle when the rest Service is not up
s2team Jan 17, 2015
749355c
Removed the @Timed because we don't need them currently
s2team Jan 17, 2015
08ce8e3
Removed useless Reactor dependencies
s2team Jan 17, 2015
9890726
Removed @Ignore from integration tests. They are fixed now.
s2team Jan 17, 2015
390d7ae
Added Architecture description
s2team Jan 17, 2015
2c1e0ac
fixed typo
s2team Jan 17, 2015
2d92ae9
added logo
s2team Jan 17, 2015
03c3274
Updated Architecture
s2team Jan 17, 2015
4365a51
added links to yowsup and yowsup-rest
s2team Jan 17, 2015
6cf3608
changed headline
s2team Jan 17, 2015
988ca38
Added HealthCheck for Yowsup-Rest
s2team Jan 18, 2015
7259fea
Ping should not reset votes.
s2team Jan 18, 2015
4ab609e
Refactored the properties
s2team Jan 18, 2015
07baa05
Added highlights to Readme
s2team Jan 18, 2015
07d6d16
Fixed some typos
s2team Jan 18, 2015
8041631
Fixed another typo
s2team Jan 18, 2015
8361797
Add WebClient for local debugging
s2team Jan 18, 2015
21c7181
Merge branch 'master' of github.com-s2team:s2team/votesapp
s2team Jan 18, 2015
aea144f
Added Diary for 17.01.
s2team Jan 18, 2015
ef21242
Added examples
s2team Jan 18, 2015
d79cbb4
Remove diary entry
s2team Jan 18, 2015
1d8f6f8
Merge branch 'master' of github.com-s2team:s2team/votesapp
s2team Jan 18, 2015
b7ec24b
Added Run it section
s2team Jan 18, 2015
48707d1
Add own messages to WebClient:w
s2team Jan 18, 2015
bdb9630
Replaced console by web
s2team Jan 18, 2015
3637e62
Fixed NPE in case of media images
s2team Jan 19, 2015
68ad949
Add /last for last incomming to debug
s2team Jan 21, 2015
888fb05
Deliver own static resources. For offline dev ;)
s2team Feb 2, 2015
486796c
Fix md error
s2team Feb 2, 2015
44c2d70
Fix case sensitivity for Attitude. Add some words.
s2team Feb 2, 2015
bb6845a
Add unescape msg.
s2team Feb 3, 2015
eecfb13
Update README.md
s2team Aug 12, 2015
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
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
release.properties
.classpath
.settings
.project
target
*~
*.swp
.sonar
.DS_Store
.springBeans
/bin
/build/
.gradle
.idea
*.iml
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
language: java

jdk:
- oraclejdk8
676 changes: 676 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

36 changes: 0 additions & 36 deletions README.adoc

This file was deleted.

215 changes: 215 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# About the project ![Image of our Solution](diary/logoVotesapp.png)
The project was developed to join the [Spring Boot Contest](https://github.com/learning-spring-boot/contest).

It is submitted by [@d0x (Christian Schneider)](https://github.com/d0x) and [@walery (Walery Strauch)](https://github.com/walery). If you have any ideas or feedback, contact us on Twitter or open a ticket on github.

During the contest we wrote a small [diary](https://github.com/s2team/contest#the-votesapp-diary).

Thank you for any kind of support! - s2.

# About VotesApp [![Build Status](https://travis-ci.org/s2team/votesapp.svg?branch=master)](https://travis-ci.org/s2team/votesapp)
**VotesApp** is a *WhatsApp* Bot that helps you and your friends to organize Votes in *WhatsApp* groups.

As *WhatsApp* User the probability that you have a group together with some friends is quite huge. If not try it, it's a great feature. And when you are in a group like, it will happend that someone asks a question like, *Who likes to join Karting on Saturday?*, or *Whats about pizza today after work?*. Maybe you have such voting once a week for a fixed event like playing soccer in the evinig? Then it becomes quite hard to manage this.

Now you just invite **VotesApp** into your *WhatsApp* group.
Once joined, **VotesApp** will listen to the Keywords, `in`, `out`, `yes`, `no` to count votes and `status` to give the names of the people which voted for this keyword.

# Technology Stack
**VotesApp** is divided into the following three major backend stacks:

* **VotesApp**:
It's purpose is to communicate via ReST with the **Yowsup-Rest** backend to send and receive WhatsApp messages.
Inside the Application we put our logic behind an own `Plugin` API (blue).
That enables us to add more and more features over the time.
We utilize Reactor (gray) to pass messages forth and back to the Plugins.
As persistance layer we choose mongodb. This layer can runs as Standalone Application by default.
To integrate it with the others the `Yowsup`-profile needs to be active.

* **[Yowsup-Rest](https://github.com/s2team/yowsup-rest)**:
Thats a Spring Boot ReST-Service which abstracts all the Yowsup communication behind the two Resources `/inbox` and `/outbox`.

* **[Yowsup](https://github.com/s2team/yowsup)**:
Yowsup is a really cool 3rd Party Framework to communicates with WhatsApp.
On-Top of yowsup we put a "File" Layer to controll it from **Yowsup-Rest**.
Why Files? See [our diary](http://votesapp.de/10-01-2015/Python_brings_us_back_to_the_basics/).

![Image of our Solution](diary/architecture.png)

We submit the VotesApp stack to the SpringBoot Content only.

# VotesApp Diary

To keep Readme clean, we moved the diary to: http://votesapp.de

# Run it on localhost
To run it in development mode, checkout the project and use the `bootRun` command to start it.

```
./gradlew bootRun
```

Now you can navigate your browser to "http://localhost:8080/" and access the WhatsApp/Yowsup mock.

> Integrating it with the real WhatsApp Servers can be done by using the `yowsup` and `mongo` profiles.
But this requieres a more complex setup (with https://github.com/s2team/yowsup and https://github.com/s2team/yowsup-rest) which we haven't submitted for the contest.
To make them run you need a SIM Card, an WhatsApp Password, ...
But to use and rate this application the "*developer*" mock should be enough.

Have a look to the Highlights section below for more insights.

# Highlights

This section describes some highlights of our Backend.

* Diary
* Extensibility with Plugins
* Developer "friendliness"
* @Value(#":}{}$:"#$@}#$!$), - em how was it?
* Environment Variables
* Cough, dude, I need some meds. Really, check my health!
* Profile Configuration
* 1, 2, 3, 4, metric

## Diary
From day zero we tried to put our experience during the contest into a diary.
Today it has more then 10 entries describing the fun we had.

## Extensibility with Plugins
During the contest we had a lot of ideas what else can be done with this.
Okay, ... the most important [Chuck Norris Plugin](https://github.com/s2team/votesapp/blob/master/src/main/java/de/votesapp/commands/plugins/ChuckNorrisCommandPlugin.java) is done.
But there are much more, - like the *Rock-paper-scissors*, *Cinema Moves*,
*Random Cats Videos* (@Kathy, this one is for you <3) or more advanced votings.

For those we provide this interface:

```java
public interface CommandPlugin {
public abstract Optional<Answer> interpret(final GroupMessage message, final Group group);
}
```

In the simplest way it can be implemented like this:

```java
@Service
public class PingCommandPlugin extends TextEqualsWordPlugin {

public static final String[] DEFAULT_RESETS = { "ping", "ping?" };

public PingCommandPlugin() {
super(DEFAULT_RESETS);
}

@Override
public Optional<Answer> matches(final GroupMessage message, final Group group) {
return Optional.of(Answer.intoGroup(group, "Pong!"));
}
}
```

To see more about Plugins, you can checkout [our Diary post](http://votesapp.de/15-01-2015/Make_it_fucking_delightful/) about it.

## Developer "friendliness"
Nobody likes to have tons of configurations to do before start hacking, right?

So we took care that the application can be started without huzzle (excpect lombok...).
Services like MongoDB and Yowsup which aren't usable by default will mocked by "runnable" replacements.
This is done by utilizing the `@Profile` annotations.
When the `default` profile is loaded, [fongo](https://github.com/fakemongo/fongo) is used to replace the real MongoDb
and our `WebClient` replaced all the Yowsup Python stuff.

To use Fongo we only need to import `compile("com.github.fakemongo:fongo:1.5.9")` and provide this neat configuration:

```java
@Profile("!mongo")
@Configuration
static class MongoDbInMemoryConfiguration extends AbstractMongoConfiguration {

@Override
public MappingMongoConverter mappingMongoConverter() throws Exception {
final MappingMongoConverter mappingMongoConverter = super.mappingMongoConverter();
// WhatsApp Keys are containing dots in domains.
mappingMongoConverter.setMapKeyDotReplacement("_");
return mappingMongoConverter;
}

@Override
protected String getDatabaseName() {
return "test";
}

@Override
public Mongo mongo() {
return new Fongo("mongo").getMongo();
}

@Override
protected String getMappingBasePackage() {
return "de.votesapp";
}
}
```

Please give [them](https://github.com/fakemongo/fongo) some fame on Github.
They are doing really great!

The `WebClient` is also activated by the same technique.
There we used the `@Profile("!yowsup")` annotation.

Once you configured both, you can active them with `--spring.profiles.active=yowsup,mongo`, or you just don't :).

## @Value(#":}{}$:"#$@}#$!$), - em how was it?
Okay, if you really use it all the day,
you probably know the syntax (because you understand whats going on).
But if I work with some Students or ppl. new to Spring, they really create every combination of parentheses,
dollars, braces and so on.

With Spring Boot those `@ConfigurationProperties` can be used.

![Image of our Solution](diary/highlightConfig.png)

Just create a counterpart of your configuration structure (yml) and it can be autowirred in your application.
That is so nice!

## Environment Variables
Our P-System is deployed using docker.
From there we don't really like to edit property files.
To get rid of them we use environment variables for the mandatory properties.

This makes it also handy to create various Eclipse 8Run Configurations* and an easy deployment.

![Image of our Solution](diary/highlightEnvVars.png)

## *Cough*, dude, I need some meds. Really, check my health!
The most critical (and not by AutoConfiguration monitored) Module in VotesApp is the communication to the Yowsup-Rest Server.
If this services isn't available, VotesApp can't work. Tracking it was really easy with SpringBoot.

Since we Poll for new message all day long, we can use this to detect the health.

We store exceptions, when they occur in an `Optional` field and anounce them over Springs `HealthIndicator`.

![Image of our Solution](diary/highlightHealth.png)

To see the status, you can check `http://localhost:8080/health`.

## Profile Configuration
We really like the feature to have properties per profile.
With this we were able to separate the configuration for mongodb, yowsup and our application.

![Image of our Solution](diary/highlightProperties.png)

## 1, 2, 3, 4, metric
We haven't expand them very much, but there are some metrics about how many messages we processed and how many are answered.
To do this we used Springs `CounterService` from the [GroupMessageListener](https://github.com/s2team/votesapp/blob/master/src%2Fmain%2Fjava%2Fde%2Fvotesapp%2Fgroups%2FGroupMessageListener.java)

As configured by default, they can be checked at `http://localhost:8080/metrics`.

# Thanks
That was a really great week! Independent of the result we like to give special thanks to:

* Greg for organizing this contest
* Tarek for your work on Yowsup. It was really interesting to have look at it
* The Spring Boot Team to make our work during this contest free of pain (regarding the java side)

We'll invite you both for beer, Greg when comming to US and Tarek when we are the next time in Berlin.
59 changes: 59 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
buildscript {
ext {
springBootVersion = '1.2.1.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'

jar {
baseName = 'VotesApp'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
mavenCentral()
}


dependencies {
compile("org.springframework.boot:spring-boot-starter-data-mongodb")
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-actuator")
//compile("org.springframework.boot:spring-boot-starter-remote-shell")
compile("org.springframework.boot:spring-boot-starter-websocket")
compile("org.springframework:spring-messaging")
compile("org.springframework.boot:spring-boot-starter-test")
compile("org.projectlombok:lombok:1.14.8")
compile("com.google.code.findbugs:jsr305:3.0.0")
compile("org.apache.commons:commons-lang3:3.3.2")
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.google.guava:guava:18.0")

testCompile("org.springframework.boot:spring-boot-starter-test")
}

eclipse {
classpath {
containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
}
}

task wrapper(type: Wrapper) {
gradleVersion = '1.12'
}
Binary file added diary/UiAutomatorViewer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/api1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/api2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/awesomeBugfix.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/domainOwner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/fileSolution.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/firstPackages.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/firstTest.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/firstTest_small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/highlightConfig.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/highlightEnvVars.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/highlightHealth.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/highlightProperties.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/highlightReactor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/logoVotesapp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/notInProfile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/onair.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/painpoint.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/pluginsDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/reactorExample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/socks.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/solution.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/storyline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/succedInTheContest.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added diary/webClient.png
Binary file added diary/whatsAppDiagram.png
Binary file added diary/yowsupFiler.png
Loading