Support links | Guides & News |
---|---|
Ссылка русскоязычную инструкцию
whatsapp-chatbot-java - library for integration with WhatsApp messenger via API service green-api.com. To use the library, you need to obtain a registration token and account ID in personal account. There is a free developer account plan.
Documentation for the REST API can be found at link. The library is a wrapper for the REST API, therefore the documentation in the link above also applies to the library itself.
To send a message or perform other GREEN API methods, the WhatsApp account in the phone app must be in authorized state. To authorize your account, go to personal account and scan the QR code using the WhatsApp application.
Maven
<dependency>
<groupId>com.green-api</groupId>
<artifactId>whatsapp-chatbot-java</artifactId>
<version>{{version}}</version>
</dependency>
Gradle
implementation group: 'com.green-api', name: 'whatsapp-chatbot-java', version: 'version'
Before launching the bot you should enable incoming notifications in instance settings by using SetSettings method.
"incomingWebhook": "yes",
"outgoingMessageWebhook": "yes",
"outgoingAPIMessageWebhook": "yes",
Once you have imported the library into your project, you need to configure your application. To do this, you will need to create your own configuration class and add the following beans to it:
@Configuration
public class BotDefaultConfigExample {
@Bean
public RestTemplate restTemplate() {
return new RestTemplateBuilder().build();
}
@Bean
public StateManager stateManager() {
return new StateManagerHashMapImpl();
}
@Bean
public BotFactory botFactory(RestTemplate restTemplate, StateManager stateManager) {
return new BotFactory(restTemplate, stateManager);
}
}
RestTemplate
is a standard Spring class that allows you to send http requests.
You can configure it yourself or use the default implementation as in the example above.
StateManager
is a library class that is responsible for managing the bot's state.
By default, session data is stored in a HashMap, but you can implement your own implementation of the StateManager interface.
If you want to add some default values to the state, you can add them at the configuration stage
for example like this:
@Configuration
public class BotDefaultConfigExample {
@Bean
public StateManager stateManager() {
var stateData = new HashMap<String, Object>();
stateData.put("defaultParameter", "value");
return new StateManagerHashMapImpl(stateData);
}
}
IMPORTANT: The "scene" parameter is reserved in stateData; it stores the current scene for each session. Since this parameter is used by the library to switch between scenes, it is not recommended to override it at the configuration stage.
Next, you need to register the host to which the bot will send requests in application.yml or application.property:
If your instances start with 7103:
green-api:
host: https://api.greenapi.com
hostMedia: https://media.greenapi.com
If not:
green-api:
host: https://api.green-api.com
hostMedia: https://media.green-api.com
BotFactory
is a library class that is responsible for configuring the bot object.
Using this class and the createBot()
method you can initialize a bot object:
@SpringBootApplication
public class BotStarterClassExample {
public static void main(String[] args) {
var context = SpringApplication.run(BotStarterClassExample.class, args);
var botFactory = context.getBean(BotFactory.class);
var bot = botFactory.createBot(
"{{instanceId}}",
"{{token}}",
new HandlerExample());
bot.setStartScene(new FullStartScene());
}
}
The createBot()
method has four parameters:
instanceId
and token
must be taken from the parameters of your instance in your personal account.
handler
and startScene
are objects of your classes in which you will need to implement the logic of your bot.
handler
is a class object that inherits from the abstract class BotHandler
. You can override in it
methods that are responsible for processing webhooks about the state of the instance and device StateInstanceChanged
and DeviceInfo
.
If you don't want to handle these types of notifications, you can simply omit the handler in the constructor, and use the default implementation:
public class BotStarterClassExample {
public static void main(String[] args) {
var context = SpringApplication.run(BotStarterClassExample.class, args);
var botFactory = context.getBean(BotFactory.class);
var bot = botFactory.createBot(
"{{instanceId}}",
"{{token}}");
bot.setStartScene(new FullStartScene());
bot.startReceivingNotifications();
}
}
startScene
is the starting scene from which communication with the bot begins. It is set by the setter bot.setStartScene(new YourStartScene());
A scene is a class object that inherits from the abstract class Scene
. Inside the scenes, webhooks are processed and your business logic is executed.
Your bot will consist of several scenes that are executed one after another in the sequence you specify.
Only one scene can be active at a time per state.
Sample scenes will be shown below.
To start receiving incoming notifications, you need to configure your instance. Open your personal account page via link. Select an instance from the list and click on it. Click Change. IN The Notifications categories include everything you need to receive.
After creating your BotHandler
successor, you need to create the first scene.
To do this, create a class that inherits from Scene
and override the handler method of the desired type.
Most often you will need processIncomingMessage()
which handles webhooks about incoming messages.
Come up with a clear name for the class; I recommend marking the starting scenes with the postfix StartScene
.
The processIncomingMessage()
method, like other handlers, returns the updated state of the bot.
If the state has not changed, it is enough to return the currentState
object.
Link to example: BaseStartScene.java.
public class BaseStartScene extends Scene {
@Override
public State processIncomingMessage(MessageWebhook incomingMessage, State currentState) {
answerWithText(incomingMessage, "Hello!", "message");
return currentState;
}
}
To start the bot, you need to call the bot.startReceivingNotifications();
function.
In this example, the bot only has one scene and will only respond to the message
.
@SpringBootApplication
public class BotStarterClassExample {
public static void main(String[] args) {
var context = SpringApplication.run(BotStarterClassExample.class, args);
var botFactory = context.getBean(BotFactory.class);
var bot = botFactory.createBot(
"{{instanceId}}",
"{{token}}",
new HandlerExample(),
new BaseStartScene());
bot.startReceivingNotifications();
}
}
You can receive not only incoming messages, but also outgoing ones, as well as their statuses and any other types of web hooks. To do this, simply override the method you need.
In this scene, the bot receives all incoming messages and outputs them to the console. Other types of web hooks are ignored, their handlers added for clarity.
Link to example: EventStartScene.java.
@Log4j2
public class EventStartScene extends Scene {
// To process incoming messages.
@Override
public State processIncomingMessage(MessageWebhook incomingMessage, State currentState) {
log.info(incomingMessage); // Output messages
return currentState;
}
// To process outgoing messages
@Override
public State processOutgoingMessage(MessageWebhook outgoingMessage, State currentState) {
return super.processOutgoingMessage(outgoingMessage, currentState);
}
// To process the statuses of outgoing messages
@Override
public State processOutgoingMessageStatus(OutgoingMessageStatus outgoingMessageStatus, State currentState) {
return super.processOutgoingMessageStatus(outgoingMessageStatus, currentState);
}
// To process incoming calls
@Override
public State processIncomingCall(IncomingCall incomingCall, State currentState) {
return super.processIncomingCall(incomingCall, currentState);
}
// To handle chat blocking
@Override
public State processIncomingBlock(IncomingBlock incomingBlock, State currentState) {
return super.processIncomingBlock(incomingBlock, currentState);
}
}
You can get incoming webhooks (messages, statuses) via HTTP API requests in the similar way as the rest of the Green API methods are implemented. Herewith, the chronological order of the webhooks following is guaranteed in the sequence in which they were received FIFO. All incoming webhooks are stored in the queue and are expected to be received within 24 hours.
To get incoming webhooks, you have to sequentially call two methods ReceiveNotification and DeleteNotification. ReceiveNotification method receives an incoming webhook. DeleteNotification method confirms successful webhook receipt and processing. To learn more about the methods, refer to respective ReceiveNotification and DeleteNotification sections.
Filtering by webhook type occurs automatically by overriding the necessary methods, as described in the paragraph above, but how to filter messages?
Since each notification is automatically cast to a java object, you can filter messages by any field of the object yourself. A description of the structure of notification objects can be found at this link: Documentation For convenience, all java objects and fields are named similarly to the documentation:
Java object | Webhook's json object |
---|---|
TextMessageWebhook |
TextMessage |
TemplateMessageWebhook |
TemplateMessage |
StickerMessageWebhook |
StickerMessage |
ReactionMessageWebhook |
ReactionMessage |
QuotedMessageWebhook |
QuotedMessage |
PollUpdateMessageWebhook |
PollUpdateMessage |
PollMessageWebhook |
PollMessage |
LocationMessageWebhook |
LocationMessage |
ListMessageWebhook |
ListMessage |
GroupInviteMessageWebhook |
GroupInviteMessage |
FileMessageWebhook |
imageMessage, videoMessage, documentMessage, audioMessage |
ExtendedTextMessageWebhook |
ExtendedTextMessage |
ButtonsMessageWebhook |
ButtonsMessage |
ContactMessageWebhook |
ContactMessage |
ContactsArrayMessageWebhook |
ContactMessage |
TemplateButtonsReplyMessageWebhook |
TemplateButtonsReplyMessage |
ButtonsResponseMessageWebhook |
ButtonsResponseMessage |
ListResponseMessageWebhook |
ListResponseMessage |
You can independently check and convert the received notification to the type you need.
Link to example: MediaStartScene.java.
public class MediaStartScene extends Scene {
@Override
public State processIncomingMessage(MessageWebhook incomingMessage, State currentState) {
if (incomingMessage instanceof ContactMessageWebhook) {
answerWithText(incomingMessage, "This is a contact message");
} else if (incomingMessage instanceof LocationMessageWebhook) {
answerWithText(incomingMessage, "This is location message");
} else if (incomingMessage instanceof FileMessageWebhook) {
answerWithText(incomingMessage, "This is a message with a file");
}
return currentState;
}
}
Also, for convenience and to improve code readability, the methods of the Scene
class are overloaded; they have built-in the most popular filters for the text of the incoming message.
Examples of using methods of this class will be described below. If you want to make a condition without executing the methods of the Scene
class,
you can use the methods of the Filter
class of this library, which return a boolean
value:
Filter Method | Description |
---|---|
Filter.isSenderIdExpected(MessageWebhook messageWebhook, String expectedSenderId) |
Returns true if expectedSenderId is equal to the sender ID in messageWebhook |
Filter.isMessageTextRegex(MessageWebhook messageWebhook, Pattern regexPattern) |
Returns true if regexPattern matches the text in messageWebhook |
Filter.isMessageTextExpected(MessageWebhook messageWebhook, String expectedMessage) |
Returns true if expectedMessage equals the text in messageWebhook |
In this example, the bot will send a message and a file in response to the rates
command.
Sending a message using the answerWithText()
method is triggered only in response to the rates
command due to the fact that the third parameter to the answerWithText
method
the string "rates"
is passed. This mechanism is implemented in all methods of the Scene
class. If you want to reply to all messages, without a filter, simply omit the third parameter.
You can also pass a regex pattern as the third parameter instead of a string.
Sending a file using the answerWithUploadFile()
method is triggered only in response to the rates
command due to the fact that the method is located in the if
block in the condition of which the method is executed
Filter.isMessageTextExpected(incomingMessage, "rates")
.
Link to example: FiltersStartScene.java.
public class FiltersStartScene extends Scene {
@Override
public State processIncomingMessage(MessageWebhook incomingMessage, State currentState) {
answerWithText(incomingMessage, "You see this because you wrote \"rates\"", "rates"); //filtering by overloaded method
if (Filter.isMessageTextExpected(incomingMessage, "rates")) { //filtering using the Filter class method
answerWithUploadFile(incomingMessage, new File("src/main/resources/data/rates.png"));
}
return currentState;
}
}
There are special methods in the Scene
class to manage scenes. They must be executed after the return
keyword inside the active scene.
These methods change the scene
parameter in the chat state, the next message from the selected chat will go into a new scene.
Scene Methods | Description |
---|---|
activateNextScene(State currentState, Scene nextScene) |
Activates the next nextScene for the current chat. |
activateStartScene(State currentState) |
Activates the start scene for the current user. |
A complete list of methods available on the stage is described at the end of the file.
To control the user's state, simply make changes to the currentState
object inside the scene and return it using return
.
At the end of each scene, the state is automatically updated.
To manage state directly, you need to use the stateManager
object, which is an instance of your implementation of the StateManager
interface.
This object is available in any scene, as it is one of its fields.
The manager has methods that perform basic crud operations on the state. You also have the option to save chat data in its state.
StateManager Methods | Description |
---|---|
get() |
Returns the state of the selected chat. |
create() |
Creates a new state for chat. |
update() |
Updates the state (the method is not present in the default implementation of the interface) |
delete() |
Removes the user's state. |
getStateData() |
Returns state data |
setStateData() |
If the state exists, overwrites the state data |
updateStateData() |
If the state exists, updates the state data (put) |
deleteStateData() |
If the state exists, then clears the state data (sets default values) |
The state identifier is the chat ID (the chatId field, not to be confused with senderId).
As an example, a bot was created to register a user.
Link to example: state.
public class StateStartScene extends Scene {
@Override
public State processIncomingMessage(MessageWebhook incomingMessage, State currentState) {
answerWithText(incomingMessage, "Hello. Tell me your username.");
return activateNextScene(currentState, new InputUsernameScene());
}
}
public class InputUsernameScene extends Scene {
@Override
public State processIncomingMessage(MessageWebhook incomingMessage, State currentState) {
var stateData = currentState.getData();
var username = getText(incomingMessage);
if (username != null && username.length() <= 20 && username.length() >= 5) {
stateData.put("username", username);
currentState.setData(stateData);
answerWithText(incomingMessage, "Please, send password");
activateNextScene(currentState, new InputPasswordScene());
} else {
answerWithText(incomingMessage, "invalid username");
}
return currentState;
}
}
public class InputPasswordScene extends Scene {
@Override
public State processIncomingMessage(MessageWebhook incomingMessage, State currentState) {
var stateData = currentState.getData();
var password = getText(incomingMessage);
if (password != null && password.length() <= 20 && password.length() >= 8) {
stateData.put("password", password);
currentState.setData(stateData);
answerWithText(incomingMessage, String.format("""
Successful account creation.
Your username: %s.
Your password: %s.
""", stateData.get("username"), password));
return activateStartScene(currentState);
} else {
answerWithText(incomingMessage, "invalid password");
}
return currentState;
}
}
As an example, a bot was created that demonstrates sending methods of the Scene class.
The bot is started with the command - /start After launching, you need to select a method from the menu, and the bot will execute it.
Link to example: full.
The start scene waits for the /start
command, after which it sends the menu and activates the next ChooseScene
.
public class FullStartScene extends Scene {
@Override
public State processIncomingMessage(MessageWebhook incomingMessage, State currentState) {
var greetingText =
"""
Please, choose Scene's method and I execute it.
1. answerWithText();
2. answerWithUrlFile();
3. answerWithPoll();
4. answerWithLocation();
5. answerWithContact();
6. Exit.
""";
var resp = answerWithText(incomingMessage, greetingText, "/start");
if (resp == null) {
var sendMessageResp = answerWithText(incomingMessage, "Hi, this is test bot.\nPlease, send me a command - /start");
return currentState;
}
return activateNextScene(currentState, new ChooseScene());
}
}
The second scene waits for the user's response and starts the execution of the desired method.
If answerWithUrlFile()
is selected, the InputLinkScene
scene is launched
public class ChooseScene extends Scene {
@Override
public State processIncomingMessage(MessageWebhook incomingMessage, State currentState) {
var text = getText(incomingMessage);
if (text == null) {
answerWithText(incomingMessage, "PLease send a text message!");
return currentState;
}
switch (text) {
case "1" -> {
answerWithText(incomingMessage, "Hi! This is answerWithText!");
return currentState;
}
case "2" -> {
answerWithText(incomingMessage, "Send me the link on File:");
return activateNextScene(currentState, new InputLinkScene());
}
case "3" -> {
var options = new ArrayList<Option>();
options.add(new Option("Red"));
options.add(new Option("Blue"));
options.add(new Option("Green"));
options.add(new Option("Pink"));
answerWithPoll(incomingMessage, "choose color", options, false);
return currentState;
}
case "4" -> {
answerWithLocation(incomingMessage, "Home", "Cdad. de La Paz 2969, Buenos Aires", -34.5553558, -58.4642510);
return currentState;
}
case "5" -> {
var contact = Contact.builder()
.firstName("first")
.lastName("last")
.middleName("middle")
.company("Green API")
.phoneContact(11111111111L)
.build();
answerWithContact(incomingMessage, contact);
return currentState;
}
case "6" -> {
answerWithText(incomingMessage, "Goodbye!");
return activateStartScene(currentState);
}
default -> {
answerWithText(incomingMessage, "Please send numbers - 1, 2, 3, 4, 5 or 6");
return currentState;
}
}
}
}
This scene waits for the user to send a link to the file.
If the link is correct, sends the file and returns to the ChooseScene
.
public class InputLinkScene extends Scene {
@Override
public State processIncomingMessage(MessageWebhook incomingMessage, State currentState) {
var fileUrl = getText(incomingMessage);
if (fileUrl != null) {
try {
answerWithUrlFile(incomingMessage, "This is your file!", fileUrl, "testFile");
} catch (Exception e) {
answerWithText(incomingMessage, "invalid link! Please send me a link, for example https://greenapi.com");
}
} else {
answerWithText(incomingMessage, "Please send me a link!");
return currentState;
}
return activateNextScene(currentState, new ChooseScene());
}
}
Scene's Methods | Description |
---|---|
activateNextScene(State currentState, Scene nextScene) |
Activates the next nextScene for the current chat. |
activateStartScene(State currentState) |
Activates the start scene for the current user. |
getText(MessageWebhook messageWebhook) |
Returns the text of the message if it is text, otherwise returns null |
answerWithText(MessageWebhook messageWebhook, String text) |
Replies with text to an incoming message. |
answerWithUploadFile(MessageWebhook messageWebhook, String caption, File file) |
Downloads and sends a file in response to an incoming message. Caption is an optional field. |
answerWithUrlFile(MessageWebhook messageWebhook, String caption, String url, String fileName) |
Sends a file from a url in response to an incoming message. Caption is an optional field. |
answerWithLocation(MessageWebhook messageWebhook, String nameLocation, String address, Double latitude, Double longitude) |
Sends geolocation in response to an incoming message. |
answerWithPoll(MessageWebhook messageWebhook, String message, List<Option> options, Boolean multipleAnswers) |
Sends a poll in response to an incoming message. |
answerWithContact(MessageWebhook messageWebhook, Contact contact) |
Sends a contact in response to an incoming message. |
In the overloaded version, message response methods may contain additional parameters
expectedMessage
andregexPattern
. If the text of the incoming message matches the condition, the method will be executed and return the method response according to the documentation, if not, then the method will returnnull
.
Documentation on service methods
Licensed under Creative Commons Attribution-NoDerivatives 4.0 International (CC BY-ND 4.0) . LICENSE.