Skip to content

Instantly share code, notes, and snippets.

@travisbrown
Last active November 10, 2017 13:19
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save travisbrown/71eddd5d4dc266decf096b02fabba1ab to your computer and use it in GitHub Desktop.
Save travisbrown/71eddd5d4dc266decf096b02fabba1ab to your computer and use it in GitHub Desktop.
import cats.data.StateT
import cats.implicits._
import scala.collection.JavaConverters._
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration
import twitter4j._, twitter4j.conf.ConfigurationBuilder
object YourTweetsAreTooLong {
private type PagingState = (Long, Option[Long])
private type OffenderState = (PagingState, Map[String, Set[Long]])
private type S[x] = StateT[Future, OffenderState, x]
private val config = new ConfigurationBuilder().setTweetModeExtended(true)
private val client: Twitter = new TwitterFactory(config.build).getInstance()
private val strikes = 3
private val threshold = 200
def isBad(status: Status): Boolean = status.getText.length > threshold
val nextBatch: StateT[Future, PagingState, List[Status]] = StateT {
case (timeToWait, lastId) =>
val paging = lastId.fold(new Paging(1, 200))(new Paging(1, 200, _))
Future {
Thread.sleep(timeToWait)
client.getHomeTimeline(paging)
}.map { result =>
val remaining = result.getRateLimitStatus.getRemaining
val nextTimeToWait = if (remaining > 0) 60000L else {
result.getRateLimitStatus.getSecondsUntilReset * 1000L
}
val statuses = result.asScala.toList
((nextTimeToWait, statuses.headOption.map(_.getId)), statuses)
}
}
val unfollow: S[Unit] = nextBatch
.transformS[OffenderState](_._1, (t, o) => (o, t._2))
.transform {
case ((state, offenders), statuses) =>
val newOffenders = offenders |+| statuses
.filter(isBad)
.groupBy(_.getUser.getScreenName)
.mapValues(_.map(_.getId).toSet)
((state, newOffenders), newOffenders)
}.flatMapF {
_.toList.filter(_._2.size >= strikes).map(_._1).traverse_ { user =>
Future {
println(s"so long $user")
client.destroyFriendship(user)
}
}
}
def main(args: Array[String]): Unit = Await.result(
unfollow.iterateWhile(_ => true).run(((0L, None), Map.empty)),
Duration.Inf
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment