From b8bf00f4b2a03e530c8d3841c04704777359d9e4 Mon Sep 17 00:00:00 2001 From: Redouane Bali Date: Sat, 29 Oct 2022 17:28:59 +0100 Subject: [PATCH] feat: implement post DM V2 endpoints (#420) * feat_impl_post_dm_endpoints * adding constructor * improving signatures --- .../redouane59/twitter/ITwitterClientV2.java | 40 ++++++ .../redouane59/twitter/TwitterClient.java | 75 ++++++++++- .../twitter/dto/dm/DmParameters.java | 26 ++++ .../twitter/dto/dm/PostDmResponse.java | 20 +++ .../redouane59/twitter/helpers/URLHelper.java | 121 ++++++++++-------- .../ITwitterClientV2AuthenticatedTest.java | 26 ++++ .../twitter/unit/UrlHelperTest.java | 2 +- 7 files changed, 248 insertions(+), 62 deletions(-) create mode 100644 src/main/java/io/github/redouane59/twitter/dto/dm/DmParameters.java create mode 100644 src/main/java/io/github/redouane59/twitter/dto/dm/PostDmResponse.java diff --git a/src/main/java/io/github/redouane59/twitter/ITwitterClientV2.java b/src/main/java/io/github/redouane59/twitter/ITwitterClientV2.java index bea7f0a9..e307ad50 100644 --- a/src/main/java/io/github/redouane59/twitter/ITwitterClientV2.java +++ b/src/main/java/io/github/redouane59/twitter/ITwitterClientV2.java @@ -2,6 +2,9 @@ import com.github.scribejava.core.model.Response; import io.github.redouane59.twitter.dto.dm.DirectMessage; +import io.github.redouane59.twitter.dto.dm.DmParameters; +import io.github.redouane59.twitter.dto.dm.DmParameters.DmMessage; +import io.github.redouane59.twitter.dto.dm.PostDmResponse; import io.github.redouane59.twitter.dto.endpoints.AdditionalParameters; import io.github.redouane59.twitter.dto.list.TwitterList; import io.github.redouane59.twitter.dto.list.TwitterListList; @@ -687,4 +690,41 @@ public interface ITwitterClientV2 { * https://api.twitter.com/2/dm_conversations/with/:participant_id/dm_events. Messages are returned in reverse chronological order. */ DirectMessage getDirectMessagesByUser(String participantId, AdditionalParameters additionalParameters); + + /** + * Creates a Direct Message on behalf of an authenticated user, and adds it to the specified conversation calling + * https://api.twitter.com/2/dm_conversations/:dm_conversation_id/messages. + */ + PostDmResponse createDirectMessage(String conversationId, String text); + + /** + * Creates a Direct Message on behalf of an authenticated user, and adds it to the specified conversation calling + * https://api.twitter.com/2/dm_conversations/:dm_conversation_id/messages. + */ + PostDmResponse createDirectMessage(String conversationId, DmMessage message); + + /** + * Creates a new group conversation and adds a Direct Message to it on behalf of an authenticated user calling + * https://api.twitter.com/2/dm_conversations + */ + PostDmResponse createGroupDmConversation(DmParameters parameters); + + /** + * Creates a new group conversation and adds a Direct Message to it on behalf of an authenticated user calling + * https://api.twitter.com/2/dm_conversations + */ + PostDmResponse createGroupDmConversation(List participantIds, String text); + + /** + * Creates a one-to-one Direct Message and adds it to the one-to-one conversation. This method either creates a new one-to-one conversation or + * retrieves the current conversation and adds the Direct Message to it calling https://api.twitter.com/2/dm_conversations/with/:participant_id/messages. + */ + PostDmResponse createUserDmConversation(String participantId, String text); + + /** + * Creates a one-to-one Direct Message and adds it to the one-to-one conversation. This method either creates a new one-to-one conversation or + * retrieves the current conversation and adds the Direct Message to it calling https://api.twitter.com/2/dm_conversations/with/:participant_id/messages. + */ + PostDmResponse createUserDmConversation(String participantId, DmMessage message); + } diff --git a/src/main/java/io/github/redouane59/twitter/TwitterClient.java b/src/main/java/io/github/redouane59/twitter/TwitterClient.java index 81d7da5b..192abb23 100644 --- a/src/main/java/io/github/redouane59/twitter/TwitterClient.java +++ b/src/main/java/io/github/redouane59/twitter/TwitterClient.java @@ -17,7 +17,9 @@ import io.github.redouane59.twitter.dto.collections.CollectionsResponse; import io.github.redouane59.twitter.dto.collections.TimeLineOrder; import io.github.redouane59.twitter.dto.dm.DirectMessage; -import io.github.redouane59.twitter.dto.dm.deprecatedV1.DmEvent; +import io.github.redouane59.twitter.dto.dm.DmParameters; +import io.github.redouane59.twitter.dto.dm.DmParameters.DmMessage; +import io.github.redouane59.twitter.dto.dm.PostDmResponse; import io.github.redouane59.twitter.dto.dm.deprecatedV1.DmListAnswer; import io.github.redouane59.twitter.dto.endpoints.AdditionalParameters; import io.github.redouane59.twitter.dto.getrelationship.IdList; @@ -861,6 +863,59 @@ public DirectMessage getDirectMessagesByUser(final String participantId, final A } + @Override + public PostDmResponse createDirectMessage(final String conversationId, String text) { + return createDirectMessage(conversationId, DmMessage.builder().text(text).build()); + } + + @Override + public PostDmResponse createDirectMessage(final String conversationId, DmMessage message) { + String url = getUrlHelper().getPostConversationDmUrl(conversationId); + String body; + try { + body = JsonHelper.toJson(message); + } catch (JsonProcessingException e) { + LOGGER.error(e.getMessage(), e); + throw new IllegalArgumentException(); + } + return getRequestHelperV1().postRequestWithBodyJson(url, null, body, PostDmResponse.class).orElseThrow(NoSuchElementException::new); + } + + public PostDmResponse createGroupDmConversation(List participantIds, String text) { + return createGroupDmConversation(DmParameters.builder() + .participantIds(participantIds) + .message(DmMessage.builder().text(text).build()) + .build()); + } + + public PostDmResponse createGroupDmConversation(DmParameters parameters) { + String url = getUrlHelper().getCreateDmConversationUrl(); + String body; + try { + body = JsonHelper.toJson(parameters); + } catch (JsonProcessingException e) { + LOGGER.error(e.getMessage(), e); + throw new IllegalArgumentException(); + } + return getRequestHelperV1().postRequestWithBodyJson(url, null, body, PostDmResponse.class).orElseThrow(NoSuchElementException::new); + } + + public PostDmResponse createUserDmConversation(String participantId, String text) { + return createUserDmConversation(participantId, DmMessage.builder().text(text).build()); + } + + public PostDmResponse createUserDmConversation(String participantId, DmMessage message) { + String url = getUrlHelper().getPostUserDmUrl(participantId); + String body; + try { + body = JsonHelper.toJson(message); + } catch (JsonProcessingException e) { + LOGGER.error(e.getMessage(), e); + throw new IllegalArgumentException(); + } + return getRequestHelperV1().postRequestWithBodyJson(url, null, body, PostDmResponse.class).orElseThrow(NoSuchElementException::new); + } + @Override public Tweet getTweet(String tweetId) { @@ -1414,18 +1469,24 @@ public List getD @Override public io.github.redouane59.twitter.dto.dm.deprecatedV1.DirectMessage getDm(String dmId) { - String url = urlHelper.getDmUrl(dmId); - DmEvent result = getRequestHelper().getRequest(url, DmEvent.class).orElseThrow(NoSuchElementException::new); + String url = urlHelper.getDmUrl(dmId); + io.github.redouane59.twitter.dto.dm.deprecatedV1.DmEvent + result = + getRequestHelper().getRequest(url, io.github.redouane59.twitter.dto.dm.deprecatedV1.DmEvent.class).orElseThrow(NoSuchElementException::new); return result.getEvent(); } @Override - public DmEvent postDm(final String text, final String userId) { - String url = urlHelper.getPostDmUrl(); + public io.github.redouane59.twitter.dto.dm.deprecatedV1.DmEvent postDm(final String text, final String userId) { + String url = urlHelper.getPostConversationDmUrl(); try { String body = JsonHelper.toJson( - DmEvent.builder().event(new io.github.redouane59.twitter.dto.dm.deprecatedV1.DirectMessage(text, userId)).build()); - return getRequestHelperV1().postRequestWithBodyJson(url, null, body, DmEvent.class).orElseThrow(NoSuchElementException::new); + io.github.redouane59.twitter.dto.dm.deprecatedV1.DmEvent.builder() + .event(new io.github.redouane59.twitter.dto.dm.deprecatedV1.DirectMessage(text, + userId)) + .build()); + return getRequestHelperV1().postRequestWithBodyJson(url, null, body, io.github.redouane59.twitter.dto.dm.deprecatedV1.DmEvent.class) + .orElseThrow(NoSuchElementException::new); } catch (JsonProcessingException e) { LOGGER.error(e.getMessage(), e); } diff --git a/src/main/java/io/github/redouane59/twitter/dto/dm/DmParameters.java b/src/main/java/io/github/redouane59/twitter/dto/dm/DmParameters.java new file mode 100644 index 00000000..abf15579 --- /dev/null +++ b/src/main/java/io/github/redouane59/twitter/dto/dm/DmParameters.java @@ -0,0 +1,26 @@ +package io.github.redouane59.twitter.dto.dm; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import lombok.Builder; +import lombok.Getter; + +@Builder +public class DmParameters { + + @JsonProperty("conversation_type") + @Builder.Default + private String conversationType = "Group"; + @JsonProperty("message") + private DmMessage message; + @JsonProperty("participant_ids") + private List participantIds; + + @Builder + @Getter + public static class DmMessage { + + private String text; + private String attachments; // @to be improved + } +} diff --git a/src/main/java/io/github/redouane59/twitter/dto/dm/PostDmResponse.java b/src/main/java/io/github/redouane59/twitter/dto/dm/PostDmResponse.java new file mode 100644 index 00000000..3f293bfe --- /dev/null +++ b/src/main/java/io/github/redouane59/twitter/dto/dm/PostDmResponse.java @@ -0,0 +1,20 @@ +package io.github.redouane59.twitter.dto.dm; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +@Getter +public class PostDmResponse { + + private PostDmEvent data; + + @Getter + public static class PostDmEvent { + + @JsonProperty("dm_conversation_id") + private String dmConversationId; + @JsonProperty("dm_event_id") + private String dmEventId; + + } +} diff --git a/src/main/java/io/github/redouane59/twitter/helpers/URLHelper.java b/src/main/java/io/github/redouane59/twitter/helpers/URLHelper.java index 94c5c180..22824876 100644 --- a/src/main/java/io/github/redouane59/twitter/helpers/URLHelper.java +++ b/src/main/java/io/github/redouane59/twitter/helpers/URLHelper.java @@ -32,75 +32,79 @@ public class URLHelper { private static final String DIRECT_MESSAGE_EVENTS = "/direct_messages/events"; // v2 - private final String idVariable = ":id"; + private final String idVariable = ":id"; @Getter - private final String searchRecentTweetsUrl = "https://api.twitter.com/2/tweets/search/recent"; + private final String searchRecentTweetsUrl = "https://api.twitter.com/2/tweets/search/recent"; @Getter - private final String searchAllTweetsUrl = "https://api.twitter.com/2/tweets/search/all"; + private final String searchAllTweetsUrl = "https://api.twitter.com/2/tweets/search/all"; @Getter - private final String filteredStreamRulesUrl = "https://api.twitter.com/2/tweets/search/stream/rules"; + private final String filteredStreamRulesUrl = "https://api.twitter.com/2/tweets/search/stream/rules"; @Getter - private final String filteredStreamUrl = "https://api.twitter.com/2/tweets/search/stream"; + private final String filteredStreamUrl = "https://api.twitter.com/2/tweets/search/stream"; @Getter - private final String sampledStreamUrl = "https://api.twitter.com/2/tweets/sample/stream"; + private final String sampledStreamUrl = "https://api.twitter.com/2/tweets/sample/stream"; @Getter - private final String tweetsCountUrl = "https://api.twitter.com/2/tweets/counts/recent"; + private final String tweetsCountUrl = "https://api.twitter.com/2/tweets/counts/recent"; @Getter - private final String tweetsCountAllUrl = "https://api.twitter.com/2/tweets/counts/all"; + private final String tweetsCountAllUrl = "https://api.twitter.com/2/tweets/counts/all"; @Getter - private final String tweetsUrl = "https://api.twitter.com/2/tweets"; + private final String tweetsUrl = "https://api.twitter.com/2/tweets"; @Getter - private final String usersByUrl = "https://api.twitter.com/2/users/by"; + private final String usersByUrl = "https://api.twitter.com/2/users/by"; @Getter - private final String usersUrl = "https://api.twitter.com/2/users"; + private final String usersUrl = "https://api.twitter.com/2/users"; @Getter - private final String spacesUrl = "https://api.twitter.com/2/spaces"; + private final String spacesUrl = "https://api.twitter.com/2/spaces"; @Getter - private final String spaceByCreatorUrl = "https://api.twitter.com/2/spaces/by/creator_ids"; + private final String spaceByCreatorUrl = "https://api.twitter.com/2/spaces/by/creator_ids"; @Getter - private final String searchSpacesUrl = "https://api.twitter.com/2/spaces/search"; + private final String searchSpacesUrl = "https://api.twitter.com/2/spaces/search"; @Getter - private final String listUrlV2 = "https://api.twitter.com/2/lists"; + private final String listUrlV2 = "https://api.twitter.com/2/lists"; @Getter - private final String postTweetUrl = "https://api.twitter.com/2/tweets"; + private final String postTweetUrl = "https://api.twitter.com/2/tweets"; @Getter - private final String dmEventsUrl = "https://api.twitter.com/2/dm_events"; - - private final String followUrl = "https://api.twitter.com/2/users/:id/following"; - private final String unfollowUrl = "https://api.twitter.com/2/users/:sourceId/following/:targetId"; - private final String followersUrl = "https://api.twitter.com/2/users/:id/followers"; - private final String followingUrl = "https://api.twitter.com/2/users/:id/following"; - private final String userUrl = "https://api.twitter.com/2/users/:id"; - private final String userUrlFromName = "https://api.twitter.com/2/users/by/username/:username"; - private final String tweetUrl = "https://api.twitter.com/2/tweets/:id"; - private final String likeUrl = "https://api.twitter.com/2/users/:id/likes"; - private final String unlikeUrl = "https://api.twitter.com/2/users/:userId/likes/:tweetId"; - private final String hideUrl = "https://api.twitter.com/2/tweets/:id/hidden"; - private final String userTimelineUrl = "https://api.twitter.com/2/users/:id/tweets"; - private final String userMentionsUrl = "https://api.twitter.com/2/users/:id/mentions"; - private final String blockUserUrl = "https://api.twitter.com/2/users/:id/blocking"; - private final String unblockUserUrl = "https://api.twitter.com/2/users/:sourceId/blocking/:targetId"; - private final String blockingUsersUrl = "https://api.twitter.com/2/users/:id/blocking"; - private final String likingUsersUrl = "https://api.twitter.com/2/tweets/:id/liking_users"; - private final String likedTweetsUrl = "https://api.twitter.com/2/users/:id/liked_tweets"; - private final String muteUserUrl = "https://api.twitter.com/2/users/:id/muting"; - private final String unmuteUserUrl = "https://api.twitter.com/2/users/:source_user_id/muting/:target_user_id"; - private final String mutedUsersUrl = "https://api.twitter.com/2/users/:id/muting"; - private final String retweetingUsersUrl = "https://api.twitter.com/2/tweets/:id/retweeted_by"; - private final String retweetTweetUrl = "https://api.twitter.com/2/users/:id/retweets"; - private final String unretweetTweetUrl = "https://api.twitter.com/2/users/:id/retweets/:source_tweet_id"; - private final String spaceUrl = "https://api.twitter.com/2/spaces/:id"; - private final String spaceBuyersUrl = "https://api.twitter.com/2/spaces/:id/buyers"; - private final String addListMemberUrl = "https://api.twitter.com/2/lists/:id/members"; - private final String removeListMemberUrl = "https://api.twitter.com/2/lists/:id/members/:user_id"; - private final String listTweetsUrl = "https://api.twitter.com/2/lists/:id/tweets"; - private final String pinListUrl = "https://api.twitter.com/2/users/:id/pinned_lists"; - private final String unpinListUrl = "https://api.twitter.com/2/users/:id/pinned_lists/:list_id"; - private final String followListUrl = "https://api.twitter.com/2/users/:id/followed_lists"; - private final String unfollowListUrl = "https://api.twitter.com/2/users/:id/followed_lists/:list_id"; - private final String ownedListUrl = "https://api.twitter.com/2/users/:id/owned_lists"; - private final String dmLookupUrl = "https://api.twitter.com/2/dm_conversations/:dm_conversation_id/dm_events"; - private final String dmUserLookupUrl = "https://api.twitter.com/2/dm_conversations/with/:participant_id/dm_events"; + private final String dmEventsUrl = "https://api.twitter.com/2/dm_events"; + @Getter + private final String createDmConversationUrl = "https://api.twitter.com/2/dm_conversations"; + + private final String followUrl = "https://api.twitter.com/2/users/:id/following"; + private final String unfollowUrl = "https://api.twitter.com/2/users/:sourceId/following/:targetId"; + private final String followersUrl = "https://api.twitter.com/2/users/:id/followers"; + private final String followingUrl = "https://api.twitter.com/2/users/:id/following"; + private final String userUrl = "https://api.twitter.com/2/users/:id"; + private final String userUrlFromName = "https://api.twitter.com/2/users/by/username/:username"; + private final String tweetUrl = "https://api.twitter.com/2/tweets/:id"; + private final String likeUrl = "https://api.twitter.com/2/users/:id/likes"; + private final String unlikeUrl = "https://api.twitter.com/2/users/:userId/likes/:tweetId"; + private final String hideUrl = "https://api.twitter.com/2/tweets/:id/hidden"; + private final String userTimelineUrl = "https://api.twitter.com/2/users/:id/tweets"; + private final String userMentionsUrl = "https://api.twitter.com/2/users/:id/mentions"; + private final String blockUserUrl = "https://api.twitter.com/2/users/:id/blocking"; + private final String unblockUserUrl = "https://api.twitter.com/2/users/:sourceId/blocking/:targetId"; + private final String blockingUsersUrl = "https://api.twitter.com/2/users/:id/blocking"; + private final String likingUsersUrl = "https://api.twitter.com/2/tweets/:id/liking_users"; + private final String likedTweetsUrl = "https://api.twitter.com/2/users/:id/liked_tweets"; + private final String muteUserUrl = "https://api.twitter.com/2/users/:id/muting"; + private final String unmuteUserUrl = "https://api.twitter.com/2/users/:source_user_id/muting/:target_user_id"; + private final String mutedUsersUrl = "https://api.twitter.com/2/users/:id/muting"; + private final String retweetingUsersUrl = "https://api.twitter.com/2/tweets/:id/retweeted_by"; + private final String retweetTweetUrl = "https://api.twitter.com/2/users/:id/retweets"; + private final String unretweetTweetUrl = "https://api.twitter.com/2/users/:id/retweets/:source_tweet_id"; + private final String spaceUrl = "https://api.twitter.com/2/spaces/:id"; + private final String spaceBuyersUrl = "https://api.twitter.com/2/spaces/:id/buyers"; + private final String addListMemberUrl = "https://api.twitter.com/2/lists/:id/members"; + private final String removeListMemberUrl = "https://api.twitter.com/2/lists/:id/members/:user_id"; + private final String listTweetsUrl = "https://api.twitter.com/2/lists/:id/tweets"; + private final String pinListUrl = "https://api.twitter.com/2/users/:id/pinned_lists"; + private final String unpinListUrl = "https://api.twitter.com/2/users/:id/pinned_lists/:list_id"; + private final String followListUrl = "https://api.twitter.com/2/users/:id/followed_lists"; + private final String unfollowListUrl = "https://api.twitter.com/2/users/:id/followed_lists/:list_id"; + private final String ownedListUrl = "https://api.twitter.com/2/users/:id/owned_lists"; + private final String dmLookupUrl = "https://api.twitter.com/2/dm_conversations/:dm_conversation_id/dm_events"; + private final String dmUserLookupUrl = "https://api.twitter.com/2/dm_conversations/with/:participant_id/dm_events"; + private final String postConversationDmUrl = "https://api.twitter.com/2/dm_conversations/:dm_conversation_id/messages"; + private final String postUserDmUrl = "https://api.twitter.com/2/dm_conversations/with/:participant_id/messages"; public String getSearchTweet30DaysUrl(String envName) { return ROOT_URL_V1 + TWEETS + SEARCH + THIRTY_DAYS + "/" + envName + JSON; @@ -259,7 +263,7 @@ public String getDmUrl(String id) { } @Deprecated - public String getPostDmUrl() { + public String getPostConversationDmUrl() { return ROOT_URL_V1 + DIRECT_MESSAGE_EVENTS + "/new.json"; } @@ -322,4 +326,13 @@ public String getDmLookupUrl(String conversationId) { public String getDmUserLookupUrl(String conversationId) { return dmUserLookupUrl.replace(":participant_id", conversationId); } + + public String getPostConversationDmUrl(String conversationId) { + return postConversationDmUrl.replace(":dm_conversation_id", conversationId); + } + + public String getPostUserDmUrl(String participantId) { + return postUserDmUrl.replace(":participant_id", participantId); + } + } diff --git a/src/test/java/io/github/redouane59/twitter/nrt/ITwitterClientV2AuthenticatedTest.java b/src/test/java/io/github/redouane59/twitter/nrt/ITwitterClientV2AuthenticatedTest.java index f93db8d9..eec2857a 100644 --- a/src/test/java/io/github/redouane59/twitter/nrt/ITwitterClientV2AuthenticatedTest.java +++ b/src/test/java/io/github/redouane59/twitter/nrt/ITwitterClientV2AuthenticatedTest.java @@ -8,6 +8,7 @@ import io.github.redouane59.RelationType; import io.github.redouane59.twitter.TwitterClient; import io.github.redouane59.twitter.dto.dm.DirectMessage; +import io.github.redouane59.twitter.dto.dm.PostDmResponse; import io.github.redouane59.twitter.dto.endpoints.AdditionalParameters; import io.github.redouane59.twitter.dto.list.TwitterList; import io.github.redouane59.twitter.dto.others.BearerToken; @@ -408,4 +409,29 @@ public void testGetDirectMessagesByUser() { assertTrue(dmEvents.getData().size() < 3); } + @Test + public void testSendDmInConversation() { + String conversationId = "92073489-1120050519182016513"; + PostDmResponse response = twitterClient.createDirectMessage(conversationId, "testSendDmInConversation()"); + assertNotNull(response.getData().getDmConversationId()); + assertNotNull(response.getData().getDmEventId()); + } + + @Test + public void testCreateDmGroupConversation() { + PostDmResponse + response = + twitterClient.createGroupDmConversation(Arrays.asList("1307302673318895621", "92073489"), + "testCreateDmGroupConversation()"); + assertNotNull(response.getData().getDmConversationId()); + assertNotNull(response.getData().getDmEventId()); + } + + @Test + public void testCreateDmUserConversation() { + PostDmResponse response = twitterClient.createUserDmConversation("1307302673318895621", "testCreateDmUserConversation()"); + assertNotNull(response.getData().getDmConversationId()); + assertNotNull(response.getData().getDmEventId()); + } + } \ No newline at end of file diff --git a/src/test/java/io/github/redouane59/twitter/unit/UrlHelperTest.java b/src/test/java/io/github/redouane59/twitter/unit/UrlHelperTest.java index 6f680546..7b5175f6 100644 --- a/src/test/java/io/github/redouane59/twitter/unit/UrlHelperTest.java +++ b/src/test/java/io/github/redouane59/twitter/unit/UrlHelperTest.java @@ -238,7 +238,7 @@ public void testDmUrl() { @Test public void testPostDmUrl() { - assertEquals("https://api.twitter.com/1.1/direct_messages/events/new.json", urlHelper.getPostDmUrl()); + assertEquals("https://api.twitter.com/1.1/direct_messages/events/new.json", urlHelper.getPostConversationDmUrl()); } @Test