WhatsappWeb4j is a standalone library built to interact with WhatsappWeb. This means that no browser, application or any additional software is necessary to use this library. This library was built for Java 16 and JakartaEE 9. Support for Java 11, the latest LTS as of this date, will come soon. Any help to this library is welcomed as long as the coding style of the project is respected.
Add this dependency to your dependencies in the pom:
<dependency>
<groupId>com.github.auties00</groupId>
<artifactId>whatsappweb4j</artifactId>
<version>1.2</version>
</dependency>
Add this dependency to your build.gradle:
implementation 'com.github.auties00:whatsappweb4j:1.2'
Javadocs for WhatsappWeb4j are available here, all contributions are welcomed!
As of today, no additional configuration is needed to edit this project. I recommend using the latest version of IntelliJ.
- Fork this project
- Clone the new repo
- Create a new branch
- Once you are implementing the new feature, create a new merge request
If you are trying to implement a feature that is present on WhatsappWeb's WebClient, for example audio or video calls, consider using WhatsappWeb4jRequestAnalyzer, a tool I built for this exact purpose.
To use this library, start by initializing an instance of WhatsappAPI:
var api = new WhatsappAPI();
Alternatively, you can provide a custom WhatsappConfiguration:
var configuration = WhatsappConfiguration.builder()
.whatsappUrl("wss://web.whatsapp.com/ws") // WhatsappWeb's WebSocket URL
.requestTag("requestTag") // The tag used for requests made to WhatsappWeb's WebSocket
.description("Whatsapp4j") // The description provided to Whatsapp during the authentication process
.shortDescription("W4J") // An acronym for the description
.reconnectWhenDisconnected((reason) -> true) // Determines whether the connection should be reclaimed
.async(true) // Determines whether requests sent to whatsapp should be asyncronous or not
.build(); // Builds an instance of WhatsappConfiguration
var api = new WhatsappAPI(configuration);
Now create a WhatsappListener, remember to implement only the methods that you need:
public class YourAwesomeListener implements WhatsappListener {
public void onLoggedIn(UserInformationResponse info, boolean firstLogin) {
System.out.println("Connected :)");
}
public void onDisconnected() {
System.out.println("Disconnected :(");
}
}
There are two ways to register listeners:
-
Manually
api.registerListener(new YourAwesomeListener());
-
Automatically
IMPORTANT: Only listeners that provide a no arguments' constructor can be discovered automatically
Annotate your listener using @RegisterListener:
import it.auties.whatsapp4j.listener.RegisterListener; import it.auties.whatsapp4j.listener.WhatsappListener; @RegisterListener public class YourAwesomeListener implements WhatsappListener { }
then enable auto-detection:
api.autodetectListeners();
Now open a connection with WhatsappWeb:
api.connect();
When your program is done, disconnect from WhatsappWeb:
api.disconnect();
Or logout:
api.logout();
All the messages, chats and contacts stored in memory can be accessed using the singleton WhatsappDataManager:
var manager = api.manager(); // Get an instance of WhatsappDataManager
var chats = manager.chats(); // Get all the chats in memory
var contacts = manager.contacts(); // Get all the contacts in memory
var number = manager.phoneNumberJid(); // Get your phone number as a jid
IMPORTANT: When your program first starts up, these fields will be empty. To be notified when they are populated, implement the corresponding method in a WhatsappListener
This class also exposes various methods to query data as explained in the javadocs:
Optional<WhatsappContact> findContactByJid(String jid);
Optional<WhatsappContact> findContactByName(String name);
Set<WhatsappContact> findContactsByName(String name);
Optional<WhatsappChat> findChatByJid(String jid);
Optional<WhatsappChat> findChatByName(String name);
Set<WhatsappChat> findChatsByName(String name);
Optional<WhatsappChat> findChatByMessage(WhatsappMessage message);
Optional<WhatsappMessage> findMessageById(WhatsappChat chat, String id);
Optional<WhatsappMessage> findQuotedMessageInChatByContext(WhatsappChat chat, ContextInfo context);
The keys linked to an active session can be accessed using WhatsappKeysManager.
var chat = api.findChatByName("My Awesome Friend").orElseThrow(); // Query a chat by name
var text = WhatsappTextMessage.newTextMessage(chat, "Hello my friend :)"); // Create a new text message
var quotedText = WhatsappTextMessage.newTextMessage(chat, "Hello my friend ;)", anotherMessage); // Create a new text message that quotes another message
var textWithBuilder = WhatsappTextMessage.newTextMessage() // Create a new WhatsappTextMessageBuilder
.chat(chat) // Set the chat for this message
.text(text) // Set the text for this message
.forwarded(true) // Set whether this message is forwarded or not
.quotedMessage(someMessage) // Set the message that this message quotes
.create();
// Read the file you want to send as an array of bytes, here are two common examples
var fileMedia = Files.readAllBytes(file.toPath());
var urlMedia = url.openStream().readAllBytes();
var media = WhatsappMediaMessage.newMediaMessage() // Create a new media message
.caption("Look at this!") // Set the caption of the message, that is the text below the file. Only available for images and videos
.media(file) // Set the media as an array of bytes
.type(WhatsappMediaMessageType.IMAGE) // Set the type of media you want to send
.create();
var location = WhatsappLocationMessage.newLocationMessage() // Create a new location message
.caption("Look at this!") // Set the caption of the message, that is the text below the file. Not available if this message is live
.coordinates(new WhatsappCoordinates(138.9193, 1183.1389, 10)) // Set the coordinates of the location to share
.accuracy(10) // Set the accuracy in meters of the coordinates to share
.live(false) // Set whether this message is live or not
.speed(19) // Set the speed of the device in meters per second
.thumbnail(thumbnail) // Set the thumbnail of this message
.create();
var invite = WhatsappGroupInviteMessage.newGroupInviteMessage()
.caption("Come join my group of fellow programmers") // Set the caption of this message
.name("Fellow Programmers 1.0") // Set the name of the group
.thumbnail(thumbnail) // Set the thumbnail of the group
.expiration(ZonedDateTime.now().plusDays(90)) // Set the date of expiration for this invite
.jid("jid@g.us") // Set the jid o
8000
f the group
.code("1931130") // Set the code of the group
.create();
var contacts = WhatsappContactMessage.newContactMessage()
.sharedContacts(List.of(contactVCard, anotherVCard))
.create();
If the options above don't satisfy your needs, open an issue and request the feature you need. In the meanwhile though, you can use you can create your own WebMessageInfo, the raw Protobuf object for a message, even though it's not recommended as it's not very developer friendly. Here is an example on how to create a raw text message:
var key = WhatsappProtobuf.MessageKey.newBuilder()
.setFromMe(true)
.setRemoteJid(recipient)
.setId(WhatsappUtils.randomId())
.build();
var conversation = WhatsappProtobuf.Message.newBuilder()
.setConversation(text)
.build();
var text = WhatsappProtobuf.WebMessageInfo.newBuilder()
.setMessage(conversation)
.setKey(key)
.setMessageTimestamp(Instant.now().getEpochSecond())
.setStatus(WhatsappProtobuf.WebMessageInfo.WebMessageInfoStatus.PENDING)
.build();
var context = WhatsappProtobuf.ContextInfo.newBuilder()
.setQuotedMessage(quotedMessage)
.setParticipant(quotedMessageSenderJid)
.setStanzaId(quotedMessageId)
.setRemoteJid(quotedMessageRemoteJid)
.setIsForwarded(forwarded)
.build();
var extendedTextMessage = WhatsappProtobuf.Message.newBuilder()
.setExtendedTextMessage(WhatsappProtobuf.ExtendedTextMessage.newBuilder()
.setText(text)
.setContextInfo(context)
.build())
.build();
var quotedText = WhatsappProtobuf.WebMessageInfo.newBuilder()
.setMessage(extendedTextMessage)
.setKey(key)
.setMessageTimestamp(Instant.now().getEpochSecond())
.setStatus(WhatsappProtobuf.WebMessageInfo.WebMessageInfoStatus.PENDING)
.build();
api.sendMessage(message);
To change your global WhatsappContactStatus:
api.changePresence(status);
To change your WhatsappContactStatus for a specific WhatsappChat:
api.changePresence(status, chat);
To query the last known status of a WhatsappContact::
var lastKnownPresenceOptional = contact.lastKnownPresence();
If the returned value is an empty Optional, the last status of the contact is unknown. As a matter of fact, Whatsapp sends updates regarding the presence of a contact only when:
- A message was recently exchanged between you and said contact
- A new message arrives from said contact
- You send a message to said contact
To force Whatsapp to send these updates use:
api.subscribeToUserPresence(contact);
Then, after the subscribeToUserPresence's future is completed, query again the presence of said contact.
var statusFuture = api.queryUserStatus(contact); // A completable future
var statusResponse = statusFuture.get(); // Wait for the future to complete
var textStatus = statusResponse.status().orElse("No status found"); // The contact's status
var pictureFuture = api.queryChatPicture(chat); // A completable future
var pictureResponse = pictureFuture.get(); // Wait for the future to complete
var pictureUrl = pictureResponse.url(); // The picture for this chat
var metadataFuture = api.queryChatPicture(group); // A completable future
var metadata = metadataFuture.get(); // The group's metadata
var groupsFuture = api.queryGroupsInCommon(contact); // A completable future
var groupsResponse = metadataFuture.get(); // Wait for the future to complete
var groups = groupsResponse.groups(); // A list of common groups
To query a chat that is not in memory:
var contactChat = api.queryChat(contact); // Loads the chat assiosiated with the contact
var jidChat = api.queryChat(chatJid); // Loads a chat assiosiated with a jid
IMPORTANT: This method does not save the queried chat in memory
If the chat is already in memory, to load more messages:
api.loadConversation(chat); // Loads the twenty messages that came chronologically before the oldest one
api.loadConversation(chat, message, numOfMessages); // Loads the numOfMessages that came chronologically before the specified message
To access messages in memory:
var messages = chat.messages();
To search messages globally on Whatsapp's servers:
var future = api.search(stringToSearch, numOfMessages, page); // A future for the request
var response = future.get(); // Wait for the future to complete
var messages = response.data().orElseThrow(); // The requested messages
To search messages for a specific chat on Whatsapp's servers:
var future = api.search(stringToSearch, chat, numOfMessages, page); // A future for the request
var response = future.get(); // Wait for the future to complete
var messages = response.data().orElseThrow(); // The requested messages
var future = api.mute(chat); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.mute(chat); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.archive(chat); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.unarchive(chat); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.enableEphemeralMessages(chat); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.markAsRead(chat); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.markAsUnread(chat); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.pin(chat); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.unpin(chat); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.add(group, contact); // A future for the request
var response = future.get(); // Wait for the future to complete
// A list of modifications made by the request
// Each entry contains the jid of the affected contact and the status of said moification
var success = response.modifications();
var future = api.remove(group, contact); // A future for the request
var response = future.get(); // Wait for the future to complete
// A list of modifications made by the request
// Each entry contains the jid of the affected contact and the status of said moification
var success = response.modifications();
var future = api.promote(group, contact); // A future for the request
var response = future.get(); // Wait for the future to complete
// A list of modifications made by the request
// Each entry contains the jid of the affected contact and the status of said moification
var success = response.modifications();
var future = api.demote(group, contact); // A future for the request
var response = future.get(); // Wait for the future to complete
// A list of modifications made by the request
// Each entry contains the jid of the affected contact and the status of said moification
var success = response.modifications();
var future = api.changeGroupName(group, newName); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.changeGroupDescription(group, newDescription); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.changeWhoCanSendMessagesInGroup(group, policy); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.changeWhoCanEditGroupInfo(group, policy); // A future for the request
var response = future.get(); // Wait for the future to complete
IMPORTANT: This method is in the API marked as Beta but is not yet implemented
var future = api.removeGroupPicture(group); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.createGroup(group, friend, friend##### friend2); // A future for the request
var response = future.get(); // Wait for the future to complete
var future = api.leave(group); // A future for the request
var response = future.get(); // Wait for the future to complete