Skip to content
This repository has been archived by the owner on Mar 11, 2023. It is now read-only.

new direct message api #229

Merged
merged 22 commits into from
Dec 1, 2018
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ lazy val standardSettings = Seq(
"UTF-8",
"-Xlint",
"-deprecation",
"-Xfatal-warnings",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fatal-warnings flag is too important to remove it.
If you cannot find a way to let the tests involving deprecation pass, I would rather delete them that removing the flag.

"-feature",
"-language:postfixOps",
"-unchecked"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.danielasfregola.twitter4s.entities

final case class DirectMessageEvent(`type`: String,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type should be an enum as we should know the set of possible values accepted by the Twitter API
ids general are of type Long, any particular reason they are of type String here?
timestamps should have a type consistent to the other types of timestamp in the code. Also, we may want to add a default to "now", so that people can just create the entity without providing one explicitly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

id: String,
created_timestamp: String,
message_create: MessageCreate)

final case class MessageCreate(target: Target, sender_id: Option[String], message_data: MessageData)

final case class Target(recipient_id: String)

final case class MessageData(text: String, entities: Option[Entities] = None, attachment: Option[Attachment] = None)

final case class Attachment(`type`: String, media: Media)

final case class Apps(id: String, name: String, url: String)

final case class SingleEvent(event: DirectMessageEvent, apps: Option[Map[String, App]])
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.danielasfregola.twitter4s.entities
import com.danielasfregola.twitter4s.entities.streaming.UserStreamingMessage

final case class DirectMessageEventList(events: List[DirectMessageEvent],
apps: Option[Map[String, Apps]],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why an Option[Map[.., ...]] and not just a Map[..., ...]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Owner

@DanielaSfregola DanielaSfregola Nov 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be changed to just Map[..., ...] unless there is a specific reason to keep the optional wrapper

next_cursor: Option[String])
extends UserStreamingMessage
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.danielasfregola.twitter4s.entities

private[twitter4s] final case class NewDirectMessageEvent(event: NewEvent)

private[twitter4s] final case class NewEvent(`type`: String = "message_create", message_create: MessageCreate)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type should be an enum

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.danielasfregola.twitter4s.http.clients.rest.directmessages

import com.danielasfregola.twitter4s.entities.{DirectMessage, RatedData}
import akka.http.scaladsl.model.{ContentType, MediaTypes}
import com.danielasfregola.twitter4s.entities._
import com.danielasfregola.twitter4s.http.clients.rest.RestClient
import com.danielasfregola.twitter4s.http.clients.rest.directmessages.parameters._
import com.danielasfregola.twitter4s.util.Configurations._
Expand All @@ -14,6 +15,47 @@ trait TwitterDirectMessageClient {
protected val restClient: RestClient

private val directMessagesUrl = s"$apiTwitterUrl/$twitterVersion/direct_messages"
private val events = s"$directMessagesUrl/events"

/**Returns all Direct Message events (both sent and received) within the last 30 days.
* Sorted in reverse-chronological order.
* Replace directMessage methods.
* @param count : Optional parameter. Max number of events to be returned. 20 default. 50 max.
* @param cursor : Optional parameter. For paging through result sets greater than 1 page,
* use the “next_cursor” property from the previous request.
* @return : list of events
*/
def eventsList(count: Int = 20, cursor: Option[String] = None): Future[DirectMessageEventList] = {
import restClient._
val parameters = EventListParameters(count, cursor)
Get(s"$events/list.json", parameters).respondAs[DirectMessageEventList]
}

/**Returns Direct Message event (both sent and received) by Id.
* @param id : Id of event.
* @return : event
*/
def eventShow(id: Long): Future[SingleEvent] = {
import restClient._
val parameters = ShowParameters(id)
Get(s"$events/show.json", parameters).respondAs[SingleEvent]
}

/**Sends a new direct message to the specified user from the authenticating user.
* Replace createDirectMessage method.
*
* @param id : Id Max number of events to be returned. 20 default. 50 max.
* @param text : The text of your direct message. Be sure to URL encode as necessary,
* and keep the message under 140 characters.
* @return : event with new message
*/
def messageCreate(id: String, text: String): Future[SingleEvent] = {
import restClient._
import org.json4s.native.Serialization.write
val parameters = NewDirectMessageEvent(
NewEvent(message_create = MessageCreate(Target(id), None, MessageData(text, None, None))))
Post(s"$events/new.json", write(parameters), ContentType(MediaTypes.`application/json`)).respondAs[SingleEvent]
}

/** Returns a single direct message, specified by an id parameter.
* For more information see
Expand All @@ -23,6 +65,7 @@ trait TwitterDirectMessageClient {
* @param id : The ID of the direct message.
* @return : The direct message.
* */
@deprecated("Twitter endpoint deprecated from 17th Sep 2018. Please use 'eventShow' instead.", "twitter4s 6.0")
def directMessage(id: Long): Future[RatedData[DirectMessage]] = {
import restClient._
val parameters = ShowParameters(id)
Expand All @@ -40,6 +83,7 @@ trait TwitterDirectMessageClient {
* and keep the message under 140 characters.
* @return : The sent direct message.
* */
@deprecated("Twitter endpoint deprecated from 17th Sep 2018. Please use 'messageCreate' instead.", "twitter4s 6.0")
def createDirectMessage(user_id: Long, text: String): Future[DirectMessage] = {
val parameters = CreateParameters(user_id = Some(user_id), text = text)
genericCreateDirectMessage(parameters)
Expand All @@ -56,6 +100,7 @@ trait TwitterDirectMessageClient {
* and keep the message under 140 characters.
* @return : The sent direct message.
* */
@deprecated("Twitter endpoint deprecated from 17th Sep 2018. Please use 'messageCreate' instead.", "twitter4s 6.0")
def createDirectMessage(screen_name: String, text: String): Future[DirectMessage] = {
val parameters = CreateParameters(screen_name = Some(screen_name), text = text)
genericCreateDirectMessage(parameters)
Expand Down Expand Up @@ -83,6 +128,7 @@ trait TwitterDirectMessageClient {
* Specifies the number of records to retrieve. Must be less than or equal to 200.
* @return : The sequence of sent direct messages.
* */
@deprecated("Twitter endpoint deprecated from 17th Sep 2018. Please use 'eventsList' instead.", "twitter4s 6.0")
def sentDirectMessages(since_id: Option[Long] = None,
max_id: Option[Long] = None,
count: Int = 200,
Expand Down Expand Up @@ -116,6 +162,7 @@ trait TwitterDirectMessageClient {
* When set to either `true` statuses will not be included in the returned user object.
* @return : The sequence of received direct messages.
* */
@deprecated("Twitter endpoint deprecated from 17th Sep 2018. Please use 'eventsList' instead.", "twitter4s 6.0")
def receivedDirectMessages(since_id: Option[Long] = None,
max_id: Option[Long] = None,
count: Int = 200,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.danielasfregola.twitter4s.http.clients.rest.directmessages.parameters
import com.danielasfregola.twitter4s.http.marshalling.Parameters

private[twitter4s] final case class EventListParameters(count: Int, next_cursor: Option[String]) extends Parameters
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.danielasfregola.twitter4s.http
package oauth

import akka.http.scaladsl.model._
import akka.http.scaladsl.model.headers.RawHeader
import akka.http.scaladsl.model.{HttpHeader, HttpRequest}
import akka.stream.Materializer
import com.danielasfregola.twitter4s.entities.{AccessToken, ConsumerToken}
import com.danielasfregola.twitter4s.util.{Encoder, UriHelpers}
Expand Down Expand Up @@ -80,7 +80,9 @@ private[twitter4s] class OAuth1Provider(consumerToken: ConsumerToken, accessToke
implicit val ec = materializer.executionContext
extractRequestBody.map { body =>
val cleanBody = body.replace("+", "%20")
if (cleanBody.nonEmpty) {
if (cleanBody.nonEmpty && request.entity
.getContentType()
.mediaType == MediaTypes.`application/x-www-form-urlencoded`) {
mitgard marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to check this before merging, as this could break other endpoints' authentication.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be we return Map() if we cannot calculate Map

val entities = cleanBody.split("&")
val bodyTokens = entities.flatMap(_.split("=", 2)).toList
bodyTokens.grouped(2).map { case List(k, v) => k -> v }.toMap
Expand Down
22 changes: 22 additions & 0 deletions src/test/resources/fixtures/rest/directmessages/event.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"event": {
"type": "message_create",
"id": "1044927409530647812",
"created_timestamp": "1537965082782",
"message_create": {
"target": {
"recipient_id": "773439338287913984"
},
"sender_id": "24130273702",
"message_data": {
"text": "New test message",
"entities": {
"hashtags": [],
"symbols": [],
"user_mentions": [],
"urls": []
}
}
}
}
}
30 changes: 30 additions & 0 deletions src/test/resources/fixtures/rest/directmessages/list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"events" : [ {
"type" : "message_create",
"id" : "1044927409530647812",
"created_timestamp" : "1537935082782",
"message_create" : {
"target" : {
"recipient_id" : "773439338287913984"
},
"sender_id" : "24130273702",
"message_data" : {
"text" : "New test message",
"entities" : {
"hashtags" : [ ],
"symbols" : [ ],
"user_mentions" : [ ],
"urls" : [ ]
}
}
}
} ],
"apps" : {
"14855158" : {
"id" : "14855158",
"name" : "TestApplication",
"url" : "http://TestApplicationUrl.ru/"
}
},
"next_cursor" : "MTA0NDkyNzQwOTAzMDY0NzgxMg"
}
22 changes: 22 additions & 0 deletions src/test/resources/twitter/rest/directmessages/event.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"event": {
"type": "message_create",
"id": "1044927409530647812",
"created_timestamp": "1537965082782",
"message_create": {
"target": {
"recipient_id": "773439338287913984"
},
"sender_id": "24130273702",
"message_data": {
"text": "New test message",
"entities": {
"hashtags": [],
"symbols": [],
"user_mentions": [],
"urls": []
}
}
}
}
}
30 changes: 30 additions & 0 deletions src/test/resources/twitter/rest/directmessages/list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"events" : [ {
"type" : "message_create",
"id" : "1044927409530647812",
"created_timestamp" : "1537935082782",
"message_create" : {
"target" : {
"recipient_id" : "773439338287913984"
},
"sender_id" : "24130273702",
"message_data" : {
"text" : "New test message",
"entities" : {
"hashtags" : [ ],
"symbols" : [ ],
"user_mentions" : [ ],
"urls" : [ ]
}
}
}
} ],
"apps" : {
"14855158" : {
"id" : "14855158",
"name" : "TestApplication",
"url" : "http://TestApplicationUrl.ru/"
}
},
"next_cursor" : "MTA0NDkyNzQwOTAzMDY0NzgxMg"
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.danielasfregola.twitter4s.http.clients.rest.directmessages

import akka.http.scaladsl.model.HttpMethods
import com.danielasfregola.twitter4s.entities.{DirectMessage, RatedData}
import akka.http.scaladsl.model.{ContentType, HttpMethods, MediaTypes}
import com.danielasfregola.twitter4s.entities._
import com.danielasfregola.twitter4s.helpers.ClientSpec

class TwitterDirectMessageClientSpec extends ClientSpec {
Expand Down Expand Up @@ -87,6 +87,42 @@ class TwitterDirectMessageClientSpec extends ClientSpec {
result === loadJsonAs[DirectMessage]("/fixtures/rest/directmessages/new.json")
}

}
"get list of direct message events" in new TwitterDirectMessageClientSpecContext {
val result: DirectMessageEventList = when(eventsList(count = 1, Some("MTA0NDkxMzE5OTMyMDA0MzUyNw")))
.expectRequest { request =>
request.method === HttpMethods.GET
request.uri.endpoint === "https://api.twitter.com/1.1/direct_messages/events/list.json"
request.uri.rawQueryString === Some("count=1&next_cursor=MTA0NDkxMzE5OTMyMDA0MzUyNw")
}
.respondWith("/twitter/rest/directmessages/list.json")
.await
result === loadJsonAs[DirectMessageEventList]("/fixtures/rest/directmessages/list.json")
}

"get direct message event" in new TwitterDirectMessageClientSpecContext {
val result: SingleEvent = when(eventShow(1044927409530647812L))
.expectRequest { request =>
request.method === HttpMethods.GET
request.uri.endpoint === "https://api.twitter.com/1.1/direct_messages/events/show.json"
request.uri.rawQueryString === Some("id=1044927409530647812")
}
.respondWith("/twitter/rest/directmessages/event.json")
.await
result === loadJsonAs[SingleEvent]("/fixtures/rest/directmessages/event.json")
}

"create a direct message" in new TwitterDirectMessageClientSpecContext {
val text = "New test message"
val result: SingleEvent = when(messageCreate("1044927409530647812", text))
.expectRequest { request =>
request.method === HttpMethods.POST
request.uri.endpoint === "https://api.twitter.com/1.1/direct_messages/events/new.json"
request.entity.getContentType() === ContentType(MediaTypes.`application/json`)
}
.respondWith("/twitter/rest/directmessages/event.json")
.await
result === loadJsonAs[SingleEvent]("/fixtures/rest/directmessages/event.json")
}

}
}