├── .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 | 
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