{"id":19163888,"url":"https://github.com/learning-spring-boot/contest-votesapp","last_synced_at":"2025-06-18T23:33:42.896Z","repository":{"id":26163312,"uuid":"29608756","full_name":"learning-spring-boot/contest-votesapp","owner":"learning-spring-boot","description":null,"archived":false,"fork":false,"pushed_at":"2015-01-21T20:06:21.000Z","size":4200,"stargazers_count":4,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-07T11:39:48.258Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/learning-spring-boot.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-01-21T20:05:05.000Z","updated_at":"2019-10-04T14:40:06.000Z","dependencies_parsed_at":"2022-08-26T04:22:22.564Z","dependency_job_id":null,"html_url":"https://github.com/learning-spring-boot/contest-votesapp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/learning-spring-boot/contest-votesapp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/learning-spring-boot%2Fcontest-votesapp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/learning-spring-boot%2Fcontest-votesapp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/learning-spring-boot%2Fcontest-votesapp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/learning-spring-boot%2Fcontest-votesapp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/learning-spring-boot","download_url":"https://codeload.github.com/learning-spring-boot/contest-votesapp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/learning-spring-boot%2Fcontest-votesapp/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260653596,"owners_count":23042642,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T09:17:03.584Z","updated_at":"2025-06-18T23:33:37.882Z","avatar_url":"https://github.com/learning-spring-boot.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ![Image of our Solution](diary/onair.png)[ON AIR] until Jan. 17th - Star us ![Image of our Solution](diary/logoVotesapp.png)\nThe project is started for the [Spring Boot Contest](https://github.com/learning-spring-boot/contest). To support us please\n\n* \"Star\" the Repository and/or\n* Tweet about it using the #VotesApp HashTag\n\nIt 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.\n\nDuring the contest we write a small [diary](https://github.com/s2team/contest#the-votesapp-diary) and maintain a [trello board for this project](https://trello.com/b/VzijoLhr/votesapp).\nIf you are really interested in recent updates, you *could* add this `crontab` :)\n\n```bash\ncrontab -l | { cat; echo \"0 13 * * * firefox http://votesapp.de/articles.html\"; } | crontab -\n```\n\nThank you for any kind of support! - s2.\n\n# About VotesApp [![Build Status](https://travis-ci.org/s2team/votesapp.svg?branch=master)](https://travis-ci.org/s2team/votesapp)\n**VotesApp** is a *WhatsApp* Bot that helps you and your friends to organize Votes in *WhatsApp* groups.\n\nAs *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.\n\nNow you just invite **VotesApp** into your *WhatsApp* group.\nOnce 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.\n\n# Technology Stack\n**VotesApp** is divided into the following three major backend stacks:\n\n* **VotesApp**:\nIt's purpose is to communicate via ReST with the **Yowsup-Rest** backend to send and receive WhatsApp messages.\nInside the Application we put our logic behind an own `Plugin` API (blue).\nThat enables us to add more and more features over the time.\nWe utilize Reactor (gray) to pass messages forth and back to the Plugins.\nAs persistance layer we choose mongodb. This layer can runs as Standalone Application by default.\nTo integrate it with the others the `Yowsup`-profile needs to be active.\n\n* **[Yowsup-Rest](https://github.com/s2team/yowsup-rest)**:\nThats a Spring Boot ReST-Service which abstracts all the Yowsup communication behind the two Resources `/inbox` and `/outbox`.\n\n* **[Yowsup](https://github.com/s2team/yowsup)**:\nYowsup is a really cool 3rd Party Framework to communicates with WhatsApp.\nOn-Top of yowsup we put a \"File\" Layer to controll it from **Yowsup-Rest**.\nWhy Files? See [our diary](http://votesapp.de/10-01-2015/Python_brings_us_back_to_the_basics/).\n\n![Image of our Solution](diary/architecture.png)\n\nWe submit the VotesApp stack to the SpringBoot Content only.\n\n# VotesApp Diary\n\nTo keep Readme clean, we moved the diary to: http://votesapp.de\n\n# Run it on localhost\nTo run it in development mode, checkout the project and use the `bootRun` command to start it.\n\n```\n./gradlew bootRun\n```\n\nNow you can navigate your browser to \"http://localhost:8080/\" and access the WhatsApp/Yowsup mock.\n\n\u003e Integrating it with the real WhatsApp Servers can be done by using the `yowsup` and `mongo` profiles.\nBut 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.\nTo make them run you need a SIM Card, an WhatsApp Password, ...\nBut to use and rate this application the \"*developer*\" mock should be enough.\n\nHave a look to the Highlights section below for more insights.\n\n# Highlights\n\nThis section describes some highlights of our Backend.\n\n* Diary\n* Extensibility with Plugins\n* Developer \"friendliness\"\n* @Value(#\":}{}$:\"#$@}#$!$), - em how was it?\n* Environment Variables\n* Cough, dude, I need some meds. Really, check my health!\n* Profile Configuration\n* 1, 2, 3, 4, metric\n\n## Diary\nFrom day zero we tried to put our experience during the contest into a diary.\nToday it has more then 10 entries describing the fun we had.\n\n## Extensibility with Plugins\nDuring the contest we had a lot of ideas what else can be done with this.\nOkay, ... the most important [Chuck Norris Plugin](https://github.com/s2team/votesapp/blob/master/src/main/java/de/votesapp/commands/plugins/ChuckNorrisCommandPlugin.java) is done.\nBut there are much more, - like the *Rock-paper-scissors*, *Cinema Moves*,\n*Random Cats Videos* (@Kathy, this one is for you \u003c3) or more advanced votings.\n\nFor those we provide this interface:\n\n```java\npublic interface CommandPlugin {\n  public abstract Optional\u003cAnswer\u003e interpret(final GroupMessage message, final Group group);\n}\n```\n\nIn the simplest way it can be implemented like this:\n\n```java\n@Service\npublic class PingCommandPlugin extends TextEqualsWordPlugin {\n\n  public static final String[] DEFAULT_RESETS = { \"ping\", \"ping?\" };\n\n  public PingCommandPlugin() {\n    super(DEFAULT_RESETS);\n  }\n\n  @Override\n  public Optional\u003cAnswer\u003e matches(final GroupMessage message, final Group group) {\n    return Optional.of(Answer.intoGroup(group, \"Pong!\"));\n  }\n}\n```\n\nTo see more about Plugins, you can checkout [our Diary post](http://votesapp.de/15-01-2015/Make_it_fucking_delightful/) about it.\n\n## Developer \"friendliness\"\nNobody likes to have tons of configurations to do before start hacking, right?\n\nSo we took care that the application can be started without huzzle (excpect lombok...).\nServices like MongoDB and Yowsup which aren't usable by default will mocked by \"runnable\" replacements.\nThis is done by utilizing the `@Profile` annotations.\nWhen the `default` profile is loaded, [fongo](https://github.com/fakemongo/fongo) is used to replace the real MongoDb\nand our `WebClient` replaced all the Yowsup Python stuff.\n\nTo use Fongo we only need to import `compile(\"com.github.fakemongo:fongo:1.5.9\")` and provide this neat configuration:\n\n```java\n@Profile(\"!mongo\")\n@Configuration\nstatic class MongoDbInMemoryConfiguration extends AbstractMongoConfiguration {\n\n  @Override\n  public MappingMongoConverter mappingMongoConverter() throws Exception {\n    final MappingMongoConverter mappingMongoConverter = super.mappingMongoConverter();\n    // WhatsApp Keys are containing dots in domains.\n    mappingMongoConverter.setMapKeyDotReplacement(\"_\");\n    return mappingMongoConverter;\n  }\n\n  @Override\n  protected String getDatabaseName() {\n    return \"test\";\n  }\n\n  @Override\n  public Mongo mongo() {\n    return new Fongo(\"mongo\").getMongo();\n  }\n\n  @Override\n  protected String getMappingBasePackage() {\n    return \"de.votesapp\";\n  }\n}\n```\n\nPlease give [them](https://github.com/fakemongo/fongo) some fame on Github.\nThey are doing really great!\n\nThe `WebClient` is also activated by the same technique.\nThere we used the `@Profile(\"!yowsup\")` annotation.\n\nOnce you configured both, you can active them with `--spring.profiles.active=yowsup,mongo`, or you just don't :).\n\n## @Value(#\":}{}$:\"#$@}#$!$), - em how was it?\nOkay, if you really use it all the day,\nyou probably know the syntax (because you understand whats going on).\nBut if I work with some Students or ppl. new to Spring, they really create every combination of parentheses,\ndollars, braces and so on.\n\nWith Spring Boot those @ConfigurationProperties` can be used.\n\n![Image of our Solution](diary/highlightConfig.png)\n\nJust create a counterpart of your configuration structure (yml) and it can be autowirred in your application.\nThat is so nice!\n\n## Environment Variables\nOur P-System is deployed using docker.\nFrom there we don't really like to edit property files.\nTo get rid of them we use environment variables for the mandatory properties.\n\nThis makes it also handy to create various Eclipse 8Run Configurations* and an easy deployment.\n\n![Image of our Solution](diary/highlightEnvVars.png)\n\n## *Cough*, dude, I need some meds. Really, check my health!\nThe most critical (and not by AutoConfiguration monitored) Module in VotesApp is the communication to the Yowsup-Rest Server.\nIf this services isn't available, VotesApp can't work. Tracking it was really easy with SpringBoot.\n\nSince we Poll for new message all day long, we can use this to detect the health.\n\nWe store exceptions, when they occur in an `Optional` field and anounce them over Springs `HealthIndicator`.\n\n![Image of our Solution](diary/highlightHealth.png)\n\nTo see the status, you can check `http://localhost:8080/health`.\n\n## Profile Configuration\nWe really like the feature to have properties per profile.\nWith this we were able to separate the configuration for mongodb, yowsup and our application.\n\n![Image of our Solution](diary/highlightProperties.png)\n\n## 1, 2, 3, 4, metric\nWe haven't expand them very much, but there are some metrics about how many messages we processed and how many are answered.\nTo do this we used Springs `CounterService` from the [GroupMessageListener](https://github.com/s2team/votesapp/blob/master/src%2Fmain%2Fjava%2Fde%2Fvotesapp%2Fgroups%2FGroupMessageListener.java)\n\nAs configured by default, they can be checked at `http://localhost:8080/metrics`.\n\n# Thanks\nThat was a really great week! Independent of the result we like to give special thanks to:\n\n* Greg for organizing this contest\n* Tarek for your work on Yowsup. It was really interesting to have look at it\n* The Spring Boot Team to make our work during this contest free of pain (regarding the java side)\n\nWe'll invite you both for beer, Greg when comming to US and Tarek when we are the next time in Berlin.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flearning-spring-boot%2Fcontest-votesapp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flearning-spring-boot%2Fcontest-votesapp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flearning-spring-boot%2Fcontest-votesapp/lists"}