├── .gitignore ├── README.md ├── pom.xml └── src ├── main ├── java │ └── me │ │ └── aboullaite │ │ ├── CallBackHandler.java │ │ ├── SpringDocBotApplication.java │ │ └── domain │ │ └── SearchResult.java └── resources │ └── application.yml └── test └── java └── me └── aboullaite └── SpringDocBotApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ 25 | /mvnw 26 | /mvnw.cmd 27 | /.mvn/ 28 | /src/main/resources/application.yml 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpringBot 2 | Messenger Bot that, based on typed text, query spring documentation and return 3 first results. Read this blog post for more details: https://aboullaite.me/my-first-java-bot/ 3 | 4 | ![](http://i.giphy.com/l0IymLb7VE5KobYXu.gif) 5 | 6 | ### Run the App 7 | First thing you need to do is to setup a Facebook app, Please follow this [link](https://developers.facebook.com/docs/messenger-platform/guides/setup) for more details. You'll need also a free hosting service like [Heroko](https://www.heroku.com/) or [Clever Cloud](https://www.clever-cloud.com/), or you can simply use [NGROK](https://ngrok.com/). and don't forget to create a [Facebook page](https://www.facebook.com/pages/create). 8 | 9 | Once all done, follow this steps: 10 | 11 | - open https://developers.facebook.com/apps 12 | click the 'Add a New App' button 13 | - enter the Display Name, e.g. spring-bot 14 | select the Category: 'Apps for Messenger' 15 | - click the 'Create App ID' button 16 | - Section 'Token Generation': Select your created FB Page 17 | - copy the 'Page Access Token' to the clipboard 18 | - copy it to your `application.yml` 19 | - navigate to 'Dashboard' 20 | - copy the 'App Secret' to the clipboard 21 | - copy it to your `application.yml` 22 | - use a randomly generated string as 'Verify Token' 23 | - deploy you app, and copy your public HTTPS URL (callback url) 24 | - navigate back to 'Messenger' 25 | - Section 'Webhooks': Click the 'Setup Webhooks' button 26 | - past the Callback URL and your verify token 27 | - select the following Subscription Fields: `messages`, `messaging_postbacks`, `messaging_optins`, `message_deliveries`, `message_reads`, `message_echoes` 28 | - click the 'Verify and Save' button 29 | - Section 'Webhooks': Select your created FB Page to subscribe your webhook to the page events 30 | - click the 'Subscribe' button 31 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | me.aboullaite 7 | spring-doc-bot 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | spring-doc-bot 12 | A Messanger bot that look into Spring do based on enrtered text 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.1.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | com.github.messenger4j 35 | messenger4j 36 | 0.8.0 37 | 38 | 39 | 40 | 41 | org.jsoup 42 | jsoup 43 | 1.10.2 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter-test 49 | test 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-maven-plugin 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-compiler-plugin 63 | 3.3 64 | 65 | 1.8 66 | 1.8 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/main/java/me/aboullaite/CallBackHandler.java: -------------------------------------------------------------------------------- 1 | package me.aboullaite; 2 | 3 | import com.github.messenger4j.MessengerPlatform; 4 | import com.github.messenger4j.exceptions.MessengerApiException; 5 | import com.github.messenger4j.exceptions.MessengerIOException; 6 | import com.github.messenger4j.exceptions.MessengerVerificationException; 7 | import com.github.messenger4j.receive.MessengerReceiveClient; 8 | import com.github.messenger4j.receive.events.AccountLinkingEvent; 9 | import com.github.messenger4j.receive.handlers.*; 10 | import com.github.messenger4j.send.*; 11 | import com.github.messenger4j.send.buttons.Button; 12 | import com.github.messenger4j.send.templates.GenericTemplate; 13 | import me.aboullaite.domain.SearchResult; 14 | import org.jsoup.Jsoup; 15 | import org.jsoup.nodes.Document; 16 | import org.jsoup.select.Elements; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | import org.springframework.beans.factory.annotation.Autowired; 20 | import org.springframework.beans.factory.annotation.Value; 21 | import org.springframework.http.HttpStatus; 22 | import org.springframework.http.ResponseEntity; 23 | import org.springframework.web.bind.annotation.*; 24 | 25 | import java.io.IOException; 26 | import java.util.List; 27 | import java.util.Date; 28 | import java.util.stream.Collectors; 29 | 30 | /** 31 | * Created by aboullaite on 2017-02-26. 32 | */ 33 | 34 | @RestController 35 | @RequestMapping("/callback") 36 | public class CallBackHandler { 37 | 38 | private static final Logger logger = LoggerFactory.getLogger(CallBackHandler.class); 39 | 40 | private static final String RESOURCE_URL = 41 | "https://raw.githubusercontent.com/fbsamples/messenger-platform-samples/master/node/public"; 42 | public static final String GOOD_ACTION = "DEVELOPER_DEFINED_PAYLOAD_FOR_GOOD_ACTION"; 43 | public static final String NOT_GOOD_ACTION = "DEVELOPER_DEFINED_PAYLOAD_FOR_NOT_GOOD_ACTION"; 44 | 45 | private final MessengerReceiveClient receiveClient; 46 | private final MessengerSendClient sendClient; 47 | 48 | /** 49 | * Constructs the {@code CallBackHandler} and initializes the {@code MessengerReceiveClient}. 50 | * 51 | * @param appSecret the {@code Application Secret} 52 | * @param verifyToken the {@code Verification Token} that has been provided by you during the setup of the {@code 53 | * Webhook} 54 | * @param sendClient the initialized {@code MessengerSendClient} 55 | */ 56 | @Autowired 57 | public CallBackHandler(@Value("${messenger4j.appSecret}") final String appSecret, 58 | @Value("${messenger4j.verifyToken}") final String verifyToken, 59 | final MessengerSendClient sendClient) { 60 | 61 | logger.debug("Initializing MessengerReceiveClient - appSecret: {} | verifyToken: {}", appSecret, verifyToken); 62 | this.receiveClient = MessengerPlatform.newReceiveClientBuilder(appSecret, verifyToken) 63 | .onTextMessageEvent(newTextMessageEventHandler()) 64 | .onQuickReplyMessageEvent(newQuickReplyMessageEventHandler()) 65 | .onPostbackEvent(newPostbackEventHandler()) 66 | .onAccountLinkingEvent(newAccountLinkingEventHandler()) 67 | .onOptInEvent(newOptInEventHandler()) 68 | .onEchoMessageEvent(newEchoMessageEventHandler()) 69 | .onMessageDeliveredEvent(newMessageDeliveredEventHandler()) 70 | .onMessageReadEvent(newMessageReadEventHandler()) 71 | .fallbackEventHandler(newFallbackEventHandler()) 72 | .build(); 73 | this.sendClient = sendClient; 74 | } 75 | 76 | /** 77 | * Webhook verification endpoint. 78 | * 79 | * The passed verification token (as query parameter) must match the configured verification token. 80 | * In case this is true, the passed challenge string must be returned by this endpoint. 81 | */ 82 | @RequestMapping(method = RequestMethod.GET) 83 | public ResponseEntity verifyWebhook(@RequestParam("hub.mode") final String mode, 84 | @RequestParam("hub.verify_token") final String verifyToken, 85 | @RequestParam("hub.challenge") final String challenge) { 86 | 87 | logger.debug("Received Webhook verification request - mode: {} | verifyToken: {} | challenge: {}", mode, 88 | verifyToken, challenge); 89 | try { 90 | return ResponseEntity.ok(this.receiveClient.verifyWebhook(mode, verifyToken, challenge)); 91 | } catch (MessengerVerificationException e) { 92 | logger.warn("Webhook verification failed: {}", e.getMessage()); 93 | return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage()); 94 | } 95 | } 96 | 97 | /** 98 | * Callback endpoint responsible for processing the inbound messages and events. 99 | */ 100 | @RequestMapping(method = RequestMethod.POST) 101 | public ResponseEntity handleCallback(@RequestBody final String payload, 102 | @RequestHeader("X-Hub-Signature") final String signature) { 103 | 104 | logger.debug("Received Messenger Platform callback - payload: {} | signature: {}", payload, signature); 105 | try { 106 | this.receiveClient.processCallbackPayload(payload, signature); 107 | logger.debug("Processed callback payload successfully"); 108 | return ResponseEntity.status(HttpStatus.OK).build(); 109 | } catch (MessengerVerificationException e) { 110 | logger.warn("Processing of callback payload failed: {}", e.getMessage()); 111 | return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); 112 | } 113 | } 114 | 115 | private TextMessageEventHandler newTextMessageEventHandler() { 116 | return event -> { 117 | logger.debug("Received TextMessageEvent: {}", event); 118 | 119 | final String messageId = event.getMid(); 120 | final String messageText = event.getText(); 121 | final String senderId = event.getSender().getId(); 122 | final Date timestamp = event.getTimestamp(); 123 | 124 | logger.info("Received message '{}' with text '{}' from user '{}' at '{}'", 125 | messageId, messageText, senderId, timestamp); 126 | 127 | try { 128 | switch (messageText.toLowerCase()) { 129 | 130 | 131 | case "yo": 132 | sendTextMessage(senderId, "Hello, What I can do for you ? Type the word you're looking for"); 133 | break; 134 | 135 | case "great": 136 | sendTextMessage(senderId, "You're welcome :) keep rocking"); 137 | break; 138 | 139 | 140 | default: 141 | sendReadReceipt(senderId); 142 | sendTypingOn(senderId); 143 | sendSpringDoc(senderId, messageText); 144 | sendQuickReply(senderId); 145 | sendTypingOff(senderId); 146 | } 147 | } catch (MessengerApiException | MessengerIOException e) { 148 | handleSendException(e); 149 | } catch (IOException e) { 150 | handleIOException(e); 151 | } 152 | }; 153 | } 154 | 155 | private void sendSpringDoc(String recipientId, String keyword) throws MessengerApiException, MessengerIOException, IOException { 156 | 157 | Document doc = Jsoup.connect(("https://spring.io/search?q=").concat(keyword)).get(); 158 | String countResult = doc.select("div.search-results--count").first().ownText(); 159 | Elements searchResult = doc.select("section.search-result"); 160 | List searchResults = searchResult.stream().map(element -> 161 | new SearchResult(element.select("a").first().ownText(), 162 | element.select("a").first().absUrl("href"), 163 | element.select("div.search-result--subtitle").first().ownText(), 164 | element.select("div.search-result--summary").first().ownText()) 165 | ).limit(3).collect(Collectors.toList()); 166 | 167 | final List