├── .gitignore ├── LICENSE.md ├── README.md ├── build.gradle ├── docs ├── allclasses-frame.html ├── allclasses-noframe.html ├── com │ └── viber │ │ └── bot │ │ ├── Request.html │ │ ├── Response.html │ │ ├── ViberSignatureValidator.html │ │ ├── api │ │ ├── ApiException.html │ │ ├── ApiResponse.html │ │ ├── ViberBot.html │ │ ├── package-frame.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── event │ │ ├── Event.html │ │ ├── callback │ │ │ ├── OnConversationStarted.html │ │ │ ├── OnMessageDelivered.html │ │ │ ├── OnMessageReceived.html │ │ │ ├── OnMessageSeen.html │ │ │ ├── OnMessageSent.html │ │ │ ├── OnSubscribe.html │ │ │ ├── OnUnsubscribe.html │ │ │ ├── package-frame.html │ │ │ ├── package-summary.html │ │ │ └── package-tree.html │ │ ├── incoming │ │ │ ├── IncomingConversationStartedEvent.html │ │ │ ├── IncomingDeliveredEvent.html │ │ │ ├── IncomingErrorEvent.html │ │ │ ├── IncomingEvent.html │ │ │ ├── IncomingMessageEvent.html │ │ │ ├── IncomingSeenEvent.html │ │ │ ├── IncomingSubscribedEvent.html │ │ │ ├── IncomingUnsubscribeEvent.html │ │ │ ├── IncomingWebhookEvent.html │ │ │ ├── package-frame.html │ │ │ ├── package-summary.html │ │ │ └── package-tree.html │ │ ├── package-frame.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── message │ │ ├── Contact.html │ │ ├── ContactMessage.html │ │ ├── FileMessage.html │ │ ├── Location.html │ │ ├── LocationMessage.html │ │ ├── Message.html │ │ ├── MessageKeyboard.html │ │ ├── PictureMessage.html │ │ ├── RichMediaMessage.html │ │ ├── RichMediaObject.html │ │ ├── StickerMessage.html │ │ ├── TextMessage.html │ │ ├── TrackingData.html │ │ ├── UrlMessage.html │ │ ├── VideoMessage.html │ │ ├── package-frame.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── package-frame.html │ │ ├── package-summary.html │ │ ├── package-tree.html │ │ └── profile │ │ ├── BotProfile.html │ │ ├── UserProfile.html │ │ ├── package-frame.html │ │ ├── package-summary.html │ │ └── package-tree.html ├── constant-values.html ├── deprecated-list.html ├── help-doc.html ├── index-files │ ├── index-1.html │ ├── index-10.html │ ├── index-11.html │ ├── index-12.html │ ├── index-13.html │ ├── index-14.html │ ├── index-15.html │ ├── index-16.html │ ├── index-17.html │ ├── index-2.html │ ├── index-3.html │ ├── index-4.html │ ├── index-5.html │ ├── index-6.html │ ├── index-7.html │ ├── index-8.html │ └── index-9.html ├── index.html ├── overview-frame.html ├── overview-summary.html ├── overview-tree.html ├── package-list ├── script.js ├── serialized-form.html └── stylesheet.css ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── nano-httpd-sample ├── README.md ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ └── main │ ├── java │ └── com │ │ └── viber │ │ └── bot │ │ └── sample │ │ └── NanoHttpdSample.java │ └── resources │ └── simplelogger.properties ├── settings.gradle ├── spring-boot-sample ├── README.md ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ └── main │ ├── java │ └── com │ │ └── viber │ │ └── bot │ │ └── sample │ │ └── SpringEchoBot.java │ └── resources │ └── application.yml └── src ├── main └── java │ └── com │ └── viber │ └── bot │ ├── Preconditions.java │ ├── Request.java │ ├── Response.java │ ├── ViberEnvironmentConfiguration.java │ ├── ViberSignatureValidator.java │ ├── api │ ├── ApiException.java │ ├── ApiResponse.java │ ├── MessageSender.java │ ├── MessageToMapConverter.java │ ├── RegexMatcherRouter.java │ ├── RequestReceiverImpl.java │ ├── ViberBot.java │ └── ViberClient.java │ ├── event │ ├── BotEventListener.java │ ├── Event.java │ ├── EventEmitter.java │ ├── callback │ │ ├── OnConversationStarted.java │ │ ├── OnMessageDelivered.java │ │ ├── OnMessageReceived.java │ │ ├── OnMessageSeen.java │ │ ├── OnMessageSent.java │ │ ├── OnSubscribe.java │ │ └── OnUnsubscribe.java │ └── incoming │ │ ├── IncomingConversationStartedEvent.java │ │ ├── IncomingDeliveredEvent.java │ │ ├── IncomingErrorEvent.java │ │ ├── IncomingEvent.java │ │ ├── IncomingMessageEvent.java │ │ ├── IncomingSeenEvent.java │ │ ├── IncomingSubscribedEvent.java │ │ ├── IncomingUnsubscribeEvent.java │ │ └── IncomingWebhookEvent.java │ ├── message │ ├── Contact.java │ ├── ContactMessage.java │ ├── FileMessage.java │ ├── Location.java │ ├── LocationMessage.java │ ├── Message.java │ ├── MessageKeyboard.java │ ├── PictureMessage.java │ ├── RichMediaMessage.java │ ├── RichMediaObject.java │ ├── StickerMessage.java │ ├── TextMessage.java │ ├── TrackingData.java │ ├── UrlMessage.java │ └── VideoMessage.java │ ├── middleware │ ├── DefaultMiddleware.java │ ├── Middleware.java │ ├── PubSubMiddleware.java │ └── RequestReceiver.java │ └── profile │ ├── BotProfile.java │ └── UserProfile.java └── test └── java └── com └── viber └── bot ├── RequestTest.java ├── ViberSignatureValidatorTest.java ├── api ├── RegexMatcherRouterTest.java └── ViberClientTest.java ├── event └── EventEmitterTest.java └── middleware └── PubSubMiddlewareTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .gradle 3 | gradle.properties 4 | build/ 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :warning: Deprecated :warning: 2 | 3 | This library is deprecated. No new development will be taking place. We recommend that you use either [NodeJS](https://github.com/Viber/viber-bot-node), [Python](https://github.com/Viber/viber-bot-python) or [REST API](https://developers.viber.com/docs/api/rest-bot-api/). 4 | 5 | # Viber Java Bot API 6 | 7 | Use this library to develop a bot for Viber platform. 8 | The library is available on **[GitHub](https://github.com/Viber/viber-bot-java)** as well as [maven central](http://central.maven.org/maven2/com/viber/viber-bot/). 9 | 10 | ## License 11 | 12 | This library is released under the terms of the Apache 2.0 license. See [License](https://github.com/Viber/viber-bot-java/blob/master/LICENSE.md) for more information. 13 | 14 | ## Library Prerequisites 15 | 16 | 1. Java >= 8 17 | 1. An Active Viber account on a platform which supports Public Accounts/ bots (iOS/Android). This account will automatically be set as the account administrator during the account creation process. 18 | 1. Active Public Account/ bot. 19 | 1. Account authentication token - unique account identifier used to validate your account in all API requests. Once your account is created your authentication token will appear in the account’s “edit info” screen (for admins only). Each request posted to Viber by the account will need to contain the token. 20 | 1. Webhook - Please use a server endpoint URL that supports HTTPS. If you deploy on your own custom server, you'll need a trusted (ca.pem) certificate, not self-signed. Read our [blog post](https://developers.viber.com/blog/2017/05/24/test-your-bots-locally) on how to test your bot locally. 21 | 22 | ## Installation 23 | 24 | This library is released on [maven central](http://central.maven.org/maven2/com/viber/viber-bot/). 25 | 26 | ### Gradle 27 | 28 | ``` 29 | compile group: 'com.viber', name: 'viber-bot', version: '1.0.11' 30 | ``` 31 | 32 | ### Maven 33 | 34 | ``` 35 | 36 | com.viber 37 | viber-bot 38 | 1.0.11 39 | 40 | ``` 41 | 42 | ## Documentation 43 | 44 | ### JavaDocs 45 | 46 | All public APIs are documented with JavaDocs which can be found in the [GitHub repository](http://htmlpreview.github.io/?https://github.com/Viber/viber-bot-java/blob/master/docs/index.html). 47 | 48 | ### Sample projects 49 | 50 | We've created two sample projects to help you get started: 51 | 52 | * [Spring-Boot Sample](https://github.com/Viber/viber-bot-java/tree/master/spring-boot-sample) using [spring-boot-starter-web](https://github.com/spring-projects/spring-boot/tree/master/spring-boot-starters/spring-boot-starter-web) package. 53 | * [NanoHTTPd Sample](https://github.com/Viber/viber-bot-java/tree/master/nano-httpd-sample/) with a tiny embedded HTTP server, called [NanoHTTPd](https://github.com/NanoHttpd/nanohttpd). 54 | 55 | ### A simple overview 56 | 57 | ```java 58 | public void botExample() { 59 | ViberBot bot = new ViberBot(new BotProfile("SampleBot", "http://viber.com/avatar.jpg"), "YOUR_AUTH_TOKEN_HERE"); 60 | bot.onMessageReceived((event, message, response) -> response.send(message)); 61 | 62 | // somewhere else in your web server of choice: 63 | bot.incoming(Request.fromJsonString("...")); 64 | } 65 | ``` 66 | 67 | You can chose to use any web server or framework you like. All you need to do is call the API with: 68 | 69 | ```java 70 | bot.incoming(Request.fromJsonString("...")); // or 71 | bot.incoming(Request.fromInputStream(inputStream)); 72 | ``` 73 | 74 | ### Should I be concerned with synchronizing my web server threads? Is this library thread-safe? 75 | 76 | The Viber bot library is *thread-safe* and highly concurrent. You do not have to worry about synchronizing anything. 77 | 78 | All calls to `ViberBot#incoming()` go through a `BlockingQueue`, and ordering is retained. 79 | All I/O calls are directly executed on the same thread they were initially called on. 80 | 81 | ### Can I make I/O calls asynchronous and still retain thread-safety for my bot? 82 | 83 | Yes. You can pass a system property to control the I/O thread pool: 84 | 85 | `com.viber.bot.executor.strategy=[DIRECT|THREAD]` (default is DIRECT) 86 | 87 | `com.viber.bot.executor.threads=N` (default is `getRuntime().availableProcessors()`) 88 | 89 | * Note: This will not change the way you use the library. You still don't have to synchronize anything. 90 | 91 | ### Do you supply a basic router for text messages? 92 | 93 | Well funny you ask. Yes we do. But a word of warning - messages sent to your router callback will also be emitted to the `ViberBot#onMessageReceived` event. 94 | 95 | ```java 96 | public void botTextRouterExample() { 97 | bot.onTextMessage("(hi|hello)", (event, message, response) -> response.send("Hi " + event.getSender().getName())); 98 | } 99 | ``` 100 | 101 | ## Community 102 | 103 | Join the conversation on **[Gitter](https://gitter.im/viber/bot-java)**. 104 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | group = projectGroup 2 | version = projectVersion 3 | 4 | buildscript { 5 | repositories { 6 | mavenCentral() 7 | } 8 | } 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | apply plugin: 'java' 15 | apply plugin: 'idea' 16 | apply plugin: 'eclipse' 17 | apply plugin: 'maven' 18 | apply plugin: 'signing' 19 | 20 | sourceCompatibility = projectSourceCompatibility 21 | 22 | project.ext { 23 | slf4jVersion = '1.7.22' 24 | jacksonVersion = '2.8.6' 25 | findbugsVersion = '3.0.1' 26 | guavaVersion = '21.0' 27 | okhttp3Version = '3.5.0' 28 | } 29 | 30 | dependencies { 31 | compile group: 'org.slf4j', name: 'slf4j-api', version: slf4jVersion 32 | compile group: 'com.squareup.okhttp3', name: 'okhttp', version: okhttp3Version 33 | compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion 34 | 35 | compile group: 'com.google.code.findbugs', name: 'jsr305', version: findbugsVersion 36 | compile group: 'com.google.guava', name: 'guava', version: guavaVersion 37 | 38 | testCompile group: 'junit', name: 'junit', version: '4.12' 39 | testCompile group: 'org.assertj', name: 'assertj-core', version: '3.6.1' 40 | testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19' 41 | testCompile group: 'com.jayway.awaitility', name: 'awaitility', version: '1.7.0' 42 | } 43 | 44 | sourceSets { 45 | main { 46 | java { 47 | srcDirs 'src/main/java' 48 | } 49 | } 50 | } 51 | 52 | task wrapper(type: Wrapper) { 53 | gradleVersion = '2.13' 54 | } 55 | 56 | task javadocJar(type: Jar) { 57 | classifier = 'javadoc' 58 | from javadoc 59 | } 60 | 61 | task sourcesJar(type: Jar) { 62 | classifier = 'sources' 63 | from sourceSets.main.allSource 64 | } 65 | 66 | artifacts { 67 | archives javadocJar, sourcesJar 68 | } 69 | 70 | signing { 71 | sign configurations.archives 72 | } 73 | 74 | uploadArchives { 75 | repositories { 76 | mavenDeployer { 77 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 78 | 79 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { 80 | authentication(userName: ossrhUsername, password: ossrhPassword) 81 | } 82 | 83 | snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { 84 | authentication(userName: ossrhUsername, password: ossrhPassword) 85 | } 86 | 87 | pom.project { 88 | name projectName 89 | packaging 'jar' 90 | artifactId projectName 91 | description 'Use this library to communicate with the Viber API to develop a bot for https://developers.viber.com/.' 92 | url 'https://developers.viber.com/' 93 | 94 | scm { 95 | connection 'scm:git:git://github.com/Viber/viber-bot-java.git' 96 | url 'https://github.com/Viber/viber-bot-java/tree/master' 97 | } 98 | 99 | licenses { 100 | license { 101 | name 'The Apache License, Version 2.0' 102 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 103 | } 104 | } 105 | 106 | developers { 107 | developer { 108 | id 'idan' 109 | name 'Idan Harel' 110 | email 'idanh@viber.com' 111 | organization 'Viber' 112 | organizationUrl 'http://viber.com' 113 | } 114 | } 115 | } 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /docs/allclasses-noframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | All Classes 7 | 8 | 9 | 10 | 11 | 12 |

All Classes

13 |
14 | 56 |
57 | 58 | 59 | -------------------------------------------------------------------------------- /docs/com/viber/bot/api/package-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.viber.bot.api 7 | 8 | 9 | 10 | 11 | 12 |

com.viber.bot.api

13 |
14 |

Classes

15 | 19 |

Exceptions

20 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /docs/com/viber/bot/event/callback/package-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.viber.bot.event.callback 7 | 8 | 9 | 10 | 11 | 12 |

com.viber.bot.event.callback

13 |
14 |

Interfaces

15 | 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/com/viber/bot/event/incoming/package-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.viber.bot.event.incoming 7 | 8 | 9 | 10 | 11 | 12 |

com.viber.bot.event.incoming

13 |
14 |

Classes

15 | 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/com/viber/bot/event/package-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.viber.bot.event 7 | 8 | 9 | 10 | 11 | 12 |

com.viber.bot.event

13 |
14 |

Enums

15 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/com/viber/bot/event/package-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.viber.bot.event 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 |
Skip navigation links
30 | 31 | 32 | 33 | 42 |
43 | 70 | 71 |
72 |

Package com.viber.bot.event

73 |
74 |
75 | 92 |
93 | 94 |
95 | 96 | 97 |
Skip navigation links
98 | 99 | 100 | 101 | 110 |
111 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /docs/com/viber/bot/event/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.viber.bot.event Class Hierarchy 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 |
Skip navigation links
30 | 31 | 32 | 33 | 42 |
43 | 70 | 71 |
72 |

Hierarchy For Package com.viber.bot.event

73 | Package Hierarchies: 74 | 77 |
78 |
79 |

Enum Hierarchy

80 | 91 |
92 | 93 |
94 | 95 | 96 |
Skip navigation links
97 | 98 | 99 | 100 | 109 |
110 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /docs/com/viber/bot/message/package-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.viber.bot.message 7 | 8 | 9 | 10 | 11 | 12 |

com.viber.bot.message

13 |
14 |

Classes

15 | 32 |
33 | 34 | 35 | -------------------------------------------------------------------------------- /docs/com/viber/bot/package-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.viber.bot 7 | 8 | 9 | 10 | 11 | 12 |

com.viber.bot

13 |
14 |

Classes

15 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/com/viber/bot/package-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.viber.bot 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 |
Skip navigation links
30 | 31 | 32 | 33 | 42 |
43 | 70 | 71 |
72 |

Package com.viber.bot

73 |
74 |
75 | 100 |
101 | 102 |
103 | 104 | 105 |
Skip navigation links
106 | 107 | 108 | 109 | 118 |
119 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /docs/com/viber/bot/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.viber.bot Class Hierarchy 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 |
Skip navigation links
30 | 31 | 32 | 33 | 42 |
43 | 70 | 71 |
72 |

Hierarchy For Package com.viber.bot

73 | Package Hierarchies: 74 | 77 |
78 |
79 |

Class Hierarchy

80 | 89 |
90 | 91 |
92 | 93 | 94 |
Skip navigation links
95 | 96 | 97 | 98 | 107 |
108 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /docs/com/viber/bot/profile/package-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.viber.bot.profile 7 | 8 | 9 | 10 | 11 | 12 |

com.viber.bot.profile

13 |
14 |

Classes

15 | 19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/com/viber/bot/profile/package-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.viber.bot.profile 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 |
Skip navigation links
30 | 31 | 32 | 33 | 42 |
43 | 70 | 71 |
72 |

Package com.viber.bot.profile

73 |
74 |
75 | 96 |
97 | 98 |
99 | 100 | 101 |
Skip navigation links
102 | 103 | 104 | 105 | 114 |
115 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /docs/com/viber/bot/profile/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.viber.bot.profile Class Hierarchy 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 |
Skip navigation links
30 | 31 | 32 | 33 | 42 |
43 | 70 | 71 |
72 |

Hierarchy For Package com.viber.bot.profile

73 | Package Hierarchies: 74 | 77 |
78 |
79 |

Class Hierarchy

80 | 88 |
89 | 90 |
91 | 92 | 93 |
Skip navigation links
94 | 95 | 96 | 97 | 106 |
107 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /docs/constant-values.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Constant Field Values 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 |
Skip navigation links
30 | 31 | 32 | 33 | 42 |
43 | 70 | 71 |
72 |

Constant Field Values

73 |

Contents

74 |
75 | 76 |
77 | 78 | 79 |
Skip navigation links
80 | 81 | 82 | 83 | 92 |
93 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /docs/deprecated-list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Deprecated List 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 |
Skip navigation links
30 | 31 | 32 | 33 | 42 |
43 | 70 | 71 |
72 |

Deprecated API

73 |

Contents

74 |
75 | 76 |
77 | 78 | 79 |
Skip navigation links
80 | 81 | 82 | 83 | 92 |
93 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Generated Documentation (Untitled) 7 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | <noscript> 68 | <div>JavaScript is disabled on your browser.</div> 69 | </noscript> 70 | <h2>Frame Alert</h2> 71 | <p>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. Link to <a href="overview-summary.html">Non-frame version</a>.</p> 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /docs/overview-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Overview List 7 | 8 | 9 | 10 | 11 | 12 |
All Classes
13 |
14 |

Packages

15 | 24 |
25 |

 

26 | 27 | 28 | -------------------------------------------------------------------------------- /docs/overview-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Overview 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 |
Skip navigation links
30 | 31 | 32 | 33 | 42 |
43 | 70 | 71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 |
Packages 
PackageDescription
com.viber.bot 
com.viber.bot.api 
com.viber.bot.event 
com.viber.bot.event.callback 
com.viber.bot.event.incoming 
com.viber.bot.message 
com.viber.bot.profile 
109 |
110 | 111 |
112 | 113 | 114 |
Skip navigation links
115 | 116 | 117 | 118 | 127 |
128 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /docs/package-list: -------------------------------------------------------------------------------- 1 | com.viber.bot 2 | com.viber.bot.api 3 | com.viber.bot.event 4 | com.viber.bot.event.callback 5 | com.viber.bot.event.incoming 6 | com.viber.bot.message 7 | com.viber.bot.profile 8 | -------------------------------------------------------------------------------- /docs/script.js: -------------------------------------------------------------------------------- 1 | function show(type) 2 | { 3 | count = 0; 4 | for (var key in methods) { 5 | var row = document.getElementById(key); 6 | if ((methods[key] & type) != 0) { 7 | row.style.display = ''; 8 | row.className = (count++ % 2) ? rowColor : altColor; 9 | } 10 | else 11 | row.style.display = 'none'; 12 | } 13 | updateTabs(type); 14 | } 15 | 16 | function updateTabs(type) 17 | { 18 | for (var value in tabs) { 19 | var sNode = document.getElementById(tabs[value][0]); 20 | var spanNode = sNode.firstChild; 21 | if (value == type) { 22 | sNode.className = activeTableTab; 23 | spanNode.innerHTML = tabs[value][1]; 24 | } 25 | else { 26 | sNode.className = tableTab; 27 | spanNode.innerHTML = "" + tabs[value][1] + ""; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /docs/serialized-form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Serialized Form 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 |
Skip navigation links
30 | 31 | 32 | 33 | 42 |
43 | 70 | 71 |
72 |

Serialized Form

73 |
74 |
75 | 87 |
88 | 89 |
90 | 91 | 92 |
Skip navigation links
93 | 94 | 95 | 96 | 105 |
106 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Viber/viber-bot-java/65be8a3e6210ade86157146c68fe78dfe1bc8d78/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Nov 30 16:58:07 IST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /nano-httpd-sample/README.md: -------------------------------------------------------------------------------- 1 | # Viber Bot API - NanoHTTPd 2 | ## Documentation 3 | For full documentation on this SDK please [click here](https://github.com/Viber/viber-bot-java/blob/master/README.md) 4 | 5 | ## Using this sample 6 | * Building the project: 7 | * For IntelliJ: `./gradlew idea` 8 | * For Eclipse: `./gradlew eclipse` 9 | * Please edit constants in [NanoHttpdSample.java](src/main/java/com/viber/bot/sample/NanoHttpdSample.java) with your SSL certificate and [auth token](https://developers.viber.com/docs/faq/#authentication-tokens). 10 | * Run [NanoHttpdSample.main()](src/main/java/com/viber/bot/sample/NanoHttpdSample.java). 11 | -------------------------------------------------------------------------------- /nano-httpd-sample/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.viber' 2 | version '1.0-SNAPSHOT' 3 | 4 | apply plugin: 'java' 5 | apply plugin: 'idea' 6 | apply plugin: 'eclipse' 7 | 8 | sourceCompatibility = 1.8 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | compile group: 'com.viber', name: 'viber-bot', version: '1.0.9' 16 | compile group: 'org.nanohttpd', name: 'nanohttpd', version: '2.3.1' 17 | compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.21' 18 | } 19 | -------------------------------------------------------------------------------- /nano-httpd-sample/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Viber/viber-bot-java/65be8a3e6210ade86157146c68fe78dfe1bc8d78/nano-httpd-sample/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /nano-httpd-sample/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 05 08:42:29 IST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip 7 | -------------------------------------------------------------------------------- /nano-httpd-sample/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /nano-httpd-sample/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /nano-httpd-sample/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'nano-httpd-sample' 2 | 3 | -------------------------------------------------------------------------------- /nano-httpd-sample/src/main/java/com/viber/bot/sample/NanoHttpdSample.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.sample; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.google.common.net.MediaType; 5 | import com.google.common.util.concurrent.Futures; 6 | import com.google.common.util.concurrent.Uninterruptibles; 7 | import com.viber.bot.Request; 8 | import com.viber.bot.ViberSignatureValidator; 9 | import com.viber.bot.api.ViberBot; 10 | import com.viber.bot.message.TextMessage; 11 | import com.viber.bot.profile.BotProfile; 12 | import fi.iki.elonen.NanoHTTPD; 13 | 14 | import javax.annotation.Nullable; 15 | import java.io.IOException; 16 | import java.io.InputStream; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | import java.util.Optional; 20 | import java.util.concurrent.ExecutionException; 21 | 22 | public class NanoHttpdSample extends NanoHTTPD { 23 | 24 | private static final int PORT = 8080; 25 | 26 | private static final String AUTH_TOKEN = "YOUR_VIBER_AUTH_TOKEN"; 27 | private static final String WEBHOOK_URL = "https://YOUR_WEBHOOK_URL:8080"; 28 | 29 | private static final String SSL_JKS = "/path/to/cert.jks"; 30 | private static final String SSL_JKS_PASSWORD = "password"; 31 | 32 | private final ViberBot bot; 33 | private final ViberSignatureValidator signatureValidator; 34 | 35 | NanoHttpdSample() throws IOException, ExecutionException, InterruptedException { 36 | super(PORT); 37 | makeSecure(NanoHTTPD.makeSSLSocketFactory(SSL_JKS, SSL_JKS_PASSWORD.toCharArray()), null); 38 | start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); 39 | 40 | bot = new ViberBot(new BotProfile("Echo Bot"), AUTH_TOKEN); 41 | signatureValidator = new ViberSignatureValidator(AUTH_TOKEN); 42 | 43 | bot.setWebhook(WEBHOOK_URL).get(); 44 | bot.onMessageReceived((event, message, response) -> response.send(message)); // echos everything back 45 | bot.onConversationStarted(event -> Futures.immediateFuture(Optional.of( // send 'Hi UserName' when conversation is started 46 | new TextMessage("Hi " + event.getUser().getName())))); 47 | } 48 | 49 | public static void main(final String[] args) throws InterruptedException, ExecutionException, IOException { 50 | new NanoHttpdSample(); 51 | } 52 | 53 | @Override 54 | public Response serve(final IHTTPSession session) { 55 | try { 56 | final String json = parsePostData(session); 57 | final String serverSideSignature = session.getHeaders().get("x-viber-content-signature"); 58 | Preconditions.checkState(signatureValidator.isSignatureValid(serverSideSignature, json), "invalid signature"); 59 | 60 | final Request request = Request.fromJsonString(json); 61 | final InputStream inputStream = Uninterruptibles.getUninterruptibly(bot.incoming(request)); 62 | 63 | return newChunkedResponse(Response.Status.OK, MediaType.JSON_UTF_8.toString(), inputStream); 64 | } catch (final ExecutionException e) { 65 | e.printStackTrace(); 66 | return newFixedLengthResponse("Error, sorry"); 67 | } 68 | } 69 | 70 | @Nullable 71 | private String parsePostData(final IHTTPSession session) { 72 | final Map body = new HashMap<>(); 73 | try { 74 | session.parseBody(body); 75 | } catch (IOException | ResponseException e) { 76 | e.printStackTrace(); 77 | } 78 | return body.get("postData"); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /nano-httpd-sample/src/main/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | org.slf4j.simpleLogger.defaultLogLevel=debug -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = projectName -------------------------------------------------------------------------------- /spring-boot-sample/README.md: -------------------------------------------------------------------------------- 1 | # Viber Bot API - Spring Boot 2 | ## Documentation 3 | For full documentation on this SDK please [click here](https://github.com/Viber/viber-bot-java/blob/master/README.md) 4 | 5 | ## Using this sample 6 | * Building the project: 7 | * For IntelliJ: `./gradlew idea` 8 | * For Eclipse: `./gradlew eclipse` 9 | * Please edit [`application.yml`](src/main/resources/application.yml) with your SSL certificate and [auth token](https://developers.viber.com/docs/faq/#authentication-tokens). 10 | * Run [SpringEchoBot.main()](src/main/java/com/viber/bot/sample/SpringEchoBot.java). 11 | 12 | -------------------------------------------------------------------------------- /spring-boot-sample/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.viber' 2 | version '1.0-SNAPSHOT' 3 | 4 | apply plugin: 'java' 5 | apply plugin: 'idea' 6 | apply plugin: 'eclipse' 7 | 8 | sourceCompatibility = 1.8 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | compile group: 'com.viber', name: 'viber-bot', version: '1.0.9' 16 | compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.4.2.RELEASE' 17 | compile group: 'javax.inject', name: 'javax.inject', version: '1' 18 | } 19 | -------------------------------------------------------------------------------- /spring-boot-sample/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Viber/viber-bot-java/65be8a3e6210ade86157146c68fe78dfe1bc8d78/spring-boot-sample/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /spring-boot-sample/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 05 08:00:49 IST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip 7 | -------------------------------------------------------------------------------- /spring-boot-sample/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /spring-boot-sample/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'spring-boot-sample' 2 | 3 | -------------------------------------------------------------------------------- /spring-boot-sample/src/main/java/com/viber/bot/sample/SpringEchoBot.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.sample; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.base.Preconditions; 5 | import com.google.common.io.CharStreams; 6 | import com.google.common.util.concurrent.Futures; 7 | import com.viber.bot.Request; 8 | import com.viber.bot.ViberSignatureValidator; 9 | import com.viber.bot.api.ViberBot; 10 | import com.viber.bot.message.TextMessage; 11 | import com.viber.bot.profile.BotProfile; 12 | import org.springframework.beans.factory.annotation.Value; 13 | import org.springframework.boot.SpringApplication; 14 | import org.springframework.boot.autoconfigure.SpringBootApplication; 15 | import org.springframework.boot.context.event.ApplicationReadyEvent; 16 | import org.springframework.context.ApplicationListener; 17 | import org.springframework.context.annotation.Bean; 18 | import org.springframework.context.annotation.Configuration; 19 | import org.springframework.web.bind.annotation.PostMapping; 20 | import org.springframework.web.bind.annotation.RequestBody; 21 | import org.springframework.web.bind.annotation.RequestHeader; 22 | import org.springframework.web.bind.annotation.RestController; 23 | 24 | import javax.annotation.Nullable; 25 | import javax.inject.Inject; 26 | import java.io.IOException; 27 | import java.io.InputStream; 28 | import java.io.InputStreamReader; 29 | import java.util.Optional; 30 | import java.util.concurrent.ExecutionException; 31 | 32 | @RestController 33 | @SpringBootApplication 34 | public class SpringEchoBot implements ApplicationListener { 35 | 36 | @Inject 37 | private ViberBot bot; 38 | 39 | @Inject 40 | private ViberSignatureValidator signatureValidator; 41 | 42 | @Value("${application.viber-bot.webhook-url}") 43 | private String webhookUrl; 44 | 45 | public static void main(String[] args) { 46 | SpringApplication.run(SpringEchoBot.class, args); 47 | } 48 | 49 | @Override 50 | public void onApplicationEvent(ApplicationReadyEvent appReadyEvent) { 51 | try { 52 | bot.setWebhook(webhookUrl).get(); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | } 56 | 57 | bot.onMessageReceived((event, message, response) -> response.send(message)); // echos everything back 58 | bot.onConversationStarted(event -> Futures.immediateFuture(Optional.of( // send 'Hi UserName' when conversation is started 59 | new TextMessage("Hi " + event.getUser().getName())))); 60 | } 61 | 62 | @PostMapping(value = "/", produces = "application/json") 63 | public String incoming(@RequestBody String json, 64 | @RequestHeader("X-Viber-Content-Signature") String serverSideSignature) 65 | throws ExecutionException, InterruptedException, IOException { 66 | Preconditions.checkState(signatureValidator.isSignatureValid(serverSideSignature, json), "invalid signature"); 67 | @Nullable InputStream response = bot.incoming(Request.fromJsonString(json)).get(); 68 | return response != null ? CharStreams.toString(new InputStreamReader(response, Charsets.UTF_16)) : null; 69 | } 70 | 71 | @Configuration 72 | public class BotConfiguration { 73 | 74 | @Value("${application.viber-bot.auth-token}") 75 | private String authToken; 76 | 77 | @Value("${application.viber-bot.name}") 78 | private String name; 79 | 80 | @Nullable 81 | @Value("${application.viber-bot.avatar:@null}") 82 | private String avatar; 83 | 84 | @Bean 85 | ViberBot viberBot() { 86 | return new ViberBot(new BotProfile(name, avatar), authToken); 87 | } 88 | 89 | @Bean 90 | ViberSignatureValidator signatureValidator() { 91 | return new ViberSignatureValidator(authToken); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /spring-boot-sample/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | ssl: 4 | enabled: true 5 | key-alias: test 6 | key-store: "path/to/cert.jks" 7 | key-store-password: "password" 8 | 9 | application.viber-bot: 10 | auth-token: "YOUR_VIBER_AUTH_TOKEN" 11 | webhook-url: "https://YOUR_WEBHOOK_URL:8080" 12 | name: "EchoBot" 13 | avatar: "http://example.com/avatar.jpg" 14 | 15 | logging.level.com.viber: DEBUG -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/Preconditions.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot; 2 | 3 | import com.google.common.base.Strings; 4 | 5 | import javax.annotation.Nonnull; 6 | import javax.annotation.Nullable; 7 | 8 | import static com.google.common.base.Preconditions.checkNotNull; 9 | 10 | public class Preconditions { 11 | public static String checkNotEmpty(final @Nonnull String str) { 12 | return checkNotEmpty(str, null); 13 | } 14 | 15 | public static String checkNotEmpty(final @Nonnull String str, final @Nullable String errorMessage) { 16 | return checkNotNull(Strings.emptyToNull(str), errorMessage); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/Request.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.base.Charsets; 5 | import com.google.common.base.MoreObjects; 6 | import com.google.common.base.Strings; 7 | import com.google.common.io.ByteStreams; 8 | import com.google.common.io.Closeables; 9 | import com.viber.bot.event.incoming.IncomingEvent; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import javax.annotation.Nonnull; 14 | import javax.annotation.Nullable; 15 | import javax.annotation.WillClose; 16 | import javax.annotation.WillNotClose; 17 | import javax.annotation.concurrent.ThreadSafe; 18 | import java.io.ByteArrayInputStream; 19 | import java.io.Closeable; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.util.concurrent.atomic.AtomicBoolean; 23 | import java.util.concurrent.atomic.AtomicReference; 24 | 25 | import static com.google.common.base.Preconditions.checkNotNull; 26 | 27 | @ThreadSafe 28 | public class Request implements Closeable { 29 | 30 | private static final Logger logger = LoggerFactory.getLogger(Request.class); 31 | private static final ObjectMapper objectMapper = new ObjectMapper(); 32 | 33 | private static final String EMPTY_JSON_OBJECT = "{}"; 34 | 35 | private final AtomicBoolean isClosed = new AtomicBoolean(false); 36 | private final AtomicReference responseInputStream = new AtomicReference<>(); 37 | private final IncomingEvent event; 38 | 39 | private Request(final String json) { 40 | logger.debug("Reading json request: {}", json); 41 | event = readJson(json, IncomingEvent.class); 42 | } 43 | 44 | /** 45 | * Creates a Request object from an InputStream. 46 | * Be noted that the InputStream will be closed by this method. 47 | * 48 | * @param inputStream the InputStream 49 | * @return Request 50 | */ 51 | public static Request fromInputStream(final @WillClose @Nonnull InputStream inputStream) { 52 | final String json = new String(readInputStream(inputStream), Charsets.UTF_8); 53 | Closeables.closeQuietly(inputStream); 54 | return Request.fromJsonString(json); 55 | } 56 | 57 | /** 58 | * Creates a Request object from a json string literal. 59 | * 60 | * @param json string 61 | * @return Request 62 | */ 63 | public static Request fromJsonString(final @Nullable String json) { 64 | return new Request(MoreObjects.firstNonNull(Strings.nullToEmpty(json), EMPTY_JSON_OBJECT)); 65 | } 66 | 67 | private static byte[] readInputStream(final @Nonnull InputStream inputStream) { 68 | final byte[] data; 69 | try { 70 | data = ByteStreams.toByteArray(inputStream); 71 | } catch (final IOException e) { 72 | logger.error("Could not read input stream", e); 73 | throw new RuntimeException(e); 74 | } 75 | return data; 76 | } 77 | 78 | private static T readJson(final String json, final Class clazz) { 79 | final T event; 80 | try { 81 | event = objectMapper.readValue(json, clazz); 82 | } catch (final Exception e) { 83 | logger.error("Could not read incoming event", e); 84 | throw new RuntimeException(e); 85 | } 86 | return event; 87 | } 88 | 89 | public IncomingEvent getEvent() { 90 | return event; 91 | } 92 | 93 | public boolean isClosed() { 94 | return isClosed.get(); 95 | } 96 | 97 | public void setResponse(final String response) { 98 | setResponse(new ByteArrayInputStream(response.getBytes(Charsets.UTF_16))); 99 | } 100 | 101 | public void setResponse(final @Nonnull @WillNotClose InputStream inputStream) { 102 | responseInputStream.set(checkNotNull(inputStream)); 103 | } 104 | 105 | @Nullable 106 | public InputStream getResponseInputStream() { 107 | return responseInputStream.get(); 108 | } 109 | 110 | @Override 111 | public void close() throws IOException { 112 | synchronized (this) { 113 | if (!isClosed.compareAndSet(false, true)) return; 114 | try { 115 | notify(); 116 | } catch (final IllegalMonitorStateException e) { 117 | logger.error("Could not notify object", e); 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/Response.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot; 2 | 3 | import com.google.common.util.concurrent.ListenableFuture; 4 | import com.viber.bot.api.ApiException; 5 | import com.viber.bot.api.ViberBot; 6 | import com.viber.bot.message.Message; 7 | import com.viber.bot.message.TextMessage; 8 | import com.viber.bot.profile.UserProfile; 9 | 10 | import javax.annotation.Nonnull; 11 | import javax.annotation.concurrent.Immutable; 12 | import java.util.Collection; 13 | 14 | @Immutable 15 | public class Response { 16 | 17 | private final UserProfile to; 18 | private final ViberBot bot; 19 | 20 | public Response(final UserProfile to, final ViberBot bot) { 21 | this.to = to; 22 | this.bot = bot; 23 | } 24 | 25 | /** 26 | * Shorthand for {@link ViberBot#sendMessage(UserProfile, Collection)} 27 | * Where the {@link UserProfile} is already set. 28 | * 29 | * @param messages collection of messages to be sent 30 | * @return future which contains a collection of message tokens as strings, that may throw {@link ApiException}. 31 | * @see ViberBot#sendMessage(UserProfile, Collection) 32 | */ 33 | public ListenableFuture> send(final @Nonnull Collection messages) { 34 | return bot.sendMessage(to, messages); 35 | } 36 | 37 | /** 38 | * Shorthand for {@link ViberBot#sendMessage(UserProfile, Message...)} 39 | * Where the {@link UserProfile} is already set. 40 | * 41 | * @param messages collection of messages to be sent 42 | * @return future which contains a collection of message tokens as strings, that may throw {@link ApiException}. 43 | * @see ViberBot#sendMessage(UserProfile, Message...) 44 | */ 45 | public ListenableFuture> send(final @Nonnull Message... messages) { 46 | return bot.sendMessage(to, messages); 47 | } 48 | 49 | /** 50 | * Shorthand for {@link ViberBot#sendMessage(UserProfile, Message...)} 51 | * The method will create a {@link TextMessage} for you with the String provided to it. 52 | * 53 | * @param textMessage string to be as text for the message 54 | * @return future which contains a collection of message tokens as strings, that may throw {@link ApiException}. 55 | * @see ViberBot#sendMessage(UserProfile, Message...) 56 | */ 57 | public ListenableFuture> send(final @Nonnull String textMessage) { 58 | return bot.sendMessage(to, new TextMessage(textMessage)); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/ViberEnvironmentConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot; 2 | 3 | import com.google.common.base.Strings; 4 | import com.google.common.util.concurrent.ListeningExecutorService; 5 | import com.google.common.util.concurrent.MoreExecutors; 6 | 7 | import javax.annotation.Nullable; 8 | import java.util.concurrent.Executors; 9 | import java.util.concurrent.atomic.AtomicReference; 10 | import java.util.function.Function; 11 | 12 | public class ViberEnvironmentConfiguration { 13 | 14 | private static final String CONFIG_PREFIX = "com.viber.bot"; 15 | private static final String CONFIG_EXECUTOR_SERVICE_STRATEGY = "executor.strategy"; 16 | private static final String CONFIG_NUMBER_OF_THREADS = "executor.threads"; 17 | 18 | private static final AtomicReference executorService = new AtomicReference<>(null); 19 | 20 | public static ListeningExecutorService getExecutorService() { 21 | if (getExecutorServiceStrategy() == ExecutorServiceStrategy.DIRECT) { 22 | return MoreExecutors.newDirectExecutorService(); 23 | } else { 24 | executorService.compareAndSet(null, MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(getNumberOfThreads()))); 25 | return executorService.get(); 26 | } 27 | } 28 | 29 | private static int getNumberOfThreads() { 30 | return getPropertyOrDefault(CONFIG_NUMBER_OF_THREADS, Integer::valueOf, Runtime.getRuntime().availableProcessors()); 31 | } 32 | 33 | private static ExecutorServiceStrategy getExecutorServiceStrategy() { 34 | return getPropertyOrDefault(CONFIG_EXECUTOR_SERVICE_STRATEGY, ExecutorServiceStrategy::valueOf, ExecutorServiceStrategy.DIRECT); 35 | } 36 | 37 | private static T getPropertyOrDefault(final String suffix, final Function compose, final @Nullable T def) { 38 | final String configProperty = String.format("%s.%s", CONFIG_PREFIX, suffix); 39 | final String configValue = System.getProperty(configProperty); 40 | return Strings.isNullOrEmpty(configValue) ? def : compose.apply(configValue.toUpperCase()); 41 | } 42 | 43 | private enum ExecutorServiceStrategy { 44 | DIRECT, THREAD 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/ViberSignatureValidator.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.hash.Hashing; 5 | import com.google.common.io.BaseEncoding; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import javax.annotation.Nonnull; 10 | 11 | import static com.viber.bot.Preconditions.checkNotEmpty; 12 | 13 | public class ViberSignatureValidator { 14 | 15 | private static final Logger logger = LoggerFactory.getLogger(ViberSignatureValidator.class); 16 | private final String secret; 17 | 18 | public ViberSignatureValidator(final @Nonnull String secret) { 19 | this.secret = checkNotEmpty(secret); 20 | } 21 | 22 | public boolean isSignatureValid(final @Nonnull String signature, final @Nonnull String data) { 23 | final String calculatedHash = encode(secret, data); 24 | logger.debug("Validating signature '{}' == '{}'", signature, calculatedHash); 25 | return calculatedHash.equals(signature); 26 | } 27 | 28 | private String encode(final @Nonnull String key, final @Nonnull String data) { 29 | final byte[] bytes = Hashing.hmacSha256(key.getBytes()).hashString(data, Charsets.UTF_8).asBytes(); 30 | return BaseEncoding.base16().lowerCase().encode(bytes); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/api/ApiException.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.api; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ExecutionException; 5 | 6 | public class ApiException extends ExecutionException { 7 | private static final String STATUS_MESSAGE = "status_message"; 8 | 9 | public ApiException(final Map responseMap) { 10 | super(responseMap.get(STATUS_MESSAGE).toString()); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/api/ApiResponse.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.api; 2 | 3 | import com.google.common.base.MoreObjects; 4 | import com.google.common.collect.ForwardingMap; 5 | 6 | import javax.annotation.Nullable; 7 | import java.util.Collections; 8 | import java.util.Map; 9 | 10 | /** 11 | * Simple Map(String, Object) wrapper class. 12 | */ 13 | public class ApiResponse extends ForwardingMap { 14 | private final Map map; 15 | 16 | ApiResponse(final @Nullable Map delegate) { 17 | this.map = Collections.unmodifiableMap(MoreObjects.firstNonNull(delegate, Collections.emptyMap())); 18 | } 19 | 20 | @Override 21 | protected Map delegate() { 22 | return map; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/api/MessageSender.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.api; 2 | 3 | import com.google.common.base.Function; 4 | import com.google.common.collect.Lists; 5 | import com.google.common.util.concurrent.Futures; 6 | import com.google.common.util.concurrent.ListenableFuture; 7 | import com.viber.bot.message.Message; 8 | import com.viber.bot.profile.BotProfile; 9 | import com.viber.bot.profile.UserProfile; 10 | 11 | import javax.annotation.Nonnull; 12 | import java.util.Collection; 13 | import java.util.Iterator; 14 | import java.util.Optional; 15 | 16 | import static com.google.common.base.Preconditions.checkNotNull; 17 | 18 | class MessageSender { 19 | private static final String MESSAGE_TOKEN = "message_token"; 20 | 21 | private final BotProfile botProfile; 22 | private final ViberClient client; 23 | 24 | MessageSender(final @Nonnull BotProfile botProfile, final @Nonnull ViberClient client) { 25 | this.botProfile = checkNotNull(botProfile); 26 | this.client = checkNotNull(client); 27 | } 28 | 29 | ListenableFuture> sendMessage(final @Nonnull UserProfile to, final @Nonnull Collection messages) { 30 | final Collection messageTokens = Lists.newArrayList(); 31 | final Iterator iterator = messages.iterator(); 32 | 33 | while (iterator.hasNext()) { 34 | final Message message = iterator.next(); 35 | 36 | if (!iterator.hasNext()) { 37 | messageTokens.add(Futures.getUnchecked(Futures.transform(sendMessageWithKeyboard(to, message), getMessageToken()))); 38 | } else { 39 | messageTokens.add(Futures.getUnchecked(Futures.transform(sendMessageWithoutKeyboard(to, message), getMessageToken()))); 40 | } 41 | } 42 | return Futures.immediateFuture(messageTokens); 43 | } 44 | 45 | private ListenableFuture sendMessageWithoutKeyboard(final UserProfile to, final Message message) { 46 | return client.sendMessage(botProfile, to, message, Optional.empty(), Optional.of(message.getTrackingData())); 47 | } 48 | 49 | private ListenableFuture sendMessageWithKeyboard(final UserProfile to, final Message message) { 50 | return client.sendMessage(botProfile, to, message, Optional.of(message.getKeyboard()), Optional.of(message.getTrackingData())); 51 | } 52 | 53 | private Function getMessageToken() { 54 | return response -> response.get(MESSAGE_TOKEN).toString(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/api/MessageToMapConverter.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.api; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.base.MoreObjects; 5 | import com.viber.bot.message.Message; 6 | import com.viber.bot.message.MessageKeyboard; 7 | import com.viber.bot.message.TrackingData; 8 | import com.viber.bot.profile.BotProfile; 9 | import com.viber.bot.profile.UserProfile; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import javax.annotation.Nonnull; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.Optional; 17 | 18 | class MessageToMapConverter { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(MessageToMapConverter.class); 21 | private static final ObjectMapper objectMapper = new ObjectMapper(); 22 | private static final String EMPTY_STRING = ""; 23 | 24 | static Map mapMessage(final @Nonnull BotProfile from, final @Nonnull UserProfile to, 25 | final @Nonnull Message message, final @Nonnull Optional optionalKeyboard, 26 | final @Nonnull Optional optionalTrackingData) { 27 | 28 | final Map messageMap = message.getMapRepresentation(); 29 | 30 | messageMap.put("tracking_data", isPresentAndNotEmpty(optionalTrackingData) ? serializeTrackingData(optionalTrackingData.get()) : null); 31 | messageMap.put("keyboard", isPresentAndNotEmpty(optionalKeyboard) ? optionalKeyboard.get() : null); 32 | 33 | return new HashMap() {{ 34 | put("receiver", to.getId()); 35 | put("sender", new HashMap() {{ 36 | put("name", from.getName()); 37 | put("avatar", MoreObjects.firstNonNull(from.getAvatar(), "")); 38 | }}); 39 | putAll(messageMap); 40 | }}; 41 | } 42 | 43 | private static String serializeTrackingData(final TrackingData trackingData) { 44 | try { 45 | return objectMapper.writeValueAsString(trackingData); 46 | } catch (final Exception e) { 47 | logger.warn("Could not serialize tracking data", trackingData); 48 | return EMPTY_STRING; 49 | } 50 | } 51 | 52 | private static boolean isPresentAndNotEmpty(final Optional optional) { 53 | return optional.isPresent() && !optional.get().isEmpty(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/api/RegexMatcherRouter.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.api; 2 | 3 | import com.google.common.collect.ArrayListMultimap; 4 | import com.google.common.collect.Multimap; 5 | import com.viber.bot.event.callback.OnMessageReceived; 6 | 7 | import javax.annotation.Nonnull; 8 | import java.util.List; 9 | import java.util.regex.Pattern; 10 | import java.util.stream.Collectors; 11 | 12 | import static com.google.common.base.Preconditions.checkNotNull; 13 | 14 | class RegexMatcherRouter { 15 | private final Multimap patterns = ArrayListMultimap.create(); 16 | 17 | void newMatcher(final @Nonnull Pattern pattern, final @Nonnull OnMessageReceived onMessageReceived) { 18 | patterns.put(checkNotNull(pattern), checkNotNull(onMessageReceived)); 19 | } 20 | 21 | List tryGetCallback(final String text) { 22 | return patterns.asMap().entrySet().stream() 23 | .filter(entry -> entry.getKey().matcher(text).find()) 24 | .flatMap(entry -> entry.getValue().stream()) 25 | .collect(Collectors.toList()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/api/RequestReceiverImpl.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.api; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.google.common.util.concurrent.Futures; 5 | import com.viber.bot.Request; 6 | import com.viber.bot.Response; 7 | import com.viber.bot.event.EventEmitter; 8 | import com.viber.bot.event.incoming.*; 9 | import com.viber.bot.message.Message; 10 | import com.viber.bot.middleware.RequestReceiver; 11 | import com.viber.bot.profile.UserProfile; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import javax.annotation.Nonnull; 16 | import java.util.Map; 17 | import java.util.Optional; 18 | import java.util.concurrent.Future; 19 | import java.util.function.Consumer; 20 | 21 | import static com.google.common.base.Preconditions.checkNotNull; 22 | 23 | class RequestReceiverImpl implements RequestReceiver { 24 | 25 | private static final Logger logger = LoggerFactory.getLogger(RequestReceiverImpl.class); 26 | private static final ObjectMapper objectMapper = new ObjectMapper(); 27 | 28 | private final EventEmitter eventEmitter; 29 | private final ViberBot bot; 30 | 31 | RequestReceiverImpl(final @Nonnull ViberBot bot, final @Nonnull EventEmitter eventEmitter) { 32 | this.bot = checkNotNull(bot); 33 | this.eventEmitter = checkNotNull(eventEmitter); 34 | } 35 | 36 | @Override 37 | public void acceptRequest(final @Nonnull Request request) { 38 | final IncomingEvent incomingEvent = request.getEvent(); 39 | switch (incomingEvent.getEvent()) { 40 | case ERROR: { 41 | logger.error("Error from Viber servers: {}", ((IncomingErrorEvent) request.getEvent()).getDescription()); 42 | break; 43 | } 44 | 45 | case CONVERSATION_STARTED: { 46 | final IncomingConversationStartedEvent incomingConversationStartedEvent = (IncomingConversationStartedEvent) request.getEvent(); 47 | eventEmitter.>emit(incomingEvent.getEvent(), incomingEvent) 48 | .forEach(setReturnedResponse(request, incomingConversationStartedEvent.getUser())); 49 | break; 50 | } 51 | 52 | case MESSAGE_RECEIVED: { 53 | final IncomingMessageEvent incomingMessageEvent = (IncomingMessageEvent) request.getEvent(); 54 | final Response response = new Response(incomingMessageEvent.getSender(), bot); 55 | eventEmitter.emit(incomingEvent.getEvent(), incomingEvent, incomingMessageEvent.getMessage(), response); 56 | break; 57 | } 58 | 59 | case SUBSCRIBED: { 60 | final IncomingSubscribedEvent incomingSubscribedEvent = (IncomingSubscribedEvent) request.getEvent(); 61 | final Response response = new Response(incomingSubscribedEvent.getUser(), bot); 62 | eventEmitter.emit(incomingEvent.getEvent(), incomingEvent, response); 63 | break; 64 | } 65 | 66 | default: { 67 | eventEmitter.emit(incomingEvent.getEvent(), incomingEvent); 68 | break; 69 | } 70 | } 71 | } 72 | 73 | private Consumer>> setReturnedResponse(final Request request, final UserProfile userProfile) { 74 | return messageFuture -> { 75 | 76 | final Optional message = Futures.getUnchecked(messageFuture); 77 | if (!message.isPresent()) return; 78 | 79 | try { 80 | final String json = objectMapper.writeValueAsString(getMessageMapping(userProfile, message.get())); 81 | request.setResponse(json); 82 | } catch (final Exception e) { 83 | logger.error("Could not send back response to conversation started event", e); 84 | } 85 | }; 86 | } 87 | 88 | private Map getMessageMapping(final UserProfile userProfile, final Message message) { 89 | return MessageToMapConverter.mapMessage( 90 | bot.getBotProfile(), userProfile, message, 91 | Optional.ofNullable(message.getKeyboard()), 92 | Optional.ofNullable(message.getTrackingData())); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/BotEventListener.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event; 2 | 3 | import com.google.common.util.concurrent.Futures; 4 | 5 | import java.util.EventListener; 6 | import java.util.concurrent.Future; 7 | 8 | public interface BotEventListener extends EventListener, EventEmitter.EmittableEvent { 9 | Future nothing = Futures.immediateFuture(null); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/Event.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | public enum Event { 6 | 7 | MESSAGE_SENT, 8 | MESSAGE_RECEIVED("message"), 9 | MESSAGE_DELIVERED("delivered"), 10 | MESSAGE_SEEN("seen"), 11 | 12 | SUBSCRIBED("subscribed"), 13 | UNSUBSCRIBED("unsubscribed"), 14 | 15 | CONVERSATION_STARTED("conversation_started"), 16 | WEBHOOK("webhook"), 17 | 18 | ERROR("failed"); 19 | 20 | private final String serverEventName; 21 | 22 | Event(final @Nullable String serverEventName) { 23 | this.serverEventName = serverEventName; 24 | } 25 | 26 | Event() { 27 | this(null); 28 | } 29 | 30 | @Nullable 31 | public String getServerEventName() { 32 | return serverEventName; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/EventEmitter.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event; 2 | 3 | import com.google.common.collect.ArrayListMultimap; 4 | import com.google.common.collect.Lists; 5 | import com.google.common.collect.Multimap; 6 | 7 | import javax.annotation.Nonnull; 8 | import java.util.Collection; 9 | import java.util.concurrent.Future; 10 | 11 | import static com.google.common.base.Preconditions.checkNotNull; 12 | 13 | public class EventEmitter { 14 | 15 | private final Multimap listeners = ArrayListMultimap.create(); 16 | 17 | public void on(final @Nonnull Event event, final @Nonnull EmittableEvent listener) { 18 | listeners.put(checkNotNull(event), checkNotNull(listener)); 19 | } 20 | 21 | public Collection> emit(final @Nonnull Event event, final Object... args) { 22 | final Collection> futures = Lists.newArrayList(); 23 | listeners.get(event).forEach(listener -> futures.add(listener.emit(args))); 24 | return futures; 25 | } 26 | 27 | public interface EmittableEvent { 28 | Future emit(Object... args); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/callback/OnConversationStarted.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.callback; 2 | 3 | import com.viber.bot.event.BotEventListener; 4 | import com.viber.bot.event.incoming.IncomingConversationStartedEvent; 5 | import com.viber.bot.message.Message; 6 | 7 | import java.util.Optional; 8 | import java.util.concurrent.Future; 9 | 10 | import static com.google.common.base.Preconditions.checkArgument; 11 | 12 | public interface OnConversationStarted extends BotEventListener> { 13 | Future> conversationStarted(IncomingConversationStartedEvent event); 14 | 15 | @Override 16 | default Future> emit(final Object... args) { 17 | checkArgument(args.length == 1); 18 | return conversationStarted((IncomingConversationStartedEvent) args[0]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/callback/OnMessageDelivered.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.callback; 2 | 3 | import com.viber.bot.event.BotEventListener; 4 | import com.viber.bot.event.incoming.IncomingDeliveredEvent; 5 | import com.viber.bot.message.Message; 6 | 7 | import java.util.concurrent.Future; 8 | 9 | import static com.google.common.base.Preconditions.checkArgument; 10 | 11 | public interface OnMessageDelivered extends BotEventListener { 12 | void messageDelivered(IncomingDeliveredEvent event, Message message); 13 | 14 | @Override 15 | default Future emit(final Object... args) { 16 | checkArgument(args.length == 2); 17 | messageDelivered((IncomingDeliveredEvent) args[0], (Message) args[1]); 18 | return nothing; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/callback/OnMessageReceived.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.callback; 2 | 3 | import com.viber.bot.Response; 4 | import com.viber.bot.event.BotEventListener; 5 | import com.viber.bot.event.incoming.IncomingMessageEvent; 6 | import com.viber.bot.message.Message; 7 | 8 | import java.util.concurrent.Future; 9 | 10 | import static com.google.common.base.Preconditions.checkArgument; 11 | 12 | public interface OnMessageReceived extends BotEventListener { 13 | void messageReceived(IncomingMessageEvent event, Message message, Response response); 14 | 15 | @Override 16 | default Future emit(final Object... args) { 17 | checkArgument(args.length == 3); 18 | messageReceived((IncomingMessageEvent) args[0], (Message) args[1], (Response) args[2]); 19 | return nothing; 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/callback/OnMessageSeen.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.callback; 2 | 3 | import com.viber.bot.event.BotEventListener; 4 | import com.viber.bot.event.incoming.IncomingSeenEvent; 5 | 6 | import java.util.concurrent.Future; 7 | 8 | import static com.google.common.base.Preconditions.checkArgument; 9 | 10 | public interface OnMessageSeen extends BotEventListener { 11 | void messageSeen(IncomingSeenEvent event); 12 | 13 | @Override 14 | default Future emit(final Object... args) { 15 | checkArgument(args.length == 1); 16 | messageSeen((IncomingSeenEvent) args[0]); 17 | return nothing; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/callback/OnMessageSent.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.callback; 2 | 3 | import com.viber.bot.event.BotEventListener; 4 | import com.viber.bot.message.Message; 5 | import com.viber.bot.profile.UserProfile; 6 | 7 | import java.util.concurrent.Future; 8 | 9 | import static com.google.common.base.Preconditions.checkArgument; 10 | 11 | public interface OnMessageSent extends BotEventListener { 12 | void messageSent(UserProfile to, Message message); 13 | 14 | @Override 15 | default Future emit(final Object... args) { 16 | checkArgument(args.length == 2); 17 | messageSent((UserProfile) args[0], (Message) args[1]); 18 | return nothing; 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/callback/OnSubscribe.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.callback; 2 | 3 | import com.viber.bot.Response; 4 | import com.viber.bot.event.BotEventListener; 5 | import com.viber.bot.event.incoming.IncomingSubscribedEvent; 6 | 7 | import java.util.concurrent.Future; 8 | 9 | import static com.google.common.base.Preconditions.checkArgument; 10 | 11 | public interface OnSubscribe extends BotEventListener { 12 | void subscribe(IncomingSubscribedEvent event, Response response); 13 | 14 | @Override 15 | default Future emit(final Object... args) { 16 | checkArgument(args.length == 2); 17 | subscribe((IncomingSubscribedEvent) args[0], (Response) args[1]); 18 | return nothing; 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/callback/OnUnsubscribe.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.callback; 2 | 3 | import com.viber.bot.event.BotEventListener; 4 | import com.viber.bot.event.incoming.IncomingUnsubscribeEvent; 5 | 6 | import java.util.concurrent.Future; 7 | 8 | import static com.google.common.base.Preconditions.checkArgument; 9 | 10 | public interface OnUnsubscribe extends BotEventListener { 11 | void unsubscribe(IncomingUnsubscribeEvent event); 12 | 13 | @Override 14 | default Future emit(final Object... args) { 15 | checkArgument(args.length == 1); 16 | unsubscribe((IncomingUnsubscribeEvent) args[0]); 17 | return nothing; 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/incoming/IncomingConversationStartedEvent.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.incoming; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.google.common.base.Strings; 6 | import com.viber.bot.event.Event; 7 | import com.viber.bot.profile.UserProfile; 8 | 9 | import javax.annotation.Nonnull; 10 | import javax.annotation.Nullable; 11 | import javax.annotation.concurrent.Immutable; 12 | 13 | import static com.google.common.base.Preconditions.checkNotNull; 14 | import static com.viber.bot.Preconditions.checkNotEmpty; 15 | 16 | @Immutable 17 | public class IncomingConversationStartedEvent extends IncomingEvent { 18 | 19 | private final UserProfile user; 20 | private final String type; 21 | private final long token; 22 | private final boolean subscribed; 23 | 24 | @Nullable 25 | private final String context; 26 | 27 | @JsonCreator 28 | IncomingConversationStartedEvent(final @JsonProperty("type") @Nonnull String type, 29 | final @JsonProperty("user") @Nonnull UserProfile user, 30 | final @JsonProperty("context") String context, 31 | final @JsonProperty("message_token") long token, 32 | final @JsonProperty("timestamp") long timestamp, 33 | final @JsonProperty("subscribed") boolean subscribed) { 34 | super(Event.CONVERSATION_STARTED, timestamp); 35 | this.user = checkNotNull(user); 36 | this.type = checkNotEmpty(type); 37 | this.context = Strings.emptyToNull(context); 38 | this.token = token; 39 | this.subscribed = subscribed; 40 | } 41 | 42 | public String getType() { 43 | return type; 44 | } 45 | 46 | public long getToken() { 47 | return token; 48 | } 49 | 50 | public UserProfile getUser() { 51 | return user; 52 | } 53 | 54 | public boolean isSubscribed() { 55 | return subscribed; 56 | } 57 | 58 | @Nullable 59 | public String getContext() { 60 | return context; 61 | } 62 | 63 | @Override 64 | public boolean equals(Object o) { 65 | if (this == o) return true; 66 | if (o == null || getClass() != o.getClass()) return false; 67 | if (!super.equals(o)) return false; 68 | 69 | IncomingConversationStartedEvent that = (IncomingConversationStartedEvent) o; 70 | 71 | if (token != that.token) return false; 72 | if (subscribed != that.subscribed) return false; 73 | if (user != null ? !user.equals(that.user) : that.user != null) return false; 74 | if (type != null ? !type.equals(that.type) : that.type != null) return false; 75 | return context != null ? context.equals(that.context) : that.context == null; 76 | 77 | } 78 | 79 | @Override 80 | public int hashCode() { 81 | int result = super.hashCode(); 82 | result = 31 * result + (user != null ? user.hashCode() : 0); 83 | result = 31 * result + (type != null ? type.hashCode() : 0); 84 | result = 31 * result + (int) (token ^ (token >>> 32)); 85 | result = 31 * result + (subscribed ? 1 : 0); 86 | result = 31 * result + (context != null ? context.hashCode() : 0); 87 | return result; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/incoming/IncomingDeliveredEvent.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.incoming; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.viber.bot.event.Event; 6 | 7 | import javax.annotation.Nonnull; 8 | import javax.annotation.concurrent.Immutable; 9 | 10 | import static com.viber.bot.Preconditions.checkNotEmpty; 11 | 12 | @Immutable 13 | public class IncomingDeliveredEvent extends IncomingEvent { 14 | 15 | private final String userId; 16 | private final Long token; 17 | 18 | @JsonCreator 19 | IncomingDeliveredEvent(final @JsonProperty("user_id") @Nonnull String userId, 20 | final @JsonProperty("message_token") long token, 21 | final @JsonProperty("timestamp") long timestamp) { 22 | super(Event.MESSAGE_DELIVERED, timestamp); 23 | this.userId = checkNotEmpty(userId); 24 | this.token = token; 25 | } 26 | 27 | public Long getToken() { 28 | return token; 29 | } 30 | 31 | public String getUserId() { 32 | return userId; 33 | } 34 | 35 | @Override 36 | public boolean equals(final Object o) { 37 | if (this == o) return true; 38 | if (o == null || getClass() != o.getClass()) return false; 39 | if (!super.equals(o)) return false; 40 | 41 | final IncomingDeliveredEvent that = (IncomingDeliveredEvent) o; 42 | 43 | if (userId != null ? !userId.equals(that.userId) : that.userId != null) return false; 44 | return token != null ? token.equals(that.token) : that.token == null; 45 | } 46 | 47 | @Override 48 | public int hashCode() { 49 | int result = super.hashCode(); 50 | result = 31 * result + (userId != null ? userId.hashCode() : 0); 51 | result = 31 * result + (token != null ? token.hashCode() : 0); 52 | return result; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/incoming/IncomingErrorEvent.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.incoming; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.viber.bot.event.Event; 6 | 7 | import javax.annotation.Nonnull; 8 | import javax.annotation.concurrent.Immutable; 9 | 10 | import static com.viber.bot.Preconditions.checkNotEmpty; 11 | 12 | @Immutable 13 | public class IncomingErrorEvent extends IncomingEvent { 14 | 15 | private final String userId; 16 | private final String description; 17 | private final Long token; 18 | 19 | @JsonCreator 20 | IncomingErrorEvent(final @JsonProperty("user_id") @Nonnull String userId, 21 | final @JsonProperty("desc") @Nonnull String description, 22 | final @JsonProperty("message_token") long token, 23 | final @JsonProperty("timestamp") long timestamp) { 24 | super(Event.ERROR, timestamp); 25 | this.userId = checkNotEmpty(userId); 26 | this.description = checkNotEmpty(description); 27 | this.token = token; 28 | } 29 | 30 | public String getDescription() { 31 | return description; 32 | } 33 | 34 | public Long getToken() { 35 | return token; 36 | } 37 | 38 | public String getUserId() { 39 | return userId; 40 | } 41 | 42 | @Override 43 | public boolean equals(final Object o) { 44 | if (this == o) return true; 45 | if (o == null || getClass() != o.getClass()) return false; 46 | if (!super.equals(o)) return false; 47 | 48 | final IncomingErrorEvent that = (IncomingErrorEvent) o; 49 | 50 | if (userId != null ? !userId.equals(that.userId) : that.userId != null) return false; 51 | if (description != null ? !description.equals(that.description) : that.description != null) return false; 52 | return token != null ? token.equals(that.token) : that.token == null; 53 | } 54 | 55 | @Override 56 | public int hashCode() { 57 | int result = super.hashCode(); 58 | result = 31 * result + (userId != null ? userId.hashCode() : 0); 59 | result = 31 * result + (description != null ? description.hashCode() : 0); 60 | result = 31 * result + (token != null ? token.hashCode() : 0); 61 | return result; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/incoming/IncomingEvent.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.incoming; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonSubTypes; 5 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 6 | import com.viber.bot.event.Event; 7 | 8 | import javax.annotation.Nonnull; 9 | import javax.annotation.concurrent.Immutable; 10 | 11 | import static com.google.common.base.Preconditions.checkNotNull; 12 | 13 | @Immutable 14 | @JsonIgnoreProperties(ignoreUnknown = true) 15 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "event") 16 | @JsonSubTypes({ 17 | @JsonSubTypes.Type(value = IncomingMessageEvent.class, name = "message"), 18 | @JsonSubTypes.Type(value = IncomingWebhookEvent.class, name = "webhook"), 19 | @JsonSubTypes.Type(value = IncomingSeenEvent.class, name = "seen"), 20 | @JsonSubTypes.Type(value = IncomingDeliveredEvent.class, name = "delivered"), 21 | @JsonSubTypes.Type(value = IncomingSubscribedEvent.class, name = "subscribed"), 22 | @JsonSubTypes.Type(value = IncomingUnsubscribeEvent.class, name = "unsubscribed"), 23 | @JsonSubTypes.Type(value = IncomingConversationStartedEvent.class, name = "conversation_started"), 24 | @JsonSubTypes.Type(value = IncomingErrorEvent.class, name = "failed") 25 | }) 26 | public class IncomingEvent { 27 | 28 | private final Event event; 29 | private final long timestamp; 30 | 31 | IncomingEvent(final @Nonnull Event event, final long timestamp) { 32 | this.event = checkNotNull(event); 33 | this.timestamp = timestamp; 34 | } 35 | 36 | public Event getEvent() { 37 | return event; 38 | } 39 | 40 | public long getTimestamp() { 41 | return timestamp; 42 | } 43 | 44 | @Override 45 | public boolean equals(final Object o) { 46 | if (this == o) return true; 47 | if (o == null || getClass() != o.getClass()) return false; 48 | 49 | final IncomingEvent that = (IncomingEvent) o; 50 | 51 | if (timestamp != that.timestamp) return false; 52 | 53 | return true; 54 | } 55 | 56 | @Override 57 | public int hashCode() { 58 | return (int) (timestamp ^ (timestamp >>> 32)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/incoming/IncomingMessageEvent.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.incoming; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.viber.bot.event.Event; 6 | import com.viber.bot.message.Message; 7 | import com.viber.bot.profile.UserProfile; 8 | 9 | import javax.annotation.Nonnull; 10 | import javax.annotation.concurrent.Immutable; 11 | 12 | import static com.google.common.base.Preconditions.checkNotNull; 13 | 14 | @Immutable 15 | public class IncomingMessageEvent extends IncomingEvent { 16 | 17 | private final Long token; 18 | private final Message message; 19 | private final UserProfile sender; 20 | 21 | @JsonCreator 22 | IncomingMessageEvent(final @JsonProperty("message") @Nonnull Message message, 23 | final @JsonProperty("sender") @Nonnull UserProfile sender, 24 | final @JsonProperty("message_token") long token, 25 | final @JsonProperty("timestamp") long timestamp) { 26 | super(Event.MESSAGE_RECEIVED, timestamp); 27 | this.message = checkNotNull(message); 28 | this.sender = checkNotNull(sender); 29 | this.token = token; 30 | } 31 | 32 | public Long getToken() { 33 | return token; 34 | } 35 | 36 | public Message getMessage() { 37 | return message; 38 | } 39 | 40 | public UserProfile getSender() { 41 | return sender; 42 | } 43 | 44 | @Override 45 | public boolean equals(final Object o) { 46 | if (this == o) return true; 47 | if (o == null || getClass() != o.getClass()) return false; 48 | if (!super.equals(o)) return false; 49 | 50 | final IncomingMessageEvent that = (IncomingMessageEvent) o; 51 | 52 | if (token != null ? !token.equals(that.token) : that.token != null) return false; 53 | if (message != null ? !message.equals(that.message) : that.message != null) return false; 54 | if (sender != null ? !sender.equals(that.sender) : that.sender != null) return false; 55 | 56 | return true; 57 | } 58 | 59 | @Override 60 | public int hashCode() { 61 | int result = super.hashCode(); 62 | result = 31 * result + (token != null ? token.hashCode() : 0); 63 | result = 31 * result + (message != null ? message.hashCode() : 0); 64 | result = 31 * result + (sender != null ? sender.hashCode() : 0); 65 | return result; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/incoming/IncomingSeenEvent.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.incoming; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.viber.bot.event.Event; 6 | 7 | import javax.annotation.Nonnull; 8 | import javax.annotation.concurrent.Immutable; 9 | 10 | import static com.viber.bot.Preconditions.checkNotEmpty; 11 | 12 | @Immutable 13 | public class IncomingSeenEvent extends IncomingEvent { 14 | 15 | private final String userId; 16 | private final Long token; 17 | 18 | @JsonCreator 19 | IncomingSeenEvent(final @JsonProperty("user_id") @Nonnull String userId, 20 | final @JsonProperty("message_token") long token, 21 | final @JsonProperty("timestamp") long timestamp) { 22 | super(Event.MESSAGE_SEEN, timestamp); 23 | this.userId = checkNotEmpty(userId); 24 | this.token = token; 25 | } 26 | 27 | public Long getToken() { 28 | return token; 29 | } 30 | 31 | public String getUserId() { 32 | return userId; 33 | } 34 | 35 | @Override 36 | public boolean equals(final Object o) { 37 | if (this == o) return true; 38 | if (o == null || getClass() != o.getClass()) return false; 39 | if (!super.equals(o)) return false; 40 | 41 | final IncomingSeenEvent that = (IncomingSeenEvent) o; 42 | 43 | if (userId != null ? !userId.equals(that.userId) : that.userId != null) return false; 44 | return token != null ? token.equals(that.token) : that.token == null; 45 | } 46 | 47 | @Override 48 | public int hashCode() { 49 | int result = super.hashCode(); 50 | result = 31 * result + (userId != null ? userId.hashCode() : 0); 51 | result = 31 * result + (token != null ? token.hashCode() : 0); 52 | return result; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/incoming/IncomingSubscribedEvent.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.incoming; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.viber.bot.event.Event; 6 | import com.viber.bot.profile.UserProfile; 7 | 8 | import javax.annotation.Nonnull; 9 | import javax.annotation.concurrent.Immutable; 10 | 11 | @Immutable 12 | public class IncomingSubscribedEvent extends IncomingEvent { 13 | 14 | private final UserProfile user; 15 | 16 | @JsonCreator 17 | IncomingSubscribedEvent(final @JsonProperty("user") @Nonnull UserProfile user, 18 | final @JsonProperty("timestamp") long timestamp) { 19 | super(Event.SUBSCRIBED, timestamp); 20 | this.user = user; 21 | } 22 | 23 | public UserProfile getUser() { 24 | return user; 25 | } 26 | 27 | @Override 28 | public boolean equals(final Object o) { 29 | if (this == o) return true; 30 | if (o == null || getClass() != o.getClass()) return false; 31 | if (!super.equals(o)) return false; 32 | 33 | final IncomingSubscribedEvent that = (IncomingSubscribedEvent) o; 34 | 35 | return user != null ? user.equals(that.user) : that.user == null; 36 | 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | int result = super.hashCode(); 42 | result = 31 * result + (user != null ? user.hashCode() : 0); 43 | return result; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/incoming/IncomingUnsubscribeEvent.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.incoming; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.viber.bot.event.Event; 6 | 7 | import javax.annotation.Nonnull; 8 | import javax.annotation.concurrent.Immutable; 9 | 10 | import static com.viber.bot.Preconditions.checkNotEmpty; 11 | 12 | @Immutable 13 | public class IncomingUnsubscribeEvent extends IncomingEvent { 14 | 15 | private final String userId; 16 | 17 | @JsonCreator 18 | IncomingUnsubscribeEvent(final @JsonProperty("user_id") @Nonnull String userId, 19 | final @JsonProperty("timestamp") long timestamp) { 20 | super(Event.UNSUBSCRIBED, timestamp); 21 | this.userId = checkNotEmpty(userId); 22 | } 23 | 24 | public String getUserId() { 25 | return userId; 26 | } 27 | 28 | @Override 29 | public boolean equals(final Object o) { 30 | if (this == o) return true; 31 | if (o == null || getClass() != o.getClass()) return false; 32 | if (!super.equals(o)) return false; 33 | 34 | final IncomingUnsubscribeEvent that = (IncomingUnsubscribeEvent) o; 35 | 36 | return userId != null ? userId.equals(that.userId) : that.userId == null; 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | int result = super.hashCode(); 42 | result = 31 * result + (userId != null ? userId.hashCode() : 0); 43 | return result; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/event/incoming/IncomingWebhookEvent.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event.incoming; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.viber.bot.event.Event; 6 | 7 | import javax.annotation.concurrent.Immutable; 8 | 9 | @Immutable 10 | public class IncomingWebhookEvent extends IncomingEvent { 11 | 12 | private final Long token; 13 | 14 | @JsonCreator 15 | IncomingWebhookEvent(final @JsonProperty("message_token") long token, 16 | final @JsonProperty("timestamp") long timestamp) { 17 | super(Event.WEBHOOK, timestamp); 18 | this.token = token; 19 | } 20 | 21 | public Long getToken() { 22 | return token; 23 | } 24 | 25 | @Override 26 | public boolean equals(final Object o) { 27 | if (this == o) return true; 28 | if (o == null || getClass() != o.getClass()) return false; 29 | if (!super.equals(o)) return false; 30 | 31 | final IncomingWebhookEvent that = (IncomingWebhookEvent) o; 32 | 33 | return token != null ? token.equals(that.token) : that.token == null; 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | int result = super.hashCode(); 39 | result = 31 * result + (token != null ? token.hashCode() : 0); 40 | return result; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/Contact.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.google.common.base.Strings; 6 | 7 | import javax.annotation.Nonnull; 8 | import javax.annotation.Nullable; 9 | import javax.annotation.concurrent.Immutable; 10 | 11 | import static com.viber.bot.Preconditions.checkNotEmpty; 12 | 13 | @Immutable 14 | public class Contact { 15 | 16 | private final String name; 17 | private final String phoneNumber; 18 | 19 | @Nullable 20 | private final String avatar; 21 | 22 | @JsonCreator 23 | public Contact(final @JsonProperty("name") @Nonnull String contactName, 24 | final @JsonProperty("phone_number") @Nonnull String contactPhoneNumber, 25 | final @JsonProperty("avatar") @Nullable String avatar) { 26 | this.name = checkNotEmpty(contactName); 27 | this.phoneNumber = checkNotEmpty(contactPhoneNumber); 28 | this.avatar = Strings.emptyToNull(avatar); 29 | } 30 | 31 | public String getName() { 32 | return name; 33 | } 34 | 35 | public String getPhoneNumber() { 36 | return phoneNumber; 37 | } 38 | 39 | @Nullable 40 | public String getAvatar() { 41 | return avatar; 42 | } 43 | 44 | @Override 45 | public boolean equals(Object o) { 46 | if (this == o) return true; 47 | if (o == null || getClass() != o.getClass()) return false; 48 | 49 | Contact contact = (Contact) o; 50 | 51 | if (name != null ? !name.equals(contact.name) : contact.name != null) return false; 52 | if (phoneNumber != null ? !phoneNumber.equals(contact.phoneNumber) : contact.phoneNumber != null) return false; 53 | return avatar != null ? avatar.equals(contact.avatar) : contact.avatar == null; 54 | } 55 | 56 | @Override 57 | public int hashCode() { 58 | int result = name != null ? name.hashCode() : 0; 59 | result = 31 * result + (phoneNumber != null ? phoneNumber.hashCode() : 0); 60 | result = 31 * result + (avatar != null ? avatar.hashCode() : 0); 61 | return result; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/ContactMessage.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | import javax.annotation.Nonnull; 8 | import javax.annotation.Nullable; 9 | import javax.annotation.concurrent.Immutable; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | import static com.google.common.base.Preconditions.checkNotNull; 14 | 15 | @Immutable 16 | public class ContactMessage extends Message { 17 | 18 | private final Contact contact; 19 | 20 | @JsonCreator 21 | public ContactMessage(final @JsonProperty("contact") @Nonnull Contact contact, 22 | final @JsonProperty("keyboard") @Nullable MessageKeyboard keyboard, 23 | final @JsonProperty("tracking_data") @Nullable TrackingData trackingData) { 24 | super("contact", keyboard, trackingData); 25 | this.contact = checkNotNull(contact); 26 | } 27 | 28 | @JsonIgnore 29 | public ContactMessage(final @Nonnull Contact contact) { 30 | this(contact, null, null); 31 | } 32 | 33 | @Override 34 | protected Map getPartialMapRepresentation() { 35 | return new HashMap() {{ 36 | put("contact", new HashMap() {{ 37 | put("name", getContact().getName()); 38 | put("phone_number", getContact().getPhoneNumber()); 39 | put("avatar", getContact().getAvatar()); 40 | }}); 41 | }}; 42 | } 43 | 44 | public Contact getContact() { 45 | return contact; 46 | } 47 | 48 | @Override 49 | public boolean equals(final Object o) { 50 | if (this == o) return true; 51 | if (o == null || getClass() != o.getClass()) return false; 52 | if (!super.equals(o)) return false; 53 | 54 | final ContactMessage that = (ContactMessage) o; 55 | 56 | return contact != null ? contact.equals(that.contact) : that.contact == null; 57 | } 58 | 59 | @Override 60 | public int hashCode() { 61 | int result = super.hashCode(); 62 | result = 31 * result + (contact != null ? contact.hashCode() : 0); 63 | return result; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/FileMessage.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | import javax.annotation.Nonnull; 8 | import javax.annotation.Nullable; 9 | import javax.annotation.concurrent.Immutable; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | import static com.viber.bot.Preconditions.checkNotEmpty; 14 | 15 | @Immutable 16 | public class FileMessage extends Message { 17 | 18 | private final String url; 19 | private final int sizeInBytes; 20 | private final String filename; 21 | 22 | @JsonCreator 23 | public FileMessage(final @JsonProperty("media") @Nonnull String url, 24 | final @JsonProperty("size") int sizeInBytes, 25 | final @JsonProperty("file_name") @Nonnull String filename, 26 | final @JsonProperty("keyboard") @Nullable MessageKeyboard keyboard, 27 | final @JsonProperty("tracking_data") @Nullable TrackingData trackingData) { 28 | super("file", keyboard, trackingData); 29 | this.url = checkNotEmpty(url); 30 | this.sizeInBytes = sizeInBytes; 31 | this.filename = checkNotEmpty(filename); 32 | } 33 | 34 | @JsonIgnore 35 | public FileMessage(final @Nonnull String url, final int sizeInBytes, final @Nonnull String filename) { 36 | this(url, sizeInBytes, filename, null, null); 37 | } 38 | 39 | @Override 40 | protected Map getPartialMapRepresentation() { 41 | return new HashMap() {{ 42 | put("media", getUrl()); 43 | put("size", getSizeInBytes()); 44 | put("file_name", getFilename()); 45 | }}; 46 | } 47 | 48 | public String getUrl() { 49 | return url; 50 | } 51 | 52 | public int getSizeInBytes() { 53 | return sizeInBytes; 54 | } 55 | 56 | public String getFilename() { 57 | return filename; 58 | } 59 | 60 | @Override 61 | public boolean equals(final Object o) { 62 | if (this == o) return true; 63 | if (o == null || getClass() != o.getClass()) return false; 64 | if (!super.equals(o)) return false; 65 | 66 | final FileMessage that = (FileMessage) o; 67 | 68 | if (sizeInBytes != that.sizeInBytes) return false; 69 | if (url != null ? !url.equals(that.url) : that.url != null) return false; 70 | return filename != null ? filename.equals(that.filename) : that.filename == null; 71 | } 72 | 73 | @Override 74 | public int hashCode() { 75 | int result = super.hashCode(); 76 | result = 31 * result + (url != null ? url.hashCode() : 0); 77 | result = 31 * result + sizeInBytes; 78 | result = 31 * result + (filename != null ? filename.hashCode() : 0); 79 | return result; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/Location.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | import javax.annotation.concurrent.Immutable; 7 | 8 | @Immutable 9 | public class Location { 10 | 11 | private final float latitude; 12 | private final float longitude; 13 | 14 | @JsonCreator 15 | public Location(final @JsonProperty("lat") float latitude, 16 | final @JsonProperty("lon") float longitude) { 17 | this.latitude = latitude; 18 | this.longitude = longitude; 19 | } 20 | 21 | public float getLatitude() { 22 | return latitude; 23 | } 24 | 25 | public float getLongitude() { 26 | return longitude; 27 | } 28 | 29 | @Override 30 | public boolean equals(final Object o) { 31 | if (this == o) return true; 32 | if (o == null || getClass() != o.getClass()) return false; 33 | 34 | final Location location = (Location) o; 35 | 36 | if (Float.compare(location.latitude, latitude) != 0) return false; 37 | return Float.compare(location.longitude, longitude) == 0; 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | int result = (latitude != +0.0f ? Float.floatToIntBits(latitude) : 0); 43 | result = 31 * result + (longitude != +0.0f ? Float.floatToIntBits(longitude) : 0); 44 | return result; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/LocationMessage.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | import javax.annotation.Nonnull; 8 | import javax.annotation.Nullable; 9 | import javax.annotation.concurrent.Immutable; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | import static com.google.common.base.Preconditions.checkNotNull; 14 | 15 | @Immutable 16 | public class LocationMessage extends Message { 17 | 18 | private final Location location; 19 | 20 | @JsonCreator 21 | public LocationMessage(final @JsonProperty("location") @Nonnull Location location, 22 | final @JsonProperty("keyboard") @Nullable MessageKeyboard keyboard, 23 | final @JsonProperty("tracking_data") @Nullable TrackingData trackingData) { 24 | super("location", keyboard, trackingData); 25 | this.location = checkNotNull(location); 26 | } 27 | 28 | @JsonIgnore 29 | public LocationMessage(final @Nonnull Location location) { 30 | this(location, null, null); 31 | } 32 | 33 | @Override 34 | protected Map getPartialMapRepresentation() { 35 | return new HashMap() {{ 36 | put("location", new HashMap() {{ 37 | put("lat", getLocation().getLatitude()); 38 | put("lon", getLocation().getLongitude()); 39 | }}); 40 | }}; 41 | } 42 | 43 | public Location getLocation() { 44 | return location; 45 | } 46 | 47 | @Override 48 | public boolean equals(final Object o) { 49 | if (this == o) return true; 50 | if (o == null || getClass() != o.getClass()) return false; 51 | if (!super.equals(o)) return false; 52 | 53 | final LocationMessage that = (LocationMessage) o; 54 | 55 | return location != null ? location.equals(that.location) : that.location == null; 56 | } 57 | 58 | @Override 59 | public int hashCode() { 60 | int result = super.hashCode(); 61 | result = 31 * result + (location != null ? location.hashCode() : 0); 62 | return result; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/Message.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAnyGetter; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonSubTypes; 6 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 7 | import com.google.common.base.MoreObjects; 8 | import com.google.common.base.Strings; 9 | 10 | import javax.annotation.Nonnull; 11 | import javax.annotation.Nullable; 12 | import javax.annotation.concurrent.Immutable; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | import static com.viber.bot.Preconditions.checkNotEmpty; 17 | 18 | @Immutable 19 | @JsonIgnoreProperties(ignoreUnknown = true) 20 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") 21 | @JsonSubTypes({ 22 | @JsonSubTypes.Type(value = RichMediaMessage.class, name = "rich_media"), 23 | @JsonSubTypes.Type(value = TextMessage.class, name = "text"), 24 | @JsonSubTypes.Type(value = ContactMessage.class, name = "contact"), 25 | @JsonSubTypes.Type(value = FileMessage.class, name = "file"), 26 | @JsonSubTypes.Type(value = LocationMessage.class, name = "location"), 27 | @JsonSubTypes.Type(value = PictureMessage.class, name = "picture"), 28 | @JsonSubTypes.Type(value = StickerMessage.class, name = "sticker"), 29 | @JsonSubTypes.Type(value = UrlMessage.class, name = "url"), 30 | @JsonSubTypes.Type(value = VideoMessage.class, name = "video") 31 | }) 32 | public abstract class Message { // todo: should be case classes when moving to scala 33 | 34 | private final String type; 35 | private final MessageKeyboard keyboard; 36 | private final TrackingData trackingData; 37 | 38 | Message(final @Nonnull String type, 39 | final @Nullable MessageKeyboard keyboard, 40 | final @Nullable TrackingData trackingData) { 41 | 42 | this.type = checkNotEmpty(type); 43 | this.keyboard = MoreObjects.firstNonNull(keyboard, new MessageKeyboard()); 44 | this.trackingData = MoreObjects.firstNonNull(trackingData, new TrackingData()); 45 | } 46 | 47 | public String getType() { 48 | return type; 49 | } 50 | 51 | public MessageKeyboard getKeyboard() { 52 | return keyboard; 53 | } 54 | 55 | public TrackingData getTrackingData() { 56 | return trackingData; 57 | } 58 | 59 | @JsonAnyGetter 60 | public Map getMapRepresentation() { 61 | return new HashMap() {{ 62 | put("type", getType()); 63 | putAll(getPartialMapRepresentation()); 64 | }}; 65 | } 66 | 67 | protected abstract Map getPartialMapRepresentation(); 68 | 69 | @Override 70 | public boolean equals(final Object o) { 71 | if (this == o) return true; 72 | if (o == null || getClass() != o.getClass()) return false; 73 | 74 | final Message message = (Message) o; 75 | 76 | if (type != null ? !type.equals(message.type) : message.type != null) return false; 77 | if (keyboard != null ? !keyboard.equals(message.keyboard) : message.keyboard != null) return false; 78 | return trackingData != null ? trackingData.equals(message.trackingData) : message.trackingData == null; 79 | } 80 | 81 | @Override 82 | public int hashCode() { 83 | int result = type != null ? type.hashCode() : 0; 84 | result = 31 * result + (keyboard != null ? keyboard.hashCode() : 0); 85 | result = 31 * result + (trackingData != null ? trackingData.hashCode() : 0); 86 | return result; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/MessageKeyboard.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.core.JsonParseException; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.core.JsonProcessingException; 6 | import com.fasterxml.jackson.databind.DeserializationContext; 7 | import com.fasterxml.jackson.databind.JsonDeserializer; 8 | import com.fasterxml.jackson.databind.JsonMappingException; 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 11 | import com.google.common.base.MoreObjects; 12 | import com.google.common.base.Strings; 13 | import com.google.common.collect.ForwardingMap; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import javax.annotation.Nullable; 18 | import javax.annotation.concurrent.Immutable; 19 | import java.io.IOException; 20 | import java.util.Collections; 21 | import java.util.Map; 22 | 23 | /** 24 | * Simple Map(String, Object) wrapper class. 25 | */ 26 | @Immutable 27 | @JsonDeserialize(using = MessageKeyboard.KeyboardDeserializer.class) 28 | public class MessageKeyboard extends ForwardingMap { 29 | private final Map map; 30 | 31 | public MessageKeyboard(final @Nullable Map delegate) { 32 | this.map = Collections.unmodifiableMap(MoreObjects.firstNonNull(delegate, Collections.emptyMap())); 33 | } 34 | 35 | public MessageKeyboard() { 36 | this(null); 37 | } 38 | 39 | @Override 40 | protected Map delegate() { 41 | return map; 42 | } 43 | 44 | static class KeyboardDeserializer extends JsonDeserializer { 45 | private static final Logger logger = LoggerFactory.getLogger(KeyboardDeserializer.class); 46 | private static final ObjectMapper objectMapper = new ObjectMapper(); 47 | private static final String EMPTY_JSON_OBJECT = "{}"; 48 | 49 | @Override 50 | public MessageKeyboard deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException, JsonProcessingException { 51 | Map messageKeyboard = null; 52 | try { 53 | messageKeyboard = objectMapper.readValue(MoreObjects.firstNonNull( 54 | Strings.emptyToNull(p.getValueAsString().trim()), EMPTY_JSON_OBJECT), Map.class); 55 | } catch (JsonMappingException | JsonParseException exception) { 56 | logger.warn("Could not deserialize message keyboard '{}'", p.getValueAsString().trim()); 57 | } 58 | return new MessageKeyboard(messageKeyboard); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/PictureMessage.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.google.common.base.Strings; 7 | 8 | import javax.annotation.Nonnull; 9 | import javax.annotation.Nullable; 10 | import javax.annotation.concurrent.Immutable; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | import static com.viber.bot.Preconditions.checkNotEmpty; 15 | 16 | @Immutable 17 | public class PictureMessage extends Message { 18 | 19 | private final String url; 20 | 21 | @Nullable 22 | private final String text; 23 | 24 | @Nullable 25 | private final String thumbnail; 26 | 27 | @JsonCreator 28 | public PictureMessage(final @JsonProperty("media") @Nonnull String url, 29 | final @JsonProperty("text") @Nullable String text, 30 | final @JsonProperty("thumbnail") @Nullable String thumbnail, 31 | final @JsonProperty("keyboard") @Nullable MessageKeyboard keyboard, 32 | final @JsonProperty("tracking_data") @Nullable TrackingData trackingData) { 33 | super("picture", keyboard, trackingData); 34 | this.url = checkNotEmpty(url); 35 | this.text = Strings.emptyToNull(text); 36 | this.thumbnail = Strings.emptyToNull(thumbnail); 37 | } 38 | 39 | @JsonIgnore 40 | public PictureMessage(final @Nonnull String url, final @Nullable String text, final @Nullable String thumbnail) { 41 | this(url, text, thumbnail, null, null); 42 | } 43 | 44 | @Override 45 | protected Map getPartialMapRepresentation() { 46 | return new HashMap() {{ 47 | put("media", getUrl()); 48 | put("text", getText()); 49 | put("thumbnail", getThumbnail()); 50 | }}; 51 | } 52 | 53 | public String getUrl() { 54 | return url; 55 | } 56 | 57 | @Nullable 58 | public String getText() { 59 | return text; 60 | } 61 | 62 | @Nullable 63 | public String getThumbnail() { 64 | return thumbnail; 65 | } 66 | 67 | @Override 68 | public boolean equals(final Object o) { 69 | if (this == o) return true; 70 | if (o == null || getClass() != o.getClass()) return false; 71 | if (!super.equals(o)) return false; 72 | 73 | final PictureMessage that = (PictureMessage) o; 74 | 75 | if (text != null ? !text.equals(that.text) : that.text != null) return false; 76 | if (url != null ? !url.equals(that.url) : that.url != null) return false; 77 | return thumbnail != null ? thumbnail.equals(that.thumbnail) : that.thumbnail == null; 78 | } 79 | 80 | @Override 81 | public int hashCode() { 82 | int result = super.hashCode(); 83 | result = 31 * result + (text != null ? text.hashCode() : 0); 84 | result = 31 * result + (url != null ? url.hashCode() : 0); 85 | result = 31 * result + (thumbnail != null ? thumbnail.hashCode() : 0); 86 | return result; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/RichMediaMessage.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | import javax.annotation.Nonnull; 8 | import javax.annotation.Nullable; 9 | import javax.annotation.concurrent.Immutable; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.Optional; 13 | 14 | import static com.google.common.base.Preconditions.checkNotNull; 15 | 16 | @Immutable 17 | public class RichMediaMessage extends Message { 18 | 19 | private final static int RICH_MEDIA_MINIMUM_API_VERSION = 2; 20 | 21 | @Nonnull 22 | private final RichMediaObject richMediaObject; 23 | 24 | @Nullable 25 | private final String alternativeText; 26 | 27 | @Nullable 28 | private final Integer minimalApiVersion; 29 | 30 | @JsonCreator 31 | public RichMediaMessage(final @JsonProperty("rich_media") @Nonnull RichMediaObject richMediaObject, 32 | final @JsonProperty("alt_text") @Nullable String alternativeText, 33 | final @JsonProperty("min_api_version") @Nullable Integer minimalApiVersion, 34 | final @JsonProperty("keyboard") @Nullable MessageKeyboard keyboard, 35 | final @JsonProperty("tracking_data") @Nullable TrackingData trackingData) { 36 | super("rich_media", keyboard, trackingData); 37 | this.richMediaObject = checkNotNull(richMediaObject); 38 | this.alternativeText = alternativeText; 39 | this.minimalApiVersion = minimalApiVersion; 40 | } 41 | 42 | @JsonIgnore 43 | public RichMediaMessage(final RichMediaObject richMedia, final String alternativeText, final Integer minimalApiVersion) { 44 | this(richMedia, alternativeText, minimalApiVersion, null, null); 45 | } 46 | 47 | @Override 48 | protected Map getPartialMapRepresentation() { 49 | return new HashMap() {{ 50 | put("rich_media", getRichMediaObject()); 51 | put("alt_text", getAlternativeText()); 52 | put("min_api_version", getMinimalApiVersion().orElse(RICH_MEDIA_MINIMUM_API_VERSION)); 53 | }}; 54 | } 55 | 56 | @Nonnull 57 | public RichMediaObject getRichMediaObject() { 58 | return richMediaObject; 59 | } 60 | 61 | @Nullable 62 | public String getAlternativeText() { 63 | return alternativeText; 64 | } 65 | 66 | @Nullable 67 | public Optional getMinimalApiVersion() { 68 | return Optional.ofNullable(minimalApiVersion); 69 | } 70 | 71 | @Override 72 | public boolean equals(final Object o) { 73 | if (this == o) return true; 74 | if (o == null || getClass() != o.getClass()) return false; 75 | if (!super.equals(o)) return false; 76 | 77 | final RichMediaMessage that = (RichMediaMessage) o; 78 | 79 | if (!richMediaObject.equals(that.richMediaObject)) return false; 80 | if (minimalApiVersion != null ? !minimalApiVersion.equals(that.minimalApiVersion) : that.minimalApiVersion != null) 81 | return false; 82 | return alternativeText != null ? !alternativeText.equals(that.alternativeText) : that.alternativeText != null; 83 | } 84 | 85 | @Override 86 | public int hashCode() { 87 | int result = super.hashCode(); 88 | result = 31 * result + richMediaObject.hashCode(); 89 | result = 31 * result + (minimalApiVersion != null ? minimalApiVersion.hashCode() : 0); 90 | result = 31 * result + (alternativeText != null ? alternativeText.hashCode() : 0); 91 | return result; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/RichMediaObject.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.core.JsonParseException; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.core.JsonProcessingException; 6 | import com.fasterxml.jackson.databind.DeserializationContext; 7 | import com.fasterxml.jackson.databind.JsonDeserializer; 8 | import com.fasterxml.jackson.databind.JsonMappingException; 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 11 | import com.google.common.base.MoreObjects; 12 | import com.google.common.base.Strings; 13 | import com.google.common.collect.ForwardingMap; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import javax.annotation.Nullable; 18 | import javax.annotation.concurrent.Immutable; 19 | import java.io.IOException; 20 | import java.util.Collections; 21 | import java.util.Map; 22 | 23 | /** 24 | * Simple Map(String, Object) wrapper class. 25 | */ 26 | @Immutable 27 | @JsonDeserialize(using = RichMediaObject.RichMediaDeserializer.class) 28 | public class RichMediaObject extends ForwardingMap { 29 | private final Map map; 30 | 31 | public RichMediaObject(final @Nullable Map delegate) { 32 | this.map = Collections.unmodifiableMap(MoreObjects.firstNonNull(delegate, Collections.emptyMap())); 33 | } 34 | 35 | public RichMediaObject() { 36 | this(null); 37 | } 38 | 39 | @Override 40 | protected Map delegate() { 41 | return map; 42 | } 43 | 44 | static class RichMediaDeserializer extends JsonDeserializer { 45 | private static final Logger logger = LoggerFactory.getLogger(RichMediaDeserializer.class); 46 | private static final ObjectMapper objectMapper = new ObjectMapper(); 47 | private static final String EMPTY_JSON_OBJECT = "{}"; 48 | 49 | @Override 50 | public RichMediaObject deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException, JsonProcessingException { 51 | Map richMediaMap = null; 52 | try { 53 | richMediaMap = objectMapper.readValue(MoreObjects.firstNonNull( 54 | Strings.emptyToNull(p.getValueAsString().trim()), EMPTY_JSON_OBJECT), Map.class); 55 | } catch (JsonMappingException | JsonParseException exception) { 56 | logger.warn("Could not deserialize message keyboard '{}'", p.getValueAsString().trim()); 57 | } 58 | return new RichMediaObject(richMediaMap); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/StickerMessage.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | import javax.annotation.Nullable; 8 | import javax.annotation.concurrent.Immutable; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | @Immutable 13 | public class StickerMessage extends Message { 14 | 15 | private final long stickerId; 16 | 17 | @JsonCreator 18 | public StickerMessage(final @JsonProperty("sticker_id") long stickerId, 19 | final @JsonProperty("keyboard") @Nullable MessageKeyboard keyboard, 20 | final @JsonProperty("tracking_data") @Nullable TrackingData trackingData) { 21 | super("sticker", keyboard, trackingData); 22 | this.stickerId = stickerId; 23 | } 24 | 25 | @JsonIgnore 26 | public StickerMessage(final long stickerId) { 27 | this(stickerId, null, null); 28 | } 29 | 30 | @Override 31 | protected Map getPartialMapRepresentation() { 32 | return new HashMap() {{ 33 | put("sticker_id", getStickerId()); 34 | }}; 35 | } 36 | 37 | public long getStickerId() { 38 | return stickerId; 39 | } 40 | 41 | @Override 42 | public boolean equals(final Object o) { 43 | if (this == o) return true; 44 | if (o == null || getClass() != o.getClass()) return false; 45 | if (!super.equals(o)) return false; 46 | 47 | final StickerMessage that = (StickerMessage) o; 48 | 49 | return stickerId == that.stickerId; 50 | } 51 | 52 | @Override 53 | public int hashCode() { 54 | int result = super.hashCode(); 55 | result = 31 * result + (int) (stickerId ^ (stickerId >>> 32)); 56 | return result; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/TextMessage.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | import javax.annotation.Nonnull; 8 | import javax.annotation.Nullable; 9 | import javax.annotation.concurrent.Immutable; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | import static com.viber.bot.Preconditions.checkNotEmpty; 14 | 15 | @Immutable 16 | public class TextMessage extends Message { 17 | 18 | private final String text; 19 | 20 | @JsonCreator 21 | public TextMessage(final @JsonProperty("text") @Nonnull String text, 22 | final @JsonProperty("keyboard") @Nullable MessageKeyboard keyboard, 23 | final @JsonProperty("tracking_data") @Nullable TrackingData trackingData) { 24 | super("text", keyboard, trackingData); 25 | this.text = checkNotEmpty(text); 26 | } 27 | 28 | @JsonIgnore 29 | public TextMessage(final @Nonnull String text) { 30 | this(text, null, null); 31 | } 32 | 33 | @Override 34 | protected Map getPartialMapRepresentation() { 35 | return new HashMap() {{ 36 | put("text", getText()); 37 | }}; 38 | } 39 | 40 | public String getText() { 41 | return text; 42 | } 43 | 44 | @Override 45 | public boolean equals(final Object o) { 46 | if (this == o) return true; 47 | if (o == null || getClass() != o.getClass()) return false; 48 | if (!super.equals(o)) return false; 49 | 50 | final TextMessage that = (TextMessage) o; 51 | return text != null ? text.equals(that.text) : that.text == null; 52 | } 53 | 54 | @Override 55 | public int hashCode() { 56 | int result = super.hashCode(); 57 | result = 31 * result + (text != null ? text.hashCode() : 0); 58 | return result; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/TrackingData.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.core.JsonParseException; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.core.JsonProcessingException; 6 | import com.fasterxml.jackson.databind.DeserializationContext; 7 | import com.fasterxml.jackson.databind.JsonDeserializer; 8 | import com.fasterxml.jackson.databind.JsonMappingException; 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 11 | import com.google.common.base.MoreObjects; 12 | import com.google.common.base.Strings; 13 | import com.google.common.collect.ForwardingMap; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import javax.annotation.Nullable; 18 | import javax.annotation.concurrent.Immutable; 19 | import java.io.IOException; 20 | import java.util.Collections; 21 | import java.util.Map; 22 | 23 | /** 24 | * Simple Map(String, Object) wrapper class. 25 | */ 26 | @Immutable 27 | @JsonDeserialize(using = TrackingData.TrackingDataDeserializer.class) 28 | public class TrackingData extends ForwardingMap { 29 | private final Map map; 30 | 31 | public TrackingData(final @Nullable Map delegate) { 32 | this.map = Collections.unmodifiableMap(MoreObjects.firstNonNull(delegate, Collections.emptyMap())); 33 | } 34 | 35 | public TrackingData() { 36 | this(null); 37 | } 38 | 39 | @Override 40 | protected Map delegate() { 41 | return map; 42 | } 43 | 44 | static class TrackingDataDeserializer extends JsonDeserializer { 45 | private static final Logger logger = LoggerFactory.getLogger(TrackingDataDeserializer.class); 46 | private static final ObjectMapper objectMapper = new ObjectMapper(); 47 | private static final String EMPTY_JSON_OBJECT = "{}"; 48 | 49 | @Override 50 | public TrackingData deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException, JsonProcessingException { 51 | Map trackingData = null; 52 | try { 53 | trackingData = objectMapper.readValue(MoreObjects.firstNonNull( 54 | Strings.emptyToNull(p.getValueAsString().trim()), EMPTY_JSON_OBJECT), Map.class); 55 | } catch (JsonMappingException | JsonParseException exception) { 56 | logger.warn("Could not deserialize tracking data '{}'", p.getValueAsString().trim()); 57 | } 58 | return new TrackingData(trackingData); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/UrlMessage.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.google.common.net.UrlEscapers; 7 | 8 | import javax.annotation.Nonnull; 9 | import javax.annotation.Nullable; 10 | import javax.annotation.concurrent.Immutable; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | import static com.viber.bot.Preconditions.checkNotEmpty; 15 | 16 | @Immutable 17 | public class UrlMessage extends Message { 18 | 19 | private final String url; 20 | 21 | @JsonCreator 22 | public UrlMessage(final @JsonProperty("media") @Nonnull String url, 23 | final @JsonProperty("keyboard") @Nullable MessageKeyboard keyboard, 24 | final @JsonProperty("tracking_data") @Nullable TrackingData trackingData) { 25 | super("url", keyboard, trackingData); 26 | this.url = UrlEscapers.urlPathSegmentEscaper().escape(checkNotEmpty(url)); 27 | } 28 | 29 | @JsonIgnore 30 | public UrlMessage(final @Nonnull String url) { 31 | this(url, null, null); 32 | } 33 | 34 | @Override 35 | protected Map getPartialMapRepresentation() { 36 | return new HashMap() {{ 37 | put("media", getUrl()); 38 | }}; 39 | } 40 | 41 | public String getUrl() { 42 | return url; 43 | } 44 | 45 | @Override 46 | public boolean equals(final Object o) { 47 | if (this == o) return true; 48 | if (o == null || getClass() != o.getClass()) return false; 49 | if (!super.equals(o)) return false; 50 | 51 | final UrlMessage that = (UrlMessage) o; 52 | 53 | return url != null ? url.equals(that.url) : that.url == null; 54 | 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | int result = super.hashCode(); 60 | result = 31 * result + (url != null ? url.hashCode() : 0); 61 | return result; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/message/VideoMessage.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.message; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.google.common.base.Strings; 7 | 8 | import javax.annotation.Nonnull; 9 | import javax.annotation.Nullable; 10 | import javax.annotation.concurrent.Immutable; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.Optional; 14 | 15 | import static com.viber.bot.Preconditions.checkNotEmpty; 16 | 17 | @Immutable 18 | public class VideoMessage extends Message { 19 | 20 | private final String url; 21 | private final int size; 22 | 23 | @Nullable 24 | private final String text; 25 | 26 | @Nullable 27 | private final String thumbnail; 28 | 29 | @Nullable 30 | private final Integer duration; 31 | 32 | @JsonCreator 33 | public VideoMessage(final @JsonProperty("media") @Nonnull String url, 34 | final @JsonProperty("size") int size, 35 | final @JsonProperty("text") @Nullable String text, 36 | final @JsonProperty("thumbnail") @Nullable String thumbnail, 37 | final @JsonProperty("duration") @Nullable Integer duration, 38 | final @JsonProperty("keyboard") @Nullable MessageKeyboard keyboard, 39 | final @JsonProperty("tracking_data") @Nullable TrackingData trackingData) { 40 | super("video", keyboard, trackingData); 41 | this.url = checkNotEmpty(url); 42 | this.size = size; 43 | this.text = text; 44 | this.thumbnail = Strings.emptyToNull(thumbnail); 45 | this.duration = duration; 46 | } 47 | 48 | @JsonIgnore 49 | public VideoMessage(final @Nonnull String url, final int size, final @Nullable String text, 50 | final @Nullable String thumbnail, final @Nullable Integer duration) { 51 | this(url, size, text, thumbnail, duration, null, null); 52 | } 53 | 54 | @Override 55 | protected Map getPartialMapRepresentation() { 56 | return new HashMap() {{ 57 | put("media", getUrl()); 58 | put("text", getText()); 59 | put("thumbnail", getThumbnail()); 60 | put("size", getSize()); 61 | put("duration", getDuration()); 62 | }}; 63 | } 64 | 65 | public String getUrl() { 66 | return url; 67 | } 68 | 69 | public int getSize() { 70 | return size; 71 | } 72 | 73 | @Nullable 74 | public String getText() { 75 | return text; 76 | } 77 | 78 | @Nullable 79 | public String getThumbnail() { 80 | return thumbnail; 81 | } 82 | 83 | public Optional getDuration() { 84 | return Optional.ofNullable(duration); 85 | } 86 | 87 | @Override 88 | public boolean equals(Object o) { 89 | if (this == o) return true; 90 | if (o == null || getClass() != o.getClass()) return false; 91 | if (!super.equals(o)) return false; 92 | 93 | VideoMessage that = (VideoMessage) o; 94 | 95 | if (size != that.size) return false; 96 | if (url != null ? !url.equals(that.url) : that.url != null) return false; 97 | if (text != null ? !text.equals(that.text) : that.text != null) return false; 98 | if (thumbnail != null ? !thumbnail.equals(that.thumbnail) : that.thumbnail != null) return false; 99 | return duration != null ? duration.equals(that.duration) : that.duration == null; 100 | } 101 | 102 | @Override 103 | public int hashCode() { 104 | int result = super.hashCode(); 105 | result = 31 * result + (url != null ? url.hashCode() : 0); 106 | result = 31 * result + size; 107 | result = 31 * result + (text != null ? text.hashCode() : 0); 108 | result = 31 * result + (thumbnail != null ? thumbnail.hashCode() : 0); 109 | result = 31 * result + (duration != null ? duration.hashCode() : 0); 110 | return result; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/middleware/DefaultMiddleware.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.middleware; 2 | 3 | import com.google.common.util.concurrent.ListenableFuture; 4 | import com.viber.bot.Request; 5 | 6 | import javax.annotation.Nonnull; 7 | import java.io.InputStream; 8 | 9 | public class DefaultMiddleware implements Middleware { 10 | 11 | private final Middleware middleware; 12 | 13 | public DefaultMiddleware(final @Nonnull RequestReceiver requestReceiver) { 14 | middleware = new PubSubMiddleware(requestReceiver); 15 | } 16 | 17 | @Override 18 | public ListenableFuture incoming(final Request request) { 19 | return middleware.incoming(request); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/middleware/Middleware.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.middleware; 2 | 3 | import com.google.common.util.concurrent.ListenableFuture; 4 | import com.viber.bot.Request; 5 | 6 | import javax.annotation.concurrent.ThreadSafe; 7 | import java.io.InputStream; 8 | 9 | @ThreadSafe 10 | public interface Middleware { 11 | ListenableFuture incoming(final Request request); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/middleware/RequestReceiver.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.middleware; 2 | 3 | import com.viber.bot.Request; 4 | 5 | public interface RequestReceiver { 6 | void acceptRequest(final Request request); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/profile/BotProfile.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.profile; 2 | 3 | import javax.annotation.Nonnull; 4 | import javax.annotation.Nullable; 5 | import javax.annotation.concurrent.Immutable; 6 | 7 | import static com.viber.bot.Preconditions.checkNotEmpty; 8 | 9 | @Immutable 10 | public class BotProfile { 11 | 12 | private final String name; 13 | private final String avatar; 14 | 15 | public BotProfile(final @Nonnull String name, final @Nullable String avatar) { 16 | this.name = checkNotEmpty(name); 17 | this.avatar = avatar; 18 | } 19 | 20 | public BotProfile(final @Nonnull String name) { 21 | this(name, null); 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | @Nullable 29 | public String getAvatar() { 30 | return avatar; 31 | } 32 | 33 | @Override 34 | public boolean equals(final Object o) { 35 | if (this == o) return true; 36 | if (o == null || getClass() != o.getClass()) return false; 37 | 38 | final BotProfile that = (BotProfile) o; 39 | 40 | if (name != null ? !name.equals(that.name) : that.name != null) return false; 41 | return avatar != null ? avatar.equals(that.avatar) : that.avatar == null; 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | int result = name != null ? name.hashCode() : 0; 47 | result = 31 * result + (avatar != null ? avatar.hashCode() : 0); 48 | return result; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/viber/bot/profile/UserProfile.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.profile; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.google.common.base.Strings; 7 | 8 | import javax.annotation.Nonnull; 9 | import javax.annotation.Nullable; 10 | import javax.annotation.concurrent.Immutable; 11 | 12 | import static com.google.common.base.Preconditions.checkNotNull; 13 | import static com.viber.bot.Preconditions.checkNotEmpty; 14 | 15 | @Immutable 16 | @JsonIgnoreProperties(ignoreUnknown = true) 17 | public class UserProfile { 18 | 19 | private final String id; 20 | private final String country; 21 | private final String language; 22 | private final Integer apiVersion; 23 | 24 | @Nullable 25 | private final String name; 26 | 27 | @Nullable 28 | private final String avatar; 29 | 30 | @JsonCreator 31 | UserProfile(final @JsonProperty("id") @Nonnull String id, 32 | final @JsonProperty("country") @Nonnull String country, 33 | final @JsonProperty("language") @Nonnull String language, 34 | final @JsonProperty("api_version") @Nonnull Integer apiVersion, 35 | final @JsonProperty("name") @Nullable String name, 36 | final @JsonProperty("avatar") @Nullable String avatar) { 37 | 38 | this.id = checkNotEmpty(id); 39 | this.name = Strings.emptyToNull(name); 40 | this.avatar = Strings.emptyToNull(avatar); 41 | this.country = checkNotEmpty(country); 42 | this.language = checkNotEmpty(language); 43 | this.apiVersion = checkNotNull(apiVersion); 44 | } 45 | 46 | public String getId() { 47 | return id; 48 | } 49 | 50 | public String getCountry() { 51 | return country; 52 | } 53 | 54 | public String getLanguage() { 55 | return language; 56 | } 57 | 58 | public Integer getApiVersion() { 59 | return apiVersion; 60 | } 61 | 62 | @Nullable 63 | public String getName() { 64 | return name; 65 | } 66 | 67 | @Nullable 68 | public String getAvatar() { 69 | return avatar; 70 | } 71 | 72 | @Override 73 | public boolean equals(Object o) { 74 | if (this == o) return true; 75 | if (o == null || getClass() != o.getClass()) return false; 76 | 77 | UserProfile that = (UserProfile) o; 78 | 79 | if (id != null ? !id.equals(that.id) : that.id != null) return false; 80 | if (name != null ? !name.equals(that.name) : that.name != null) return false; 81 | if (avatar != null ? !avatar.equals(that.avatar) : that.avatar != null) return false; 82 | if (country != null ? !country.equals(that.country) : that.country != null) return false; 83 | if (apiVersion != null ? !apiVersion.equals(that.apiVersion) : that.apiVersion != null) return false; 84 | return language != null ? language.equals(that.language) : that.language == null; 85 | } 86 | 87 | @Override 88 | public int hashCode() { 89 | int result = id != null ? id.hashCode() : 0; 90 | result = 31 * result + (name != null ? name.hashCode() : 0); 91 | result = 31 * result + (avatar != null ? avatar.hashCode() : 0); 92 | result = 31 * result + (country != null ? country.hashCode() : 0); 93 | result = 31 * result + (language != null ? language.hashCode() : 0); 94 | result = 31 * result + (apiVersion != null ? apiVersion.hashCode() : 0); 95 | return result; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/com/viber/bot/RequestTest.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot; 2 | 3 | import com.viber.bot.event.incoming.IncomingDeliveredEvent; 4 | import org.junit.Test; 5 | 6 | import java.io.ByteArrayInputStream; 7 | import java.nio.charset.Charset; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | public class RequestTest { 12 | 13 | private static final String VALID_JSON = "{\"event\":\"delivered\",\"timestamp\":1457764197627,\"message_token\":4912661846655238145,\"user_id\":\"01234567890A=\"}"; 14 | 15 | @Test 16 | public void testValidJson() { 17 | final Request request = Request.fromJsonString(VALID_JSON); 18 | assertThat(request.getEvent()).isInstanceOf(IncomingDeliveredEvent.class); 19 | } 20 | 21 | @Test 22 | public void testValidInputStream() { 23 | final Request request = Request.fromInputStream(new ByteArrayInputStream(VALID_JSON.getBytes(Charset.defaultCharset()))); 24 | assertThat(request.getEvent()).isInstanceOf(IncomingDeliveredEvent.class); 25 | } 26 | 27 | @Test(expected = RuntimeException.class) 28 | public void testJsonWithMissingFields() { 29 | Request.fromJsonString("{\"event\":\"delivered\"}"); 30 | } 31 | 32 | @Test(expected = RuntimeException.class) 33 | public void testInvalidJson() { 34 | Request.fromJsonString("{"); 35 | } 36 | 37 | @Test(expected = RuntimeException.class) 38 | public void testJsonWithInvalidTypeInField() { 39 | Request.fromJsonString("{\"event\":\"delivered\",\"timestamp\":\"hi\"}"); 40 | } 41 | } -------------------------------------------------------------------------------- /src/test/java/com/viber/bot/ViberSignatureValidatorTest.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | 7 | public class ViberSignatureValidatorTest { 8 | 9 | private static final String AUTH_TOKEN = "44dafb7e0f40021e-61a47a1e6778d187-f2c5a676a07050b3"; 10 | private static final String VALID_SIGNATURE = "d21b343448c8aee33b8e93768ef6ceb64a6ba6163099973a2b8bd028fea510ef"; 11 | private static final String SERVER_MESSAGE = "{\"event\":\"webhook\",\"timestamp\":4977069964384421269,\"message_token\":1478683725125}"; 12 | 13 | @Test 14 | public void testSignatureValidationSanity() { 15 | final ViberSignatureValidator validator = new ViberSignatureValidator(AUTH_TOKEN); 16 | assertThat(validator.isSignatureValid(VALID_SIGNATURE, SERVER_MESSAGE)).isTrue(); 17 | } 18 | 19 | @Test 20 | public void testSignatureIsInvalid() { 21 | final ViberSignatureValidator validator = new ViberSignatureValidator("abc"); 22 | assertThat(validator.isSignatureValid(VALID_SIGNATURE, SERVER_MESSAGE)).isFalse(); 23 | } 24 | 25 | @Test 26 | public void testInvalidDataInSignature() { 27 | final ViberSignatureValidator validator = new ViberSignatureValidator(AUTH_TOKEN); 28 | assertThat(validator.isSignatureValid(VALID_SIGNATURE, "{}")).isFalse(); 29 | } 30 | 31 | @Test(expected = NullPointerException.class) 32 | public void testEmptySecret() { 33 | new ViberSignatureValidator(null); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /src/test/java/com/viber/bot/api/RegexMatcherRouterTest.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.api; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.concurrent.atomic.AtomicBoolean; 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | import java.util.regex.Pattern; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | public class RegexMatcherRouterTest { 12 | 13 | private final RegexMatcherRouter regexMatcherRouter = new RegexMatcherRouter(); 14 | 15 | @Test 16 | public void testRegexMatcherRouterSanity() { 17 | final AtomicBoolean isMessageReceived = new AtomicBoolean(false); 18 | 19 | regexMatcherRouter.newMatcher(Pattern.compile("hi", Pattern.CASE_INSENSITIVE), 20 | (event, message, response) -> isMessageReceived.compareAndSet(false, true)); 21 | regexMatcherRouter.tryGetCallback("Hi").forEach(callback -> callback.emit(null, null, null)); 22 | 23 | assertThat(isMessageReceived.get()).isEqualTo(true); 24 | } 25 | 26 | @Test 27 | public void testRegexMatcherRouterMultipleMatches() { 28 | final AtomicInteger numberOfMatches = new AtomicInteger(0); 29 | 30 | regexMatcherRouter.newMatcher(Pattern.compile("hi", Pattern.CASE_INSENSITIVE), 31 | (event, message, response) -> numberOfMatches.incrementAndGet()); 32 | 33 | regexMatcherRouter.newMatcher(Pattern.compile("(hi|hello)", Pattern.CASE_INSENSITIVE), 34 | (event, message, response) -> numberOfMatches.incrementAndGet()); 35 | 36 | regexMatcherRouter.tryGetCallback("Hi").forEach(callback -> callback.emit(null, null, null)); 37 | assertThat(numberOfMatches.get()).isEqualTo(2); 38 | } 39 | 40 | @Test 41 | public void testRegexMatcherRouterNoMatches() { 42 | final AtomicBoolean isMessageReceived = new AtomicBoolean(false); 43 | 44 | regexMatcherRouter.newMatcher(Pattern.compile("hi", Pattern.CASE_INSENSITIVE), 45 | (event, message, response) -> isMessageReceived.compareAndSet(false, true)); 46 | regexMatcherRouter.tryGetCallback("Hello").forEach(callback -> callback.emit(null, null, null)); 47 | 48 | assertThat(isMessageReceived.get()).isEqualTo(false); 49 | } 50 | } -------------------------------------------------------------------------------- /src/test/java/com/viber/bot/api/ViberClientTest.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.api; 2 | 3 | import okhttp3.MediaType; 4 | import okhttp3.Request; 5 | import okhttp3.RequestBody; 6 | import org.junit.Test; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | public class ViberClientTest { 11 | 12 | private static final ViberClient client = new ViberClient("http://url.com", "TOKEN"); 13 | private static final RequestBody EMPTY_REQUEST_BODY = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), ""); 14 | 15 | @Test 16 | public void testCreateRequestAuthTokenExistsSanity() { 17 | Request request = client.createRequest(ViberClient.Endpoint.SEND_MESSAGE, EMPTY_REQUEST_BODY); 18 | assertThat(request.headers().get(ViberClient.VIBER_AUTH_TOKEN_HEADER)).isNotEmpty(); 19 | assertThat(request.headers().get(ViberClient.VIBER_AUTH_TOKEN_HEADER)).isEqualTo("TOKEN"); 20 | } 21 | 22 | @Test 23 | public void testCreateRequestUserAgentExistsSanity() { 24 | Request request = client.createRequest(ViberClient.Endpoint.SEND_MESSAGE, EMPTY_REQUEST_BODY); 25 | assertThat(request.headers().get(ViberClient.USER_AGENT_HEADER_FIELD)).isNotEmpty(); 26 | assertThat(request.headers().get(ViberClient.USER_AGENT_HEADER_FIELD)) 27 | .isEqualTo(String.format("%s%s", ViberClient.USER_AGENT_HEADER_VALUE, ViberClient.VIBER_LIBRARY_VERSION)); 28 | } 29 | } -------------------------------------------------------------------------------- /src/test/java/com/viber/bot/event/EventEmitterTest.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.event; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.google.common.util.concurrent.Futures; 5 | import org.junit.Test; 6 | 7 | import java.util.List; 8 | import java.util.concurrent.ExecutionException; 9 | import java.util.concurrent.Future; 10 | import java.util.concurrent.atomic.AtomicBoolean; 11 | import java.util.concurrent.atomic.AtomicInteger; 12 | import java.util.stream.IntStream; 13 | 14 | import static com.jayway.awaitility.Awaitility.await; 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | import static org.hamcrest.core.IsEqual.equalTo; 17 | 18 | public class EventEmitterTest { 19 | 20 | private final EventEmitter eventEmitter = new EventEmitter(); 21 | 22 | @Test 23 | public void testEventEmitterSanity() { 24 | final AtomicBoolean isEventReceived = new AtomicBoolean(false); 25 | eventEmitter.on(Event.ERROR, args -> { 26 | isEventReceived.compareAndSet(false, true); 27 | return null; 28 | }); 29 | 30 | eventEmitter.emit(Event.ERROR); 31 | await().untilAtomic(isEventReceived, equalTo(true)); 32 | } 33 | 34 | @Test 35 | public void testEventEmitterMultipleEmitsInOrder() throws ExecutionException, InterruptedException { 36 | final int nTests = 10; 37 | final AtomicInteger numberOfEvents = new AtomicInteger(0); 38 | 39 | eventEmitter.on(Event.WEBHOOK, args -> { 40 | numberOfEvents.incrementAndGet(); 41 | return Futures.immediateFuture(args[0]); 42 | }); 43 | 44 | final List> futures = Lists.newArrayList(); 45 | IntStream.range(0, nTests).forEach(i -> futures.addAll(eventEmitter.emit(Event.WEBHOOK, i))); 46 | await().untilAtomic(numberOfEvents, equalTo(nTests)); 47 | 48 | for (int i = 0; i < nTests; i++) { 49 | assertThat(futures.get(i).get()).isEqualTo(i); 50 | } 51 | } 52 | 53 | @Test 54 | public void testEventEmitterMultipleListenersInOrder() throws ExecutionException, InterruptedException { 55 | final int nTests = 10; 56 | final AtomicInteger numberOfEvents = new AtomicInteger(0); 57 | 58 | eventEmitter.on(Event.WEBHOOK, args -> { 59 | numberOfEvents.incrementAndGet(); 60 | return Futures.immediateFuture("a_" + args[0]); 61 | }); 62 | 63 | eventEmitter.on(Event.WEBHOOK, args -> { 64 | numberOfEvents.incrementAndGet(); 65 | return Futures.immediateFuture("b_" + args[0]); 66 | }); 67 | 68 | final List> futures = Lists.newArrayList(); 69 | IntStream.range(0, nTests).forEach(i -> futures.addAll(eventEmitter.emit(Event.WEBHOOK, i))); 70 | await().untilAtomic(numberOfEvents, equalTo(nTests * 2)); 71 | 72 | for (int i = 0; i < nTests; i += 2) { 73 | final int expected = i / 2; 74 | assertThat(futures.get(i).get()).isEqualTo("a_" + expected); 75 | assertThat(futures.get(i + 1).get()).isEqualTo("b_" + expected); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /src/test/java/com/viber/bot/middleware/PubSubMiddlewareTest.java: -------------------------------------------------------------------------------- 1 | package com.viber.bot.middleware; 2 | 3 | import com.viber.bot.Request; 4 | import org.assertj.core.util.Closeables; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | import org.mockito.Mock; 8 | import org.mockito.MockitoAnnotations; 9 | 10 | import java.io.IOException; 11 | import java.util.concurrent.atomic.AtomicBoolean; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | import java.util.stream.IntStream; 14 | 15 | import static com.jayway.awaitility.Awaitility.await; 16 | import static org.hamcrest.core.IsEqual.equalTo; 17 | import static org.mockito.Mockito.doAnswer; 18 | import static org.mockito.Mockito.when; 19 | 20 | public class PubSubMiddlewareTest { 21 | 22 | @Mock 23 | private Request request; 24 | 25 | @Test 26 | public void testMiddlewareSanity() { 27 | final AtomicBoolean gotRequest = new AtomicBoolean(false); 28 | final Middleware middleware = new PubSubMiddleware(request -> { 29 | gotRequest.compareAndSet(false, true); 30 | Closeables.closeQuietly(request); 31 | }); 32 | 33 | middleware.incoming(request); 34 | await().untilAtomic(gotRequest, equalTo(true)); 35 | } 36 | 37 | @Test 38 | public void testMiddlewareMultipleRequestsTestSequence() { 39 | final int nRequests = 10; 40 | final AtomicInteger numberOfRequests = new AtomicInteger(0); 41 | final Middleware middleware = new PubSubMiddleware(request -> { 42 | numberOfRequests.incrementAndGet(); 43 | Closeables.closeQuietly(request); 44 | }); 45 | 46 | IntStream.range(0, nRequests).forEach(i -> middleware.incoming(request)); 47 | await().untilAtomic(numberOfRequests, equalTo(nRequests)); 48 | } 49 | 50 | @Before 51 | public void setup() throws IOException { 52 | MockitoAnnotations.initMocks(this); 53 | 54 | final AtomicBoolean isRequestClosed = new AtomicBoolean(false); 55 | doAnswer(invocation -> { 56 | synchronized (request) { 57 | request.notify(); 58 | isRequestClosed.set(true); 59 | } 60 | return null; 61 | }).when(request).close(); 62 | when(request.isClosed()).then(invocation -> isRequestClosed.get()); 63 | } 64 | 65 | @Test(expected = NullPointerException.class) 66 | public void testMiddlewareNullRequest() { 67 | final Middleware middleware = new PubSubMiddleware(request -> { 68 | }); 69 | middleware.incoming(null); 70 | } 71 | } --------------------------------------------------------------------------------