mirror of
https://github.com/twitter/the-algorithm.git
synced 2024-11-16 08:29:21 +01:00
[docx] final commit @ Tue 23 Jan 19:23:08 EET 2024 for file 6906
Signed-off-by: Ari Archer <ari.web.xyz@gmail.com>
This commit is contained in:
parent
e90b21ec83
commit
c53a0a714d
Binary file not shown.
@ -1,181 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.notifications
|
|
||||||
|
|
||||||
import com.twitter.finagle.stats.StatsReceiver
|
|
||||||
import com.twitter.notificationservice.model.notification.Notification
|
|
||||||
import com.twitter.notificationservice.model.notification.NotificationType
|
|
||||||
import com.twitter.notificationservice.model.notification.SimpleActivityNotification
|
|
||||||
import com.twitter.servo.util.Gate
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.visibility.VisibilityLibrary
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.builder.tweets.CommunityNotificationFeatures
|
|
||||||
import com.twitter.visibility.builder.tweets.UnmentionNotificationFeatures
|
|
||||||
import com.twitter.visibility.builder.users.AuthorDeviceFeatures
|
|
||||||
import com.twitter.visibility.builder.users.AuthorFeatures
|
|
||||||
import com.twitter.visibility.builder.users.RelationshipFeatures
|
|
||||||
import com.twitter.visibility.builder.users.ViewerAdvancedFilteringFeatures
|
|
||||||
import com.twitter.visibility.builder.users.ViewerFeatures
|
|
||||||
import com.twitter.visibility.common.TweetSource
|
|
||||||
import com.twitter.visibility.common.UserDeviceSource
|
|
||||||
import com.twitter.visibility.common.UserRelationshipSource
|
|
||||||
import com.twitter.visibility.common.UserSource
|
|
||||||
import com.twitter.visibility.features.AuthorUserLabels
|
|
||||||
import com.twitter.visibility.features.FeatureMap
|
|
||||||
import com.twitter.visibility.models.ContentId.NotificationId
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.NotificationsWriterV2
|
|
||||||
import com.twitter.visibility.models.ViewerContext
|
|
||||||
import com.twitter.visibility.rules.State.FeatureFailed
|
|
||||||
import com.twitter.visibility.rules.State.MissingFeature
|
|
||||||
import com.twitter.visibility.rules.Action
|
|
||||||
import com.twitter.visibility.rules.RuleResult
|
|
||||||
import com.twitter.visibility.rules.{Allow => AllowAction}
|
|
||||||
|
|
||||||
object NotificationsVisibilityLibrary {
|
|
||||||
type Type = Notification => Stitch[NotificationsFilteringResponse]
|
|
||||||
|
|
||||||
private val AllowResponse: Stitch[NotificationsFilteringResponse] = Stitch.value(Allow)
|
|
||||||
|
|
||||||
def isApplicableOrganicNotificationType(notificationType: NotificationType): Boolean = {
|
|
||||||
NotificationType.isTlsActivityType(notificationType) ||
|
|
||||||
NotificationType.isReactionType(notificationType)
|
|
||||||
}
|
|
||||||
|
|
||||||
def apply(
|
|
||||||
visibilityLibrary: VisibilityLibrary,
|
|
||||||
userSource: UserSource,
|
|
||||||
userRelationshipSource: UserRelationshipSource,
|
|
||||||
userDeviceSource: UserDeviceSource,
|
|
||||||
tweetSource: TweetSource,
|
|
||||||
enableShimFeatureHydration: Gate[Unit] = Gate.False,
|
|
||||||
enableCommunityTweetHydration: Gate[Long] = Gate.False,
|
|
||||||
enableUnmentionHydration: Gate[Long] = Gate.False,
|
|
||||||
): Type = {
|
|
||||||
val libraryStatsReceiver = visibilityLibrary.statsReceiver
|
|
||||||
lazy val vfEngineCounter = libraryStatsReceiver.counter("vf_engine_requests")
|
|
||||||
|
|
||||||
val authorFeatures = new AuthorFeatures(userSource, libraryStatsReceiver)
|
|
||||||
val authorDeviceFeatures = new AuthorDeviceFeatures(userDeviceSource, libraryStatsReceiver)
|
|
||||||
val viewerFeatures = new ViewerFeatures(userSource, libraryStatsReceiver)
|
|
||||||
val communityNotificationFeatures =
|
|
||||||
new CommunityNotificationFeatures(
|
|
||||||
tweetSource,
|
|
||||||
enableCommunityTweetHydration,
|
|
||||||
libraryStatsReceiver)
|
|
||||||
|
|
||||||
val unmentionNotificationFeatures = new UnmentionNotificationFeatures(
|
|
||||||
tweetSource = tweetSource,
|
|
||||||
enableUnmentionHydration = enableUnmentionHydration,
|
|
||||||
statsReceiver = libraryStatsReceiver
|
|
||||||
)
|
|
||||||
|
|
||||||
val viewerAdvancedFilteringFeatures =
|
|
||||||
new ViewerAdvancedFilteringFeatures(userSource, libraryStatsReceiver)
|
|
||||||
val relationshipFeatures =
|
|
||||||
new RelationshipFeatures(userRelationshipSource, libraryStatsReceiver)
|
|
||||||
|
|
||||||
val isShimFeatureHydrationEnabled = enableShimFeatureHydration()
|
|
||||||
|
|
||||||
def runRuleEngine(
|
|
||||||
visibilityLibrary: VisibilityLibrary,
|
|
||||||
candidate: Notification
|
|
||||||
): Stitch[VisibilityResult] = {
|
|
||||||
candidate match {
|
|
||||||
case notification: SimpleActivityNotification[_] =>
|
|
||||||
vfEngineCounter.incr()
|
|
||||||
|
|
||||||
val featureMap = visibilityLibrary.featureMapBuilder(
|
|
||||||
Seq(
|
|
||||||
viewerFeatures.forViewerId(Some(notification.target)),
|
|
||||||
viewerAdvancedFilteringFeatures.forViewerId(Some(notification.target)),
|
|
||||||
authorFeatures.forAuthorId(notification.subjectId),
|
|
||||||
authorDeviceFeatures.forAuthorId(notification.subjectId),
|
|
||||||
relationshipFeatures
|
|
||||||
.forAuthorId(notification.subjectId, Some(notification.target)),
|
|
||||||
communityNotificationFeatures.forNotification(notification),
|
|
||||||
unmentionNotificationFeatures.forNotification(notification)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (isShimFeatureHydrationEnabled) {
|
|
||||||
FeatureMap.resolve(featureMap, libraryStatsReceiver).flatMap { resolvedFeatureMap =>
|
|
||||||
visibilityLibrary.runRuleEngine(
|
|
||||||
contentId =
|
|
||||||
featureMap = resolvedFeatureMap,
|
|
||||||
viewerContext =
|
|
||||||
ViewerContext.fromContextWithViewerIdFallback(Some(notification.target)),
|
|
||||||
safetyLevel = NotificationsWriterV2
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
visibilityLibrary.runRuleEngine(
|
|
||||||
contentId = NotificationId(tweetId = None),
|
|
||||||
featureMap = featureMap,
|
|
||||||
viewerContext =
|
|
||||||
ViewerContext.fromContextWithViewerIdFallback(Some(notification.target)),
|
|
||||||
safetyLevel = NotificationsWriterV2
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
case candidate if isApplicableOrganicNotificationType(candidate.notificationType) =>
|
|
||||||
runRuleEngine(visibilityLibrary, candidate)
|
|
||||||
.flatMap(failCloseForFailures(_, libraryStatsReceiver))
|
|
||||||
case _ =>
|
|
||||||
AllowResponse
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def failCloseForFailures(
|
|
||||||
visibilityResult: VisibilityResult,
|
|
||||||
stats: StatsReceiver
|
|
||||||
): Stitch[NotificationsFilteringResponse] = {
|
|
||||||
lazy val vfEngineSuccess = stats.counter("vf_engine_success")
|
|
||||||
lazy val vfEngineFailures = stats.counter("vf_engine_failures")
|
|
||||||
lazy val vfEngineFailuresMissing = stats.scope("vf_engine_failures").counter("missing")
|
|
||||||
lazy val vfEngineFailuresFailed = stats.scope("vf_engine_failures").counter("failed")
|
|
||||||
lazy val vfEngineFiltered = stats.counter("vf_engine_filtered")
|
|
||||||
|
|
||||||
val isFailedOrMissingFeature: RuleResult => Boolean = {
|
|
||||||
case RuleResult(_, FeatureFailed(features)) =>
|
|
||||||
!(features.contains(AuthorUserLabels) && features.size == 1)
|
|
||||||
case RuleResult(_, MissingFeature(_)) => true
|
|
||||||
case _ => false
|
|
||||||
}
|
|
||||||
|
|
||||||
val failedRuleResults =
|
|
||||||
visibilityResult.ruleResultMap.values.filter(isFailedOrMissingFeature(_))
|
|
||||||
|
|
||||||
val (failedFeatures, missingFeatures) = failedRuleResults.partition {
|
|
||||||
case RuleResult(_, FeatureFailed(_)) => true
|
|
||||||
case RuleResult(_, MissingFeature(_)) => false
|
|
||||||
case _ => false
|
|
||||||
}
|
|
||||||
|
|
||||||
val failedOrMissingFeatures = failedRuleResults
|
|
||||||
.collect {
|
|
||||||
case RuleResult(_, FeatureFailed(features)) => features.keySet
|
|
||||||
case RuleResult(_, MissingFeature(features)) => features
|
|
||||||
}.toSet.flatten
|
|
||||||
|
|
||||||
visibilityResult.verdict match {
|
|
||||||
case AllowAction if failedOrMissingFeatures.isEmpty =>
|
|
||||||
vfEngineSuccess.incr()
|
|
||||||
AllowResponse
|
|
||||||
case AllowAction if failedOrMissingFeatures.nonEmpty =>
|
|
||||||
vfEngineFailures.incr()
|
|
||||||
if (missingFeatures.nonEmpty) {
|
|
||||||
vfEngineFailuresMissing.incr()
|
|
||||||
}
|
|
||||||
if (failedFeatures.nonEmpty) {
|
|
||||||
vfEngineFailuresFailed.incr()
|
|
||||||
}
|
|
||||||
|
|
||||||
Stitch.value(Failed(failedOrMissingFeatures))
|
|
||||||
case action: Action =>
|
|
||||||
vfEngineFiltered.incr()
|
|
||||||
Stitch.value(Filtered(action))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"3rdparty/jvm/com/twitter/storehaus:core",
|
|
||||||
"src/thrift/com/twitter/spam/rtf:tweet-rtf-event-scala",
|
|
||||||
"src/thrift/com/twitter/tweetypie:events-scala",
|
|
||||||
"src/thrift/com/twitter/tweetypie:tweet-scala",
|
|
||||||
"stitch/stitch-core",
|
|
||||||
"stitch/stitch-tweetypie/src/main/scala",
|
|
||||||
"strato/src/main/scala/com/twitter/strato/catalog",
|
|
||||||
"strato/src/main/scala/com/twitter/strato/client",
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common",
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common/stitch",
|
|
||||||
"visibility/common/src/main/thrift/com/twitter/visibility:action-scala",
|
|
||||||
"visibility/lib/src/main/resources/config",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/media",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/tweets",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/users",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/configapi/params",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/features",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/interfaces/common/tweets",
|
|
||||||
],
|
|
||||||
exports = [
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,21 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.push_service
|
|
||||||
|
|
||||||
import com.twitter.finagle.stats.StatsReceiver
|
|
||||||
import com.twitter.spam.rtf.thriftscala.SafetyLabelMap
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.strato.client.{Client => StratoClient}
|
|
||||||
import com.twitter.strato.thrift.ScroogeConvImplicits._
|
|
||||||
import com.twitter.visibility.common.stitch.StitchHelpers
|
|
||||||
|
|
||||||
object PushServiceSafetyLabelMapFetcher {
|
|
||||||
val Column = "frigate/magicrecs/tweetSafetyLabels"
|
|
||||||
|
|
||||||
def apply(
|
|
||||||
client: StratoClient,
|
|
||||||
statsReceiver: StatsReceiver
|
|
||||||
): Long => Stitch[Option[SafetyLabelMap]] = {
|
|
||||||
val stats = statsReceiver.scope("strato_tweet_safety_labels")
|
|
||||||
lazy val fetcher = client.fetcher[Long, SafetyLabelMap](Column)
|
|
||||||
tweetId => StitchHelpers.observe(stats)(fetcher.fetch(tweetId).map(_.v))
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,179 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.push_service
|
|
||||||
|
|
||||||
import com.twitter.gizmoduck.thriftscala.User
|
|
||||||
import com.twitter.servo.util.Gate
|
|
||||||
import com.twitter.finagle.stats.StatsReceiver
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.stitch.tweetypie.TweetyPie.TweetyPieResult
|
|
||||||
import com.twitter.storehaus.ReadableStore
|
|
||||||
import com.twitter.strato.client.{Client => StratoClient}
|
|
||||||
import com.twitter.tweetypie.thriftscala.Tweet
|
|
||||||
import com.twitter.visibility.VisibilityLibrary
|
|
||||||
import com.twitter.visibility.builder.tweets.TweetFeatures
|
|
||||||
import com.twitter.visibility.builder.tweets.StratoTweetLabelMaps
|
|
||||||
import com.twitter.visibility.builder.users.AuthorFeatures
|
|
||||||
import com.twitter.visibility.builder.users.RelationshipFeatures
|
|
||||||
import com.twitter.visibility.builder.users.ViewerFeatures
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.common._
|
|
||||||
import com.twitter.visibility.common.UserRelationshipSource
|
|
||||||
import com.twitter.visibility.common.UserSource
|
|
||||||
import com.twitter.visibility.features.FeatureMap
|
|
||||||
import com.twitter.visibility.features.TweetIsInnerQuotedTweet
|
|
||||||
import com.twitter.visibility.features.TweetIsRetweet
|
|
||||||
import com.twitter.visibility.features.TweetIsSourceTweet
|
|
||||||
import com.twitter.visibility.interfaces.push_service.PushServiceVisibilityLibraryUtil._
|
|
||||||
import com.twitter.visibility.models.ContentId
|
|
||||||
import com.twitter.visibility.models.ViewerContext
|
|
||||||
|
|
||||||
object TweetType extends Enumeration {
|
|
||||||
type TweetType = Value
|
|
||||||
val ORIGINAL, SOURCE, QUOTED = Value
|
|
||||||
}
|
|
||||||
import com.twitter.visibility.interfaces.push_service.TweetType._
|
|
||||||
|
|
||||||
object PushServiceVisibilityLibrary {
|
|
||||||
type Type = PushServiceVisibilityRequest => Stitch[PushServiceVisibilityResponse]
|
|
||||||
|
|
||||||
def apply(
|
|
||||||
visibilityLibrary: VisibilityLibrary,
|
|
||||||
userSource: UserSource,
|
|
||||||
userRelationshipSource: UserRelationshipSource,
|
|
||||||
stratoClient: StratoClient,
|
|
||||||
enableParityTest: Gate[Unit] = Gate.False,
|
|
||||||
cachedTweetyPieStoreV2: ReadableStore[Long, TweetyPieResult] = ReadableStore.empty,
|
|
||||||
safeCachedTweetyPieStoreV2: ReadableStore[Long, TweetyPieResult] = ReadableStore.empty,
|
|
||||||
)(
|
|
||||||
implicit statsReceiver: StatsReceiver
|
|
||||||
): Type = {
|
|
||||||
val stats = statsReceiver.scope("push_service_vf")
|
|
||||||
val candidateTweetCounter = stats.counter("request_cnt")
|
|
||||||
val allowedTweetCounter = stats.counter("allow_cnt")
|
|
||||||
val droppedTweetCounter = stats.counter("drop_cnt")
|
|
||||||
val failedTweetCounter = stats.counter("fail_cnt")
|
|
||||||
val authorLabelsEmptyCount = stats.counter("author_labels_empty_cnt")
|
|
||||||
val authorLabelsCount = stats.counter("author_labels_cnt")
|
|
||||||
|
|
||||||
val tweetLabelMaps = new StratoTweetLabelMaps(
|
|
||||||
SafetyLabelMapSource.fromSafetyLabelMapFetcher(
|
|
||||||
PushServiceSafetyLabelMapFetcher(stratoClient, stats)))
|
|
||||||
|
|
||||||
val viewerFeatures = new ViewerFeatures(UserSource.empty, stats)
|
|
||||||
val tweetFeatures = new TweetFeatures(tweetLabelMaps, stats)
|
|
||||||
val authorFeatures = new AuthorFeatures(userSource, stats)
|
|
||||||
val relationshipFeatures = new RelationshipFeatures(UserRelationshipSource.empty, stats)
|
|
||||||
|
|
||||||
val parityTester = new PushServiceVisibilityLibraryParity(
|
|
||||||
cachedTweetyPieStoreV2,
|
|
||||||
safeCachedTweetyPieStoreV2
|
|
||||||
)(statsReceiver)
|
|
||||||
|
|
||||||
def buildFeatureMap(
|
|
||||||
request: PushServiceVisibilityRequest,
|
|
||||||
tweet: Tweet,
|
|
||||||
tweetType: TweetType,
|
|
||||||
author: Option[User] = None,
|
|
||||||
): FeatureMap = {
|
|
||||||
val authorId = author.map(_.id) orElse getAuthorId(tweet)
|
|
||||||
(author.map(authorFeatures.forAuthor(_)) orElse
|
|
||||||
getAuthorId(tweet).map(authorFeatures.forAuthorId(_))) match {
|
|
||||||
case Some(authorVisibilityFeatures) =>
|
|
||||||
visibilityLibrary.featureMapBuilder(
|
|
||||||
Seq(
|
|
||||||
viewerFeatures.forViewerContext(ViewerContext.fromContextWithViewerIdFallback(None)),
|
|
||||||
tweetFeatures.forTweet(tweet),
|
|
||||||
authorVisibilityFeatures,
|
|
||||||
relationshipFeatures.forAuthorId(authorId.get, None),
|
|
||||||
_.withConstantFeature(TweetIsInnerQuotedTweet, tweetType == QUOTED),
|
|
||||||
_.withConstantFeature(TweetIsRetweet, request.isRetweet),
|
|
||||||
_.withConstantFeature(TweetIsSourceTweet, tweetType == SOURCE)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
case _ =>
|
|
||||||
visibilityLibrary.featureMapBuilder(
|
|
||||||
Seq(
|
|
||||||
viewerFeatures.forViewerContext(ViewerContext.fromContextWithViewerIdFallback(None)),
|
|
||||||
tweetFeatures.forTweet(tweet),
|
|
||||||
_.withConstantFeature(TweetIsInnerQuotedTweet, tweetType == QUOTED),
|
|
||||||
_.withConstantFeature(TweetIsRetweet, request.isRetweet),
|
|
||||||
_.withConstantFeature(TweetIsSourceTweet, tweetType == SOURCE)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def runRuleEngineForTweet(
|
|
||||||
request: PushServiceVisibilityRequest,
|
|
||||||
tweet: Tweet,
|
|
||||||
tweetType: TweetType,
|
|
||||||
author: Option[User] = None,
|
|
||||||
): Stitch[VisibilityResult] = {
|
|
||||||
val featureMap = buildFeatureMap(request, tweet, tweetType, author)
|
|
||||||
val contentId = ContentId.TweetId(tweet.id)
|
|
||||||
visibilityLibrary.runRuleEngine(
|
|
||||||
contentId,
|
|
||||||
featureMap,
|
|
||||||
request.viewerContext,
|
|
||||||
request.safetyLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
def runRuleEngineForAuthor(
|
|
||||||
request: PushServiceVisibilityRequest,
|
|
||||||
tweet: Tweet,
|
|
||||||
tweetType: TweetType,
|
|
||||||
author: Option[User] = None,
|
|
||||||
): Stitch[VisibilityResult] = {
|
|
||||||
val featureMap = buildFeatureMap(request, tweet, tweetType, author)
|
|
||||||
val authorId = author.map(_.id).getOrElse(getAuthorId(tweet).get)
|
|
||||||
val contentId = ContentId.UserId(authorId)
|
|
||||||
visibilityLibrary.runRuleEngine(
|
|
||||||
contentId,
|
|
||||||
featureMap,
|
|
||||||
request.viewerContext,
|
|
||||||
request.safetyLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
def getAllVisibilityFilters(
|
|
||||||
request: PushServiceVisibilityRequest
|
|
||||||
): Stitch[PushServiceVisibilityResponse] = {
|
|
||||||
val tweetResult =
|
|
||||||
runRuleEngineForTweet(request, request.tweet, ORIGINAL, Some(request.author))
|
|
||||||
val authorResult =
|
|
||||||
runRuleEngineForAuthor(request, request.tweet, ORIGINAL, Some(request.author))
|
|
||||||
val sourceTweetResult = request.sourceTweet
|
|
||||||
.map(runRuleEngineForTweet(request, _, SOURCE).map(Some(_))).getOrElse(Stitch.None)
|
|
||||||
val quotedTweetResult = request.quotedTweet
|
|
||||||
.map(runRuleEngineForTweet(request, _, QUOTED).map(Some(_))).getOrElse(Stitch.None)
|
|
||||||
|
|
||||||
Stitch.join(tweetResult, authorResult, sourceTweetResult, quotedTweetResult).map {
|
|
||||||
case (tweetResult, authorResult, sourceTweetResult, quotedTweetResult) =>
|
|
||||||
PushServiceVisibilityResponse(
|
|
||||||
tweetResult,
|
|
||||||
authorResult,
|
|
||||||
sourceTweetResult,
|
|
||||||
quotedTweetResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{ request: PushServiceVisibilityRequest =>
|
|
||||||
candidateTweetCounter.incr()
|
|
||||||
|
|
||||||
request.author.labels match {
|
|
||||||
case Some(labels) if (!labels._1.isEmpty) => authorLabelsCount.incr()
|
|
||||||
case _ => authorLabelsEmptyCount.incr()
|
|
||||||
}
|
|
||||||
|
|
||||||
val response = getAllVisibilityFilters(request)
|
|
||||||
.onSuccess { response =>
|
|
||||||
if (response.shouldAllow) allowedTweetCounter.incr() else droppedTweetCounter.incr()
|
|
||||||
}.onFailure { _ => failedTweetCounter.incr() }
|
|
||||||
|
|
||||||
if (enableParityTest()) {
|
|
||||||
response.applyEffect { resp => Stitch.async(parityTester.runParityTest(request, resp)) }
|
|
||||||
} else {
|
|
||||||
response
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,74 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.push_service
|
|
||||||
|
|
||||||
import com.twitter.finagle.stats.StatsReceiver
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.stitch.tweetypie.TweetyPie.TweetyPieResult
|
|
||||||
import com.twitter.storehaus.ReadableStore
|
|
||||||
import com.twitter.logging.Logger
|
|
||||||
import com.twitter.visibility.models.SafetyLevel
|
|
||||||
|
|
||||||
class PushServiceVisibilityLibraryParity(
|
|
||||||
magicRecsV2tweetyPieStore: ReadableStore[Long, TweetyPieResult],
|
|
||||||
magicRecsAggressiveV2tweetyPieStore: ReadableStore[Long, TweetyPieResult]
|
|
||||||
)(
|
|
||||||
implicit statsReceiver: StatsReceiver) {
|
|
||||||
|
|
||||||
private val stats = statsReceiver.scope("push_service_vf_parity")
|
|
||||||
private val requests = stats.counter("requests")
|
|
||||||
private val equal = stats.counter("equal")
|
|
||||||
private val notEqual = stats.counter("notEqual")
|
|
||||||
private val failures = stats.counter("failures")
|
|
||||||
private val bothAllow = stats.counter("bothAllow")
|
|
||||||
private val bothReject = stats.counter("bothReject")
|
|
||||||
private val onlyTweetypieRejects = stats.counter("onlyTweetypieRejects")
|
|
||||||
private val onlyPushServiceRejects = stats.counter("onlyPushServiceRejects")
|
|
||||||
|
|
||||||
val log = Logger.get("pushservice_vf_parity")
|
|
||||||
|
|
||||||
def runParityTest(
|
|
||||||
req: PushServiceVisibilityRequest,
|
|
||||||
resp: PushServiceVisibilityResponse
|
|
||||||
): Stitch[Unit] = {
|
|
||||||
requests.incr()
|
|
||||||
getTweetypieResult(req).map { tweetypieResult =>
|
|
||||||
val isSameVerdict = (tweetypieResult == resp.shouldAllow)
|
|
||||||
isSameVerdict match {
|
|
||||||
case true => equal.incr()
|
|
||||||
case false => notEqual.incr()
|
|
||||||
}
|
|
||||||
(tweetypieResult, resp.shouldAllow) match {
|
|
||||||
case (true, true) => bothAllow.incr()
|
|
||||||
case (true, false) => onlyPushServiceRejects.incr()
|
|
||||||
case (false, true) => onlyTweetypieRejects.incr()
|
|
||||||
case (false, false) => bothReject.incr()
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.getDropRules.foreach { dropRule =>
|
|
||||||
stats.counter(s"rules/${dropRule.name}/requests").incr()
|
|
||||||
stats
|
|
||||||
.counter(
|
|
||||||
s"rules/${dropRule.name}/" ++ (if (isSameVerdict) "equal" else "notEqual")).incr()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isSameVerdict) {
|
|
||||||
val dropRuleNames = resp.getDropRules.map("<<" ++ _.name ++ ">>").mkString(",")
|
|
||||||
val safetyLevelStr = req.safetyLevel match {
|
|
||||||
case SafetyLevel.MagicRecsAggressiveV2 => "aggr"
|
|
||||||
case _ => " "
|
|
||||||
}
|
|
||||||
log.info(
|
|
||||||
s"ttweetId:${req.tweet.id} () push:${resp.shouldAllow}, tweety:${tweetypieResult}, rules=[${dropRuleNames}] lvl=${safetyLevelStr}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
def getTweetypieResult(request: PushServiceVisibilityRequest): Stitch[Boolean] = {
|
|
||||||
val tweetypieStore = request.safetyLevel match {
|
|
||||||
case SafetyLevel.MagicRecsAggressiveV2 => magicRecsAggressiveV2tweetyPieStore
|
|
||||||
case _ => magicRecsV2tweetyPieStore
|
|
||||||
}
|
|
||||||
Stitch.callFuture(
|
|
||||||
tweetypieStore.get(request.tweet.id).onFailure(_ => failures.incr()).map(x => x.isDefined))
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,57 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.push_service
|
|
||||||
|
|
||||||
import com.twitter.finagle.stats.StatsReceiver
|
|
||||||
import com.twitter.tweetypie.thriftscala.Tweet
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.rules.Rule
|
|
||||||
import com.twitter.visibility.rules.RuleResult
|
|
||||||
import com.twitter.visibility.rules.State
|
|
||||||
|
|
||||||
object PushServiceVisibilityLibraryUtil {
|
|
||||||
def ruleEnabled(ruleResult: RuleResult): Boolean = {
|
|
||||||
ruleResult.state match {
|
|
||||||
case State.Disabled => false
|
|
||||||
case State.ShortCircuited => false
|
|
||||||
case _ => true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def getMissingFeatures(ruleResult: RuleResult): Set[String] = {
|
|
||||||
ruleResult.state match {
|
|
||||||
case State.MissingFeature(features) => features.map(f => f.name)
|
|
||||||
case _ => Set.empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def getMissingFeatureCounts(results: Seq[VisibilityResult]): Map[String, Int] = {
|
|
||||||
results
|
|
||||||
.flatMap(_.ruleResultMap.values.toList)
|
|
||||||
.flatMap(getMissingFeatures(_).toList).groupBy(identity).mapValues(_.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
def logAllStats(
|
|
||||||
response: PushServiceVisibilityResponse
|
|
||||||
)(
|
|
||||||
implicit statsReceiver: StatsReceiver
|
|
||||||
) = {
|
|
||||||
val rulesStatsReceiver = statsReceiver.scope("rules")
|
|
||||||
logStats(response.tweetVisibilityResult, rulesStatsReceiver.scope("tweet"))
|
|
||||||
logStats(response.authorVisibilityResult, rulesStatsReceiver.scope("author"))
|
|
||||||
}
|
|
||||||
|
|
||||||
def logStats(result: VisibilityResult, statsReceiver: StatsReceiver) = {
|
|
||||||
result.ruleResultMap.toList
|
|
||||||
.filter { case (_, ruleResult) => ruleEnabled(ruleResult) }
|
|
||||||
.flatMap { case (rule, ruleResult) => getCounters(rule, ruleResult) }
|
|
||||||
.foreach(statsReceiver.counter(_).incr())
|
|
||||||
}
|
|
||||||
|
|
||||||
def getCounters(rule: Rule, ruleResult: RuleResult): List[String] = {
|
|
||||||
val missingFeatures = getMissingFeatures(ruleResult)
|
|
||||||
List(s"${rule.name}/${ruleResult.action.name}") ++
|
|
||||||
missingFeatures.map(feat => s"${rule.name}/${feat}") ++
|
|
||||||
missingFeatures
|
|
||||||
}
|
|
||||||
|
|
||||||
def getAuthorId(tweet: Tweet): Option[Long] = tweet.coreData.map(_.userId)
|
|
||||||
def isRetweet(tweet: Tweet): Boolean = tweet.coreData.flatMap(_.share).isDefined
|
|
||||||
def isQuotedTweet(tweet: Tweet): Boolean = tweet.quotedTweet.isDefined
|
|
||||||
}
|
|
Binary file not shown.
@ -1,19 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.push_service
|
|
||||||
|
|
||||||
import com.twitter.gizmoduck.thriftscala.User
|
|
||||||
import com.twitter.tweetypie.thriftscala.Tweet
|
|
||||||
import com.twitter.visibility.models.SafetyLevel
|
|
||||||
import com.twitter.visibility.models.ViewerContext
|
|
||||||
|
|
||||||
case class PushServiceVisibilityRequest(
|
|
||||||
tweet: Tweet,
|
|
||||||
author: User,
|
|
||||||
viewerContext: ViewerContext,
|
|
||||||
safetyLevel: SafetyLevel,
|
|
||||||
sourceTweet: Option[Tweet] = None,
|
|
||||||
quotedTweet: Option[Tweet] = None,
|
|
||||||
isRetweet: Boolean = false,
|
|
||||||
isInnerQuotedTweet: Boolean = false,
|
|
||||||
isSourceTweet: Boolean = false,
|
|
||||||
isOutOfNetworkTweet: Boolean = true,
|
|
||||||
)
|
|
Binary file not shown.
@ -1,52 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.push_service
|
|
||||||
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.rules.Action
|
|
||||||
import com.twitter.visibility.rules.Allow
|
|
||||||
import com.twitter.visibility.rules.Drop
|
|
||||||
import com.twitter.visibility.rules.Rule
|
|
||||||
import com.twitter.visibility.rules.RuleResult
|
|
||||||
|
|
||||||
case class PushServiceVisibilityResponse(
|
|
||||||
tweetVisibilityResult: VisibilityResult,
|
|
||||||
authorVisibilityResult: VisibilityResult,
|
|
||||||
sourceTweetVisibilityResult: Option[VisibilityResult] = None,
|
|
||||||
quotedTweetVisibilityResult: Option[VisibilityResult] = None,
|
|
||||||
) {
|
|
||||||
|
|
||||||
def allVisibilityResults: List[VisibilityResult] = {
|
|
||||||
List(
|
|
||||||
Some(tweetVisibilityResult),
|
|
||||||
Some(authorVisibilityResult),
|
|
||||||
sourceTweetVisibilityResult,
|
|
||||||
quotedTweetVisibilityResult,
|
|
||||||
).collect { case Some(result) => result }
|
|
||||||
}
|
|
||||||
|
|
||||||
val shouldAllow: Boolean = !allVisibilityResults.exists(isDrop(_))
|
|
||||||
|
|
||||||
def isDrop(response: VisibilityResult): Boolean = response.verdict match {
|
|
||||||
case _: Drop => true
|
|
||||||
case Allow => false
|
|
||||||
case _ => false
|
|
||||||
}
|
|
||||||
def isDrop(response: Option[VisibilityResult]): Boolean = response.map(isDrop(_)).getOrElse(false)
|
|
||||||
|
|
||||||
def getDropRules(visibilityResult: VisibilityResult): List[Rule] = {
|
|
||||||
val ruleResultMap = visibilityResult.ruleResultMap
|
|
||||||
val ruleResults = ruleResultMap.toList
|
|
||||||
val denyRules = ruleResults.collect { case (rule, RuleResult(Drop(_, _), _)) => rule }
|
|
||||||
denyRules
|
|
||||||
}
|
|
||||||
def getAuthorDropRules: List[Rule] = getDropRules(authorVisibilityResult)
|
|
||||||
def getTweetDropRules: List[Rule] = getDropRules(tweetVisibilityResult)
|
|
||||||
def getDropRules: List[Rule] = getAuthorDropRules ++ getTweetDropRules
|
|
||||||
def getVerdict: Action = {
|
|
||||||
if (isDrop(authorVisibilityResult)) authorVisibilityResult.verdict
|
|
||||||
else tweetVisibilityResult.verdict
|
|
||||||
}
|
|
||||||
|
|
||||||
def missingFeatures: Map[String, Int] = PushServiceVisibilityLibraryUtil.getMissingFeatureCounts(
|
|
||||||
Seq(tweetVisibilityResult, authorVisibilityResult))
|
|
||||||
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
platform = "java8",
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"3rdparty/jvm/com/twitter/src/java/com/twitter/logpipeline/client:logpipeline-event-publisher-thin",
|
|
||||||
"decider/src/main/scala",
|
|
||||||
"mediaservices/media-util/src/main/scala",
|
|
||||||
"servo/decider/src/main/scala",
|
|
||||||
"src/thrift/com/twitter/escherbird:media-annotation-structs-scala",
|
|
||||||
"src/thrift/com/twitter/spam/rtf:safety-level-scala",
|
|
||||||
"src/thrift/com/twitter/spam/rtf:tweet-rtf-event-scala",
|
|
||||||
"src/thrift/com/twitter/tweetypie:tweet-scala",
|
|
||||||
"stitch/stitch-core",
|
|
||||||
"strato/src/main/scala/com/twitter/strato/client",
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common",
|
|
||||||
"visibility/lib/src/main/resources/config",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/media",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/tweets",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/users",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/configapi/configs",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/features",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/interfaces/common/search",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/interfaces/common/tweets",
|
|
||||||
"visibility/lib/src/main/thrift/com/twitter/visibility/logging:vf-logging-scala",
|
|
||||||
],
|
|
||||||
exports = [
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,9 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.search
|
|
||||||
|
|
||||||
import com.twitter.visibility.interfaces.common.search.SearchVFRequestContext
|
|
||||||
import com.twitter.visibility.models.ViewerContext
|
|
||||||
|
|
||||||
case class BatchSearchVisibilityRequest(
|
|
||||||
tweetContexts: Seq[TweetContext],
|
|
||||||
viewerContext: ViewerContext,
|
|
||||||
searchVFRequestContext: SearchVFRequestContext)
|
|
Binary file not shown.
@ -1,5 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.search
|
|
||||||
|
|
||||||
case class BatchSearchVisibilityResponse(
|
|
||||||
visibilityResults: Map[Long, CombinedVisibilityResult],
|
|
||||||
failedTweetIds: Seq[Long])
|
|
Binary file not shown.
@ -1,7 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.search
|
|
||||||
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
|
|
||||||
case class CombinedVisibilityResult(
|
|
||||||
tweetVisibilityResult: VisibilityResult,
|
|
||||||
quotedTweetVisibilityResult: Option[VisibilityResult])
|
|
Binary file not shown.
@ -1,466 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.search
|
|
||||||
|
|
||||||
import com.twitter.decider.Decider
|
|
||||||
import com.twitter.finagle.stats.StatsReceiver
|
|
||||||
import com.twitter.mediaservices.media_util.GenericMediaKey
|
|
||||||
import com.twitter.servo.util.Gate
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.strato.client.{Client => StratoClient}
|
|
||||||
import com.twitter.tweetypie.thriftscala.Tweet
|
|
||||||
import com.twitter.util.Return
|
|
||||||
import com.twitter.util.Stopwatch
|
|
||||||
import com.twitter.util.Try
|
|
||||||
import com.twitter.visibility.VisibilityLibrary
|
|
||||||
import com.twitter.visibility.builder.VerdictLogger
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.builder.media.MediaFeatures
|
|
||||||
import com.twitter.visibility.builder.media.StratoMediaLabelMaps
|
|
||||||
import com.twitter.visibility.builder.tweets._
|
|
||||||
import com.twitter.visibility.builder.users.AuthorFeatures
|
|
||||||
import com.twitter.visibility.builder.users.RelationshipFeatures
|
|
||||||
import com.twitter.visibility.builder.users.ViewerFeatures
|
|
||||||
import com.twitter.visibility.common.MediaSafetyLabelMapSource
|
|
||||||
import com.twitter.visibility.common.MisinformationPolicySource
|
|
||||||
import com.twitter.visibility.common.SafetyLabelMapSource
|
|
||||||
import com.twitter.visibility.common.TrustedFriendsSource
|
|
||||||
import com.twitter.visibility.common.UserRelationshipSource
|
|
||||||
import com.twitter.visibility.common.UserSource
|
|
||||||
import com.twitter.visibility.rules.ComposableActions._
|
|
||||||
import com.twitter.visibility.configapi.configs.VisibilityDeciderGates
|
|
||||||
import com.twitter.visibility.features.FeatureMap
|
|
||||||
import com.twitter.visibility.features.TweetIsInnerQuotedTweet
|
|
||||||
import com.twitter.visibility.features.TweetIsRetweet
|
|
||||||
import com.twitter.visibility.features.TweetIsSourceTweet
|
|
||||||
import com.twitter.visibility.interfaces.common.search.SearchVFRequestContext
|
|
||||||
import com.twitter.visibility.interfaces.search.SearchVisibilityLibrary.EvaluateTweet
|
|
||||||
import com.twitter.visibility.interfaces.search.SearchVisibilityLibrary.RequestTweetId
|
|
||||||
import com.twitter.visibility.interfaces.search.TweetType.EvaluateTweetType
|
|
||||||
import com.twitter.visibility.logging.thriftscala.VFLibType
|
|
||||||
import com.twitter.visibility.models.ContentId
|
|
||||||
import com.twitter.visibility.models.ContentId.BlenderTweetId
|
|
||||||
import com.twitter.visibility.models.ContentId.TweetId
|
|
||||||
import com.twitter.visibility.models.SafetyLevel
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.toThrift
|
|
||||||
import com.twitter.visibility.models.ViewerContext
|
|
||||||
import com.twitter.visibility.rules.Action
|
|
||||||
import com.twitter.visibility.rules.Allow
|
|
||||||
import com.twitter.visibility.rules.Drop
|
|
||||||
import com.twitter.visibility.rules.Interstitial
|
|
||||||
import com.twitter.visibility.rules.TweetInterstitial
|
|
||||||
|
|
||||||
object TweetType extends Enumeration {
|
|
||||||
type EvaluateTweetType = Value
|
|
||||||
val REQUEST: TweetType.Value = Value(1)
|
|
||||||
val QUOTED: TweetType.Value = Value(2)
|
|
||||||
val SOURCE: TweetType.Value = Value(3)
|
|
||||||
}
|
|
||||||
|
|
||||||
import com.twitter.visibility.interfaces.search.TweetType._
|
|
||||||
|
|
||||||
object SearchVisibilityLibrary {
|
|
||||||
type RequestTweetId = Long
|
|
||||||
type EvaluateTweetId = Long
|
|
||||||
type EvaluateTweet = Tweet
|
|
||||||
|
|
||||||
def buildWithStratoClient(
|
|
||||||
visibilityLibrary: VisibilityLibrary,
|
|
||||||
decider: Decider,
|
|
||||||
stratoClient: StratoClient,
|
|
||||||
userSource: UserSource,
|
|
||||||
userRelationshipSource: UserRelationshipSource
|
|
||||||
): SearchVisibilityLibrary = new SearchVisibilityLibrary(
|
|
||||||
visibilityLibrary,
|
|
||||||
decider,
|
|
||||||
stratoClient,
|
|
||||||
userSource,
|
|
||||||
userRelationshipSource,
|
|
||||||
None
|
|
||||||
)
|
|
||||||
|
|
||||||
def buildWithSafetyLabelMapSource(
|
|
||||||
visibilityLibrary: VisibilityLibrary,
|
|
||||||
decider: Decider,
|
|
||||||
stratoClient: StratoClient,
|
|
||||||
userSource: UserSource,
|
|
||||||
userRelationshipSource: UserRelationshipSource,
|
|
||||||
safetyLabelMapSource: SafetyLabelMapSource
|
|
||||||
): SearchVisibilityLibrary = new SearchVisibilityLibrary(
|
|
||||||
visibilityLibrary,
|
|
||||||
decider,
|
|
||||||
stratoClient,
|
|
||||||
userSource,
|
|
||||||
userRelationshipSource,
|
|
||||||
Some(safetyLabelMapSource)
|
|
||||||
)
|
|
||||||
|
|
||||||
def createVerdictLogger(
|
|
||||||
enableVerdictLogger: Gate[Unit],
|
|
||||||
decider: Decider,
|
|
||||||
statsReceiver: StatsReceiver
|
|
||||||
): VerdictLogger = {
|
|
||||||
if (enableVerdictLogger()) {
|
|
||||||
VerdictLogger(statsReceiver, decider)
|
|
||||||
} else {
|
|
||||||
VerdictLogger.Empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def scribeVisibilityVerdict(
|
|
||||||
result: CombinedVisibilityResult,
|
|
||||||
enableVerdictScribing: Gate[Unit],
|
|
||||||
verdictLogger: VerdictLogger,
|
|
||||||
viewerId: Option[Long],
|
|
||||||
safetyLevel: SafetyLevel
|
|
||||||
): Unit = if (enableVerdictScribing()) {
|
|
||||||
verdictLogger.scribeVerdict(
|
|
||||||
visibilityResult = result.tweetVisibilityResult,
|
|
||||||
viewerId = viewerId,
|
|
||||||
safetyLevel = toThrift(safetyLevel),
|
|
||||||
vfLibType = VFLibType.SearchVisibilityLibrary)
|
|
||||||
|
|
||||||
result.quotedTweetVisibilityResult.map(quotedTweetVisibilityResult =>
|
|
||||||
verdictLogger.scribeVerdict(
|
|
||||||
visibilityResult = quotedTweetVisibilityResult,
|
|
||||||
viewerId = viewerId,
|
|
||||||
safetyLevel = toThrift(safetyLevel),
|
|
||||||
vfLibType = VFLibType.SearchVisibilityLibrary))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SearchVisibilityLibrary(
|
|
||||||
visibilityLibrary: VisibilityLibrary,
|
|
||||||
decider: Decider,
|
|
||||||
stratoClient: StratoClient,
|
|
||||||
userSource: UserSource,
|
|
||||||
userRelationshipSource: UserRelationshipSource,
|
|
||||||
safetyLabelMapSourceOption: Option[SafetyLabelMapSource]) {
|
|
||||||
|
|
||||||
val libraryStatsReceiver = visibilityLibrary.statsReceiver
|
|
||||||
val stratoClientStatsReceiver = visibilityLibrary.statsReceiver.scope("strato")
|
|
||||||
val vfEngineCounter = libraryStatsReceiver.counter("vf_engine_requests")
|
|
||||||
val svlRequestCounter = libraryStatsReceiver.counter("svl_requests")
|
|
||||||
val vfLatencyOverallStat = libraryStatsReceiver.stat("vf_latency_overall")
|
|
||||||
val vfLatencyStitchBuildStat = libraryStatsReceiver.stat("vf_latency_stitch_build")
|
|
||||||
val vfLatencyStitchRunStat = libraryStatsReceiver.stat("vf_latency_stitch_run")
|
|
||||||
val visibilityDeciderGates = VisibilityDeciderGates(decider)
|
|
||||||
val verdictLogger = SearchVisibilityLibrary.createVerdictLogger(
|
|
||||||
visibilityDeciderGates.enableVerdictLoggerSVL,
|
|
||||||
decider,
|
|
||||||
libraryStatsReceiver)
|
|
||||||
|
|
||||||
val tweetLabels = safetyLabelMapSourceOption match {
|
|
||||||
case Some(safetyLabelMapSource) =>
|
|
||||||
new StratoTweetLabelMaps(safetyLabelMapSource)
|
|
||||||
case None =>
|
|
||||||
new StratoTweetLabelMaps(
|
|
||||||
SafetyLabelMapSource.fromStrato(stratoClient, stratoClientStatsReceiver))
|
|
||||||
}
|
|
||||||
|
|
||||||
val mediaLabelMaps = new StratoMediaLabelMaps(
|
|
||||||
MediaSafetyLabelMapSource.fromStrato(stratoClient, stratoClientStatsReceiver))
|
|
||||||
|
|
||||||
val tweetFeatures = new TweetFeatures(tweetLabels, libraryStatsReceiver)
|
|
||||||
val searchContextFeatures = new SearchContextFeatures(libraryStatsReceiver)
|
|
||||||
val authorFeatures = new AuthorFeatures(userSource, libraryStatsReceiver)
|
|
||||||
val viewerFeatures = new ViewerFeatures(userSource, libraryStatsReceiver)
|
|
||||||
val relationshipFeatures =
|
|
||||||
new RelationshipFeatures(userRelationshipSource, libraryStatsReceiver)
|
|
||||||
val misinfoPolicySource =
|
|
||||||
MisinformationPolicySource.fromStrato(stratoClient, stratoClientStatsReceiver)
|
|
||||||
val misinfoPolicyFeatures =
|
|
||||||
new MisinformationPolicyFeatures(misinfoPolicySource, stratoClientStatsReceiver)
|
|
||||||
val exclusiveTweetFeatures =
|
|
||||||
new ExclusiveTweetFeatures(userRelationshipSource, libraryStatsReceiver)
|
|
||||||
val mediaFeatures = new MediaFeatures(mediaLabelMaps, libraryStatsReceiver)
|
|
||||||
val trustedFriendsTweetFeatures = new TrustedFriendsFeatures(
|
|
||||||
trustedFriendsSource = TrustedFriendsSource.fromStrato(stratoClient, stratoClientStatsReceiver))
|
|
||||||
val editTweetFeatures = new EditTweetFeatures(libraryStatsReceiver)
|
|
||||||
|
|
||||||
def batchProcessSearchVisibilityRequest(
|
|
||||||
batchSvRequest: BatchSearchVisibilityRequest
|
|
||||||
): Stitch[BatchSearchVisibilityResponse] = {
|
|
||||||
val elapsed = Stopwatch.start()
|
|
||||||
svlRequestCounter.incr()
|
|
||||||
|
|
||||||
val response: Stitch[BatchSearchVisibilityResponse] =
|
|
||||||
batchSvRequest.tweetContexts.groupBy(tweetContext => tweetContext.safetyLevel) map {
|
|
||||||
case (safetyLevel: SafetyLevel, tweetContexts: Seq[TweetContext]) =>
|
|
||||||
val (contentsToBeEvaluated, contentVisResultTypes) =
|
|
||||||
extractContentsToBeEvaluated(tweetContexts, batchSvRequest.viewerContext)
|
|
||||||
|
|
||||||
getVisibilityResult(
|
|
||||||
contentsToBeEvaluated,
|
|
||||||
safetyLevel,
|
|
||||||
batchSvRequest.viewerContext,
|
|
||||||
batchSvRequest.searchVFRequestContext)
|
|
||||||
.map { contentVisResults: Seq[Try[VisibilityResult]] =>
|
|
||||||
(contentVisResultTypes zip contentVisResults)
|
|
||||||
.map(handleVisibilityResultByTweetType)
|
|
||||||
.groupBy {
|
|
||||||
case (requestTweetId: RequestTweetId, (_, _)) => requestTweetId
|
|
||||||
}.mapValues(combineVisibilityResult)
|
|
||||||
}.onSuccess(res =>
|
|
||||||
res.values.flatten.foreach(_ =>
|
|
||||||
SearchVisibilityLibrary.scribeVisibilityVerdict(
|
|
||||||
_,
|
|
||||||
visibilityDeciderGates.enableVerdictScribingSVL,
|
|
||||||
verdictLogger,
|
|
||||||
batchSvRequest.viewerContext.userId,
|
|
||||||
safetyLevel)))
|
|
||||||
} reduceLeft { (left, right) =>
|
|
||||||
Stitch.joinMap(left, right)((visResultsA, visResultsB) => visResultsA ++ visResultsB)
|
|
||||||
} map { visResults =>
|
|
||||||
val (succeed, failed) = visResults.partition { case (_, visResult) => visResult.nonEmpty }
|
|
||||||
val failedTweetIds: Seq[Long] = failed.keys.toSeq
|
|
||||||
BatchSearchVisibilityResponse(
|
|
||||||
visibilityResults = succeed.mapValues(visResult => visResult.get),
|
|
||||||
failedTweetIds = failedTweetIds
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val runStitchStartMs = elapsed().inMilliseconds
|
|
||||||
val buildStitchStatMs = elapsed().inMilliseconds
|
|
||||||
vfLatencyStitchBuildStat.add(buildStitchStatMs)
|
|
||||||
|
|
||||||
response
|
|
||||||
.onSuccess(_ => {
|
|
||||||
val overallMs = elapsed().inMilliseconds
|
|
||||||
vfLatencyOverallStat.add(overallMs)
|
|
||||||
val stitchRunMs = elapsed().inMilliseconds - runStitchStartMs
|
|
||||||
vfLatencyStitchRunStat.add(stitchRunMs)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private def extractContentsToBeEvaluated(
|
|
||||||
tweetContexts: Seq[TweetContext],
|
|
||||||
viewerContext: ViewerContext
|
|
||||||
): (
|
|
||||||
Seq[(TweetContext, EvaluateTweetType, EvaluateTweet, ContentId)],
|
|
||||||
Seq[
|
|
||||||
(RequestTweetId, EvaluateTweetType)
|
|
||||||
]
|
|
||||||
) = {
|
|
||||||
val contentsToBeEvaluated: Seq[
|
|
||||||
(TweetContext, EvaluateTweetType, EvaluateTweet, ContentId)
|
|
||||||
] = tweetContexts.map(tc =>
|
|
||||||
(
|
|
||||||
tc,
|
|
||||||
REQUEST,
|
|
||||||
tc.tweet,
|
|
||||||
getContentId(
|
|
||||||
viewerId = viewerContext.userId,
|
|
||||||
authorId = tc.tweet.coreData.get.userId,
|
|
||||||
tweet = tc.tweet))) ++
|
|
||||||
tweetContexts
|
|
||||||
.filter(tc => tc.quotedTweet.nonEmpty).map(tc =>
|
|
||||||
(
|
|
||||||
tc,
|
|
||||||
QUOTED,
|
|
||||||
tc.quotedTweet.get,
|
|
||||||
getContentId(
|
|
||||||
viewerId = viewerContext.userId,
|
|
||||||
authorId = tc.quotedTweet.get.coreData.get.userId,
|
|
||||||
tweet = tc.quotedTweet.get))) ++
|
|
||||||
tweetContexts
|
|
||||||
.filter(tc => tc.retweetSourceTweet.nonEmpty).map(tc =>
|
|
||||||
(
|
|
||||||
tc,
|
|
||||||
SOURCE,
|
|
||||||
tc.retweetSourceTweet.get,
|
|
||||||
getContentId(
|
|
||||||
viewerId = viewerContext.userId,
|
|
||||||
authorId = tc.retweetSourceTweet.get.coreData.get.userId,
|
|
||||||
tweet = tc.retweetSourceTweet.get)))
|
|
||||||
|
|
||||||
val contentVisResultTypes: Seq[(RequestTweetId, EvaluateTweetType)] = {
|
|
||||||
contentsToBeEvaluated.map {
|
|
||||||
case (tc: TweetContext, tweetType: EvaluateTweetType, _, _) =>
|
|
||||||
(tc.tweet.id, tweetType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(contentsToBeEvaluated, contentVisResultTypes)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def combineVisibilityResult(
|
|
||||||
visResults: Seq[(RequestTweetId, (EvaluateTweetType, Try[VisibilityResult]))]
|
|
||||||
): Option[CombinedVisibilityResult] = {
|
|
||||||
visResults.sortBy(_._2._1)(ValueOrdering) match {
|
|
||||||
case Seq(
|
|
||||||
(_, (REQUEST, Return(requestTweetVisResult))),
|
|
||||||
(_, (QUOTED, Return(quotedTweetVisResult))),
|
|
||||||
(_, (SOURCE, Return(sourceTweetVisResult)))) =>
|
|
||||||
requestTweetVisResult.verdict match {
|
|
||||||
case Allow =>
|
|
||||||
Some(CombinedVisibilityResult(sourceTweetVisResult, Some(quotedTweetVisResult)))
|
|
||||||
case _ =>
|
|
||||||
Some(CombinedVisibilityResult(requestTweetVisResult, Some(quotedTweetVisResult)))
|
|
||||||
}
|
|
||||||
case Seq(
|
|
||||||
(_, (REQUEST, Return(requestTweetVisResult))),
|
|
||||||
(_, (QUOTED, Return(quotedTweetVisResult)))) =>
|
|
||||||
Some(CombinedVisibilityResult(requestTweetVisResult, Some(quotedTweetVisResult)))
|
|
||||||
case Seq(
|
|
||||||
(_, (REQUEST, Return(requestTweetVisResult))),
|
|
||||||
(_, (SOURCE, Return(sourceTweetVisResult)))) =>
|
|
||||||
requestTweetVisResult.verdict match {
|
|
||||||
case Allow =>
|
|
||||||
Some(CombinedVisibilityResult(sourceTweetVisResult, None))
|
|
||||||
case _ =>
|
|
||||||
Some(CombinedVisibilityResult(requestTweetVisResult, None))
|
|
||||||
}
|
|
||||||
|
|
||||||
case Seq((_, (REQUEST, Return(requestTweetVisResult)))) =>
|
|
||||||
Some(CombinedVisibilityResult(requestTweetVisResult, None))
|
|
||||||
case _ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def getVisibilityResult(
|
|
||||||
contents: Seq[(TweetContext, EvaluateTweetType, EvaluateTweet, ContentId)],
|
|
||||||
safetyLevel: SafetyLevel,
|
|
||||||
viewerContext: ViewerContext,
|
|
||||||
svRequestContext: SearchVFRequestContext
|
|
||||||
): Stitch[Seq[Try[VisibilityResult]]] = {
|
|
||||||
|
|
||||||
val contentContext: Map[ContentId, (TweetContext, EvaluateTweetType, EvaluateTweet)] =
|
|
||||||
contents.map {
|
|
||||||
case (
|
|
||||||
tweetContext: TweetContext,
|
|
||||||
tweetType: EvaluateTweetType,
|
|
||||||
tweet: EvaluateTweet,
|
|
||||||
contentId: ContentId) =>
|
|
||||||
contentId -> ((tweetContext, tweetType, tweet))
|
|
||||||
}.toMap
|
|
||||||
|
|
||||||
val featureMapProvider: (ContentId, SafetyLevel) => FeatureMap = {
|
|
||||||
case (contentId: ContentId, _) =>
|
|
||||||
val (tweetContext, tweetType, tweet) = contentContext(contentId)
|
|
||||||
buildFeatureMap(
|
|
||||||
evaluatedTweet = tweet,
|
|
||||||
tweetType = tweetType,
|
|
||||||
tweetContext = tweetContext,
|
|
||||||
viewerContext = viewerContext,
|
|
||||||
svRequestContext = svRequestContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
visibilityLibrary.runRuleEngineBatch(
|
|
||||||
contentIds = contents.map { case (_, _, _, id: ContentId) => id },
|
|
||||||
featureMapProvider = featureMapProvider,
|
|
||||||
viewerContext = viewerContext,
|
|
||||||
safetyLevel = safetyLevel
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def getContentId(viewerId: Option[Long], authorId: Long, tweet: Tweet): ContentId = {
|
|
||||||
if (viewerId.contains(authorId))
|
|
||||||
TweetId(tweet.id)
|
|
||||||
else BlenderTweetId(tweet.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def buildFeatureMap(
|
|
||||||
evaluatedTweet: Tweet,
|
|
||||||
tweetType: EvaluateTweetType,
|
|
||||||
tweetContext: TweetContext,
|
|
||||||
viewerContext: ViewerContext,
|
|
||||||
svRequestContext: SearchVFRequestContext
|
|
||||||
): FeatureMap = {
|
|
||||||
val authorId = evaluatedTweet.coreData.get.userId
|
|
||||||
val viewerId = viewerContext.userId
|
|
||||||
val isRetweet =
|
|
||||||
if (tweetType.equals(REQUEST)) tweetContext.retweetSourceTweet.nonEmpty else false
|
|
||||||
val isSourceTweet = tweetType.equals(SOURCE)
|
|
||||||
val isQuotedTweet = tweetType.equals(QUOTED)
|
|
||||||
val tweetMediaKeys: Seq[GenericMediaKey] = evaluatedTweet.media
|
|
||||||
.getOrElse(Seq.empty)
|
|
||||||
.flatMap(_.mediaKey.map(GenericMediaKey.apply))
|
|
||||||
|
|
||||||
visibilityLibrary.featureMapBuilder(
|
|
||||||
Seq(
|
|
||||||
viewerFeatures
|
|
||||||
.forViewerSearchContext(svRequestContext, viewerContext),
|
|
||||||
relationshipFeatures.forAuthorId(authorId, viewerId),
|
|
||||||
tweetFeatures.forTweet(evaluatedTweet),
|
|
||||||
mediaFeatures.forMediaKeys(tweetMediaKeys),
|
|
||||||
authorFeatures.forAuthorId(authorId),
|
|
||||||
searchContextFeatures.forSearchContext(svRequestContext),
|
|
||||||
_.withConstantFeature(TweetIsRetweet, isRetweet),
|
|
||||||
misinfoPolicyFeatures.forTweet(evaluatedTweet, viewerContext),
|
|
||||||
exclusiveTweetFeatures.forTweet(evaluatedTweet, viewerContext),
|
|
||||||
trustedFriendsTweetFeatures.forTweet(evaluatedTweet, viewerId),
|
|
||||||
editTweetFeatures.forTweet(evaluatedTweet),
|
|
||||||
_.withConstantFeature(TweetIsInnerQuotedTweet, isQuotedTweet),
|
|
||||||
_.withConstantFeature(TweetIsSourceTweet, isSourceTweet),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def handleVisibilityResultByTweetType(
|
|
||||||
zipVisResult: ((RequestTweetId, EvaluateTweetType), Try[VisibilityResult])
|
|
||||||
): (RequestTweetId, (EvaluateTweetType, Try[VisibilityResult])) = {
|
|
||||||
zipVisResult match {
|
|
||||||
case ((id: RequestTweetId, REQUEST), Return(visResult)) =>
|
|
||||||
(id, (REQUEST, Return(handleComposableVisibilityResult(visResult))))
|
|
||||||
case ((id: RequestTweetId, QUOTED), Return(visResult)) =>
|
|
||||||
(
|
|
||||||
id,
|
|
||||||
(
|
|
||||||
QUOTED,
|
|
||||||
Return(
|
|
||||||
handleInnerQuotedTweetVisibilityResult(handleComposableVisibilityResult(visResult)))))
|
|
||||||
case ((id: RequestTweetId, SOURCE), Return(visResult)) =>
|
|
||||||
(id, (SOURCE, Return(handleComposableVisibilityResult(visResult))))
|
|
||||||
case ((id: RequestTweetId, tweetType: EvaluateTweetType), result: Try[VisibilityResult]) =>
|
|
||||||
(id, (tweetType, result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def handleComposableVisibilityResult(result: VisibilityResult): VisibilityResult = {
|
|
||||||
if (result.secondaryVerdicts.nonEmpty) {
|
|
||||||
result.copy(verdict = composeActions(result.verdict, result.secondaryVerdicts))
|
|
||||||
} else {
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def composeActions(primary: Action, secondary: Seq[Action]): Action = {
|
|
||||||
if (primary.isComposable && secondary.nonEmpty) {
|
|
||||||
val actions = Seq[Action] { primary } ++ secondary
|
|
||||||
val interstitialOpt = Action.getFirstInterstitial(actions: _*)
|
|
||||||
val softInterventionOpt = Action.getFirstSoftIntervention(actions: _*)
|
|
||||||
val limitedEngagementsOpt = Action.getFirstLimitedEngagements(actions: _*)
|
|
||||||
val avoidOpt = Action.getFirstAvoid(actions: _*)
|
|
||||||
|
|
||||||
val numActions =
|
|
||||||
Seq[Option[_]](interstitialOpt, softInterventionOpt, limitedEngagementsOpt, avoidOpt)
|
|
||||||
.count(_.isDefined)
|
|
||||||
if (numActions > 1) {
|
|
||||||
TweetInterstitial(
|
|
||||||
interstitialOpt,
|
|
||||||
softInterventionOpt,
|
|
||||||
limitedEngagementsOpt,
|
|
||||||
None,
|
|
||||||
avoidOpt
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
primary
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
primary
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def handleInnerQuotedTweetVisibilityResult(
|
|
||||||
result: VisibilityResult
|
|
||||||
): VisibilityResult = {
|
|
||||||
val newVerdict: Action =
|
|
||||||
result.verdict match {
|
|
||||||
case interstitial: Interstitial => Drop(interstitial.reason)
|
|
||||||
case ComposableActionsWithInterstitial(tweetInterstitial) => Drop(tweetInterstitial.reason)
|
|
||||||
case verdict => verdict
|
|
||||||
}
|
|
||||||
|
|
||||||
result.copy(verdict = newVerdict)
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,10 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.search
|
|
||||||
|
|
||||||
import com.twitter.tweetypie.thriftscala.Tweet
|
|
||||||
import com.twitter.visibility.models.SafetyLevel
|
|
||||||
|
|
||||||
case class TweetContext(
|
|
||||||
tweet: Tweet,
|
|
||||||
quotedTweet: Option[Tweet],
|
|
||||||
retweetSourceTweet: Option[Tweet] = None,
|
|
||||||
safetyLevel: SafetyLevel)
|
|
@ -1,37 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
platform = "java8",
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"decider/src/main/scala",
|
|
||||||
"servo/decider/src/main/scala",
|
|
||||||
"src/scala/com/twitter/search/blender/services/strato",
|
|
||||||
"src/thrift/com/twitter/escherbird:media-annotation-structs-scala",
|
|
||||||
"src/thrift/com/twitter/gizmoduck:user-thrift-scala",
|
|
||||||
"src/thrift/com/twitter/spam/rtf:safety-level-scala",
|
|
||||||
"src/thrift/com/twitter/spam/rtf:tweet-rtf-event-scala",
|
|
||||||
"src/thrift/com/twitter/tweetypie:tweet-scala",
|
|
||||||
"stitch/stitch-core",
|
|
||||||
"strato/src/main/scala/com/twitter/strato/client",
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common",
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common/stitch",
|
|
||||||
"visibility/lib/src/main/resources/config",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/common",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/spaces",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/tweets",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/users",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/configapi/configs",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/features",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/interfaces/common/tweets",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/rules/providers",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/rules/utils",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/util",
|
|
||||||
],
|
|
||||||
exports = [
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,117 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.spaces
|
|
||||||
|
|
||||||
import com.twitter.servo.util.Gate
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.strato.client.{Client => StratoClient}
|
|
||||||
import com.twitter.visibility.VisibilityLibrary
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.builder.common.MutedKeywordFeatures
|
|
||||||
import com.twitter.visibility.builder.spaces.SpaceFeatures
|
|
||||||
import com.twitter.visibility.builder.spaces.StratoSpaceLabelMaps
|
|
||||||
import com.twitter.visibility.builder.users.AuthorFeatures
|
|
||||||
import com.twitter.visibility.builder.users.RelationshipFeatures
|
|
||||||
import com.twitter.visibility.builder.users.ViewerFeatures
|
|
||||||
import com.twitter.visibility.common._
|
|
||||||
import com.twitter.visibility.common.stitch.StitchHelpers
|
|
||||||
import com.twitter.visibility.features.FeatureMap
|
|
||||||
import com.twitter.visibility.models.ContentId.SpaceId
|
|
||||||
import com.twitter.visibility.models.ContentId.SpacePlusUserId
|
|
||||||
import com.twitter.visibility.rules.EvaluationContext
|
|
||||||
import com.twitter.visibility.rules.providers.ProvidedEvaluationContext
|
|
||||||
import com.twitter.visibility.rules.utils.ShimUtils
|
|
||||||
|
|
||||||
object SpaceVisibilityLibrary {
|
|
||||||
type Type = SpaceVisibilityRequest => Stitch[VisibilityResult]
|
|
||||||
|
|
||||||
def apply(
|
|
||||||
visibilityLibrary: VisibilityLibrary,
|
|
||||||
stratoClient: StratoClient,
|
|
||||||
userSource: UserSource,
|
|
||||||
userRelationshipSource: UserRelationshipSource,
|
|
||||||
enableVfFeatureHydrationSpaceShim: Gate[Unit] = Gate.False
|
|
||||||
): Type = {
|
|
||||||
val libraryStatsReceiver = visibilityLibrary.statsReceiver
|
|
||||||
val stratoClientStatsReceiver = visibilityLibrary.statsReceiver.scope("strato")
|
|
||||||
val vfLatencyStatsReceiver = visibilityLibrary.statsReceiver.scope("vf_latency")
|
|
||||||
val vfEngineCounter = libraryStatsReceiver.counter("vf_engine_requests")
|
|
||||||
|
|
||||||
val spaceLabelMaps = new StratoSpaceLabelMaps(
|
|
||||||
SpaceSafetyLabelMapSource.fromStrato(stratoClient, stratoClientStatsReceiver),
|
|
||||||
libraryStatsReceiver)
|
|
||||||
val audioSpaceSource = AudioSpaceSource.fromStrato(stratoClient, stratoClientStatsReceiver)
|
|
||||||
|
|
||||||
val viewerFeatures = new ViewerFeatures(userSource, libraryStatsReceiver)
|
|
||||||
val authorFeatures = new AuthorFeatures(userSource, libraryStatsReceiver)
|
|
||||||
val relationshipFeatures =
|
|
||||||
new RelationshipFeatures(userRelationshipSource, libraryStatsReceiver)
|
|
||||||
val mutedKeywordFeatures = new MutedKeywordFeatures(
|
|
||||||
userSource,
|
|
||||||
userRelationshipSource,
|
|
||||||
KeywordMatcher.matcher(libraryStatsReceiver),
|
|
||||||
libraryStatsReceiver,
|
|
||||||
Gate.False
|
|
||||||
)
|
|
||||||
val spaceFeatures =
|
|
||||||
new SpaceFeatures(
|
|
||||||
spaceLabelMaps,
|
|
||||||
authorFeatures,
|
|
||||||
relationshipFeatures,
|
|
||||||
mutedKeywordFeatures,
|
|
||||||
audioSpaceSource)
|
|
||||||
|
|
||||||
{ r: SpaceVisibilityRequest =>
|
|
||||||
vfEngineCounter.incr()
|
|
||||||
|
|
||||||
val isVfFeatureHydrationEnabled = enableVfFeatureHydrationSpaceShim()
|
|
||||||
val viewerId = r.viewerContext.userId
|
|
||||||
val authorIds: Option[Seq[Long]] = r.spaceHostAndAdminUserIds
|
|
||||||
val contentId = {
|
|
||||||
(viewerId, authorIds) match {
|
|
||||||
case (Some(viewer), Some(authors)) if authors.contains(viewer) => SpaceId(r.spaceId)
|
|
||||||
case _ => SpacePlusUserId(r.spaceId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val featureMap =
|
|
||||||
visibilityLibrary.featureMapBuilder(
|
|
||||||
Seq(
|
|
||||||
spaceFeatures.forSpaceAndAuthorIds(r.spaceId, viewerId, authorIds),
|
|
||||||
viewerFeatures.forViewerContext(r.viewerContext),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val resp = if (isVfFeatureHydrationEnabled) {
|
|
||||||
val evaluationContext = ProvidedEvaluationContext.injectRuntimeRulesIntoEvaluationContext(
|
|
||||||
evaluationContext = EvaluationContext(
|
|
||||||
r.safetyLevel,
|
|
||||||
visibilityLibrary.getParams(r.viewerContext, r.safetyLevel),
|
|
||||||
visibilityLibrary.statsReceiver)
|
|
||||||
)
|
|
||||||
|
|
||||||
val preFilteredFeatureMap =
|
|
||||||
ShimUtils.preFilterFeatureMap(featureMap, r.safetyLevel, contentId, evaluationContext)
|
|
||||||
|
|
||||||
FeatureMap
|
|
||||||
.resolve(preFilteredFeatureMap, libraryStatsReceiver).flatMap { resolvedFeatureMap =>
|
|
||||||
visibilityLibrary
|
|
||||||
.runRuleEngine(
|
|
||||||
contentId,
|
|
||||||
resolvedFeatureMap,
|
|
||||||
r.viewerContext,
|
|
||||||
r.safetyLevel
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
visibilityLibrary
|
|
||||||
.runRuleEngine(
|
|
||||||
contentId,
|
|
||||||
featureMap,
|
|
||||||
r.viewerContext,
|
|
||||||
r.safetyLevel
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
StitchHelpers.profileStitch(resp, Seq(vfLatencyStatsReceiver))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,10 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.spaces
|
|
||||||
|
|
||||||
import com.twitter.visibility.models.SafetyLevel
|
|
||||||
import com.twitter.visibility.models.ViewerContext
|
|
||||||
|
|
||||||
case class SpaceVisibilityRequest(
|
|
||||||
spaceId: String,
|
|
||||||
safetyLevel: SafetyLevel,
|
|
||||||
viewerContext: ViewerContext,
|
|
||||||
spaceHostAndAdminUserIds: Option[Seq[Long]])
|
|
@ -1,47 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
platform = "java8",
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"3rdparty/jvm/com/twitter/src/java/com/twitter/logpipeline/client:logpipeline-event-publisher-thin",
|
|
||||||
"decider/src/main/scala",
|
|
||||||
"featureswitches/featureswitches-core/src/main/scala",
|
|
||||||
"mediaservices/media-util/src/main/scala",
|
|
||||||
"servo/decider/src/main/scala",
|
|
||||||
"src/thrift/com/twitter/context:twitter-context-scala",
|
|
||||||
"src/thrift/com/twitter/escherbird:media-annotation-structs-scala",
|
|
||||||
"src/thrift/com/twitter/gizmoduck:user-thrift-scala",
|
|
||||||
"src/thrift/com/twitter/spam/rtf:safety-level-scala",
|
|
||||||
"src/thrift/com/twitter/spam/rtf:tweet-rtf-event-scala",
|
|
||||||
"src/thrift/com/twitter/tweetypie:tweet-scala",
|
|
||||||
"stitch/stitch-core",
|
|
||||||
"strato/src/main/scala/com/twitter/strato/catalog",
|
|
||||||
"strato/src/main/scala/com/twitter/strato/client",
|
|
||||||
"twitter-context/src/main/scala",
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common",
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common/actions/converter/scala",
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common/tweets",
|
|
||||||
"visibility/common/src/main/thrift/com/twitter/visibility/tweets:tweets-scala",
|
|
||||||
"visibility/lib/src/main/resources/config",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/common",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/media",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/tweets",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/users",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/configapi/configs",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/features",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/generators",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/interfaces/common/tweets",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/interfaces/tweets/enrichments",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/rules/providers",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/rules/utils",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/util",
|
|
||||||
"visibility/lib/src/main/thrift/com/twitter/visibility/logging:vf-logging-scala",
|
|
||||||
],
|
|
||||||
exports = [
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,59 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.tweets
|
|
||||||
|
|
||||||
import com.twitter.decider.Decider
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.visibility.VisibilityLibrary
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.features.TweetDeleteReason
|
|
||||||
import com.twitter.visibility.features.TweetIsInnerQuotedTweet
|
|
||||||
import com.twitter.visibility.features.TweetIsRetweet
|
|
||||||
import com.twitter.visibility.generators.TombstoneGenerator
|
|
||||||
import com.twitter.visibility.models.ContentId.DeleteTweetId
|
|
||||||
import com.twitter.visibility.models.SafetyLevel
|
|
||||||
import com.twitter.visibility.models.TweetDeleteReason.TweetDeleteReason
|
|
||||||
import com.twitter.visibility.models.ViewerContext
|
|
||||||
|
|
||||||
object DeletedTweetVisibilityLibrary {
|
|
||||||
type Type = DeletedTweetVisibilityLibrary.Request => Stitch[VisibilityResult]
|
|
||||||
|
|
||||||
case class Request(
|
|
||||||
tweetId: Long,
|
|
||||||
safetyLevel: SafetyLevel,
|
|
||||||
viewerContext: ViewerContext,
|
|
||||||
tweetDeleteReason: TweetDeleteReason,
|
|
||||||
isRetweet: Boolean,
|
|
||||||
isInnerQuotedTweet: Boolean,
|
|
||||||
)
|
|
||||||
|
|
||||||
def apply(
|
|
||||||
visibilityLibrary: VisibilityLibrary,
|
|
||||||
decider: Decider,
|
|
||||||
tombstoneGenerator: TombstoneGenerator,
|
|
||||||
): Type = {
|
|
||||||
val vfEngineCounter = visibilityLibrary.statsReceiver.counter("vf_engine_requests")
|
|
||||||
|
|
||||||
(request: Request) => {
|
|
||||||
vfEngineCounter.incr()
|
|
||||||
val contentId = DeleteTweetId(request.tweetId)
|
|
||||||
val language = request.viewerContext.requestLanguageCode.getOrElse("en")
|
|
||||||
|
|
||||||
val featureMap =
|
|
||||||
visibilityLibrary.featureMapBuilder(
|
|
||||||
Seq(
|
|
||||||
_.withConstantFeature(TweetIsInnerQuotedTweet, request.isInnerQuotedTweet),
|
|
||||||
_.withConstantFeature(TweetIsRetweet, request.isRetweet),
|
|
||||||
_.withConstantFeature(TweetDeleteReason, request.tweetDeleteReason)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
visibilityLibrary
|
|
||||||
.runRuleEngine(
|
|
||||||
contentId,
|
|
||||||
featureMap,
|
|
||||||
request.viewerContext,
|
|
||||||
request.safetyLevel
|
|
||||||
)
|
|
||||||
.map(tombstoneGenerator(_, language))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,150 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.tweets
|
|
||||||
|
|
||||||
import com.twitter.decider.Decider
|
|
||||||
import com.twitter.servo.util.Gate
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.visibility.VisibilityLibrary
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.builder.users.AuthorFeatures
|
|
||||||
import com.twitter.visibility.builder.users.QuotedTweetFeatures
|
|
||||||
import com.twitter.visibility.builder.users.RelationshipFeatures
|
|
||||||
import com.twitter.visibility.builder.users.ViewerFeatures
|
|
||||||
import com.twitter.visibility.common.UserRelationshipSource
|
|
||||||
import com.twitter.visibility.common.UserSource
|
|
||||||
import com.twitter.visibility.configapi.configs.VisibilityDeciderGates
|
|
||||||
import com.twitter.visibility.features.FeatureMap
|
|
||||||
import com.twitter.visibility.models.ContentId.QuotedTweetRelationship
|
|
||||||
import com.twitter.visibility.models.SafetyLevel
|
|
||||||
import com.twitter.visibility.models.UserUnavailableStateEnum
|
|
||||||
import com.twitter.visibility.models.ViewerContext
|
|
||||||
import com.twitter.visibility.rules.Drop
|
|
||||||
import com.twitter.visibility.rules.EvaluationContext
|
|
||||||
import com.twitter.visibility.rules.Reason.AuthorBlocksViewer
|
|
||||||
import com.twitter.visibility.rules.Reason.DeactivatedAuthor
|
|
||||||
import com.twitter.visibility.rules.Reason.ErasedAuthor
|
|
||||||
import com.twitter.visibility.rules.Reason.OffboardedAuthor
|
|
||||||
import com.twitter.visibility.rules.Reason.ProtectedAuthor
|
|
||||||
import com.twitter.visibility.rules.Reason.SuspendedAuthor
|
|
||||||
import com.twitter.visibility.rules.Reason.ViewerBlocksAuthor
|
|
||||||
import com.twitter.visibility.rules.Reason.ViewerHardMutedAuthor
|
|
||||||
import com.twitter.visibility.rules.Reason.ViewerMutesAuthor
|
|
||||||
import com.twitter.visibility.rules.providers.ProvidedEvaluationContext
|
|
||||||
import com.twitter.visibility.rules.utils.ShimUtils
|
|
||||||
|
|
||||||
case class TweetAndAuthor(tweetId: Long, authorId: Long)
|
|
||||||
|
|
||||||
case class QuotedTweetVisibilityRequest(
|
|
||||||
quotedTweet: TweetAndAuthor,
|
|
||||||
outerTweet: TweetAndAuthor,
|
|
||||||
viewerContext: ViewerContext,
|
|
||||||
safetyLevel: SafetyLevel)
|
|
||||||
|
|
||||||
object QuotedTweetVisibilityLibrary {
|
|
||||||
|
|
||||||
type Type = QuotedTweetVisibilityRequest => Stitch[VisibilityResult]
|
|
||||||
|
|
||||||
def apply(
|
|
||||||
visibilityLibrary: VisibilityLibrary,
|
|
||||||
userSource: UserSource,
|
|
||||||
userRelationshipSource: UserRelationshipSource,
|
|
||||||
decider: Decider,
|
|
||||||
userStateVisibilityLibrary: UserUnavailableStateVisibilityLibrary.Type,
|
|
||||||
enableVfFeatureHydration: Gate[Unit] = Gate.False
|
|
||||||
): Type = {
|
|
||||||
val libraryStatsReceiver = visibilityLibrary.statsReceiver
|
|
||||||
val visibilityDeciderGates = VisibilityDeciderGates(decider)
|
|
||||||
val vfEngineCounter = libraryStatsReceiver.counter("vf_engine_requests")
|
|
||||||
|
|
||||||
{
|
|
||||||
case QuotedTweetVisibilityRequest(quotedTweet, outerTweet, viewerContext, safetyLevel) =>
|
|
||||||
vfEngineCounter.incr()
|
|
||||||
val contentId = QuotedTweetRelationship(
|
|
||||||
outer = outerTweet.tweetId,
|
|
||||||
inner = quotedTweet.tweetId
|
|
||||||
)
|
|
||||||
|
|
||||||
val innerAuthorId = quotedTweet.authorId
|
|
||||||
val outerAuthorId = outerTweet.authorId
|
|
||||||
val viewerId = viewerContext.userId
|
|
||||||
val isFeatureHydrationInShimEnabled = enableVfFeatureHydration()
|
|
||||||
|
|
||||||
val authorFeatures = new AuthorFeatures(userSource, libraryStatsReceiver)
|
|
||||||
val viewerFeatures = new ViewerFeatures(userSource, libraryStatsReceiver)
|
|
||||||
val relationshipFeatures =
|
|
||||||
new RelationshipFeatures(userRelationshipSource, libraryStatsReceiver)
|
|
||||||
val quotedTweetFeatures =
|
|
||||||
new QuotedTweetFeatures(relationshipFeatures, libraryStatsReceiver)
|
|
||||||
|
|
||||||
val featureMap = visibilityLibrary.featureMapBuilder(
|
|
||||||
Seq(
|
|
||||||
viewerFeatures.forViewerContext(viewerContext),
|
|
||||||
authorFeatures.forAuthorId(innerAuthorId),
|
|
||||||
relationshipFeatures.forAuthorId(innerAuthorId, viewerId),
|
|
||||||
quotedTweetFeatures.forOuterAuthor(outerAuthorId, innerAuthorId)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val resp = if (isFeatureHydrationInShimEnabled) {
|
|
||||||
val evaluationContext = ProvidedEvaluationContext.injectRuntimeRulesIntoEvaluationContext(
|
|
||||||
evaluationContext = EvaluationContext(
|
|
||||||
SafetyLevel.QuotedTweetRules,
|
|
||||||
visibilityLibrary.getParams(viewerContext, SafetyLevel.QuotedTweetRules),
|
|
||||||
visibilityLibrary.statsReceiver)
|
|
||||||
)
|
|
||||||
|
|
||||||
val preFilteredFeatureMap =
|
|
||||||
ShimUtils.preFilterFeatureMap(
|
|
||||||
featureMap,
|
|
||||||
SafetyLevel.QuotedTweetRules,
|
|
||||||
contentId,
|
|
||||||
evaluationContext)
|
|
||||||
|
|
||||||
FeatureMap.resolve(preFilteredFeatureMap, libraryStatsReceiver).flatMap {
|
|
||||||
resolvedFeatureMap =>
|
|
||||||
visibilityLibrary
|
|
||||||
.runRuleEngine(
|
|
||||||
contentId,
|
|
||||||
resolvedFeatureMap,
|
|
||||||
viewerContext,
|
|
||||||
SafetyLevel.QuotedTweetRules
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
visibilityLibrary
|
|
||||||
.runRuleEngine(
|
|
||||||
contentId,
|
|
||||||
featureMap,
|
|
||||||
viewerContext,
|
|
||||||
SafetyLevel.QuotedTweetRules
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.flatMap { visResult =>
|
|
||||||
val userStateOpt = visResult.verdict match {
|
|
||||||
case Drop(DeactivatedAuthor, _) => Some(UserUnavailableStateEnum.Deactivated)
|
|
||||||
case Drop(OffboardedAuthor, _) => Some(UserUnavailableStateEnum.Offboarded)
|
|
||||||
case Drop(ErasedAuthor, _) => Some(UserUnavailableStateEnum.Erased)
|
|
||||||
case Drop(ProtectedAuthor, _) => Some(UserUnavailableStateEnum.Protected)
|
|
||||||
case Drop(SuspendedAuthor, _) => Some(UserUnavailableStateEnum.Suspended)
|
|
||||||
case Drop(AuthorBlocksViewer, _) => Some(UserUnavailableStateEnum.AuthorBlocksViewer)
|
|
||||||
case Drop(ViewerBlocksAuthor, _) => Some(UserUnavailableStateEnum.ViewerBlocksAuthor)
|
|
||||||
case Drop(ViewerMutesAuthor, _) => Some(UserUnavailableStateEnum.ViewerMutesAuthor)
|
|
||||||
case Drop(ViewerHardMutedAuthor, _) => Some(UserUnavailableStateEnum.ViewerMutesAuthor)
|
|
||||||
case _ => None
|
|
||||||
}
|
|
||||||
|
|
||||||
userStateOpt
|
|
||||||
.map(userState =>
|
|
||||||
userStateVisibilityLibrary(
|
|
||||||
UserUnavailableStateVisibilityRequest(
|
|
||||||
safetyLevel,
|
|
||||||
quotedTweet.tweetId,
|
|
||||||
viewerContext,
|
|
||||||
userState,
|
|
||||||
isRetweet = false,
|
|
||||||
isInnerQuotedTweet = true,
|
|
||||||
))).getOrElse(Stitch.value(visResult))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,421 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.tweets
|
|
||||||
|
|
||||||
import com.twitter.decider.Decider
|
|
||||||
import com.twitter.featureswitches.v2.FeatureSwitches
|
|
||||||
import com.twitter.finagle.stats.StatsReceiver
|
|
||||||
import com.twitter.mediaservices.media_util.GenericMediaKey
|
|
||||||
import com.twitter.servo.util.Gate
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.strato.client.{Client => StratoClient}
|
|
||||||
import com.twitter.util.Stopwatch
|
|
||||||
import com.twitter.visibility.VisibilityLibrary
|
|
||||||
import com.twitter.visibility.builder.VerdictLogger
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.builder.common.MutedKeywordFeatures
|
|
||||||
import com.twitter.visibility.builder.media._
|
|
||||||
import com.twitter.visibility.builder.tweets.TweetVisibilityNudgeSourceWrapper
|
|
||||||
import com.twitter.visibility.builder.tweets._
|
|
||||||
import com.twitter.visibility.builder.users.AuthorFeatures
|
|
||||||
import com.twitter.visibility.builder.users.RelationshipFeatures
|
|
||||||
import com.twitter.visibility.builder.users.ViewerFeatures
|
|
||||||
import com.twitter.visibility.builder.users.ViewerSearchSafetyFeatures
|
|
||||||
import com.twitter.visibility.builder.users.ViewerSensitiveMediaSettingsFeatures
|
|
||||||
import com.twitter.visibility.common._
|
|
||||||
import com.twitter.visibility.common.actions.LimitedAction
|
|
||||||
import com.twitter.visibility.common.actions.LimitedActionType
|
|
||||||
import com.twitter.visibility.common.actions.LimitedActionsPolicy
|
|
||||||
import com.twitter.visibility.rules.ComposableActions._
|
|
||||||
import com.twitter.visibility.configapi.configs.VisibilityDeciderGates
|
|
||||||
import com.twitter.visibility.features.TweetIsInnerQuotedTweet
|
|
||||||
import com.twitter.visibility.features.TweetIsRetweet
|
|
||||||
import com.twitter.visibility.features.TweetIsSourceTweet
|
|
||||||
import com.twitter.visibility.generators.LocalizedInterstitialGenerator
|
|
||||||
import com.twitter.visibility.generators.TombstoneGenerator
|
|
||||||
import com.twitter.visibility.interfaces.tweets.enrichments.ComplianceTweetNoticeEnrichment
|
|
||||||
import com.twitter.visibility.interfaces.tweets.enrichments.LimitedActionsPolicyEnrichment
|
|
||||||
import com.twitter.visibility.interfaces.tweets.enrichments.TweetVisibilityNudgeEnrichment
|
|
||||||
import com.twitter.visibility.logging.thriftscala.VFLibType
|
|
||||||
import com.twitter.visibility.models.ContentId.TweetId
|
|
||||||
import com.twitter.visibility.models.SafetyLevel
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.toThrift
|
|
||||||
import com.twitter.visibility.rules._
|
|
||||||
|
|
||||||
object TweetVisibilityLibrary {
|
|
||||||
type Type = TweetVisibilityRequest => Stitch[VisibilityResult]
|
|
||||||
|
|
||||||
def apply(
|
|
||||||
visibilityLibrary: VisibilityLibrary,
|
|
||||||
userSource: UserSource,
|
|
||||||
userRelationshipSource: UserRelationshipSource,
|
|
||||||
keywordMatcher: KeywordMatcher.Matcher,
|
|
||||||
invitedToConversationRepo: InvitedToConversationRepo,
|
|
||||||
decider: Decider,
|
|
||||||
stratoClient: StratoClient,
|
|
||||||
localizationSource: LocalizationSource,
|
|
||||||
tweetPerspectiveSource: TweetPerspectiveSource,
|
|
||||||
tweetMediaMetadataSource: TweetMediaMetadataSource,
|
|
||||||
tombstoneGenerator: TombstoneGenerator,
|
|
||||||
interstitialGenerator: LocalizedInterstitialGenerator,
|
|
||||||
limitedActionsFeatureSwitches: FeatureSwitches,
|
|
||||||
enableParityTest: Gate[Unit] = Gate.False
|
|
||||||
): Type = {
|
|
||||||
val libraryStatsReceiver = visibilityLibrary.statsReceiver
|
|
||||||
val stratoClientStatsReceiver = visibilityLibrary.statsReceiver.scope("strato")
|
|
||||||
val vfEngineCounter = libraryStatsReceiver.counter("vf_engine_requests")
|
|
||||||
val vfLatencyOverallStat = libraryStatsReceiver.stat("vf_latency_overall")
|
|
||||||
val vfLatencyStitchBuildStat = libraryStatsReceiver.stat("vf_latency_stitch_build")
|
|
||||||
val vfLatencyStitchRunStat = libraryStatsReceiver.stat("vf_latency_stitch_run")
|
|
||||||
val visibilityDeciderGates = VisibilityDeciderGates(decider)
|
|
||||||
val verdictLogger =
|
|
||||||
createVerdictLogger(
|
|
||||||
visibilityDeciderGates.enableVerdictLoggerTVL,
|
|
||||||
decider,
|
|
||||||
libraryStatsReceiver)
|
|
||||||
|
|
||||||
val tweetLabelMaps = new StratoTweetLabelMaps(
|
|
||||||
SafetyLabelMapSource.fromStrato(stratoClient, stratoClientStatsReceiver))
|
|
||||||
|
|
||||||
val mediaLabelMaps = new StratoMediaLabelMaps(
|
|
||||||
MediaSafetyLabelMapSource.fromStrato(stratoClient, stratoClientStatsReceiver))
|
|
||||||
|
|
||||||
val tweetFeatures = new TweetFeatures(tweetLabelMaps, libraryStatsReceiver)
|
|
||||||
val tweetPerspectiveFeatures =
|
|
||||||
new TweetPerspectiveFeatures(tweetPerspectiveSource, libraryStatsReceiver)
|
|
||||||
val mediaFeatures = new MediaFeatures(mediaLabelMaps, libraryStatsReceiver)
|
|
||||||
val tweetMediaMetadataFeatures =
|
|
||||||
new TweetMediaMetadataFeatures(tweetMediaMetadataSource, libraryStatsReceiver)
|
|
||||||
val authorFeatures = new AuthorFeatures(userSource, libraryStatsReceiver)
|
|
||||||
val viewerFeatures = new ViewerFeatures(userSource, libraryStatsReceiver)
|
|
||||||
val mutedKeywordFeatures =
|
|
||||||
new MutedKeywordFeatures(
|
|
||||||
userSource,
|
|
||||||
userRelationshipSource,
|
|
||||||
keywordMatcher,
|
|
||||||
libraryStatsReceiver,
|
|
||||||
visibilityDeciderGates.enableFollowCheckInMutedKeyword
|
|
||||||
)
|
|
||||||
val relationshipFeatures =
|
|
||||||
new RelationshipFeatures(userRelationshipSource, libraryStatsReceiver)
|
|
||||||
val fonsrRelationshipFeatures =
|
|
||||||
new FosnrRelationshipFeatures(
|
|
||||||
tweetLabels = tweetLabelMaps,
|
|
||||||
userRelationshipSource = userRelationshipSource,
|
|
||||||
statsReceiver = libraryStatsReceiver)
|
|
||||||
val conversationControlFeatures =
|
|
||||||
new ConversationControlFeatures(
|
|
||||||
relationshipFeatures,
|
|
||||||
invitedToConversationRepo,
|
|
||||||
libraryStatsReceiver
|
|
||||||
)
|
|
||||||
val exclusiveTweetFeatures =
|
|
||||||
new ExclusiveTweetFeatures(userRelationshipSource, libraryStatsReceiver)
|
|
||||||
|
|
||||||
val viewerSearchSafetyFeatures = new ViewerSearchSafetyFeatures(
|
|
||||||
UserSearchSafetySource.fromStrato(stratoClient, stratoClientStatsReceiver),
|
|
||||||
libraryStatsReceiver)
|
|
||||||
|
|
||||||
val viewerSensitiveMediaSettingsFeatures = new ViewerSensitiveMediaSettingsFeatures(
|
|
||||||
UserSensitiveMediaSettingsSource.fromStrato(stratoClient, stratoClientStatsReceiver),
|
|
||||||
libraryStatsReceiver)
|
|
||||||
|
|
||||||
val toxicReplyFilterFeature = new ToxicReplyFilterFeature(statsReceiver = libraryStatsReceiver)
|
|
||||||
|
|
||||||
val misinfoPolicySource =
|
|
||||||
MisinformationPolicySource.fromStrato(stratoClient, stratoClientStatsReceiver)
|
|
||||||
val misinfoPolicyFeatures =
|
|
||||||
new MisinformationPolicyFeatures(misinfoPolicySource, stratoClientStatsReceiver)
|
|
||||||
|
|
||||||
val communityTweetFeatures = new CommunityTweetFeaturesV2(
|
|
||||||
communitiesSource = CommunitiesSource.fromStrato(
|
|
||||||
stratoClient,
|
|
||||||
stratoClientStatsReceiver
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val trustedFriendsTweetFeatures = new TrustedFriendsFeatures(
|
|
||||||
trustedFriendsSource =
|
|
||||||
TrustedFriendsSource.fromStrato(stratoClient, stratoClientStatsReceiver))
|
|
||||||
|
|
||||||
val editTweetFeatures = new EditTweetFeatures(libraryStatsReceiver)
|
|
||||||
|
|
||||||
val parityTest = new TweetVisibilityLibraryParityTest(libraryStatsReceiver, stratoClient)
|
|
||||||
|
|
||||||
val localizedNudgeSource =
|
|
||||||
LocalizedNudgeSource.fromLocalizationSource(localizationSource)
|
|
||||||
val tweetVisibilityNudgeFeatures =
|
|
||||||
new TweetVisibilityNudgeSourceWrapper(localizedNudgeSource)
|
|
||||||
|
|
||||||
val localizedLimitedActionsSource =
|
|
||||||
LocalizedLimitedActionsSource.fromLocalizationSource(localizationSource)
|
|
||||||
|
|
||||||
{ r: TweetVisibilityRequest =>
|
|
||||||
val elapsed = Stopwatch.start()
|
|
||||||
var runStitchStartMs = 0L
|
|
||||||
vfEngineCounter.incr()
|
|
||||||
|
|
||||||
val contentId = TweetId(r.tweet.id)
|
|
||||||
val viewerId = r.viewerContext.userId
|
|
||||||
val authorId = coreData.userId
|
|
||||||
val tweetGenericMediaKeys = r.tweet.mediaRefs
|
|
||||||
.getOrElse(Seq.empty)
|
|
||||||
.flatMap { mediaRef =>
|
|
||||||
GenericMediaKey.fromStringKey(mediaRef.genericMediaKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
val tpf =
|
|
||||||
tweetPerspectiveFeatures.forTweet(
|
|
||||||
r.tweet,
|
|
||||||
viewerId,
|
|
||||||
visibilityDeciderGates.enableFetchTweetReportedPerspective())
|
|
||||||
|
|
||||||
val featureMap =
|
|
||||||
visibilityLibrary.featureMapBuilder(
|
|
||||||
Seq(
|
|
||||||
mutedKeywordFeatures.forTweet(r.tweet, viewerId, authorId),
|
|
||||||
viewerFeatures.forViewerContext(r.viewerContext),
|
|
||||||
viewerSearchSafetyFeatures.forViewerId(viewerId),
|
|
||||||
viewerSensitiveMediaSettingsFeatures.forViewerId(viewerId),
|
|
||||||
relationshipFeatures.forAuthorId(authorId, viewerId),
|
|
||||||
fonsrRelationshipFeatures
|
|
||||||
.forTweetAndAuthorId(tweet = r.tweet, authorId = authorId, viewerId = viewerId),
|
|
||||||
tweetFeatures.forTweet(r.tweet),
|
|
||||||
tpf,
|
|
||||||
mediaFeatures.forMediaKeys(tweetGenericMediaKeys),
|
|
||||||
authorFeatures.forAuthorId(authorId),
|
|
||||||
conversationControlFeatures.forTweet(r.tweet, viewerId),
|
|
||||||
_.withConstantFeature(TweetIsInnerQuotedTweet, r.isInnerQuotedTweet),
|
|
||||||
_.withConstantFeature(TweetIsRetweet, r.isRetweet),
|
|
||||||
_.withConstantFeature(TweetIsSourceTweet, r.isSourceTweet),
|
|
||||||
misinfoPolicyFeatures.forTweet(r.tweet, r.viewerContext),
|
|
||||||
exclusiveTweetFeatures.forTweet(r.tweet, r.viewerContext),
|
|
||||||
communityTweetFeatures.forTweet(r.tweet, r.viewerContext),
|
|
||||||
tweetMediaMetadataFeatures
|
|
||||||
.forTweet(
|
|
||||||
r.tweet,
|
|
||||||
tweetGenericMediaKeys,
|
|
||||||
visibilityDeciderGates.enableFetchTweetMediaMetadata()),
|
|
||||||
trustedFriendsTweetFeatures.forTweet(r.tweet, viewerId),
|
|
||||||
editTweetFeatures.forTweet(r.tweet),
|
|
||||||
toxicReplyFilterFeature.forTweet(r.tweet, viewerId),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val languageCode = r.viewerContext.requestLanguageCode.getOrElse("en")
|
|
||||||
val countryCode = r.viewerContext.requestCountryCode
|
|
||||||
|
|
||||||
val response = visibilityLibrary
|
|
||||||
.runRuleEngine(
|
|
||||||
contentId,
|
|
||||||
featureMap,
|
|
||||||
r.viewerContext,
|
|
||||||
r.safetyLevel
|
|
||||||
)
|
|
||||||
.map(
|
|
||||||
TweetVisibilityNudgeEnrichment(
|
|
||||||
_,
|
|
||||||
tweetVisibilityNudgeFeatures,
|
|
||||||
languageCode,
|
|
||||||
countryCode))
|
|
||||||
.map(verdict => {
|
|
||||||
if (visibilityDeciderGates.enableBackendLimitedActions()) {
|
|
||||||
LimitedActionsPolicyEnrichment(
|
|
||||||
verdict,
|
|
||||||
localizedLimitedActionsSource,
|
|
||||||
languageCode,
|
|
||||||
countryCode,
|
|
||||||
limitedActionsFeatureSwitches,
|
|
||||||
libraryStatsReceiver)
|
|
||||||
} else {
|
|
||||||
verdict
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(
|
|
||||||
handleComposableVisibilityResult(
|
|
||||||
_,
|
|
||||||
visibilityDeciderGates.enableMediaInterstitialComposition(),
|
|
||||||
visibilityDeciderGates.enableBackendLimitedActions()))
|
|
||||||
.map(handleInnerQuotedTweetVisibilityResult(_, r.isInnerQuotedTweet))
|
|
||||||
.map(tombstoneGenerator(_, languageCode))
|
|
||||||
.map(interstitialGenerator(_, languageCode))
|
|
||||||
.map(ComplianceTweetNoticeEnrichment(_, libraryStatsReceiver))
|
|
||||||
.onSuccess(_ => {
|
|
||||||
val overallStatMs = elapsed().inMilliseconds
|
|
||||||
vfLatencyOverallStat.add(overallStatMs)
|
|
||||||
val runStitchEndMs = elapsed().inMilliseconds
|
|
||||||
vfLatencyStitchRunStat.add(runStitchEndMs - runStitchStartMs)
|
|
||||||
})
|
|
||||||
.onSuccess(
|
|
||||||
scribeVisibilityVerdict(
|
|
||||||
_,
|
|
||||||
visibilityDeciderGates.enableVerdictScribingTVL,
|
|
||||||
verdictLogger,
|
|
||||||
r.viewerContext.userId,
|
|
||||||
r.safetyLevel))
|
|
||||||
|
|
||||||
runStitchStartMs = elapsed().inMilliseconds
|
|
||||||
val buildStitchStatMs = elapsed().inMilliseconds
|
|
||||||
vfLatencyStitchBuildStat.add(buildStitchStatMs)
|
|
||||||
|
|
||||||
if (enableParityTest()) {
|
|
||||||
response.applyEffect { resp =>
|
|
||||||
Stitch.async(parityTest.runParityTest(r, resp))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
response
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def handleComposableVisibilityResult(
|
|
||||||
result: VisibilityResult,
|
|
||||||
enableMediaInterstitialComposition: Boolean,
|
|
||||||
enableBackendLimitedActions: Boolean
|
|
||||||
): VisibilityResult = {
|
|
||||||
if (result.secondaryVerdicts.nonEmpty || enableBackendLimitedActions) {
|
|
||||||
result.copy(verdict = composeActions(
|
|
||||||
result.verdict,
|
|
||||||
result.secondaryVerdicts,
|
|
||||||
enableMediaInterstitialComposition,
|
|
||||||
enableBackendLimitedActions))
|
|
||||||
} else {
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def handleInnerQuotedTweetVisibilityResult(
|
|
||||||
result: VisibilityResult,
|
|
||||||
isInnerQuotedTweet: Boolean
|
|
||||||
): VisibilityResult = {
|
|
||||||
val newVerdict: Action =
|
|
||||||
result.verdict match {
|
|
||||||
case Interstitial(Reason.Nsfw | Reason.NsfwMedia, _, _) if isInnerQuotedTweet =>
|
|
||||||
Drop(Reason.Nsfw)
|
|
||||||
case ComposableActionsWithInterstitial(tweetInterstitial)
|
|
||||||
if isInnerQuotedTweet && (tweetInterstitial.reason == Reason.Nsfw || tweetInterstitial.reason == Reason.NsfwMedia) =>
|
|
||||||
Drop(Reason.Nsfw)
|
|
||||||
case verdict => verdict
|
|
||||||
}
|
|
||||||
|
|
||||||
result.copy(verdict = newVerdict)
|
|
||||||
}
|
|
||||||
|
|
||||||
def hasTweetRules(safetyLevel: SafetyLevel): Boolean = RuleBase.hasTweetRules(safetyLevel)
|
|
||||||
|
|
||||||
def composeActions(
|
|
||||||
primary: Action,
|
|
||||||
secondary: Seq[Action],
|
|
||||||
enableMediaInterstitialComposition: Boolean,
|
|
||||||
enableBackendLimitedActions: Boolean
|
|
||||||
): Action = {
|
|
||||||
if (primary.isComposable && (secondary.nonEmpty || enableBackendLimitedActions)) {
|
|
||||||
val actions = Seq[Action] { primary } ++ secondary
|
|
||||||
val interstitialOpt = Action.getFirstInterstitial(actions: _*)
|
|
||||||
val softInterventionOpt = Action.getFirstSoftIntervention(actions: _*)
|
|
||||||
val downrankOpt = Action.getFirstDownrankHomeTimeline(actions: _*)
|
|
||||||
val avoidOpt = Action.getFirstAvoid(actions: _*)
|
|
||||||
val tweetVisibilityNudgeOpt = Action.getFirstTweetVisibilityNudge(actions: _*)
|
|
||||||
|
|
||||||
val mediaInterstitialOpt = {
|
|
||||||
val firstMediaInterstitialOpt = Action.getFirstMediaInterstitial(actions: _*)
|
|
||||||
if (enableMediaInterstitialComposition && interstitialOpt != firstMediaInterstitialOpt) {
|
|
||||||
firstMediaInterstitialOpt
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val limitedEngagementsOpt = enableBackendLimitedActions match {
|
|
||||||
case true => buildCompositeLimitedEngagements(Action.getAllLimitedEngagements(actions: _*))
|
|
||||||
case false => Action.getFirstLimitedEngagements(actions: _*)
|
|
||||||
}
|
|
||||||
|
|
||||||
val abusiveQualityOpt = {
|
|
||||||
if (actions.contains(ConversationSectionAbusiveQuality)) {
|
|
||||||
Some(ConversationSectionAbusiveQuality)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val numActions =
|
|
||||||
Seq[Option[_]](
|
|
||||||
interstitialOpt,
|
|
||||||
softInterventionOpt,
|
|
||||||
limitedEngagementsOpt,
|
|
||||||
downrankOpt,
|
|
||||||
avoidOpt,
|
|
||||||
mediaInterstitialOpt,
|
|
||||||
tweetVisibilityNudgeOpt,
|
|
||||||
abusiveQualityOpt)
|
|
||||||
.count(_.isDefined)
|
|
||||||
if (numActions > 1) {
|
|
||||||
TweetInterstitial(
|
|
||||||
interstitialOpt,
|
|
||||||
softInterventionOpt,
|
|
||||||
limitedEngagementsOpt,
|
|
||||||
downrankOpt,
|
|
||||||
avoidOpt,
|
|
||||||
mediaInterstitialOpt,
|
|
||||||
tweetVisibilityNudgeOpt,
|
|
||||||
abusiveQualityOpt
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
if (enableBackendLimitedActions) {
|
|
||||||
limitedEngagementsOpt.getOrElse(primary)
|
|
||||||
} else {
|
|
||||||
primary
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
primary
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def scribeVisibilityVerdict(
|
|
||||||
result: VisibilityResult,
|
|
||||||
enableVerdictScribing: Gate[Unit],
|
|
||||||
verdictLogger: VerdictLogger,
|
|
||||||
viewerId: Option[Long],
|
|
||||||
safetyLevel: SafetyLevel
|
|
||||||
): Unit = if (enableVerdictScribing()) {
|
|
||||||
verdictLogger.scribeVerdict(
|
|
||||||
visibilityResult = result,
|
|
||||||
viewerId = viewerId,
|
|
||||||
safetyLevel = toThrift(safetyLevel),
|
|
||||||
vfLibType = VFLibType.TweetVisibilityLibrary)
|
|
||||||
}
|
|
||||||
|
|
||||||
def buildCompositeLimitedEngagements(
|
|
||||||
limitedEngagements: Seq[IsLimitedEngagements]
|
|
||||||
): Option[LimitedEngagements] = {
|
|
||||||
limitedEngagements.headOption.flatMap { limitedEngagement =>
|
|
||||||
val distinctLimitedActions = limitedEngagements
|
|
||||||
.collect({ case IsLimitedEngagements(Some(policy), _) => policy.limitedActions })
|
|
||||||
.flatten
|
|
||||||
.foldRight(Map.empty[LimitedActionType, LimitedAction])({ (limitedAction, acc) =>
|
|
||||||
acc + ((limitedAction.limitedActionType, limitedAction))
|
|
||||||
})
|
|
||||||
.values
|
|
||||||
.toSeq
|
|
||||||
|
|
||||||
if (distinctLimitedActions.nonEmpty) {
|
|
||||||
val limitedActionsPolicy = Some(LimitedActionsPolicy(distinctLimitedActions))
|
|
||||||
Some(LimitedEngagements(limitedEngagement.getLimitedEngagementReason, limitedActionsPolicy))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def createVerdictLogger(
|
|
||||||
enableVerdictLogger: Gate[Unit],
|
|
||||||
decider: Decider,
|
|
||||||
statsReceiver: StatsReceiver
|
|
||||||
): VerdictLogger = {
|
|
||||||
if (enableVerdictLogger()) {
|
|
||||||
VerdictLogger(statsReceiver, decider)
|
|
||||||
} else {
|
|
||||||
VerdictLogger.Empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,109 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.tweets
|
|
||||||
|
|
||||||
import com.twitter.spam.rtf.{thriftscala => t}
|
|
||||||
import com.twitter.context.TwitterContext
|
|
||||||
import com.twitter.context.thriftscala.Viewer
|
|
||||||
import com.twitter.finagle.stats.StatsReceiver
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.strato.catalog.Fetch
|
|
||||||
import com.twitter.strato.client.Client
|
|
||||||
import com.twitter.strato.client.Fetcher
|
|
||||||
import com.twitter.strato.thrift.ScroogeConvImplicits._
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.common.tweets.TweetVisibilityResultMapper
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.toThrift
|
|
||||||
import com.twitter.visibility.models.ViewerContext
|
|
||||||
import com.twitter.visibility.thriftscala.TweetVisibilityResult
|
|
||||||
|
|
||||||
class TweetVisibilityLibraryParityTest(statsReceiver: StatsReceiver, stratoClient: Client) {
|
|
||||||
|
|
||||||
private val parityTestScope = statsReceiver.scope("tweet_visibility_library_parity")
|
|
||||||
private val requests = parityTestScope.counter("requests")
|
|
||||||
private val equal = parityTestScope.counter("equal")
|
|
||||||
private val incorrect = parityTestScope.counter("incorrect")
|
|
||||||
private val empty = parityTestScope.counter("empty")
|
|
||||||
private val failures = parityTestScope.counter("failures")
|
|
||||||
|
|
||||||
private val fetcher: Fetcher[Long, t.SafetyLevel, TweetVisibilityResult] =
|
|
||||||
stratoClient.fetcher[Long, t.SafetyLevel, TweetVisibilityResult](
|
|
||||||
"visibility/service/TweetVisibilityResult.Tweet"
|
|
||||||
)
|
|
||||||
|
|
||||||
def runParityTest(
|
|
||||||
req: TweetVisibilityRequest,
|
|
||||||
resp: VisibilityResult
|
|
||||||
): Stitch[Unit] = {
|
|
||||||
requests.incr()
|
|
||||||
|
|
||||||
val twitterContext = TwitterContext(TwitterContextPermit)
|
|
||||||
|
|
||||||
val viewer: Option[Viewer] = {
|
|
||||||
|
|
||||||
val remoteViewerContext = ViewerContext.fromContext
|
|
||||||
|
|
||||||
if (remoteViewerContext != req.viewerContext) {
|
|
||||||
val updatedRemoteViewerContext = remoteViewerContext.copy(
|
|
||||||
userId = req.viewerContext.userId
|
|
||||||
)
|
|
||||||
|
|
||||||
if (updatedRemoteViewerContext == req.viewerContext) {
|
|
||||||
twitterContext() match {
|
|
||||||
case None =>
|
|
||||||
Some(Viewer(userId = req.viewerContext.userId))
|
|
||||||
case Some(v) =>
|
|
||||||
Some(v.copy(userId = req.viewerContext.userId))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val tweetypieContext = TweetypieContext(
|
|
||||||
isQuotedTweet = req.isInnerQuotedTweet,
|
|
||||||
isRetweet = req.isRetweet,
|
|
||||||
hydrateConversationControl = req.hydrateConversationControl
|
|
||||||
)
|
|
||||||
|
|
||||||
val parityCheck: Stitch[Fetch.Result[TweetVisibilityResult]] = {
|
|
||||||
Stitch.callFuture {
|
|
||||||
TweetypieContext.let(tweetypieContext) {
|
|
||||||
viewer match {
|
|
||||||
case Some(viewer) =>
|
|
||||||
twitterContext.let(viewer) {
|
|
||||||
Stitch.run(fetcher.fetch(req.tweet.id, toThrift(req.safetyLevel)))
|
|
||||||
}
|
|
||||||
case None =>
|
|
||||||
Stitch.run(fetcher.fetch(req.tweet.id, toThrift(req.safetyLevel)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parityCheck
|
|
||||||
.flatMap { parityResponse =>
|
|
||||||
val tvr = TweetVisibilityResultMapper.fromAction(resp.verdict.toActionThrift())
|
|
||||||
|
|
||||||
parityResponse.v match {
|
|
||||||
case Some(ptvr) =>
|
|
||||||
if (tvr == ptvr) {
|
|
||||||
equal.incr()
|
|
||||||
} else {
|
|
||||||
incorrect.incr()
|
|
||||||
}
|
|
||||||
|
|
||||||
case None =>
|
|
||||||
empty.incr()
|
|
||||||
}
|
|
||||||
|
|
||||||
Stitch.Done
|
|
||||||
}.rescue {
|
|
||||||
case t: Throwable =>
|
|
||||||
failures.incr()
|
|
||||||
Stitch.Done
|
|
||||||
|
|
||||||
}.unit
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,14 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.tweets
|
|
||||||
|
|
||||||
import com.twitter.tweetypie.thriftscala.Tweet
|
|
||||||
import com.twitter.visibility.models.SafetyLevel
|
|
||||||
import com.twitter.visibility.models.ViewerContext
|
|
||||||
|
|
||||||
case class TweetVisibilityRequest(
|
|
||||||
tweet: Tweet,
|
|
||||||
safetyLevel: SafetyLevel,
|
|
||||||
viewerContext: ViewerContext,
|
|
||||||
isInnerQuotedTweet: Boolean,
|
|
||||||
isRetweet: Boolean,
|
|
||||||
hydrateConversationControl: Boolean = false,
|
|
||||||
isSourceTweet: Boolean = false)
|
|
Binary file not shown.
@ -1,59 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.tweets
|
|
||||||
|
|
||||||
import com.twitter.finagle.context.Contexts
|
|
||||||
import com.twitter.io.Buf
|
|
||||||
import com.twitter.io.BufByteWriter
|
|
||||||
import com.twitter.io.ByteReader
|
|
||||||
import com.twitter.util.Future
|
|
||||||
import com.twitter.util.Return
|
|
||||||
import com.twitter.util.Throw
|
|
||||||
import com.twitter.util.Try
|
|
||||||
|
|
||||||
case class TweetypieContext(
|
|
||||||
isQuotedTweet: Boolean,
|
|
||||||
isRetweet: Boolean,
|
|
||||||
hydrateConversationControl: Boolean)
|
|
||||||
|
|
||||||
object TweetypieContext {
|
|
||||||
|
|
||||||
def let[U](value: TweetypieContext)(f: => Future[U]): Future[U] =
|
|
||||||
Contexts.broadcast.let(TweetypieContextKey, value)(f)
|
|
||||||
|
|
||||||
def get(): Option[TweetypieContext] =
|
|
||||||
Contexts.broadcast.get(TweetypieContextKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
object TweetypieContextKey
|
|
||||||
extends Contexts.broadcast.Key[TweetypieContext](
|
|
||||||
"com.twitter.visibility.interfaces.tweets.TweetypieContext"
|
|
||||||
) {
|
|
||||||
|
|
||||||
override def marshal(value: TweetypieContext): Buf = {
|
|
||||||
val bw = BufByteWriter.fixed(1)
|
|
||||||
val byte =
|
|
||||||
((if (value.isQuotedTweet) 1 else 0) << 0) |
|
|
||||||
((if (value.isRetweet) 1 else 0) << 1) |
|
|
||||||
((if (value.hydrateConversationControl) 1 else 0) << 2)
|
|
||||||
bw.writeByte(byte)
|
|
||||||
bw.owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
override def tryUnmarshal(buf: Buf): Try[TweetypieContext] = {
|
|
||||||
if (buf.length != 1) {
|
|
||||||
Throw(
|
|
||||||
new IllegalArgumentException(
|
|
||||||
s"Could not extract Boolean from Buf. Length ${buf.length} but required 1"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
val byte: Byte = ByteReader(buf).readByte()
|
|
||||||
Return(
|
|
||||||
TweetypieContext(
|
|
||||||
isQuotedTweet = ((byte & 1) == 1),
|
|
||||||
isRetweet = ((byte & 2) == 2),
|
|
||||||
hydrateConversationControl = ((byte & 4) == 4)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,138 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.tweets
|
|
||||||
|
|
||||||
import com.twitter.decider.Decider
|
|
||||||
import com.twitter.finagle.stats.StatsReceiver
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.visibility.VisibilityLibrary
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.builder.users.UserUnavailableFeatures
|
|
||||||
import com.twitter.visibility.common.actions.converter.scala.DropReasonConverter
|
|
||||||
import com.twitter.visibility.configapi.configs.VisibilityDeciderGates
|
|
||||||
import com.twitter.visibility.features.TweetIsInnerQuotedTweet
|
|
||||||
import com.twitter.visibility.features.TweetIsRetweet
|
|
||||||
import com.twitter.visibility.generators.LocalizedInterstitialGenerator
|
|
||||||
import com.twitter.visibility.generators.TombstoneGenerator
|
|
||||||
import com.twitter.visibility.models.ContentId.UserUnavailableState
|
|
||||||
import com.twitter.visibility.models.UserUnavailableStateEnum
|
|
||||||
import com.twitter.visibility.rules.Drop
|
|
||||||
import com.twitter.visibility.rules.Interstitial
|
|
||||||
import com.twitter.visibility.rules.Reason
|
|
||||||
import com.twitter.visibility.rules.Tombstone
|
|
||||||
import com.twitter.visibility.thriftscala.UserVisibilityResult
|
|
||||||
|
|
||||||
object UserUnavailableStateVisibilityLibrary {
|
|
||||||
type Type = UserUnavailableStateVisibilityRequest => Stitch[VisibilityResult]
|
|
||||||
|
|
||||||
def apply(
|
|
||||||
visibilityLibrary: VisibilityLibrary,
|
|
||||||
decider: Decider,
|
|
||||||
tombstoneGenerator: TombstoneGenerator,
|
|
||||||
interstitialGenerator: LocalizedInterstitialGenerator
|
|
||||||
): Type = {
|
|
||||||
val libraryStatsReceiver = visibilityLibrary.statsReceiver.scope("user_unavailable_vis_library")
|
|
||||||
val defaultDropScope = visibilityLibrary.statsReceiver.scope("default_drop")
|
|
||||||
val vfEngineCounter = libraryStatsReceiver.counter("vf_engine_requests")
|
|
||||||
|
|
||||||
val userUnavailableFeatures = UserUnavailableFeatures(libraryStatsReceiver)
|
|
||||||
val visibilityDeciderGates = VisibilityDeciderGates(decider)
|
|
||||||
|
|
||||||
{ r: UserUnavailableStateVisibilityRequest =>
|
|
||||||
vfEngineCounter.incr()
|
|
||||||
val contentId = UserUnavailableState(r.tweetId)
|
|
||||||
|
|
||||||
val featureMap =
|
|
||||||
visibilityLibrary.featureMapBuilder(
|
|
||||||
Seq(
|
|
||||||
_.withConstantFeature(TweetIsInnerQuotedTweet, r.isInnerQuotedTweet),
|
|
||||||
_.withConstantFeature(TweetIsRetweet, r.isRetweet),
|
|
||||||
userUnavailableFeatures.forState(r.userUnavailableState)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val language = r.viewerContext.requestLanguageCode.getOrElse("en")
|
|
||||||
|
|
||||||
val reason = visibilityLibrary
|
|
||||||
.runRuleEngine(
|
|
||||||
contentId,
|
|
||||||
featureMap,
|
|
||||||
r.viewerContext,
|
|
||||||
r.safetyLevel
|
|
||||||
).map(defaultToDrop(r.userUnavailableState, defaultDropScope))
|
|
||||||
.map(tombstoneGenerator(_, language))
|
|
||||||
.map(visibilityResult => {
|
|
||||||
if (visibilityDeciderGates.enableLocalizedInterstitialInUserStateLibrary()) {
|
|
||||||
interstitialGenerator(visibilityResult, language)
|
|
||||||
} else {
|
|
||||||
visibilityResult
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
reason
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def defaultToDrop(
|
|
||||||
userUnavailableState: UserUnavailableStateEnum,
|
|
||||||
defaultDropScope: StatsReceiver
|
|
||||||
)(
|
|
||||||
result: VisibilityResult
|
|
||||||
): VisibilityResult =
|
|
||||||
result.verdict match {
|
|
||||||
case _: Drop | _: Tombstone => result
|
|
||||||
|
|
||||||
case _: Interstitial => result
|
|
||||||
case _ =>
|
|
||||||
result.copy(verdict =
|
|
||||||
Drop(userUnavailableStateToDropReason(userUnavailableState, defaultDropScope)))
|
|
||||||
}
|
|
||||||
|
|
||||||
private[this] def userUnavailableStateToDropReason(
|
|
||||||
userUnavailableState: UserUnavailableStateEnum,
|
|
||||||
stats: StatsReceiver
|
|
||||||
): Reason =
|
|
||||||
userUnavailableState match {
|
|
||||||
case UserUnavailableStateEnum.Erased =>
|
|
||||||
stats.counter("erased").incr()
|
|
||||||
Reason.ErasedAuthor
|
|
||||||
case UserUnavailableStateEnum.Protected =>
|
|
||||||
stats.counter("protected").incr()
|
|
||||||
Reason.ProtectedAuthor
|
|
||||||
case UserUnavailableStateEnum.Offboarded =>
|
|
||||||
stats.counter("offboarded").incr()
|
|
||||||
Reason.OffboardedAuthor
|
|
||||||
case UserUnavailableStateEnum.AuthorBlocksViewer =>
|
|
||||||
stats.counter("author_blocks_viewer").incr()
|
|
||||||
Reason.AuthorBlocksViewer
|
|
||||||
case UserUnavailableStateEnum.Suspended =>
|
|
||||||
stats.counter("suspended_author").incr()
|
|
||||||
Reason.SuspendedAuthor
|
|
||||||
case UserUnavailableStateEnum.Deactivated =>
|
|
||||||
stats.counter("deactivated_author").incr()
|
|
||||||
Reason.DeactivatedAuthor
|
|
||||||
case UserUnavailableStateEnum.Filtered(result) =>
|
|
||||||
stats.counter("filtered").incr()
|
|
||||||
userVisibilityResultToDropReason(result, stats.scope("filtered"))
|
|
||||||
case UserUnavailableStateEnum.Unavailable =>
|
|
||||||
stats.counter("unspecified").incr()
|
|
||||||
Reason.Unspecified
|
|
||||||
case _ =>
|
|
||||||
stats.counter("unknown").incr()
|
|
||||||
stats.scope("unknown").counter(userUnavailableState.name).incr()
|
|
||||||
Reason.Unspecified
|
|
||||||
}
|
|
||||||
|
|
||||||
private[this] def userVisibilityResultToDropReason(
|
|
||||||
result: UserVisibilityResult,
|
|
||||||
stats: StatsReceiver
|
|
||||||
): Reason =
|
|
||||||
result.action
|
|
||||||
.flatMap(DropReasonConverter.fromAction)
|
|
||||||
.map { dropReason =>
|
|
||||||
val reason = Reason.fromDropReason(dropReason)
|
|
||||||
stats.counter(reason.name).incr()
|
|
||||||
reason
|
|
||||||
}.getOrElse {
|
|
||||||
stats.counter("empty")
|
|
||||||
Reason.Unspecified
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,14 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.tweets
|
|
||||||
|
|
||||||
import com.twitter.visibility.models.UserUnavailableStateEnum
|
|
||||||
import com.twitter.visibility.models.SafetyLevel
|
|
||||||
import com.twitter.visibility.models.ViewerContext
|
|
||||||
|
|
||||||
case class UserUnavailableStateVisibilityRequest(
|
|
||||||
safetyLevel: SafetyLevel,
|
|
||||||
tweetId: Long,
|
|
||||||
viewerContext: ViewerContext,
|
|
||||||
userUnavailableState: UserUnavailableStateEnum,
|
|
||||||
isRetweet: Boolean,
|
|
||||||
isInnerQuotedTweet: Boolean,
|
|
||||||
)
|
|
@ -1,25 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
platform = "java8",
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"featureswitches/featureswitches-core/src/main/scala",
|
|
||||||
"src/thrift/com/twitter/spam/rtf:safety-result-scala",
|
|
||||||
"stitch/stitch-core",
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common",
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common/actions",
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common/actions/converter/scala",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/tweets",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/features",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/rules",
|
|
||||||
"visibility/results/src/main/scala/com/twitter/visibility/results/richtext",
|
|
||||||
],
|
|
||||||
exports = [
|
|
||||||
"featureswitches/featureswitches-core/src/main/scala",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,55 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.tweets.enrichments
|
|
||||||
|
|
||||||
import com.twitter.finagle.stats.StatsReceiver
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.results.richtext.PublicInterestReasonToPlainText
|
|
||||||
import com.twitter.visibility.rules.Action
|
|
||||||
import com.twitter.visibility.rules.ComplianceTweetNoticePreEnrichment
|
|
||||||
import com.twitter.visibility.rules.PublicInterest
|
|
||||||
import com.twitter.visibility.rules.Reason
|
|
||||||
|
|
||||||
object ComplianceTweetNoticeEnrichment {
|
|
||||||
val ComplianceTweetNoticeEnrichmentScope = "compliance_tweet_notice_enrichment"
|
|
||||||
val ComplianceTweetNoticePreEnrichmentActionScope =
|
|
||||||
"compliance_tweet_notice_pre_enrichment_action"
|
|
||||||
|
|
||||||
val englishLanguageTag = "en"
|
|
||||||
|
|
||||||
def apply(result: VisibilityResult, statsReceiver: StatsReceiver): VisibilityResult = {
|
|
||||||
val scopedStatsReceiver = statsReceiver.scope(ComplianceTweetNoticeEnrichmentScope)
|
|
||||||
|
|
||||||
val enrichedVerdict = enrichVerdict(result.verdict, scopedStatsReceiver)
|
|
||||||
result.copy(verdict = enrichedVerdict)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def enrichVerdict(
|
|
||||||
verdict: Action,
|
|
||||||
statsReceiver: StatsReceiver
|
|
||||||
): Action = {
|
|
||||||
val preEnrichmentActionScope =
|
|
||||||
statsReceiver.scope(ComplianceTweetNoticePreEnrichmentActionScope)
|
|
||||||
|
|
||||||
verdict match {
|
|
||||||
case complianceTweetNoticePreEnrichmentVerdict: ComplianceTweetNoticePreEnrichment =>
|
|
||||||
preEnrichmentActionScope.counter("").incr()
|
|
||||||
|
|
||||||
val verdictWithDetailsAndUrl = complianceTweetNoticePreEnrichmentVerdict.reason match {
|
|
||||||
case Reason.Unspecified =>
|
|
||||||
preEnrichmentActionScope.counter("reason_unspecified").incr()
|
|
||||||
complianceTweetNoticePreEnrichmentVerdict
|
|
||||||
|
|
||||||
case reason =>
|
|
||||||
preEnrichmentActionScope.counter("reason_specified").incr()
|
|
||||||
val safetyResultReason = PublicInterest.ReasonToSafetyResultReason(reason)
|
|
||||||
val (details, url) =
|
|
||||||
PublicInterestReasonToPlainText(safetyResultReason, englishLanguageTag)
|
|
||||||
complianceTweetNoticePreEnrichmentVerdict.copy(
|
|
||||||
details = Some(details),
|
|
||||||
extendedDetailsUrl = Some(url))
|
|
||||||
}
|
|
||||||
verdictWithDetailsAndUrl.toComplianceTweetNotice()
|
|
||||||
|
|
||||||
case _ => verdict
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,173 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.tweets.enrichments
|
|
||||||
|
|
||||||
import com.twitter.featureswitches.FSRecipient
|
|
||||||
import com.twitter.featureswitches.v2.FeatureSwitches
|
|
||||||
import com.twitter.finagle.stats.StatsReceiver
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.common.LocalizedLimitedActionsSource
|
|
||||||
import com.twitter.visibility.common.actions.converter.scala.LimitedActionTypeConverter
|
|
||||||
import com.twitter.visibility.common.actions.LimitedActionsPolicy
|
|
||||||
import com.twitter.visibility.common.actions.LimitedActionType
|
|
||||||
import com.twitter.visibility.common.actions.LimitedEngagementReason
|
|
||||||
import com.twitter.visibility.rules.Action
|
|
||||||
import com.twitter.visibility.rules.EmergencyDynamicInterstitial
|
|
||||||
import com.twitter.visibility.rules.InterstitialLimitedEngagements
|
|
||||||
import com.twitter.visibility.rules.LimitedEngagements
|
|
||||||
|
|
||||||
case class PolicyFeatureSwitchResults(
|
|
||||||
limitedActionTypes: Option[Seq[LimitedActionType]],
|
|
||||||
copyNamespace: String,
|
|
||||||
promptType: String,
|
|
||||||
learnMoreUrl: Option[String])
|
|
||||||
|
|
||||||
object LimitedActionsPolicyEnrichment {
|
|
||||||
object FeatureSwitchKeys {
|
|
||||||
val LimitedActionTypes = "limited_actions_policy_limited_actions"
|
|
||||||
val CopyNamespace = "limited_actions_policy_copy_namespace"
|
|
||||||
val PromptType = "limited_actions_policy_prompt_type"
|
|
||||||
val LearnMoreUrl = "limited_actions_policy_prompt_learn_more_url"
|
|
||||||
}
|
|
||||||
|
|
||||||
val DefaultCopyNameSpace = "Default"
|
|
||||||
val DefaultPromptType = "basic"
|
|
||||||
val LimitedActionsPolicyEnrichmentScope = "limited_actions_policy_enrichment"
|
|
||||||
val MissingLimitedActionTypesScope = "missing_limited_action_types"
|
|
||||||
val ExecutedScope = "executed"
|
|
||||||
|
|
||||||
def apply(
|
|
||||||
result: VisibilityResult,
|
|
||||||
localizedLimitedActionSource: LocalizedLimitedActionsSource,
|
|
||||||
languageCode: String,
|
|
||||||
countryCode: Option[String],
|
|
||||||
featureSwitches: FeatureSwitches,
|
|
||||||
statsReceiver: StatsReceiver
|
|
||||||
): VisibilityResult = {
|
|
||||||
val scopedStatsReceiver = statsReceiver.scope(LimitedActionsPolicyEnrichmentScope)
|
|
||||||
|
|
||||||
val enrichVerdict_ = enrichVerdict(
|
|
||||||
_: Action,
|
|
||||||
localizedLimitedActionSource,
|
|
||||||
languageCode,
|
|
||||||
countryCode,
|
|
||||||
featureSwitches,
|
|
||||||
scopedStatsReceiver
|
|
||||||
)
|
|
||||||
|
|
||||||
result.copy(
|
|
||||||
verdict = enrichVerdict_(result.verdict),
|
|
||||||
secondaryVerdicts = result.secondaryVerdicts.map(enrichVerdict_)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def enrichVerdict(
|
|
||||||
verdict: Action,
|
|
||||||
localizedLimitedActionsSource: LocalizedLimitedActionsSource,
|
|
||||||
languageCode: String,
|
|
||||||
countryCode: Option[String],
|
|
||||||
featureSwitches: FeatureSwitches,
|
|
||||||
statsReceiver: StatsReceiver
|
|
||||||
): Action = {
|
|
||||||
val limitedActionsPolicyForReason_ = limitedActionsPolicyForReason(
|
|
||||||
_: LimitedEngagementReason,
|
|
||||||
localizedLimitedActionsSource,
|
|
||||||
languageCode,
|
|
||||||
countryCode,
|
|
||||||
featureSwitches,
|
|
||||||
statsReceiver
|
|
||||||
)
|
|
||||||
val executedCounter = statsReceiver.scope(ExecutedScope)
|
|
||||||
|
|
||||||
verdict match {
|
|
||||||
case le: LimitedEngagements => {
|
|
||||||
executedCounter.counter("").incr()
|
|
||||||
executedCounter.counter(le.name).incr()
|
|
||||||
le.copy(
|
|
||||||
policy = limitedActionsPolicyForReason_(le.getLimitedEngagementReason)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case ile: InterstitialLimitedEngagements => {
|
|
||||||
executedCounter.counter("").incr()
|
|
||||||
executedCounter.counter(ile.name).incr()
|
|
||||||
ile.copy(
|
|
||||||
policy = limitedActionsPolicyForReason_(
|
|
||||||
ile.getLimitedEngagementReason
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case edi: EmergencyDynamicInterstitial => {
|
|
||||||
executedCounter.counter("").incr()
|
|
||||||
executedCounter.counter(edi.name).incr()
|
|
||||||
EmergencyDynamicInterstitial(
|
|
||||||
copy = edi.copy,
|
|
||||||
linkOpt = edi.linkOpt,
|
|
||||||
localizedMessage = edi.localizedMessage,
|
|
||||||
policy = limitedActionsPolicyForReason_(edi.getLimitedEngagementReason)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case _ => verdict
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def limitedActionsPolicyForReason(
|
|
||||||
reason: LimitedEngagementReason,
|
|
||||||
localizedLimitedActionsSource: LocalizedLimitedActionsSource,
|
|
||||||
languageCode: String,
|
|
||||||
countryCode: Option[String],
|
|
||||||
featureSwitches: FeatureSwitches,
|
|
||||||
statsReceiver: StatsReceiver
|
|
||||||
): Option[LimitedActionsPolicy] = {
|
|
||||||
val policyConfig = getPolicyFeatureSwitchResults(featureSwitches, reason)
|
|
||||||
|
|
||||||
policyConfig.limitedActionTypes match {
|
|
||||||
case Some(limitedActionTypes) if limitedActionTypes.nonEmpty =>
|
|
||||||
Some(
|
|
||||||
LimitedActionsPolicy(
|
|
||||||
limitedActionTypes.map(
|
|
||||||
localizedLimitedActionsSource.fetch(
|
|
||||||
_,
|
|
||||||
languageCode,
|
|
||||||
countryCode,
|
|
||||||
policyConfig.promptType,
|
|
||||||
policyConfig.copyNamespace,
|
|
||||||
policyConfig.learnMoreUrl
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
case _ => {
|
|
||||||
statsReceiver
|
|
||||||
.scope(MissingLimitedActionTypesScope).counter(reason.toLimitedActionsString).incr()
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def getPolicyFeatureSwitchResults(
|
|
||||||
featureSwitches: FeatureSwitches,
|
|
||||||
reason: LimitedEngagementReason
|
|
||||||
): PolicyFeatureSwitchResults = {
|
|
||||||
val recipient = FSRecipient().withCustomFields(
|
|
||||||
("LimitedEngagementReason", reason.toLimitedActionsString)
|
|
||||||
)
|
|
||||||
val featureSwitchesResults = featureSwitches
|
|
||||||
.matchRecipient(recipient)
|
|
||||||
|
|
||||||
val limitedActionTypes = featureSwitchesResults
|
|
||||||
.getStringArray(FeatureSwitchKeys.LimitedActionTypes)
|
|
||||||
.map(_.map(LimitedActionTypeConverter.fromString).flatten)
|
|
||||||
|
|
||||||
val copyNamespace = featureSwitchesResults
|
|
||||||
.getString(FeatureSwitchKeys.CopyNamespace)
|
|
||||||
.getOrElse(DefaultCopyNameSpace)
|
|
||||||
|
|
||||||
val promptType = featureSwitchesResults
|
|
||||||
.getString(FeatureSwitchKeys.PromptType)
|
|
||||||
.getOrElse(DefaultPromptType)
|
|
||||||
|
|
||||||
val learnMoreUrl = featureSwitchesResults
|
|
||||||
.getString(FeatureSwitchKeys.LearnMoreUrl)
|
|
||||||
.filter(_.nonEmpty)
|
|
||||||
|
|
||||||
PolicyFeatureSwitchResults(limitedActionTypes, copyNamespace, promptType, learnMoreUrl)
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,96 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.tweets.enrichments
|
|
||||||
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.builder.tweets.TweetVisibilityNudgeSourceWrapper
|
|
||||||
import com.twitter.visibility.common.actions.TweetVisibilityNudgeReason.SemanticCoreMisinformationLabelReason
|
|
||||||
import com.twitter.visibility.rules.Action
|
|
||||||
import com.twitter.visibility.rules.LocalizedNudge
|
|
||||||
import com.twitter.visibility.rules.SoftIntervention
|
|
||||||
import com.twitter.visibility.rules.TweetVisibilityNudge
|
|
||||||
|
|
||||||
object TweetVisibilityNudgeEnrichment {
|
|
||||||
|
|
||||||
def apply(
|
|
||||||
result: VisibilityResult,
|
|
||||||
tweetVisibilityNudgeSourceWrapper: TweetVisibilityNudgeSourceWrapper,
|
|
||||||
languageCode: String,
|
|
||||||
countryCode: Option[String]
|
|
||||||
): VisibilityResult = {
|
|
||||||
|
|
||||||
val softIntervention = extractSoftIntervention(result.verdict, result.secondaryVerdicts)
|
|
||||||
|
|
||||||
val enrichedPrimaryVerdict = enrichAction(
|
|
||||||
result.verdict,
|
|
||||||
tweetVisibilityNudgeSourceWrapper,
|
|
||||||
softIntervention,
|
|
||||||
languageCode,
|
|
||||||
countryCode)
|
|
||||||
|
|
||||||
val enrichedSecondaryVerdicts: Seq[Action] =
|
|
||||||
result.secondaryVerdicts.map(sv =>
|
|
||||||
enrichAction(
|
|
||||||
sv,
|
|
||||||
tweetVisibilityNudgeSourceWrapper,
|
|
||||||
softIntervention,
|
|
||||||
languageCode,
|
|
||||||
countryCode))
|
|
||||||
|
|
||||||
result.copy(verdict = enrichedPrimaryVerdict, secondaryVerdicts = enrichedSecondaryVerdicts)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def extractSoftIntervention(
|
|
||||||
primary: Action,
|
|
||||||
secondaries: Seq[Action]
|
|
||||||
): Option[SoftIntervention] = {
|
|
||||||
primary match {
|
|
||||||
case si: SoftIntervention => Some(si)
|
|
||||||
case _ =>
|
|
||||||
secondaries.collectFirst {
|
|
||||||
case sv: SoftIntervention => sv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def enrichAction(
|
|
||||||
action: Action,
|
|
||||||
tweetVisibilityNudgeSourceWrapper: TweetVisibilityNudgeSourceWrapper,
|
|
||||||
softIntervention: Option[SoftIntervention],
|
|
||||||
languageCode: String,
|
|
||||||
countryCode: Option[String]
|
|
||||||
): Action = {
|
|
||||||
action match {
|
|
||||||
case TweetVisibilityNudge(reason, None) =>
|
|
||||||
val localizedNudge =
|
|
||||||
tweetVisibilityNudgeSourceWrapper.getLocalizedNudge(reason, languageCode, countryCode)
|
|
||||||
if (reason == SemanticCoreMisinformationLabelReason)
|
|
||||||
TweetVisibilityNudge(
|
|
||||||
reason,
|
|
||||||
enrichLocalizedMisInfoNudge(localizedNudge, softIntervention))
|
|
||||||
else
|
|
||||||
TweetVisibilityNudge(reason, localizedNudge)
|
|
||||||
case _ => action
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def enrichLocalizedMisInfoNudge(
|
|
||||||
localizedNudge: Option[LocalizedNudge],
|
|
||||||
softIntervention: Option[SoftIntervention]
|
|
||||||
): Option[LocalizedNudge] = {
|
|
||||||
softIntervention match {
|
|
||||||
case Some(si) => {
|
|
||||||
val enrichedLocalizedNudge = localizedNudge.map { ln =>
|
|
||||||
val enrichedLocalizedNudgeActions = ln.localizedNudgeActions.map { na =>
|
|
||||||
val enrichedPayload = na.nudgeActionPayload.map { payload =>
|
|
||||||
payload.copy(ctaUrl = si.detailsUrl, heading = si.warning)
|
|
||||||
}
|
|
||||||
na.copy(nudgeActionPayload = enrichedPayload)
|
|
||||||
}
|
|
||||||
ln.copy(localizedNudgeActions = enrichedLocalizedNudgeActions)
|
|
||||||
}
|
|
||||||
enrichedLocalizedNudge
|
|
||||||
}
|
|
||||||
case None => localizedNudge
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
platform = "java8",
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"decider/src/main/scala",
|
|
||||||
"servo/decider/src/main/scala",
|
|
||||||
"src/thrift/com/twitter/gizmoduck:user-thrift-scala",
|
|
||||||
"stitch/stitch-core",
|
|
||||||
"strato/src/main/scala/com/twitter/strato/client",
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common",
|
|
||||||
"visibility/lib/src/main/resources/config",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/builder/users",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/configapi/configs",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/features",
|
|
||||||
"visibility/lib/src/main/thrift/com/twitter/visibility/context:vf-context-scala",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,111 +0,0 @@
|
|||||||
package com.twitter.visibility.interfaces.users
|
|
||||||
|
|
||||||
import com.twitter.decider.Decider
|
|
||||||
import com.twitter.gizmoduck.thriftscala.User
|
|
||||||
import com.twitter.servo.decider.DeciderGateBuilder
|
|
||||||
import com.twitter.stitch.Stitch
|
|
||||||
import com.twitter.strato.client.Client
|
|
||||||
import com.twitter.visibility.VisibilityLibrary
|
|
||||||
import com.twitter.visibility.builder.users.AuthorFeatures
|
|
||||||
import com.twitter.visibility.builder.users.RelationshipFeatures
|
|
||||||
import com.twitter.visibility.builder.users.ViewerAdvancedFilteringFeatures
|
|
||||||
import com.twitter.visibility.builder.users.ViewerFeatures
|
|
||||||
import com.twitter.visibility.builder.users.ViewerSearchSafetyFeatures
|
|
||||||
import com.twitter.visibility.builder.VisibilityResult
|
|
||||||
import com.twitter.visibility.builder.users.SearchFeatures
|
|
||||||
import com.twitter.visibility.common.UserRelationshipSource
|
|
||||||
import com.twitter.visibility.common.UserSearchSafetySource
|
|
||||||
import com.twitter.visibility.common.UserSource
|
|
||||||
import com.twitter.visibility.configapi.configs.VisibilityDeciderGates
|
|
||||||
import com.twitter.visibility.context.thriftscala.UserVisibilityFilteringContext
|
|
||||||
import com.twitter.visibility.models.ContentId.UserId
|
|
||||||
import com.twitter.visibility.models.SafetyLevel
|
|
||||||
import com.twitter.visibility.models.ViewerContext
|
|
||||||
import com.twitter.visibility.rules.Reason.Unspecified
|
|
||||||
import com.twitter.visibility.rules.Allow
|
|
||||||
import com.twitter.visibility.rules.Drop
|
|
||||||
import com.twitter.visibility.rules.RuleBase
|
|
||||||
|
|
||||||
object UserVisibilityLibrary {
|
|
||||||
type Type =
|
|
||||||
(User, SafetyLevel, ViewerContext, UserVisibilityFilteringContext) => Stitch[VisibilityResult]
|
|
||||||
|
|
||||||
def apply(
|
|
||||||
visibilityLibrary: VisibilityLibrary,
|
|
||||||
userSource: UserSource = UserSource.empty,
|
|
||||||
userRelationshipSource: UserRelationshipSource = UserRelationshipSource.empty,
|
|
||||||
stratoClient: Client,
|
|
||||||
decider: Decider
|
|
||||||
): Type = {
|
|
||||||
val libraryStatsReceiver = visibilityLibrary.statsReceiver.scope("user_library")
|
|
||||||
val stratoClientStatsReceiver = visibilityLibrary.statsReceiver.scope("strato")
|
|
||||||
|
|
||||||
val visibilityDeciderGates = VisibilityDeciderGates(decider)
|
|
||||||
|
|
||||||
val vfEngineCounter = libraryStatsReceiver.counter("vf_engine_requests")
|
|
||||||
val noUserRulesCounter = libraryStatsReceiver.counter("no_user_rules_requests")
|
|
||||||
val viewerIsAuthorCounter = libraryStatsReceiver.counter("viewer_is_author_requests")
|
|
||||||
|
|
||||||
val authorFeatures = new AuthorFeatures(userSource, libraryStatsReceiver)
|
|
||||||
val viewerFeatures = new ViewerFeatures(userSource, libraryStatsReceiver)
|
|
||||||
val relationshipFeatures =
|
|
||||||
new RelationshipFeatures(userRelationshipSource, libraryStatsReceiver)
|
|
||||||
val searchFeatures = new SearchFeatures(libraryStatsReceiver)
|
|
||||||
|
|
||||||
val viewerSafeSearchFeatures = new ViewerSearchSafetyFeatures(
|
|
||||||
UserSearchSafetySource.fromStrato(stratoClient, stratoClientStatsReceiver),
|
|
||||||
libraryStatsReceiver)
|
|
||||||
|
|
||||||
val deciderGateBuilder = new DeciderGateBuilder(decider)
|
|
||||||
val advancedFilteringFeatures =
|
|
||||||
new ViewerAdvancedFilteringFeatures(userSource, libraryStatsReceiver)
|
|
||||||
|
|
||||||
(user, safetyLevel, viewerContext, userVisibilityFilteringContext) => {
|
|
||||||
val contentId = UserId(user.id)
|
|
||||||
val viewerId = viewerContext.userId
|
|
||||||
|
|
||||||
if (!RuleBase.hasUserRules(safetyLevel)) {
|
|
||||||
noUserRulesCounter.incr()
|
|
||||||
Stitch.value(VisibilityResult(contentId = contentId, verdict = Allow))
|
|
||||||
} else {
|
|
||||||
if (viewerId.contains(user.id)) {
|
|
||||||
viewerIsAuthorCounter.incr()
|
|
||||||
|
|
||||||
Stitch.value(VisibilityResult(contentId = contentId, verdict = Allow))
|
|
||||||
} else {
|
|
||||||
vfEngineCounter.incr()
|
|
||||||
|
|
||||||
val featureMap =
|
|
||||||
visibilityLibrary.featureMapBuilder(
|
|
||||||
Seq(
|
|
||||||
viewerFeatures.forViewerContext(viewerContext),
|
|
||||||
viewerSafeSearchFeatures.forViewerId(viewerId),
|
|
||||||
relationshipFeatures.forAuthor(user, viewerId),
|
|
||||||
authorFeatures.forAuthor(user),
|
|
||||||
advancedFilteringFeatures.forViewerId(viewerId),
|
|
||||||
searchFeatures.forSearchContext(userVisibilityFilteringContext.searchContext)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
visibilityLibrary.runRuleEngine(
|
|
||||||
contentId,
|
|
||||||
featureMap,
|
|
||||||
viewerContext,
|
|
||||||
safetyLevel
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def Const(shouldDrop: Boolean): Type =
|
|
||||||
(user, _, _, _) =>
|
|
||||||
Stitch.value(
|
|
||||||
VisibilityResult(
|
|
||||||
contentId = UserId(user.id),
|
|
||||||
verdict = if (shouldDrop) Drop(Unspecified) else Allow,
|
|
||||||
finished = true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
scala_library(
|
|
||||||
sources = ["*.scala"],
|
|
||||||
compiler_option_sets = ["fatal_warnings"],
|
|
||||||
platform = "java8",
|
|
||||||
strict_deps = True,
|
|
||||||
tags = ["bazel-compatible"],
|
|
||||||
dependencies = [
|
|
||||||
"configapi/configapi-core",
|
|
||||||
"datatools/src/thrift/com/twitter/datatools/entityservice:entity-entities-thrift-scala",
|
|
||||||
"escherbird/src/thrift/com/twitter/escherbird/softintervention:softintervention_thrift-scala",
|
|
||||||
"featureswitches/featureswitches-core",
|
|
||||||
"finatra-internal/request/src/main/scala",
|
|
||||||
"src/thrift/com/twitter/content-health/sensitivemediasettings:sensitivemediasettings-scala",
|
|
||||||
"src/thrift/com/twitter/context:twitter-context-scala",
|
|
||||||
"src/thrift/com/twitter/escherbird:tweet-annotation-scala",
|
|
||||||
"src/thrift/com/twitter/escherbird/common:common-scala",
|
|
||||||
"src/thrift/com/twitter/gizmoduck:user-thrift-scala",
|
|
||||||
"src/thrift/com/twitter/spam/rtf:safety-level-scala",
|
|
||||||
"src/thrift/com/twitter/spam/rtf:safety-result-scala",
|
|
||||||
"src/thrift/com/twitter/spam/rtf:tweet-rtf-event-scala",
|
|
||||||
"src/thrift/com/twitter/tweetypie:tweet-scala",
|
|
||||||
"timelines/src/main/scala/com/twitter/timelines/model/candidate",
|
|
||||||
"timelines/src/main/scala/com/twitter/timelines/util/client_info",
|
|
||||||
"twitter-context/src/main/scala",
|
|
||||||
"visibility/common/src/main/scala/com/twitter/visibility/common/actions",
|
|
||||||
"visibility/common/src/main/thrift/com/twitter/visibility:action-scala",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/configapi/params",
|
|
||||||
"visibility/lib/src/main/scala/com/twitter/visibility/util",
|
|
||||||
"visibility/lib/src/main/thrift/com/twitter/visibility/safety_label_store:safety-label-store-scala",
|
|
||||||
"visibility/lib/src/main/thrift/com/twitter/visibility/strato:vf-strato-scala",
|
|
||||||
],
|
|
||||||
)
|
|
Binary file not shown.
Binary file not shown.
@ -1,23 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
import com.twitter.tweetypie.thriftscala.Communities
|
|
||||||
import com.twitter.tweetypie.thriftscala.Tweet
|
|
||||||
|
|
||||||
object CommunityTweet {
|
|
||||||
def getCommunityId(communities: Communities): Option[CommunityId] =
|
|
||||||
communities.communityIds.headOption
|
|
||||||
|
|
||||||
def getCommunityId(tweet: Tweet): Option[CommunityId] =
|
|
||||||
tweet.communities.flatMap(getCommunityId)
|
|
||||||
|
|
||||||
def apply(tweet: Tweet): Option[CommunityTweet] =
|
|
||||||
getCommunityId(tweet).map { communityId =>
|
|
||||||
val authorId = tweet.coreData.get.userId
|
|
||||||
CommunityTweet(tweet, communityId, authorId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class CommunityTweet(
|
|
||||||
tweet: Tweet,
|
|
||||||
communityId: CommunityId,
|
|
||||||
authorId: Long)
|
|
Binary file not shown.
@ -1,22 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
sealed trait ContentId
|
|
||||||
|
|
||||||
object ContentId {
|
|
||||||
case class TweetId(id: Long) extends ContentId
|
|
||||||
case class UserId(id: Long) extends ContentId
|
|
||||||
case class CardId(url: String) extends ContentId
|
|
||||||
case class QuotedTweetRelationship(outer: Long, inner: Long) extends ContentId
|
|
||||||
case class NotificationId(tweetId: Option[Long]) extends ContentId
|
|
||||||
case class DmId(id: Long) extends ContentId
|
|
||||||
case class BlenderTweetId(id: Long) extends ContentId
|
|
||||||
case class SpaceId(id: String) extends ContentId
|
|
||||||
case class SpacePlusUserId(id: String) extends ContentId
|
|
||||||
case class DmConversationId(id: String) extends ContentId
|
|
||||||
case class DmEventId(id: Long) extends ContentId
|
|
||||||
case class UserUnavailableState(tweetId: Long) extends ContentId
|
|
||||||
case class TwitterArticleId(id: Long) extends ContentId
|
|
||||||
case class DeleteTweetId(tweetId: Long) extends ContentId
|
|
||||||
case class MediaId(id: String) extends ContentId
|
|
||||||
case class CommunityId(communityId: Long) extends ContentId
|
|
||||||
}
|
|
Binary file not shown.
@ -1,61 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
import com.twitter.spam.rtf.thriftscala.SafetyResultReason
|
|
||||||
import java.util.regex.Pattern
|
|
||||||
|
|
||||||
sealed trait LabelSource {
|
|
||||||
val name: String
|
|
||||||
}
|
|
||||||
|
|
||||||
object LabelSource {
|
|
||||||
val BotRulePrefix = "bot_id_"
|
|
||||||
val AbusePrefix = "Abuse"
|
|
||||||
val HSEPrefix = "hse"
|
|
||||||
val AgentSourceNames = Set(
|
|
||||||
SafetyResultReason.OneOff.name,
|
|
||||||
SafetyResultReason.VotingMisinformation.name,
|
|
||||||
SafetyResultReason.HackedMaterials.name,
|
|
||||||
SafetyResultReason.Scams.name,
|
|
||||||
SafetyResultReason.PlatformManipulation.name
|
|
||||||
)
|
|
||||||
|
|
||||||
val Regex = "\\|"
|
|
||||||
val pattern: Pattern = Pattern.compile(Regex)
|
|
||||||
|
|
||||||
def fromString(name: String): Option[LabelSource] = Some(name) collect {
|
|
||||||
case _ if name.startsWith(BotRulePrefix) =>
|
|
||||||
BotMakerRule(name.substring(BotRulePrefix.length).toLong)
|
|
||||||
case _ if name == "A" || name == "B" || name == "AB" =>
|
|
||||||
SmyteSource(name)
|
|
||||||
case _ if name.startsWith(AbusePrefix) =>
|
|
||||||
AbuseSource(name)
|
|
||||||
case _ if name.startsWith(HSEPrefix) =>
|
|
||||||
HSESource(name)
|
|
||||||
case _ if AgentSourceNames.contains(name) =>
|
|
||||||
AgentSource(name)
|
|
||||||
case _ =>
|
|
||||||
StringSource(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
def parseStringSource(source: String): (String, Option[String]) = {
|
|
||||||
pattern.split(source, 2) match {
|
|
||||||
case Array(copy, "") => (copy, None)
|
|
||||||
case Array(copy, link) => (copy, Some(link))
|
|
||||||
case Array(copy) => (copy, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class BotMakerRule(ruleId: Long) extends LabelSource {
|
|
||||||
override lazy val name: String = s"${BotRulePrefix}${ruleId}"
|
|
||||||
}
|
|
||||||
|
|
||||||
case class SmyteSource(name: String) extends LabelSource
|
|
||||||
|
|
||||||
case class AbuseSource(name: String) extends LabelSource
|
|
||||||
|
|
||||||
case class AgentSource(name: String) extends LabelSource
|
|
||||||
|
|
||||||
case class HSESource(name: String) extends LabelSource
|
|
||||||
|
|
||||||
case class StringSource(name: String) extends LabelSource
|
|
||||||
}
|
|
Binary file not shown.
@ -1,89 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
import com.twitter.visibility.safety_label_store.{thriftscala => s}
|
|
||||||
import com.twitter.visibility.util.NamingUtils
|
|
||||||
|
|
||||||
sealed trait MediaSafetyLabelType extends SafetyLabelType {
|
|
||||||
lazy val name: String = NamingUtils.getFriendlyName(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
object MediaSafetyLabelType extends SafetyLabelType {
|
|
||||||
|
|
||||||
val List: List[MediaSafetyLabelType] = s.MediaSafetyLabelType.list.map(fromThrift)
|
|
||||||
|
|
||||||
val ActiveLabels: List[MediaSafetyLabelType] = List.filter { labelType =>
|
|
||||||
labelType != Unknown && labelType != Deprecated
|
|
||||||
}
|
|
||||||
|
|
||||||
private lazy val nameToValueMap: Map[String, MediaSafetyLabelType] =
|
|
||||||
List.map(l => l.name.toLowerCase -> l).toMap
|
|
||||||
def fromName(name: String): Option[MediaSafetyLabelType] = nameToValueMap.get(name.toLowerCase)
|
|
||||||
|
|
||||||
private val UnknownThriftSafetyLabelType =
|
|
||||||
s.MediaSafetyLabelType.EnumUnknownMediaSafetyLabelType(UnknownEnumValue)
|
|
||||||
|
|
||||||
private lazy val thriftToModelMap: Map[s.MediaSafetyLabelType, MediaSafetyLabelType] = Map(
|
|
||||||
s.MediaSafetyLabelType.NsfwHighPrecision -> NsfwHighPrecision,
|
|
||||||
s.MediaSafetyLabelType.NsfwHighRecall -> NsfwHighRecall,
|
|
||||||
s.MediaSafetyLabelType.NsfwNearPerfect -> NsfwNearPerfect,
|
|
||||||
s.MediaSafetyLabelType.NsfwCardImage -> NsfwCardImage,
|
|
||||||
s.MediaSafetyLabelType.Pdna -> Pdna,
|
|
||||||
s.MediaSafetyLabelType.PdnaNoTreatmentIfVerified -> PdnaNoTreatmentIfVerified,
|
|
||||||
s.MediaSafetyLabelType.DmcaWithheld -> DmcaWithheld,
|
|
||||||
s.MediaSafetyLabelType.LegalDemandsWithheld -> LegalDemandsWithheld,
|
|
||||||
s.MediaSafetyLabelType.LocalLawsWithheld -> LocalLawsWithheld,
|
|
||||||
s.MediaSafetyLabelType.Reserved10 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved11 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved12 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved13 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved14 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved15 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved16 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved17 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved18 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved19 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved20 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved21 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved22 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved23 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved24 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved25 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved26 -> Deprecated,
|
|
||||||
s.MediaSafetyLabelType.Reserved27 -> Deprecated,
|
|
||||||
)
|
|
||||||
|
|
||||||
private lazy val modelToThriftMap: Map[MediaSafetyLabelType, s.MediaSafetyLabelType] =
|
|
||||||
(for ((k, v) <- thriftToModelMap) yield (v, k)) ++ Map(
|
|
||||||
Deprecated -> s.MediaSafetyLabelType.EnumUnknownMediaSafetyLabelType(DeprecatedEnumValue),
|
|
||||||
)
|
|
||||||
|
|
||||||
case object NsfwHighPrecision extends MediaSafetyLabelType
|
|
||||||
case object NsfwHighRecall extends MediaSafetyLabelType
|
|
||||||
case object NsfwNearPerfect extends MediaSafetyLabelType
|
|
||||||
case object NsfwCardImage extends MediaSafetyLabelType
|
|
||||||
case object Pdna extends MediaSafetyLabelType
|
|
||||||
case object PdnaNoTreatmentIfVerified extends MediaSafetyLabelType
|
|
||||||
case object DmcaWithheld extends MediaSafetyLabelType
|
|
||||||
case object LegalDemandsWithheld extends MediaSafetyLabelType
|
|
||||||
case object LocalLawsWithheld extends MediaSafetyLabelType
|
|
||||||
|
|
||||||
case object Deprecated extends MediaSafetyLabelType
|
|
||||||
case object Unknown extends MediaSafetyLabelType
|
|
||||||
|
|
||||||
def fromThrift(safetyLabelType: s.MediaSafetyLabelType): MediaSafetyLabelType =
|
|
||||||
thriftToModelMap.get(safetyLabelType) match {
|
|
||||||
case Some(mediaSafetyLabelType) => mediaSafetyLabelType
|
|
||||||
case _ =>
|
|
||||||
safetyLabelType match {
|
|
||||||
case s.MediaSafetyLabelType.EnumUnknownMediaSafetyLabelType(DeprecatedEnumValue) =>
|
|
||||||
Deprecated
|
|
||||||
case _ =>
|
|
||||||
Unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def toThrift(safetyLabelType: MediaSafetyLabelType): s.MediaSafetyLabelType = {
|
|
||||||
modelToThriftMap
|
|
||||||
.get(safetyLabelType).getOrElse(UnknownThriftSafetyLabelType)
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,179 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
import com.twitter.datatools.entityservice.entities.thriftscala.FleetInterstitial
|
|
||||||
import com.twitter.datatools.entityservice.entities.{thriftscala => t}
|
|
||||||
import com.twitter.escherbird.softintervention.thriftscala.MisinformationLocalizedPolicy
|
|
||||||
import com.twitter.escherbird.thriftscala.TweetEntityAnnotation
|
|
||||||
|
|
||||||
case class MisinformationPolicy(
|
|
||||||
semanticCoreAnnotation: SemanticCoreAnnotation,
|
|
||||||
priority: Long = MisinformationPolicy.DefaultPriority,
|
|
||||||
filteringLevel: Int = MisinformationPolicy.DefaultFilteringLevel,
|
|
||||||
publishedState: PublishedState = MisinformationPolicy.DefaultPublishedState,
|
|
||||||
engagementNudge: Boolean = MisinformationPolicy.DefaultEngagementNudge,
|
|
||||||
suppressAutoplay: Boolean = MisinformationPolicy.DefaultSuppressAutoplay,
|
|
||||||
warning: Option[String] = None,
|
|
||||||
detailsUrl: Option[String] = None,
|
|
||||||
displayType: Option[MisinfoPolicyDisplayType] = None,
|
|
||||||
applicableCountries: Seq[String] = Seq.empty,
|
|
||||||
fleetInterstitial: Option[FleetInterstitial] = None)
|
|
||||||
|
|
||||||
object MisinformationPolicy {
|
|
||||||
private val DefaultPriority = 0
|
|
||||||
private val DefaultFilteringLevel = 1
|
|
||||||
private val DefaultPublishedState = PublishedState.Published
|
|
||||||
private val DefaultEngagementNudge = true
|
|
||||||
private val DefaultSuppressAutoplay = true
|
|
||||||
|
|
||||||
def apply(
|
|
||||||
annotation: TweetEntityAnnotation,
|
|
||||||
misinformation: MisinformationLocalizedPolicy
|
|
||||||
): MisinformationPolicy = {
|
|
||||||
MisinformationPolicy(
|
|
||||||
semanticCoreAnnotation = SemanticCoreAnnotation(
|
|
||||||
groupId = annotation.groupId,
|
|
||||||
domainId = annotation.domainId,
|
|
||||||
entityId = annotation.entityId
|
|
||||||
),
|
|
||||||
priority = misinformation.priority.getOrElse(DefaultPriority),
|
|
||||||
filteringLevel = misinformation.filteringLevel.getOrElse(DefaultFilteringLevel),
|
|
||||||
publishedState = misinformation.publishedState match {
|
|
||||||
case Some(t.PublishedState.Draft) => PublishedState.Draft
|
|
||||||
case Some(t.PublishedState.Dogfood) => PublishedState.Dogfood
|
|
||||||
case Some(t.PublishedState.Published) => PublishedState.Published
|
|
||||||
case _ => DefaultPublishedState
|
|
||||||
},
|
|
||||||
displayType = misinformation.displayType collect {
|
|
||||||
case t.MisinformationDisplayType.GetTheLatest => MisinfoPolicyDisplayType.GetTheLatest
|
|
||||||
case t.MisinformationDisplayType.StayInformed => MisinfoPolicyDisplayType.StayInformed
|
|
||||||
case t.MisinformationDisplayType.Misleading => MisinfoPolicyDisplayType.Misleading
|
|
||||||
case t.MisinformationDisplayType.GovernmentRequested =>
|
|
||||||
MisinfoPolicyDisplayType.GovernmentRequested
|
|
||||||
},
|
|
||||||
applicableCountries = misinformation.applicableCountries match {
|
|
||||||
case Some(countries) => countries.map(countryCode => countryCode.toLowerCase)
|
|
||||||
case _ => Seq.empty
|
|
||||||
},
|
|
||||||
fleetInterstitial = misinformation.fleetInterstitial,
|
|
||||||
engagementNudge = misinformation.engagementNudge.getOrElse(DefaultEngagementNudge),
|
|
||||||
suppressAutoplay = misinformation.suppressAutoplay.getOrElse(DefaultSuppressAutoplay),
|
|
||||||
warning = misinformation.warning,
|
|
||||||
detailsUrl = misinformation.detailsUrl,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait MisinformationPolicyTransform {
|
|
||||||
def apply(policies: Seq[MisinformationPolicy]): Seq[MisinformationPolicy]
|
|
||||||
def andThen(transform: MisinformationPolicyTransform): MisinformationPolicyTransform =
|
|
||||||
(policies: Seq[MisinformationPolicy]) => transform(this.apply(policies))
|
|
||||||
}
|
|
||||||
|
|
||||||
object MisinformationPolicyTransform {
|
|
||||||
|
|
||||||
def prioritize: MisinformationPolicyTransform =
|
|
||||||
(policies: Seq[MisinformationPolicy]) =>
|
|
||||||
policies
|
|
||||||
.sortBy(p => p.filteringLevel)(Ordering[Int].reverse)
|
|
||||||
.sortBy(p => p.priority)(Ordering[Long].reverse)
|
|
||||||
|
|
||||||
def filter(filters: Seq[MisinformationPolicy => Boolean]): MisinformationPolicyTransform =
|
|
||||||
(policies: Seq[MisinformationPolicy]) =>
|
|
||||||
policies.filter { policy => filters.forall { filter => filter(policy) } }
|
|
||||||
|
|
||||||
def filterLevelAndState(
|
|
||||||
filteringLevel: Int,
|
|
||||||
publishedStates: Seq[PublishedState]
|
|
||||||
): MisinformationPolicyTransform =
|
|
||||||
filter(
|
|
||||||
Seq(
|
|
||||||
hasFilteringLevelAtLeast(filteringLevel),
|
|
||||||
hasPublishedStates(publishedStates)
|
|
||||||
))
|
|
||||||
|
|
||||||
def filterLevelAndStateAndLocalized(
|
|
||||||
filteringLevel: Int,
|
|
||||||
publishedStates: Seq[PublishedState]
|
|
||||||
): MisinformationPolicyTransform =
|
|
||||||
filter(
|
|
||||||
Seq(
|
|
||||||
hasFilteringLevelAtLeast(filteringLevel),
|
|
||||||
hasPublishedStates(publishedStates),
|
|
||||||
hasNonEmptyLocalization,
|
|
||||||
))
|
|
||||||
|
|
||||||
def filterState(
|
|
||||||
publishedStates: Seq[PublishedState]
|
|
||||||
): MisinformationPolicyTransform =
|
|
||||||
filter(
|
|
||||||
Seq(
|
|
||||||
hasPublishedStates(publishedStates)
|
|
||||||
))
|
|
||||||
|
|
||||||
def filterStateAndLocalized(
|
|
||||||
publishedStates: Seq[PublishedState]
|
|
||||||
): MisinformationPolicyTransform =
|
|
||||||
filter(
|
|
||||||
Seq(
|
|
||||||
hasPublishedStates(publishedStates),
|
|
||||||
hasNonEmptyLocalization,
|
|
||||||
))
|
|
||||||
|
|
||||||
def filterApplicableCountries(
|
|
||||||
countryCode: Option[String],
|
|
||||||
): MisinformationPolicyTransform =
|
|
||||||
filter(Seq(policyAppliesToCountry(countryCode)))
|
|
||||||
|
|
||||||
def filterOutGeoSpecific(): MisinformationPolicyTransform =
|
|
||||||
filter(Seq(policyIsGlobal()))
|
|
||||||
|
|
||||||
def filterNonEngagementNudges(): MisinformationPolicyTransform =
|
|
||||||
filter(
|
|
||||||
Seq(
|
|
||||||
hasEngagementNudge,
|
|
||||||
))
|
|
||||||
|
|
||||||
def policyAppliesToCountry(countryCode: Option[String]): MisinformationPolicy => Boolean =
|
|
||||||
policy =>
|
|
||||||
policy.applicableCountries.isEmpty ||
|
|
||||||
(countryCode.nonEmpty && policy.applicableCountries.contains(countryCode.get))
|
|
||||||
|
|
||||||
def policyIsGlobal(): MisinformationPolicy => Boolean =
|
|
||||||
policy => policy.applicableCountries.isEmpty
|
|
||||||
|
|
||||||
def hasFilteringLevelAtLeast(filteringLevel: Int): MisinformationPolicy => Boolean =
|
|
||||||
_.filteringLevel >= filteringLevel
|
|
||||||
|
|
||||||
def hasPublishedStates(
|
|
||||||
publishedStates: Seq[PublishedState]
|
|
||||||
): MisinformationPolicy => Boolean =
|
|
||||||
policy => publishedStates.contains(policy.publishedState)
|
|
||||||
|
|
||||||
def hasNonEmptyLocalization: MisinformationPolicy => Boolean =
|
|
||||||
policy => policy.warning.nonEmpty && policy.detailsUrl.nonEmpty
|
|
||||||
|
|
||||||
def hasEngagementNudge: MisinformationPolicy => Boolean =
|
|
||||||
policy => policy.engagementNudge
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed trait PublishedState
|
|
||||||
object PublishedState {
|
|
||||||
case object Draft extends PublishedState
|
|
||||||
case object Dogfood extends PublishedState
|
|
||||||
case object Published extends PublishedState
|
|
||||||
|
|
||||||
val PublicPublishedStates = Seq(PublishedState.Published)
|
|
||||||
val EmployeePublishedStates = Seq(PublishedState.Published, PublishedState.Dogfood)
|
|
||||||
}
|
|
||||||
sealed trait MisinfoPolicyDisplayType
|
|
||||||
object MisinfoPolicyDisplayType {
|
|
||||||
case object GetTheLatest extends MisinfoPolicyDisplayType
|
|
||||||
case object StayInformed extends MisinfoPolicyDisplayType
|
|
||||||
case object Misleading extends MisinfoPolicyDisplayType
|
|
||||||
case object GovernmentRequested extends MisinfoPolicyDisplayType
|
|
||||||
}
|
|
||||||
|
|
||||||
object SemanticCoreMisinformation {
|
|
||||||
val domainId: Long = 148L
|
|
||||||
}
|
|
Binary file not shown.
@ -1,3 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
case class MutedKeyword(keyword: Option[String])
|
|
Binary file not shown.
@ -1,90 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
import com.twitter.spam.rtf.{thriftscala => s}
|
|
||||||
import com.twitter.visibility.safety_label_store.{thriftscala => store}
|
|
||||||
|
|
||||||
case class SafetyLabel(
|
|
||||||
score: Option[Double] = None,
|
|
||||||
applicableUsers: Set[Long] = Set.empty,
|
|
||||||
source: Option[LabelSource] = None,
|
|
||||||
modelMetadata: Option[TweetModelMetadata] = None,
|
|
||||||
createdAtMsec: Option[Long] = None,
|
|
||||||
expiresAtMsec: Option[Long] = None,
|
|
||||||
labelMetadata: Option[SafetyLabelMetadata] = None,
|
|
||||||
applicableCountries: Option[Seq[String]] = None)
|
|
||||||
|
|
||||||
object SafetyLabel {
|
|
||||||
def fromThrift(safetyLabel: s.SafetyLabel): SafetyLabel = {
|
|
||||||
SafetyLabel(
|
|
||||||
score = safetyLabel.score,
|
|
||||||
applicableUsers = safetyLabel.applicableUsers
|
|
||||||
.map { perspectivalUsers =>
|
|
||||||
(perspectivalUsers map {
|
|
||||||
_.userId
|
|
||||||
}).toSet
|
|
||||||
}.getOrElse(Set.empty),
|
|
||||||
source = safetyLabel.source.flatMap(LabelSource.fromString),
|
|
||||||
modelMetadata = safetyLabel.modelMetadata.flatMap(TweetModelMetadata.fromThrift),
|
|
||||||
createdAtMsec = safetyLabel.createdAtMsec,
|
|
||||||
expiresAtMsec = safetyLabel.expiresAtMsec,
|
|
||||||
labelMetadata = safetyLabel.labelMetadata.map(SafetyLabelMetadata.fromThrift(_)),
|
|
||||||
applicableCountries = safetyLabel.applicableCountries
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
def toThrift(safetyLabel: SafetyLabel): s.SafetyLabel = {
|
|
||||||
s.SafetyLabel(
|
|
||||||
score = safetyLabel.score,
|
|
||||||
applicableUsers = if (safetyLabel.applicableUsers.nonEmpty) {
|
|
||||||
Some(safetyLabel.applicableUsers.toSeq.map {
|
|
||||||
s.PerspectivalUser(_)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
source = safetyLabel.source.map(_.name),
|
|
||||||
modelMetadata = safetyLabel.modelMetadata.map(TweetModelMetadata.toThrift),
|
|
||||||
createdAtMsec = safetyLabel.createdAtMsec,
|
|
||||||
expiresAtMsec = safetyLabel.expiresAtMsec,
|
|
||||||
labelMetadata = safetyLabel.labelMetadata.map(_.toThrift),
|
|
||||||
applicableCountries = safetyLabel.applicableCountries
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait SafetyLabelWithType[EntitySafetyLabelType <: SafetyLabelType] {
|
|
||||||
val safetyLabelType: EntitySafetyLabelType
|
|
||||||
val safetyLabel: SafetyLabel
|
|
||||||
}
|
|
||||||
|
|
||||||
case class MediaSafetyLabel(
|
|
||||||
override val safetyLabelType: MediaSafetyLabelType,
|
|
||||||
override val safetyLabel: SafetyLabel)
|
|
||||||
extends SafetyLabelWithType[MediaSafetyLabelType] {
|
|
||||||
|
|
||||||
def fromThrift(
|
|
||||||
thriftType: store.MediaSafetyLabelType,
|
|
||||||
thriftLabel: s.SafetyLabel
|
|
||||||
): MediaSafetyLabel = {
|
|
||||||
MediaSafetyLabel(
|
|
||||||
MediaSafetyLabelType.fromThrift(thriftType),
|
|
||||||
SafetyLabel.fromThrift(thriftLabel)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class SpaceSafetyLabel(
|
|
||||||
override val safetyLabelType: SpaceSafetyLabelType,
|
|
||||||
override val safetyLabel: SafetyLabel)
|
|
||||||
extends SafetyLabelWithType[SpaceSafetyLabelType] {
|
|
||||||
|
|
||||||
def fromThrift(
|
|
||||||
thriftType: store.SpaceSafetyLabelType,
|
|
||||||
thriftLabel: s.SafetyLabel
|
|
||||||
): SpaceSafetyLabel = {
|
|
||||||
SpaceSafetyLabel(
|
|
||||||
SpaceSafetyLabelType.fromThrift(thriftType),
|
|
||||||
SafetyLabel.fromThrift(thriftLabel)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,25 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
import com.twitter.guano.commons.thriftscala.PolicyInViolation
|
|
||||||
import com.twitter.spam.rtf.{thriftscala => s}
|
|
||||||
|
|
||||||
case class SafetyLabelMetadata(
|
|
||||||
policyInViolation: Option[PolicyInViolation] = None,
|
|
||||||
policyUrl: Option[String] = None) {
|
|
||||||
|
|
||||||
def toThrift: s.SafetyLabelMetadata = {
|
|
||||||
s.SafetyLabelMetadata(
|
|
||||||
policyInViolation,
|
|
||||||
policyUrl
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object SafetyLabelMetadata {
|
|
||||||
def fromThrift(metadata: s.SafetyLabelMetadata): SafetyLabelMetadata = {
|
|
||||||
SafetyLabelMetadata(
|
|
||||||
metadata.policyInViolation,
|
|
||||||
metadata.policyUrl
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,7 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
trait SafetyLabelType {
|
|
||||||
val DeprecatedEnumValue: Short = -1
|
|
||||||
val UnknownEnumValue: Short = -2
|
|
||||||
val StratoOnlyLabelEnumValue: Short = -3
|
|
||||||
}
|
|
Binary file not shown.
@ -1,855 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
import com.twitter.spam.rtf.thriftscala.{SafetyLevel => ThriftSafetyLevel}
|
|
||||||
import com.twitter.visibility.configapi.params.SafetyLevelParam
|
|
||||||
import com.twitter.visibility.configapi.params.SafetyLevelParams._
|
|
||||||
|
|
||||||
sealed trait SafetyLevel {
|
|
||||||
val name: String = this.getClass.getSimpleName.dropRight(1)
|
|
||||||
def enabledParam: SafetyLevelParam
|
|
||||||
}
|
|
||||||
|
|
||||||
object SafetyLevel {
|
|
||||||
private lazy val nameToSafetyLevelMap: Map[String, SafetyLevel] =
|
|
||||||
SafetyLevel.List.map(s => s.name.toLowerCase -> s).toMap
|
|
||||||
def fromName(name: String): Option[SafetyLevel] = {
|
|
||||||
nameToSafetyLevelMap.get(name.toLowerCase)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val DeprecatedEnumValue = -1
|
|
||||||
|
|
||||||
private lazy val thriftToModelMap: Map[ThriftSafetyLevel, SafetyLevel] = Map(
|
|
||||||
ThriftSafetyLevel.AccessInternalPromotedContent -> AccessInternalPromotedContent,
|
|
||||||
ThriftSafetyLevel.AdsBusinessSettings -> AdsBusinessSettings,
|
|
||||||
ThriftSafetyLevel.AdsCampaign -> AdsCampaign,
|
|
||||||
ThriftSafetyLevel.AdsManager -> AdsManager,
|
|
||||||
ThriftSafetyLevel.AdsReportingDashboard -> AdsReportingDashboard,
|
|
||||||
ThriftSafetyLevel.AllSubscribedLists -> AllSubscribedLists,
|
|
||||||
ThriftSafetyLevel.Appeals -> Appeals,
|
|
||||||
ThriftSafetyLevel.ArticleTweetTimeline -> ArticleTweetTimeline,
|
|
||||||
ThriftSafetyLevel.BaseQig -> BaseQig,
|
|
||||||
ThriftSafetyLevel.BirdwatchNoteAuthor -> BirdwatchNoteAuthor,
|
|
||||||
ThriftSafetyLevel.BirdwatchNoteTweetsTimeline -> BirdwatchNoteTweetsTimeline,
|
|
||||||
ThriftSafetyLevel.BirdwatchNeedsYourHelpNotifications -> BirdwatchNeedsYourHelpNotifications,
|
|
||||||
ThriftSafetyLevel.BlockMuteUsersTimeline -> BlockMuteUsersTimeline,
|
|
||||||
ThriftSafetyLevel.BrandSafety -> BrandSafety,
|
|
||||||
ThriftSafetyLevel.CardPollVoting -> CardPollVoting,
|
|
||||||
ThriftSafetyLevel.CardsService -> CardsService,
|
|
||||||
ThriftSafetyLevel.Communities -> Communities,
|
|
||||||
ThriftSafetyLevel.ContentControlToolInstall -> ContentControlToolInstall,
|
|
||||||
ThriftSafetyLevel.ConversationFocalPrehydration -> ConversationFocalPrehydration,
|
|
||||||
ThriftSafetyLevel.ConversationFocalTweet -> ConversationFocalTweet,
|
|
||||||
ThriftSafetyLevel.ConversationInjectedTweet -> ConversationInjectedTweet,
|
|
||||||
ThriftSafetyLevel.ConversationReply -> ConversationReply,
|
|
||||||
ThriftSafetyLevel.CuratedTrendsRepresentativeTweet -> CuratedTrendsRepresentativeTweet,
|
|
||||||
ThriftSafetyLevel.CurationPolicyViolations -> CurationPolicyViolations,
|
|
||||||
ThriftSafetyLevel.DevPlatformGetListTweets -> DevPlatformGetListTweets,
|
|
||||||
ThriftSafetyLevel.DesFollowingAndFollowersUserList -> DesFollowingAndFollowersUserList,
|
|
||||||
ThriftSafetyLevel.DesHomeTimeline -> DesHomeTimeline,
|
|
||||||
ThriftSafetyLevel.DesQuoteTweetTimeline -> DesQuoteTweetTimeline,
|
|
||||||
ThriftSafetyLevel.DesRealtime -> DesRealtime,
|
|
||||||
ThriftSafetyLevel.DesRealtimeSpamEnrichment -> DesRealtimeSpamEnrichment,
|
|
||||||
ThriftSafetyLevel.DesRealtimeTweetFilter -> DesRealtimeTweetFilter,
|
|
||||||
ThriftSafetyLevel.DesRetweetingUsers -> DesRetweetingUsers,
|
|
||||||
ThriftSafetyLevel.DesTweetDetail -> DesTweetDetail,
|
|
||||||
ThriftSafetyLevel.DesTweetLikingUsers -> DesTweetLikingUsers,
|
|
||||||
ThriftSafetyLevel.DesUserBookmarks -> DesUserBookmarks,
|
|
||||||
ThriftSafetyLevel.DesUserLikedTweets -> DesUserLikedTweets,
|
|
||||||
ThriftSafetyLevel.DesUserMentions -> DesUserMentions,
|
|
||||||
ThriftSafetyLevel.DesUserTweets -> DesUserTweets,
|
|
||||||
ThriftSafetyLevel.DevPlatformComplianceStream -> DevPlatformComplianceStream,
|
|
||||||
ThriftSafetyLevel.DirectMessages -> DirectMessages,
|
|
||||||
ThriftSafetyLevel.DirectMessagesConversationList -> DirectMessagesConversationList,
|
|
||||||
ThriftSafetyLevel.DirectMessagesConversationTimeline -> DirectMessagesConversationTimeline,
|
|
||||||
ThriftSafetyLevel.DirectMessagesInbox -> DirectMessagesInbox,
|
|
||||||
ThriftSafetyLevel.DirectMessagesMutedUsers -> DirectMessagesMutedUsers,
|
|
||||||
ThriftSafetyLevel.DirectMessagesPinned -> DirectMessagesPinned,
|
|
||||||
ThriftSafetyLevel.DirectMessagesSearch -> DirectMessagesSearch,
|
|
||||||
ThriftSafetyLevel.EditHistoryTimeline -> EditHistoryTimeline,
|
|
||||||
ThriftSafetyLevel.ElevatedQuoteTweetTimeline -> ElevatedQuoteTweetTimeline,
|
|
||||||
ThriftSafetyLevel.EmbeddedTweet -> EmbeddedTweet,
|
|
||||||
ThriftSafetyLevel.EmbedsPublicInterestNotice -> EmbedsPublicInterestNotice,
|
|
||||||
ThriftSafetyLevel.EmbedTweetMarkup -> EmbedTweetMarkup,
|
|
||||||
ThriftSafetyLevel.ExploreRecommendations -> ExploreRecommendations,
|
|
||||||
ThriftSafetyLevel.WritePathLimitedActionsEnforcement -> WritePathLimitedActionsEnforcement,
|
|
||||||
ThriftSafetyLevel.FilterAll -> FilterAll,
|
|
||||||
ThriftSafetyLevel.FilterAllPlaceholder -> FilterAllPlaceholder,
|
|
||||||
ThriftSafetyLevel.FilterDefault -> FilterDefault,
|
|
||||||
ThriftSafetyLevel.FilterNone -> FilterNone,
|
|
||||||
ThriftSafetyLevel.FollowedTopicsTimeline -> FollowedTopicsTimeline,
|
|
||||||
ThriftSafetyLevel.FollowerConnections -> FollowerConnections,
|
|
||||||
ThriftSafetyLevel.FollowingAndFollowersUserList -> FollowingAndFollowersUserList,
|
|
||||||
ThriftSafetyLevel.ForDevelopmentOnly -> ForDevelopmentOnly,
|
|
||||||
ThriftSafetyLevel.FriendsFollowingList -> FriendsFollowingList,
|
|
||||||
ThriftSafetyLevel.GraphqlDefault -> GraphqlDefault,
|
|
||||||
ThriftSafetyLevel.HumanizationNudge -> HumanizationNudge,
|
|
||||||
ThriftSafetyLevel.KitchenSinkDevelopment -> KitchenSinkDevelopment,
|
|
||||||
ThriftSafetyLevel.ListHeader -> ListHeader,
|
|
||||||
ThriftSafetyLevel.ListMemberships -> ListMemberships,
|
|
||||||
ThriftSafetyLevel.ListOwnerships -> ListOwnerships,
|
|
||||||
ThriftSafetyLevel.ListRecommendations -> ListRecommendations,
|
|
||||||
ThriftSafetyLevel.ListSearch -> ListSearch,
|
|
||||||
ThriftSafetyLevel.ListSubscriptions -> ListSubscriptions,
|
|
||||||
ThriftSafetyLevel.LivePipelineEngagementCounts -> LivePipelineEngagementCounts,
|
|
||||||
ThriftSafetyLevel.LiveVideoTimeline -> LiveVideoTimeline,
|
|
||||||
ThriftSafetyLevel.MagicRecs -> MagicRecs,
|
|
||||||
ThriftSafetyLevel.MagicRecsV2 -> MagicRecsV2,
|
|
||||||
ThriftSafetyLevel.MagicRecsAggressive -> MagicRecsAggressive,
|
|
||||||
ThriftSafetyLevel.MagicRecsAggressiveV2 -> MagicRecsAggressiveV2,
|
|
||||||
ThriftSafetyLevel.Minimal -> Minimal,
|
|
||||||
ThriftSafetyLevel.ModeratedTweetsTimeline -> ModeratedTweetsTimeline,
|
|
||||||
ThriftSafetyLevel.Moments -> Moments,
|
|
||||||
ThriftSafetyLevel.NearbyTimeline -> NearbyTimeline,
|
|
||||||
ThriftSafetyLevel.NewUserExperience -> NewUserExperience,
|
|
||||||
ThriftSafetyLevel.NotificationsIbis -> NotificationsIbis,
|
|
||||||
ThriftSafetyLevel.NotificationsPlatform -> NotificationsPlatform,
|
|
||||||
ThriftSafetyLevel.NotificationsPlatformPush -> NotificationsPlatformPush,
|
|
||||||
ThriftSafetyLevel.NotificationsQig -> NotificationsQig,
|
|
||||||
ThriftSafetyLevel.NotificationsRead -> NotificationsRead,
|
|
||||||
ThriftSafetyLevel.NotificationsTimelineDeviceFollow -> NotificationsTimelineDeviceFollow,
|
|
||||||
ThriftSafetyLevel.NotificationsWrite -> NotificationsWrite,
|
|
||||||
ThriftSafetyLevel.NotificationsWriterTweetHydrator -> NotificationsWriterTweetHydrator,
|
|
||||||
ThriftSafetyLevel.NotificationsWriterV2 -> NotificationsWriterV2,
|
|
||||||
ThriftSafetyLevel.ProfileMixerMedia -> ProfileMixerMedia,
|
|
||||||
ThriftSafetyLevel.ProfileMixerFavorites -> ProfileMixerFavorites,
|
|
||||||
ThriftSafetyLevel.QuickPromoteTweetEligibility -> QuickPromoteTweetEligibility,
|
|
||||||
ThriftSafetyLevel.QuoteTweetTimeline -> QuoteTweetTimeline,
|
|
||||||
ThriftSafetyLevel.QuotedTweetRules -> QuotedTweetRules,
|
|
||||||
ThriftSafetyLevel.Recommendations -> Recommendations,
|
|
||||||
ThriftSafetyLevel.RecosVideo -> RecosVideo,
|
|
||||||
ThriftSafetyLevel.RecosWritePath -> RecosWritePath,
|
|
||||||
ThriftSafetyLevel.RepliesGrouping -> RepliesGrouping,
|
|
||||||
ThriftSafetyLevel.ReportCenter -> ReportCenter,
|
|
||||||
ThriftSafetyLevel.ReturningUserExperience -> ReturningUserExperience,
|
|
||||||
ThriftSafetyLevel.ReturningUserExperienceFocalTweet -> ReturningUserExperienceFocalTweet,
|
|
||||||
ThriftSafetyLevel.Revenue -> Revenue,
|
|
||||||
ThriftSafetyLevel.RitoActionedTweetTimeline -> RitoActionedTweetTimeline,
|
|
||||||
ThriftSafetyLevel.SafeSearchMinimal -> SafeSearchMinimal,
|
|
||||||
ThriftSafetyLevel.SafeSearchStrict -> SafeSearchStrict,
|
|
||||||
ThriftSafetyLevel.SearchHydration -> SearchHydration,
|
|
||||||
ThriftSafetyLevel.SearchLatest -> SearchLatest,
|
|
||||||
ThriftSafetyLevel.SearchTop -> SearchTop,
|
|
||||||
ThriftSafetyLevel.SearchTopQig -> SearchTopQig,
|
|
||||||
ThriftSafetyLevel.SearchMixerSrpMinimal -> SearchMixerSrpMinimal,
|
|
||||||
ThriftSafetyLevel.SearchMixerSrpStrict -> SearchMixerSrpStrict,
|
|
||||||
ThriftSafetyLevel.SearchPeopleSrp -> SearchPeopleSrp,
|
|
||||||
ThriftSafetyLevel.SearchPeopleTypeahead -> SearchPeopleTypeahead,
|
|
||||||
ThriftSafetyLevel.SearchPhoto -> SearchPhoto,
|
|
||||||
ThriftSafetyLevel.SearchTrendTakeoverPromotedTweet -> SearchTrendTakeoverPromotedTweet,
|
|
||||||
ThriftSafetyLevel.SearchVideo -> SearchVideo,
|
|
||||||
ThriftSafetyLevel.SearchBlenderUserRules -> SearchBlenderUserRules,
|
|
||||||
ThriftSafetyLevel.SearchLatestUserRules -> SearchLatestUserRules,
|
|
||||||
ThriftSafetyLevel.ShoppingManagerSpyMode -> ShoppingManagerSpyMode,
|
|
||||||
ThriftSafetyLevel.SignalsReactions -> SignalsReactions,
|
|
||||||
ThriftSafetyLevel.SignalsTweetReactingUsers -> SignalsTweetReactingUsers,
|
|
||||||
ThriftSafetyLevel.SocialProof -> SocialProof,
|
|
||||||
ThriftSafetyLevel.SoftInterventionPivot -> SoftInterventionPivot,
|
|
||||||
ThriftSafetyLevel.SpaceFleetline -> SpaceFleetline,
|
|
||||||
ThriftSafetyLevel.SpaceHomeTimelineUpranking -> SpaceHomeTimelineUpranking,
|
|
||||||
ThriftSafetyLevel.SpaceJoinScreen -> SpaceJoinScreen,
|
|
||||||
ThriftSafetyLevel.SpaceNotifications -> SpaceNotifications,
|
|
||||||
ThriftSafetyLevel.Spaces -> Spaces,
|
|
||||||
ThriftSafetyLevel.SpacesParticipants -> SpacesParticipants,
|
|
||||||
ThriftSafetyLevel.SpacesSellerApplicationStatus -> SpacesSellerApplicationStatus,
|
|
||||||
ThriftSafetyLevel.SpacesSharing -> SpacesSharing,
|
|
||||||
ThriftSafetyLevel.SpaceTweetAvatarHomeTimeline -> SpaceTweetAvatarHomeTimeline,
|
|
||||||
ThriftSafetyLevel.StickersTimeline -> StickersTimeline,
|
|
||||||
ThriftSafetyLevel.StratoExtLimitedEngagements -> StratoExtLimitedEngagements,
|
|
||||||
ThriftSafetyLevel.StreamServices -> StreamServices,
|
|
||||||
ThriftSafetyLevel.SuperFollowerConnections -> SuperFollowerConnections,
|
|
||||||
ThriftSafetyLevel.SuperLike -> SuperLike,
|
|
||||||
ThriftSafetyLevel.Test -> Test,
|
|
||||||
ThriftSafetyLevel.TimelineBookmark -> TimelineBookmark,
|
|
||||||
ThriftSafetyLevel.TimelineContentControls -> TimelineContentControls,
|
|
||||||
ThriftSafetyLevel.TimelineConversations -> TimelineConversations,
|
|
||||||
ThriftSafetyLevel.TimelineConversationsDownranking -> TimelineConversationsDownranking,
|
|
||||||
ThriftSafetyLevel.TimelineConversationsDownrankingMinimal -> TimelineConversationsDownrankingMinimal,
|
|
||||||
ThriftSafetyLevel.TimelineFavorites -> TimelineFavorites,
|
|
||||||
ThriftSafetyLevel.TimelineFavoritesSelfView -> TimelineFavoritesSelfView,
|
|
||||||
ThriftSafetyLevel.TimelineFocalTweet -> TimelineFocalTweet,
|
|
||||||
ThriftSafetyLevel.TimelineFollowingActivity -> TimelineFollowingActivity,
|
|
||||||
ThriftSafetyLevel.TimelineHome -> TimelineHome,
|
|
||||||
ThriftSafetyLevel.TimelineHomeCommunities -> TimelineHomeCommunities,
|
|
||||||
ThriftSafetyLevel.TimelineHomeHydration -> TimelineHomeHydration,
|
|
||||||
ThriftSafetyLevel.TimelineHomeLatest -> TimelineHomeLatest,
|
|
||||||
ThriftSafetyLevel.TimelineHomePromotedHydration -> TimelineHomePromotedHydration,
|
|
||||||
ThriftSafetyLevel.TimelineHomeRecommendations -> TimelineHomeRecommendations,
|
|
||||||
ThriftSafetyLevel.TimelineHomeTopicFollowRecommendations -> TimelineHomeTopicFollowRecommendations,
|
|
||||||
ThriftSafetyLevel.TimelineScorer -> TimelineScorer,
|
|
||||||
ThriftSafetyLevel.TimelineInjection -> TimelineInjection,
|
|
||||||
ThriftSafetyLevel.TimelineLikedBy -> TimelineLikedBy,
|
|
||||||
ThriftSafetyLevel.TimelineLists -> TimelineLists,
|
|
||||||
ThriftSafetyLevel.TimelineMedia -> TimelineMedia,
|
|
||||||
ThriftSafetyLevel.TimelineMentions -> TimelineMentions,
|
|
||||||
ThriftSafetyLevel.TimelineModeratedTweetsHydration -> TimelineModeratedTweetsHydration,
|
|
||||||
ThriftSafetyLevel.TimelineProfile -> TimelineProfile,
|
|
||||||
ThriftSafetyLevel.TimelineProfileAll -> TimelineProfileAll,
|
|
||||||
ThriftSafetyLevel.TimelineProfileSpaces -> TimelineProfileSpaces,
|
|
||||||
ThriftSafetyLevel.TimelineProfileSuperFollows -> TimelineProfileSuperFollows,
|
|
||||||
ThriftSafetyLevel.TimelineReactiveBlending -> TimelineReactiveBlending,
|
|
||||||
ThriftSafetyLevel.TimelineRetweetedBy -> TimelineRetweetedBy,
|
|
||||||
ThriftSafetyLevel.TimelineSuperLikedBy -> TimelineSuperLikedBy,
|
|
||||||
ThriftSafetyLevel.Tombstoning -> Tombstoning,
|
|
||||||
ThriftSafetyLevel.TopicRecommendations -> TopicRecommendations,
|
|
||||||
ThriftSafetyLevel.TopicsLandingPageTopicRecommendations -> TopicsLandingPageTopicRecommendations,
|
|
||||||
ThriftSafetyLevel.TrendsRepresentativeTweet -> TrendsRepresentativeTweet,
|
|
||||||
ThriftSafetyLevel.TrustedFriendsUserList -> TrustedFriendsUserList,
|
|
||||||
ThriftSafetyLevel.TwitterDelegateUserList -> TwitterDelegateUserList,
|
|
||||||
ThriftSafetyLevel.GryphonDecksAndColumns -> GryphonDecksAndColumns,
|
|
||||||
ThriftSafetyLevel.TweetDetail -> TweetDetail,
|
|
||||||
ThriftSafetyLevel.TweetDetailNonToo -> TweetDetailNonToo,
|
|
||||||
ThriftSafetyLevel.TweetDetailWithInjectionsHydration -> TweetDetailWithInjectionsHydration,
|
|
||||||
ThriftSafetyLevel.TweetEngagers -> TweetEngagers,
|
|
||||||
ThriftSafetyLevel.TweetReplyNudge -> TweetReplyNudge,
|
|
||||||
ThriftSafetyLevel.TweetScopedTimeline -> TweetScopedTimeline,
|
|
||||||
ThriftSafetyLevel.TweetWritesApi -> TweetWritesApi,
|
|
||||||
ThriftSafetyLevel.TwitterArticleCompose -> TwitterArticleCompose,
|
|
||||||
ThriftSafetyLevel.TwitterArticleProfileTab -> TwitterArticleProfileTab,
|
|
||||||
ThriftSafetyLevel.TwitterArticleRead -> TwitterArticleRead,
|
|
||||||
ThriftSafetyLevel.UserProfileHeader -> UserProfileHeader,
|
|
||||||
ThriftSafetyLevel.UserMilestoneRecommendation -> UserMilestoneRecommendation,
|
|
||||||
ThriftSafetyLevel.UserScopedTimeline -> UserScopedTimeline,
|
|
||||||
ThriftSafetyLevel.UserSearchSrp -> UserSearchSrp,
|
|
||||||
ThriftSafetyLevel.UserSearchTypeahead -> UserSearchTypeahead,
|
|
||||||
ThriftSafetyLevel.UserSelfViewOnly -> UserSelfViewOnly,
|
|
||||||
ThriftSafetyLevel.UserSettings -> UserSettings,
|
|
||||||
ThriftSafetyLevel.VideoAds -> VideoAds,
|
|
||||||
ThriftSafetyLevel.ZipbirdConsumerArchives -> ZipbirdConsumerArchives,
|
|
||||||
ThriftSafetyLevel.TweetAward -> TweetAward,
|
|
||||||
)
|
|
||||||
|
|
||||||
private lazy val modelToThriftMap: Map[SafetyLevel, ThriftSafetyLevel] =
|
|
||||||
for ((k, v) <- thriftToModelMap) yield (v, k)
|
|
||||||
|
|
||||||
case object AdsBusinessSettings extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableAdsBusinessSettingsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object AdsCampaign extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableAdsCampaignSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object AdsManager extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableAdsManagerSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object AdsReportingDashboard extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableAdsReportingDashboardSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object AllSubscribedLists extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableAllSubscribedListsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object Appeals extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableAppealsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ArticleTweetTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableArticleTweetTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object BaseQig extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableBaseQigSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object BirdwatchNoteAuthor extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableBirdwatchNoteAuthorSafetyLevel
|
|
||||||
}
|
|
||||||
case object BirdwatchNoteTweetsTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableBirdwatchNoteTweetsTimelineSafetyLevel
|
|
||||||
}
|
|
||||||
case object BirdwatchNeedsYourHelpNotifications extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableBirdwatchNeedsYourHelpNotificationsSafetyLevel
|
|
||||||
}
|
|
||||||
case object BlockMuteUsersTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableBlockMuteUsersTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object BrandSafety extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableBrandSafetySafetyLevelParam
|
|
||||||
}
|
|
||||||
case object CardPollVoting extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableCardPollVotingSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object CardsService extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableCardsServiceSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object Communities extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableCommunitiesSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ContentControlToolInstall extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableContentControlToolInstallSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ConversationFocalPrehydration extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableConversationFocalPrehydrationSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ConversationFocalTweet extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableConversationFocalTweetSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ConversationInjectedTweet extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableConversationInjectedTweetSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ConversationReply extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableConversationReplySafetyLevelParam
|
|
||||||
}
|
|
||||||
case object AccessInternalPromotedContent extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableAccessInternalPromotedContentSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object CuratedTrendsRepresentativeTweet extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableCuratedTrendsRepresentativeTweet
|
|
||||||
}
|
|
||||||
case object CurationPolicyViolations extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableCurationPolicyViolations
|
|
||||||
}
|
|
||||||
case object DevPlatformGetListTweets extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDevPlatformGetListTweetsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DesFollowingAndFollowersUserList extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableDESFollowingAndFollowersUserListSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DesHomeTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDESHomeTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DesQuoteTweetTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDESQuoteTweetTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DesRealtime extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDESRealtimeSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DesRealtimeSpamEnrichment extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDESRealtimeSpamEnrichmentSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DesRealtimeTweetFilter extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDESRealtimeTweetFilterSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DesRetweetingUsers extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDESRetweetingUsersSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DesTweetDetail extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDesTweetDetailSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DesTweetLikingUsers extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDESTweetLikingUsersSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DesUserBookmarks extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDESUserBookmarksSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DesUserLikedTweets extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDESUserLikedTweetSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DesUserMentions extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDESUserMentionsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DesUserTweets extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDESUserTweetsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DevPlatformComplianceStream extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDevPlatformComplianceStreamSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DirectMessages extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDirectMessagesSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DirectMessagesConversationList extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableDirectMessagesConversationListSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DirectMessagesConversationTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableDirectMessagesConversationTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DirectMessagesInbox extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableDirectMessagesInboxSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DirectMessagesMutedUsers extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDirectMessagesMutedUsersSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DirectMessagesPinned extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDirectMessagesPinnedSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object DirectMessagesSearch extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDirectMessagesSearchSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object EditHistoryTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableEditHistoryTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ElevatedQuoteTweetTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableElevatedQuoteTweetTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object EmbeddedTweet extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableEmbeddedTweetSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object EmbedsPublicInterestNotice extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableEmbedsPublicInterestNoticeSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object EmbedTweetMarkup extends SafetyLevel {
|
|
||||||
override def enabledParam: SafetyLevelParam = EnableEmbedTweetMarkupSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object WritePathLimitedActionsEnforcement extends SafetyLevel {
|
|
||||||
override def enabledParam: SafetyLevelParam =
|
|
||||||
EnableWritePathLimitedActionsEnforcementSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object FilterNone extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableFilterNoneSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object FilterAll extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableFilterAllSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object FilterAllPlaceholder extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableFilterDefaultSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object FilterDefault extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableFilterDefaultSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object FollowedTopicsTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableFollowedTopicsTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object FollowerConnections extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableFollowerConnectionsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object FollowingAndFollowersUserList extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableFollowingAndFollowersUserListSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ForDevelopmentOnly extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableForDevelopmentOnlySafetyLevelParam
|
|
||||||
}
|
|
||||||
case object FriendsFollowingList extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableFriendsFollowingListSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object GraphqlDefault extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableGraphqlDefaultSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object GryphonDecksAndColumns extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableGryphonDecksAndColumnsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object HumanizationNudge extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableHumanizationNudgeSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object KitchenSinkDevelopment extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableKitchenSinkDevelopmentSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ListHeader extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableListHeaderSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ListMemberships extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableListMembershipsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ListOwnerships extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableListOwnershipsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ListRecommendations extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableListRecommendationsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ListSearch extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableListSearchSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ListSubscriptions extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableListSubscriptionsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object LivePipelineEngagementCounts extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableLivePipelineEngagementCountsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object LiveVideoTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableLiveVideoTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object MagicRecs extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableMagicRecsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object MagicRecsAggressive extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableMagicRecsAggressiveSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object MagicRecsAggressiveV2 extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableMagicRecsAggressiveV2SafetyLevelParam
|
|
||||||
}
|
|
||||||
case object MagicRecsV2 extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableMagicRecsV2SafetyLevelParam
|
|
||||||
}
|
|
||||||
case object Minimal extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableMinimalSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ModeratedTweetsTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableModeratedTweetsTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object Moments extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableMomentsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object NearbyTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableNearbySafetyLevelParam
|
|
||||||
}
|
|
||||||
case object NewUserExperience extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableNewUserExperienceSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object NotificationsIbis extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableNotificationsIbisSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object NotificationsPlatform extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableNotificationsPlatformSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object NotificationsPlatformPush extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableNotificationsPlatformPushSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object NotificationsQig extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableNotificationsQigSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object NotificationsRead extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableNotificationsReadSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object NotificationsTimelineDeviceFollow extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableNotificationsTimelineDeviceFollowSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object NotificationsWrite extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableNotificationsWriteSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object NotificationsWriterV2 extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableNotificationsWriterV2SafetyLevelParam
|
|
||||||
}
|
|
||||||
case object NotificationsWriterTweetHydrator extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableNotificationsWriterTweetHydratorSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ProfileMixerMedia extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableProfileMixerMediaSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ProfileMixerFavorites extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableProfileMixerFavoritesSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object QuickPromoteTweetEligibility extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableQuickPromoteTweetEligibilitySafetyLevelParam
|
|
||||||
}
|
|
||||||
case object QuoteTweetTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableQuoteTweetTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object QuotedTweetRules extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableQuotedTweetRulesParam
|
|
||||||
}
|
|
||||||
case object Recommendations extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableRecommendationsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object RecosVideo extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableRecosVideoSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object RecosWritePath extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableRecosWritePathSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object RepliesGrouping extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableRepliesGroupingSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ReportCenter extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableReportCenterSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ReturningUserExperience extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableReturningUserExperienceSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ReturningUserExperienceFocalTweet extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableReturningUserExperienceFocalTweetSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object Revenue extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableRevenueSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object RitoActionedTweetTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableRitoActionedTweetTimelineParam
|
|
||||||
}
|
|
||||||
case object SafeSearchMinimal extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSafeSearchMinimalSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SafeSearchStrict extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSafeSearchStrictSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SearchHydration extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSearchHydrationSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SearchLatest extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSearchLatestSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SearchMixerSrpMinimal extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSearchMixerSrpMinimalSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SearchMixerSrpStrict extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSearchMixerSrpStrictSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SearchPeopleSrp extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSearchPeopleSearchResultPageSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SearchPeopleTypeahead extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSearchPeopleTypeaheadSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SearchPhoto extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSearchPhotoSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ShoppingManagerSpyMode extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableShoppingManagerSpyModeSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object StratoExtLimitedEngagements extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableStratoExtLimitedEngagementsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SearchTop extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSearchTopSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SearchTopQig extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSearchTopQigSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SearchTrendTakeoverPromotedTweet extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = SearchTrendTakeoverPromotedTweetSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SearchVideo extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSearchVideoSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SearchBlenderUserRules extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSearchBlenderUserRulesSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SearchLatestUserRules extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSearchLatestUserRulesSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SignalsReactions extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSignalsReactionsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SignalsTweetReactingUsers extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSignalsTweetReactingUsersSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SocialProof extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSocialProofSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SoftInterventionPivot extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSoftInterventionPivotSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SpaceFleetline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSpaceFleetlineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SpaceHomeTimelineUpranking extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSpaceHomeTimelineUprankingSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SpaceJoinScreen extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSpaceJoinScreenSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SpaceNotifications extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSpaceNotificationSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object Spaces extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSpacesSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SpacesParticipants extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSpacesParticipantsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SpacesSellerApplicationStatus extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableSpacesSellerApplicationStatusSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SpacesSharing extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSpacesSharingSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SpaceTweetAvatarHomeTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSpaceTweetAvatarHomeTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object StickersTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableStickersTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object StreamServices extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableStreamServicesSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SuperFollowerConnections extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSuperFollowerConnectionsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object SuperLike extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableSuperLikeSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object Test extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTestSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineConversations extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineConversationsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineConversationsDownranking extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableTimelineConversationsDownrankingSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineConversationsDownrankingMinimal extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableTimelineConversationsDownrankingMinimalSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineFollowingActivity extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineFollowingActivitySafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineHome extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineHomeSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineHomeCommunities extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineHomeCommunitiesSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineHomeHydration extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineHomeHydrationSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineHomePromotedHydration extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableTimelineHomePromotedHydrationSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineHomeRecommendations extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineHomeRecommendationsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineHomeTopicFollowRecommendations extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableTimelineHomeTopicFollowRecommendationsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineScorer extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableTimelineScorerSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TopicsLandingPageTopicRecommendations extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableTopicsLandingPageTopicRecommendationsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ExploreRecommendations extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableExploreRecommendationsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineModeratedTweetsHydration extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableTimelineModeratedTweetsHydrationSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineInjection extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineInjectionSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineMentions extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineMentionsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineHomeLatest extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineHomeLatestSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineLikedBy extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineLikedBySafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineRetweetedBy extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineRetweetedBySafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineSuperLikedBy extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineLikedBySafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineBookmark extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineBookmarkSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineContentControls extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineContentControlsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineMedia extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineMediaSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineReactiveBlending extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineReactiveBlendingSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineFavorites extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineFavoritesSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineFavoritesSelfView extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineFavoritesSelfViewSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineLists extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineListsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineProfile extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineProfileSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineProfileAll extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineProfileAllSafetyLevelParam
|
|
||||||
}
|
|
||||||
|
|
||||||
case object TimelineProfileSpaces extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineProfileSpacesSafetyLevelParam
|
|
||||||
}
|
|
||||||
|
|
||||||
case object TimelineProfileSuperFollows extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineProfileSuperFollowsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TimelineFocalTweet extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTimelineFocalTweetSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object Tombstoning extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTombstoningSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TopicRecommendations extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTopicRecommendationsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TrendsRepresentativeTweet extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTrendsRepresentativeTweetSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TrustedFriendsUserList extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTrustedFriendsUserListSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TwitterDelegateUserList extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTwitterDelegateUserListSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TweetDetail extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTweetDetailSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TweetDetailNonToo extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTweetDetailNonTooSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TweetDetailWithInjectionsHydration extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam =
|
|
||||||
EnableTweetDetailWithInjectionsHydrationSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TweetEngagers extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTweetEngagersSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TweetReplyNudge extends SafetyLevel {
|
|
||||||
override def enabledParam: SafetyLevelParam = EnableTweetReplyNudgeParam
|
|
||||||
}
|
|
||||||
case object TweetScopedTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTweetScopedTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TweetWritesApi extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTweetWritesApiSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TwitterArticleCompose extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTwitterArticleComposeSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TwitterArticleProfileTab extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTwitterArticleProfileTabSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TwitterArticleRead extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTwitterArticleReadSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object UserProfileHeader extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableUserProfileHeaderSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object UserMilestoneRecommendation extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableUserMilestoneRecommendationSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object UserScopedTimeline extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableUserScopedTimelineSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object UserSearchSrp extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableUserSearchSrpSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object UserSearchTypeahead extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableUserSearchTypeaheadSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object UserSelfViewOnly extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableUserSelfViewOnlySafetyLevelParam
|
|
||||||
}
|
|
||||||
case object UserSettings extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableUserSettingsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object VideoAds extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableVideoAdsSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object ZipbirdConsumerArchives extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableZipbirdConsumerArchivesSafetyLevelParam
|
|
||||||
}
|
|
||||||
case object TweetAward extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableTweetAwardSafetyLevelParam
|
|
||||||
}
|
|
||||||
|
|
||||||
case object DeprecatedSafetyLevel extends SafetyLevel {
|
|
||||||
override val enabledParam: SafetyLevelParam = EnableDeprecatedSafetyLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def fromThrift(safetyLevel: ThriftSafetyLevel): SafetyLevel =
|
|
||||||
thriftToModelMap.get(safetyLevel).getOrElse(DeprecatedSafetyLevel)
|
|
||||||
|
|
||||||
def toThrift(safetyLevel: SafetyLevel): ThriftSafetyLevel =
|
|
||||||
modelToThriftMap
|
|
||||||
.get(safetyLevel).getOrElse(ThriftSafetyLevel.EnumUnknownSafetyLevel(DeprecatedEnumValue))
|
|
||||||
|
|
||||||
val List: Seq[SafetyLevel] =
|
|
||||||
ThriftSafetyLevel.list.map(fromThrift).filter(_ != DeprecatedSafetyLevel)
|
|
||||||
}
|
|
Binary file not shown.
@ -1,554 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.AccessInternalPromotedContent
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.AdsBusinessSettings
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.AdsCampaign
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.AdsManager
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.AdsReportingDashboard
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.AllSubscribedLists
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.Appeals
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ArticleTweetTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.BaseQig
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.BirdwatchNeedsYourHelpNotifications
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.BirdwatchNoteAuthor
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.BirdwatchNoteTweetsTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.BlockMuteUsersTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.BrandSafety
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.CardPollVoting
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.CardsService
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ContentControlToolInstall
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ConversationFocalPrehydration
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ConversationFocalTweet
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ConversationInjectedTweet
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ConversationReply
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.CuratedTrendsRepresentativeTweet
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.CurationPolicyViolations
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DesFollowingAndFollowersUserList
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DesHomeTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DesQuoteTweetTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DesRealtime
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DesRealtimeSpamEnrichment
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DesRealtimeTweetFilter
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DesRetweetingUsers
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DesTweetDetail
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DesTweetLikingUsers
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DesUserBookmarks
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DesUserLikedTweets
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DesUserMentions
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DesUserTweets
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DevPlatformComplianceStream
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DevPlatformGetListTweets
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DirectMessages
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DirectMessagesConversationList
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DirectMessagesConversationTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DirectMessagesInbox
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DirectMessagesMutedUsers
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DirectMessagesPinned
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.DirectMessagesSearch
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.EditHistoryTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ElevatedQuoteTweetTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.EmbedTweetMarkup
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.EmbeddedTweet
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.EmbedsPublicInterestNotice
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ExploreRecommendations
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.FilterAll
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.FilterAllPlaceholder
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.FilterDefault
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.FilterNone
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.FollowedTopicsTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.FollowerConnections
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.FollowingAndFollowersUserList
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ForDevelopmentOnly
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.FriendsFollowingList
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.GraphqlDefault
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.GryphonDecksAndColumns
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.HumanizationNudge
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.KitchenSinkDevelopment
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ListHeader
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ListMemberships
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ListOwnerships
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ListRecommendations
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ListSearch
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ListSubscriptions
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.LivePipelineEngagementCounts
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.LiveVideoTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.MagicRecs
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.MagicRecsAggressive
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.MagicRecsAggressiveV2
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.MagicRecsV2
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.Minimal
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ModeratedTweetsTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.Moments
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.NearbyTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.NewUserExperience
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.NotificationsIbis
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.NotificationsPlatform
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.NotificationsPlatformPush
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.NotificationsQig
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.NotificationsRead
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.NotificationsTimelineDeviceFollow
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.NotificationsWrite
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.NotificationsWriterTweetHydrator
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.NotificationsWriterV2
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ProfileMixerFavorites
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ProfileMixerMedia
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.QuickPromoteTweetEligibility
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.QuoteTweetTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.QuotedTweetRules
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.RecosVideo
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.RecosWritePath
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.RepliesGrouping
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ReportCenter
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ReturningUserExperienceFocalTweet
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.Revenue
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SafeSearchMinimal
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SafeSearchStrict
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SearchBlenderUserRules
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SearchHydration
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SearchLatest
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SearchLatestUserRules
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SearchMixerSrpMinimal
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SearchMixerSrpStrict
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SearchPeopleSrp
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SearchPeopleTypeahead
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SearchPhoto
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SearchTop
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SearchTopQig
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SearchTrendTakeoverPromotedTweet
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SearchVideo
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ShoppingManagerSpyMode
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SignalsReactions
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SignalsTweetReactingUsers
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SoftInterventionPivot
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SpaceFleetline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SpaceHomeTimelineUpranking
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SpaceJoinScreen
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SpaceNotifications
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SpaceTweetAvatarHomeTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SpacesParticipants
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SpacesSellerApplicationStatus
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SpacesSharing
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.StickersTimeline
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.StratoExtLimitedEngagements
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.StreamServices
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SuperFollowerConnections
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.SuperLike
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.Test
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineBookmark
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineContentControls
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineConversations
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineConversationsDownranking
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineConversationsDownrankingMinimal
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineFavorites
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineFavoritesSelfView
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineFocalTweet
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineFollowingActivity
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineHomeCommunities
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineHomeHydration
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineHomeLatest
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineHomePromotedHydration
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineHomeRecommendations
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineHomeTopicFollowRecommendations
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineInjection
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineLikedBy
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineLists
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineMedia
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineMentions
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineModeratedTweetsHydration
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineProfileAll
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineProfileSpaces
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineProfileSuperFollows
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TimelineScorer
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.Tombstoning
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TopicsLandingPageTopicRecommendations
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TrendsRepresentativeTweet
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TrustedFriendsUserList
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TweetDetail
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TweetDetailNonToo
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TweetDetailWithInjectionsHydration
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TweetEngagers
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TweetReplyNudge
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TweetWritesApi
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TwitterArticleCompose
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TwitterArticleProfileTab
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.TwitterArticleRead
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.UserMilestoneRecommendation
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.UserProfileHeader
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.UserSelfViewOnly
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.UserSettings
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.VideoAds
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.WritePathLimitedActionsEnforcement
|
|
||||||
import com.twitter.visibility.models.SafetyLevel.ZipbirdConsumerArchives
|
|
||||||
|
|
||||||
sealed trait SafetyLevelGroup { val levels: Set[SafetyLevel] }
|
|
||||||
|
|
||||||
object SafetyLevelGroup {
|
|
||||||
case object Ads extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
AdsBusinessSettings,
|
|
||||||
AdsCampaign,
|
|
||||||
AdsManager,
|
|
||||||
AdsReportingDashboard,
|
|
||||||
BrandSafety,
|
|
||||||
VideoAds,
|
|
||||||
QuickPromoteTweetEligibility
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object ArticleTimeline extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
ArticleTweetTimeline,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object ArticleTweets extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
TwitterArticleCompose,
|
|
||||||
TwitterArticleProfileTab,
|
|
||||||
TwitterArticleRead,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Birdwatch extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
BirdwatchNoteAuthor,
|
|
||||||
BirdwatchNoteTweetsTimeline,
|
|
||||||
BirdwatchNeedsYourHelpNotifications,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Cards extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
CardPollVoting,
|
|
||||||
CardsService,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Communities extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
SafetyLevel.Communities
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Conversation extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
ConversationFocalPrehydration,
|
|
||||||
ConversationFocalTweet,
|
|
||||||
ConversationInjectedTweet,
|
|
||||||
ConversationReply,
|
|
||||||
Tombstoning,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object CreativeContainerService extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
AccessInternalPromotedContent
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Des extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
DevPlatformGetListTweets,
|
|
||||||
DesFollowingAndFollowersUserList,
|
|
||||||
DesHomeTimeline,
|
|
||||||
DesQuoteTweetTimeline,
|
|
||||||
DesRetweetingUsers,
|
|
||||||
DesTweetDetail,
|
|
||||||
DesTweetLikingUsers,
|
|
||||||
DesUserBookmarks,
|
|
||||||
DesUserLikedTweets,
|
|
||||||
DesUserMentions,
|
|
||||||
DesUserTweets,
|
|
||||||
DevPlatformComplianceStream,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object DesStream extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
DesRealtime,
|
|
||||||
DesRealtimeSpamEnrichment,
|
|
||||||
DesRealtimeTweetFilter,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Dm extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
DirectMessages,
|
|
||||||
DirectMessagesConversationList,
|
|
||||||
DirectMessagesConversationTimeline,
|
|
||||||
DirectMessagesInbox,
|
|
||||||
DirectMessagesMutedUsers,
|
|
||||||
DirectMessagesPinned,
|
|
||||||
DirectMessagesSearch,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Followers extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
FollowedTopicsTimeline,
|
|
||||||
FollowerConnections,
|
|
||||||
FollowingAndFollowersUserList,
|
|
||||||
FriendsFollowingList,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Graphql extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
FilterDefault,
|
|
||||||
GraphqlDefault,
|
|
||||||
SoftInterventionPivot,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Jiminy extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
HumanizationNudge,
|
|
||||||
TweetReplyNudge,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Lists extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
AllSubscribedLists,
|
|
||||||
ListHeader,
|
|
||||||
ListMemberships,
|
|
||||||
ListOwnerships,
|
|
||||||
ListRecommendations,
|
|
||||||
ListSearch,
|
|
||||||
ListSubscriptions,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Notifications extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
NotificationsIbis,
|
|
||||||
NotificationsPlatform,
|
|
||||||
NotificationsPlatformPush,
|
|
||||||
NotificationsQig,
|
|
||||||
NotificationsRead,
|
|
||||||
NotificationsTimelineDeviceFollow,
|
|
||||||
NotificationsWrite,
|
|
||||||
NotificationsWriterTweetHydrator,
|
|
||||||
NotificationsWriterV2,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Other extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
CuratedTrendsRepresentativeTweet,
|
|
||||||
CurationPolicyViolations,
|
|
||||||
BaseQig,
|
|
||||||
Appeals,
|
|
||||||
ContentControlToolInstall,
|
|
||||||
EditHistoryTimeline,
|
|
||||||
ElevatedQuoteTweetTimeline,
|
|
||||||
EmbeddedTweet,
|
|
||||||
EmbedsPublicInterestNotice,
|
|
||||||
EmbedTweetMarkup,
|
|
||||||
ExploreRecommendations,
|
|
||||||
WritePathLimitedActionsEnforcement,
|
|
||||||
LiveVideoTimeline,
|
|
||||||
LivePipelineEngagementCounts,
|
|
||||||
Minimal,
|
|
||||||
Moments,
|
|
||||||
NearbyTimeline,
|
|
||||||
NewUserExperience,
|
|
||||||
QuoteTweetTimeline,
|
|
||||||
QuotedTweetRules,
|
|
||||||
ReportCenter,
|
|
||||||
Revenue,
|
|
||||||
ShoppingManagerSpyMode,
|
|
||||||
StickersTimeline,
|
|
||||||
SuperLike,
|
|
||||||
TrendsRepresentativeTweet,
|
|
||||||
TrustedFriendsUserList,
|
|
||||||
GryphonDecksAndColumns,
|
|
||||||
TweetEngagers,
|
|
||||||
TweetWritesApi,
|
|
||||||
UserMilestoneRecommendation,
|
|
||||||
StreamServices,
|
|
||||||
ZipbirdConsumerArchives
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Profile extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
UserProfileHeader,
|
|
||||||
UserSelfViewOnly,
|
|
||||||
UserSettings,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Reactions extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
SignalsReactions,
|
|
||||||
SignalsTweetReactingUsers,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Recommendations extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
MagicRecs,
|
|
||||||
MagicRecsV2,
|
|
||||||
MagicRecsAggressive,
|
|
||||||
MagicRecsAggressiveV2,
|
|
||||||
SafetyLevel.Recommendations,
|
|
||||||
RecosVideo,
|
|
||||||
RecosWritePath,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object ReturningUserExperience extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
SafetyLevel.ReturningUserExperience,
|
|
||||||
ReturningUserExperienceFocalTweet,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object SafeSearch extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
SafeSearchMinimal,
|
|
||||||
SafeSearchStrict,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Search extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
SearchHydration,
|
|
||||||
SearchLatest,
|
|
||||||
SearchTop,
|
|
||||||
SearchTopQig,
|
|
||||||
SearchPeopleSrp,
|
|
||||||
SearchPeopleTypeahead,
|
|
||||||
SearchPhoto,
|
|
||||||
SearchTrendTakeoverPromotedTweet,
|
|
||||||
SearchVideo,
|
|
||||||
SearchBlenderUserRules,
|
|
||||||
SearchLatestUserRules,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object SearchMixer extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
SearchMixerSrpMinimal,
|
|
||||||
SearchMixerSrpStrict,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Socialproof extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
SafetyLevel.SocialProof
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Spaces extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
SpaceFleetline,
|
|
||||||
SpaceHomeTimelineUpranking,
|
|
||||||
SpaceJoinScreen,
|
|
||||||
SpaceNotifications,
|
|
||||||
SafetyLevel.Spaces,
|
|
||||||
SpacesParticipants,
|
|
||||||
SpacesSellerApplicationStatus,
|
|
||||||
SpacesSharing,
|
|
||||||
SpaceTweetAvatarHomeTimeline,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Strato extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
StratoExtLimitedEngagements
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Superfollows extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
SuperFollowerConnections,
|
|
||||||
TimelineProfileSuperFollows,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Testing extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
ForDevelopmentOnly,
|
|
||||||
KitchenSinkDevelopment,
|
|
||||||
Test,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Timeline extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
BlockMuteUsersTimeline,
|
|
||||||
TimelineBookmark,
|
|
||||||
TimelineContentControls,
|
|
||||||
TimelineConversationsDownranking,
|
|
||||||
TimelineConversationsDownrankingMinimal,
|
|
||||||
TimelineFavorites,
|
|
||||||
TimelineFavoritesSelfView,
|
|
||||||
TimelineFollowingActivity,
|
|
||||||
TimelineScorer,
|
|
||||||
TimelineInjection,
|
|
||||||
TimelineLikedBy,
|
|
||||||
TimelineLists,
|
|
||||||
TimelineMedia,
|
|
||||||
TimelineMentions,
|
|
||||||
ModeratedTweetsTimeline,
|
|
||||||
TimelineModeratedTweetsHydration,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object TopicRecommendations extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
SafetyLevel.TopicRecommendations,
|
|
||||||
TopicsLandingPageTopicRecommendations,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object TimelineProfile extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
SafetyLevel.TimelineProfile,
|
|
||||||
TimelineProfileAll,
|
|
||||||
TimelineProfileSpaces,
|
|
||||||
TimelineMedia,
|
|
||||||
ProfileMixerMedia,
|
|
||||||
TimelineFavorites,
|
|
||||||
ProfileMixerFavorites
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object TimelineHome extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
SafetyLevel.TimelineHome,
|
|
||||||
TimelineHomeCommunities,
|
|
||||||
TimelineHomeHydration,
|
|
||||||
TimelineHomeLatest,
|
|
||||||
TimelineHomePromotedHydration,
|
|
||||||
TimelineHomeRecommendations,
|
|
||||||
TimelineHomeTopicFollowRecommendations,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object TlsApi extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
TimelineConversations,
|
|
||||||
TimelineFocalTweet,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object TweetDetails extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
TweetDetail,
|
|
||||||
TweetDetailNonToo,
|
|
||||||
TweetDetailWithInjectionsHydration,
|
|
||||||
RepliesGrouping,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case object Special extends SafetyLevelGroup {
|
|
||||||
override val levels: Set[SafetyLevel] = Set(
|
|
||||||
FilterAll,
|
|
||||||
FilterAllPlaceholder,
|
|
||||||
FilterNone,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,3 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
case class SemanticCoreAnnotation(groupId: Long, domainId: Long, entityId: Long)
|
|
Binary file not shown.
@ -1,91 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
import com.twitter.visibility.safety_label_store.{thriftscala => s}
|
|
||||||
import com.twitter.visibility.util.NamingUtils
|
|
||||||
|
|
||||||
sealed trait SpaceSafetyLabelType extends SafetyLabelType {
|
|
||||||
lazy val name: String = NamingUtils.getFriendlyName(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
object SpaceSafetyLabelType extends SafetyLabelType {
|
|
||||||
|
|
||||||
val List: List[SpaceSafetyLabelType] = s.SpaceSafetyLabelType.list.map(fromThrift)
|
|
||||||
|
|
||||||
val ActiveLabels: List[SpaceSafetyLabelType] = List.filter { labelType =>
|
|
||||||
labelType != Unknown && labelType != Deprecated
|
|
||||||
}
|
|
||||||
|
|
||||||
private lazy val nameToValueMap: Map[String, SpaceSafetyLabelType] =
|
|
||||||
List.map(l => l.name.toLowerCase -> l).toMap
|
|
||||||
def fromName(name: String): Option[SpaceSafetyLabelType] = nameToValueMap.get(name.toLowerCase)
|
|
||||||
|
|
||||||
private val UnknownThriftSafetyLabelType =
|
|
||||||
s.SpaceSafetyLabelType.EnumUnknownSpaceSafetyLabelType(UnknownEnumValue)
|
|
||||||
|
|
||||||
private lazy val thriftToModelMap: Map[s.SpaceSafetyLabelType, SpaceSafetyLabelType] = Map(
|
|
||||||
s.SpaceSafetyLabelType.DoNotAmplify -> DoNotAmplify,
|
|
||||||
s.SpaceSafetyLabelType.CoordinatedHarmfulActivityHighRecall -> CoordinatedHarmfulActivityHighRecall,
|
|
||||||
s.SpaceSafetyLabelType.UntrustedUrl -> UntrustedUrl,
|
|
||||||
s.SpaceSafetyLabelType.MisleadingHighRecall -> MisleadingHighRecall,
|
|
||||||
s.SpaceSafetyLabelType.NsfwHighPrecision -> NsfwHighPrecision,
|
|
||||||
s.SpaceSafetyLabelType.NsfwHighRecall -> NsfwHighRecall,
|
|
||||||
s.SpaceSafetyLabelType.CivicIntegrityMisinfo -> CivicIntegrityMisinfo,
|
|
||||||
s.SpaceSafetyLabelType.MedicalMisinfo -> MedicalMisinfo,
|
|
||||||
s.SpaceSafetyLabelType.GenericMisinfo -> GenericMisinfo,
|
|
||||||
s.SpaceSafetyLabelType.DmcaWithheld -> DmcaWithheld,
|
|
||||||
s.SpaceSafetyLabelType.HatefulHighRecall -> HatefulHighRecall,
|
|
||||||
s.SpaceSafetyLabelType.ViolenceHighRecall -> ViolenceHighRecall,
|
|
||||||
s.SpaceSafetyLabelType.HighToxicityModelScore -> HighToxicityModelScore,
|
|
||||||
s.SpaceSafetyLabelType.DeprecatedSpaceSafetyLabel14 -> Deprecated,
|
|
||||||
s.SpaceSafetyLabelType.DeprecatedSpaceSafetyLabel15 -> Deprecated,
|
|
||||||
s.SpaceSafetyLabelType.Reserved16 -> Deprecated,
|
|
||||||
s.SpaceSafetyLabelType.Reserved17 -> Deprecated,
|
|
||||||
s.SpaceSafetyLabelType.Reserved18 -> Deprecated,
|
|
||||||
s.SpaceSafetyLabelType.Reserved19 -> Deprecated,
|
|
||||||
s.SpaceSafetyLabelType.Reserved20 -> Deprecated,
|
|
||||||
s.SpaceSafetyLabelType.Reserved21 -> Deprecated,
|
|
||||||
s.SpaceSafetyLabelType.Reserved22 -> Deprecated,
|
|
||||||
s.SpaceSafetyLabelType.Reserved23 -> Deprecated,
|
|
||||||
s.SpaceSafetyLabelType.Reserved24 -> Deprecated,
|
|
||||||
s.SpaceSafetyLabelType.Reserved25 -> Deprecated,
|
|
||||||
)
|
|
||||||
|
|
||||||
private lazy val modelToThriftMap: Map[SpaceSafetyLabelType, s.SpaceSafetyLabelType] =
|
|
||||||
(for ((k, v) <- thriftToModelMap) yield (v, k)) ++ Map(
|
|
||||||
Deprecated -> s.SpaceSafetyLabelType.EnumUnknownSpaceSafetyLabelType(DeprecatedEnumValue),
|
|
||||||
)
|
|
||||||
|
|
||||||
case object DoNotAmplify extends SpaceSafetyLabelType
|
|
||||||
case object CoordinatedHarmfulActivityHighRecall extends SpaceSafetyLabelType
|
|
||||||
case object UntrustedUrl extends SpaceSafetyLabelType
|
|
||||||
case object MisleadingHighRecall extends SpaceSafetyLabelType
|
|
||||||
case object NsfwHighPrecision extends SpaceSafetyLabelType
|
|
||||||
case object NsfwHighRecall extends SpaceSafetyLabelType
|
|
||||||
case object CivicIntegrityMisinfo extends SpaceSafetyLabelType
|
|
||||||
case object MedicalMisinfo extends SpaceSafetyLabelType
|
|
||||||
case object GenericMisinfo extends SpaceSafetyLabelType
|
|
||||||
case object DmcaWithheld extends SpaceSafetyLabelType
|
|
||||||
case object HatefulHighRecall extends SpaceSafetyLabelType
|
|
||||||
case object ViolenceHighRecall extends SpaceSafetyLabelType
|
|
||||||
case object HighToxicityModelScore extends SpaceSafetyLabelType
|
|
||||||
|
|
||||||
case object Deprecated extends SpaceSafetyLabelType
|
|
||||||
case object Unknown extends SpaceSafetyLabelType
|
|
||||||
|
|
||||||
def fromThrift(safetyLabelType: s.SpaceSafetyLabelType): SpaceSafetyLabelType =
|
|
||||||
thriftToModelMap.get(safetyLabelType) match {
|
|
||||||
case Some(spaceSafetyLabelType) => spaceSafetyLabelType
|
|
||||||
case _ =>
|
|
||||||
safetyLabelType match {
|
|
||||||
case s.SpaceSafetyLabelType.EnumUnknownSpaceSafetyLabelType(DeprecatedEnumValue) =>
|
|
||||||
Deprecated
|
|
||||||
case _ =>
|
|
||||||
Unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def toThrift(safetyLabelType: SpaceSafetyLabelType): s.SpaceSafetyLabelType = {
|
|
||||||
modelToThriftMap
|
|
||||||
.get(safetyLabelType).getOrElse(UnknownThriftSafetyLabelType)
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,6 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
object TweetDeleteReason extends Enumeration {
|
|
||||||
type TweetDeleteReason = Value
|
|
||||||
val Deleted, BounceDeleted = Value
|
|
||||||
}
|
|
Binary file not shown.
@ -1,23 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
import com.twitter.spam.rtf.{thriftscala => s}
|
|
||||||
|
|
||||||
case class TweetModelMetadata(
|
|
||||||
version: Option[Int] = None,
|
|
||||||
calibratedLanguage: Option[String] = None)
|
|
||||||
|
|
||||||
object TweetModelMetadata {
|
|
||||||
|
|
||||||
def fromThrift(metadata: s.ModelMetadata): Option[TweetModelMetadata] = {
|
|
||||||
metadata match {
|
|
||||||
case s.ModelMetadata.ModelMetadataV1(s.ModelMetadataV1(version, calibratedLanguage)) =>
|
|
||||||
Some(TweetModelMetadata(version, calibratedLanguage))
|
|
||||||
case _ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def toThrift(metadata: TweetModelMetadata): s.ModelMetadata = {
|
|
||||||
s.ModelMetadata.ModelMetadataV1(
|
|
||||||
s.ModelMetadataV1(metadata.version, metadata.calibratedLanguage))
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,360 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
import com.twitter.spam.rtf.thriftscala.SafetyLabelSource
|
|
||||||
import com.twitter.spam.rtf.{thriftscala => s}
|
|
||||||
import com.twitter.util.Time
|
|
||||||
import com.twitter.visibility.util.NamingUtils
|
|
||||||
|
|
||||||
sealed trait TweetSafetyLabelType extends SafetyLabelType with Product with Serializable {
|
|
||||||
lazy val name: String = NamingUtils.getFriendlyName(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
case class TweetSafetyLabel(
|
|
||||||
labelType: TweetSafetyLabelType,
|
|
||||||
source: Option[LabelSource] = None,
|
|
||||||
applicableUsers: Set[Long] = Set.empty,
|
|
||||||
modelMetadata: Option[TweetModelMetadata] = None,
|
|
||||||
score: Option[Double] = None,
|
|
||||||
safetyLabelSource: Option[SafetyLabelSource] = None)
|
|
||||||
|
|
||||||
object TweetSafetyLabelType extends SafetyLabelType {
|
|
||||||
|
|
||||||
val List: List[TweetSafetyLabelType] = s.SafetyLabelType.list.map(fromThrift)
|
|
||||||
|
|
||||||
val ActiveLabels: List[TweetSafetyLabelType] = List.filter { labelType =>
|
|
||||||
labelType != Unknown && labelType != Deprecated
|
|
||||||
}
|
|
||||||
|
|
||||||
private lazy val nameToValueMap: Map[String, TweetSafetyLabelType] =
|
|
||||||
List.map(l => l.name.toLowerCase -> l).toMap
|
|
||||||
def fromName(name: String): Option[TweetSafetyLabelType] = nameToValueMap.get(name.toLowerCase)
|
|
||||||
|
|
||||||
private val UnknownThriftSafetyLabelType =
|
|
||||||
s.SafetyLabelType.EnumUnknownSafetyLabelType(UnknownEnumValue)
|
|
||||||
|
|
||||||
private lazy val thriftToModelMap: Map[s.SafetyLabelType, TweetSafetyLabelType] = Map(
|
|
||||||
s.SafetyLabelType.Abusive -> Abusive,
|
|
||||||
s.SafetyLabelType.AbusiveBehavior -> AbusiveBehavior,
|
|
||||||
s.SafetyLabelType.AbusiveBehaviorInsults -> AbusiveBehaviorInsults,
|
|
||||||
s.SafetyLabelType.AbusiveBehaviorViolentThreat -> AbusiveBehaviorViolentThreat,
|
|
||||||
s.SafetyLabelType.AbusiveBehaviorMajorAbuse -> AbusiveBehaviorMajorAbuse,
|
|
||||||
s.SafetyLabelType.AbusiveHighRecall -> AbusiveHighRecall,
|
|
||||||
s.SafetyLabelType.AdsManagerDenyList -> AdsManagerDenyList,
|
|
||||||
s.SafetyLabelType.AgathaSpam -> AgathaSpam,
|
|
||||||
s.SafetyLabelType.Automation -> Automation,
|
|
||||||
s.SafetyLabelType.AutomationHighRecall -> AutomationHighRecall,
|
|
||||||
s.SafetyLabelType.Bounce -> Bounce,
|
|
||||||
s.SafetyLabelType.BounceEdits -> BounceEdits,
|
|
||||||
s.SafetyLabelType.BrandSafetyNsfaAggregate -> BrandSafetyNsfaAggregate,
|
|
||||||
s.SafetyLabelType.BrandSafetyExperimental1 -> BrandSafetyExperimental1,
|
|
||||||
s.SafetyLabelType.BrandSafetyExperimental2 -> BrandSafetyExperimental2,
|
|
||||||
s.SafetyLabelType.BrandSafetyExperimental3 -> BrandSafetyExperimental3,
|
|
||||||
s.SafetyLabelType.BrandSafetyExperimental4 -> BrandSafetyExperimental4,
|
|
||||||
s.SafetyLabelType.BystanderAbusive -> BystanderAbusive,
|
|
||||||
s.SafetyLabelType.CopypastaSpam -> CopypastaSpam,
|
|
||||||
s.SafetyLabelType.DoNotAmplify -> DoNotAmplify,
|
|
||||||
s.SafetyLabelType.DownrankSpamReply -> DownrankSpamReply,
|
|
||||||
s.SafetyLabelType.DuplicateContent -> DuplicateContent,
|
|
||||||
s.SafetyLabelType.DuplicateMention -> DuplicateMention,
|
|
||||||
s.SafetyLabelType.DynamicProductAd -> DynamicProductAd,
|
|
||||||
s.SafetyLabelType.EdiDevelopmentOnly -> EdiDevelopmentOnly,
|
|
||||||
s.SafetyLabelType.ExperimentalNudge -> ExperimentalNudge,
|
|
||||||
s.SafetyLabelType.ExperimentalSensitiveIllegal2 -> ExperimentalSensitiveIllegal2,
|
|
||||||
s.SafetyLabelType.ForEmergencyUseOnly -> ForEmergencyUseOnly,
|
|
||||||
s.SafetyLabelType.GoreAndViolence -> GoreAndViolence,
|
|
||||||
s.SafetyLabelType.GoreAndViolenceHighPrecision -> GoreAndViolenceHighPrecision,
|
|
||||||
s.SafetyLabelType.GoreAndViolenceHighRecall -> GoreAndViolenceHighRecall,
|
|
||||||
s.SafetyLabelType.GoreAndViolenceReportedHeuristics -> GoreAndViolenceReportedHeuristics,
|
|
||||||
s.SafetyLabelType.GoreAndViolenceTopicHighRecall -> GoreAndViolenceTopicHighRecall,
|
|
||||||
s.SafetyLabelType.HatefulConduct -> HatefulConduct,
|
|
||||||
s.SafetyLabelType.HatefulConductViolentThreat -> HatefulConductViolentThreat,
|
|
||||||
s.SafetyLabelType.HighCryptospamScore -> HighCryptospamScore,
|
|
||||||
s.SafetyLabelType.HighPReportedTweetScore -> HighPReportedTweetScore,
|
|
||||||
s.SafetyLabelType.HighPSpammyTweetScore -> HighPSpammyTweetScore,
|
|
||||||
s.SafetyLabelType.HighPblockScore -> HighPblockScore,
|
|
||||||
s.SafetyLabelType.HighProactiveTosScore -> HighProactiveTosScore,
|
|
||||||
s.SafetyLabelType.HighSpammyTweetContentScore -> HighSpammyTweetContentScore,
|
|
||||||
s.SafetyLabelType.HighToxicityScore -> HighToxicityScore,
|
|
||||||
s.SafetyLabelType.HighlyReportedAndMidhighToxicityScore -> HighlyReportedAndMidhighToxicityScore,
|
|
||||||
s.SafetyLabelType.HighlyReportedTweet -> HighlyReportedTweet,
|
|
||||||
s.SafetyLabelType.InterstitialDevelopmentOnly -> InterstitialDevelopmentOnly,
|
|
||||||
s.SafetyLabelType.IpiDevelopmentOnly -> IpiDevelopmentOnly,
|
|
||||||
s.SafetyLabelType.LiveLowQuality -> LiveLowQuality,
|
|
||||||
s.SafetyLabelType.LowQuality -> LowQuality,
|
|
||||||
s.SafetyLabelType.LowQualityMention -> LowQualityMention,
|
|
||||||
s.SafetyLabelType.MisinfoCivic -> MisinfoCivic,
|
|
||||||
s.SafetyLabelType.MisinfoCrisis -> MisinfoCrisis,
|
|
||||||
s.SafetyLabelType.MisinfoGeneric -> MisinfoGeneric,
|
|
||||||
s.SafetyLabelType.MisinfoMedical -> MisinfoMedical,
|
|
||||||
s.SafetyLabelType.NsfaHighPrecision -> NsfaHighPrecision,
|
|
||||||
s.SafetyLabelType.NsfaHighRecall -> NsfaHighRecall,
|
|
||||||
s.SafetyLabelType.NsfwCardImage -> NsfwCardImage,
|
|
||||||
s.SafetyLabelType.NsfwHighPrecision -> NsfwHighPrecision,
|
|
||||||
s.SafetyLabelType.NsfwHighRecall -> NsfwHighRecall,
|
|
||||||
s.SafetyLabelType.NsfwReportedHeuristics -> NsfwReportedHeuristics,
|
|
||||||
s.SafetyLabelType.NsfwText -> NsfwText,
|
|
||||||
s.SafetyLabelType.NsfwTextHighPrecision -> NsfwTextHighPrecision,
|
|
||||||
s.SafetyLabelType.NsfwVideo -> NsfwVideo,
|
|
||||||
s.SafetyLabelType.PNegMultimodalHighPrecision -> PNegMultimodalHighPrecision,
|
|
||||||
s.SafetyLabelType.PNegMultimodalHighRecall -> PNegMultimodalHighRecall,
|
|
||||||
s.SafetyLabelType.Pdna -> Pdna,
|
|
||||||
s.SafetyLabelType.RecommendationsLowQuality -> RecommendationsLowQuality,
|
|
||||||
s.SafetyLabelType.RitoActionedTweet -> RitoActionedTweet,
|
|
||||||
s.SafetyLabelType.SafetyCrisis -> SafetyCrisis,
|
|
||||||
s.SafetyLabelType.SearchBlacklist -> SearchBlacklist,
|
|
||||||
s.SafetyLabelType.SearchBlacklistHighRecall -> SearchBlacklistHighRecall,
|
|
||||||
s.SafetyLabelType.SemanticCoreMisinformation -> SemanticCoreMisinformation,
|
|
||||||
s.SafetyLabelType.SmyteSpamTweet -> SmyteSpamTweet,
|
|
||||||
s.SafetyLabelType.Spam -> Spam,
|
|
||||||
s.SafetyLabelType.SpamHighRecall -> SpamHighRecall,
|
|
||||||
s.SafetyLabelType.TombstoneDevelopmentOnly -> TombstoneDevelopmentOnly,
|
|
||||||
s.SafetyLabelType.TweetContainsHatefulConductSlurHighSeverity -> TweetContainsHatefulConductSlurHighSeverity,
|
|
||||||
s.SafetyLabelType.TweetContainsHatefulConductSlurMediumSeverity -> TweetContainsHatefulConductSlurMediumSeverity,
|
|
||||||
s.SafetyLabelType.TweetContainsHatefulConductSlurLowSeverity -> TweetContainsHatefulConductSlurLowSeverity,
|
|
||||||
s.SafetyLabelType.UnsafeUrl -> UnsafeUrl,
|
|
||||||
s.SafetyLabelType.UntrustedUrl -> UntrustedUrl,
|
|
||||||
s.SafetyLabelType.FosnrHatefulConduct -> FosnrHatefulConduct,
|
|
||||||
s.SafetyLabelType.FosnrHatefulConductLowSeveritySlur -> FosnrHatefulConductLowSeveritySlur,
|
|
||||||
s.SafetyLabelType.AbusiveHighRecall2 -> Deprecated,
|
|
||||||
s.SafetyLabelType.AbusiveHighRecall3 -> Deprecated,
|
|
||||||
s.SafetyLabelType.BrazilianPoliticalTweet -> Deprecated,
|
|
||||||
s.SafetyLabelType.BystanderAbusive2 -> Deprecated,
|
|
||||||
s.SafetyLabelType.BystanderAbusive3 -> Deprecated,
|
|
||||||
s.SafetyLabelType.DeprecatedLabel144 -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental10Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental11Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental12Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental13Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental14Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental15Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental16Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental17Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental18Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental19Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental1Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental20Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental21Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental22Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental23Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental24Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental25Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental2Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental3Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental4Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental5Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental6Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental7Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental8Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimental9Seh -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalHighHealthModelScore1 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalHighHealthModelScore10 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalHighHealthModelScore2 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalHighHealthModelScore3 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalHighHealthModelScore4 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalHighHealthModelScore5 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalHighHealthModelScore6 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalHighHealthModelScore7 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalHighHealthModelScore8 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalHighHealthModelScore9 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalSensitiveIllegal1 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalSensitiveIllegal3 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalSensitiveIllegal4 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalSensitiveIllegal5 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalSensitiveIllegal6 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalSpam1 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalSpam2 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ExperimentalSpam3 -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimentation -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimentation2 -> Deprecated,
|
|
||||||
s.SafetyLabelType.Experimentation3 -> Deprecated,
|
|
||||||
s.SafetyLabelType.HighlyReportedImage -> Deprecated,
|
|
||||||
s.SafetyLabelType.HighToxicityHoldbackModelScore -> Deprecated,
|
|
||||||
s.SafetyLabelType.LowQualityHighRecall -> Deprecated,
|
|
||||||
s.SafetyLabelType.MagicRecsDenylist -> Deprecated,
|
|
||||||
s.SafetyLabelType.MisinfoCovid19 -> Deprecated,
|
|
||||||
s.SafetyLabelType.MsnfoBrazilianElection -> Deprecated,
|
|
||||||
s.SafetyLabelType.MsnfoCovid19Vaccine -> Deprecated,
|
|
||||||
s.SafetyLabelType.MsnfoFrenchElection -> Deprecated,
|
|
||||||
s.SafetyLabelType.MsnfoPhilippineElection -> Deprecated,
|
|
||||||
s.SafetyLabelType.MsnfoUsElection -> Deprecated,
|
|
||||||
s.SafetyLabelType.NsfwNearPerfect -> Deprecated,
|
|
||||||
s.SafetyLabelType.PersonaNonGrata -> Deprecated,
|
|
||||||
s.SafetyLabelType.PMisinfoCombined15 -> Deprecated,
|
|
||||||
s.SafetyLabelType.PMisinfoCombined30 -> Deprecated,
|
|
||||||
s.SafetyLabelType.PMisinfoCombined50 -> Deprecated,
|
|
||||||
s.SafetyLabelType.PMisinfoDenylist -> Deprecated,
|
|
||||||
s.SafetyLabelType.PMisinfoPVeracityNudge -> Deprecated,
|
|
||||||
s.SafetyLabelType.PoliticalTweetExperimental1 -> Deprecated,
|
|
||||||
s.SafetyLabelType.ProactiveTosHighRecall -> Deprecated,
|
|
||||||
s.SafetyLabelType.ProactiveTosHighRecallContainsSelfHarm -> Deprecated,
|
|
||||||
s.SafetyLabelType.ProactiveTosHighRecallEncourageSelfHarm -> Deprecated,
|
|
||||||
s.SafetyLabelType.ProactiveTosHighRecallEpisodic -> Deprecated,
|
|
||||||
s.SafetyLabelType.ProactiveTosHighRecallEpisodicHatefulConduct -> Deprecated,
|
|
||||||
s.SafetyLabelType.ProactiveTosHighRecallOtherAbusePolicy -> Deprecated,
|
|
||||||
s.SafetyLabelType.ProjectLibra -> Deprecated,
|
|
||||||
s.SafetyLabelType.SearchHighVisibilityDenylist -> Deprecated,
|
|
||||||
s.SafetyLabelType.SearchHighVisibilityHighRecallDenylist -> Deprecated,
|
|
||||||
s.SafetyLabelType.Reserved162 -> Deprecated,
|
|
||||||
s.SafetyLabelType.Reserved163 -> Deprecated,
|
|
||||||
s.SafetyLabelType.Reserved164 -> Deprecated,
|
|
||||||
s.SafetyLabelType.Reserved165 -> Deprecated,
|
|
||||||
s.SafetyLabelType.Reserved166 -> Deprecated,
|
|
||||||
s.SafetyLabelType.Reserved167 -> Deprecated,
|
|
||||||
s.SafetyLabelType.Reserved168 -> Deprecated,
|
|
||||||
s.SafetyLabelType.Reserved169 -> Deprecated,
|
|
||||||
s.SafetyLabelType.Reserved170 -> Deprecated,
|
|
||||||
)
|
|
||||||
|
|
||||||
private lazy val modelToThriftMap: Map[TweetSafetyLabelType, s.SafetyLabelType] =
|
|
||||||
(for ((k, v) <- thriftToModelMap) yield (v, k)) ++ Map(
|
|
||||||
Deprecated -> s.SafetyLabelType.EnumUnknownSafetyLabelType(DeprecatedEnumValue),
|
|
||||||
)
|
|
||||||
|
|
||||||
case object Abusive extends TweetSafetyLabelType
|
|
||||||
case object AbusiveBehavior extends TweetSafetyLabelType
|
|
||||||
case object AbusiveBehaviorInsults extends TweetSafetyLabelType
|
|
||||||
case object AbusiveBehaviorViolentThreat extends TweetSafetyLabelType
|
|
||||||
case object AbusiveBehaviorMajorAbuse extends TweetSafetyLabelType
|
|
||||||
case object AbusiveHighRecall extends TweetSafetyLabelType
|
|
||||||
case object Automation extends TweetSafetyLabelType
|
|
||||||
case object AutomationHighRecall extends TweetSafetyLabelType
|
|
||||||
case object Bounce extends TweetSafetyLabelType
|
|
||||||
case object BystanderAbusive extends TweetSafetyLabelType
|
|
||||||
case object NsfaHighRecall extends TweetSafetyLabelType
|
|
||||||
case object DuplicateContent extends TweetSafetyLabelType
|
|
||||||
case object DuplicateMention extends TweetSafetyLabelType
|
|
||||||
case object GoreAndViolence extends TweetSafetyLabelType {
|
|
||||||
|
|
||||||
val DeprecatedAt: Time = Time.at("2019-09-12 00:00:00 UTC")
|
|
||||||
}
|
|
||||||
case object GoreAndViolenceHighRecall extends TweetSafetyLabelType
|
|
||||||
case object LiveLowQuality extends TweetSafetyLabelType
|
|
||||||
case object LowQuality extends TweetSafetyLabelType
|
|
||||||
case object LowQualityMention extends TweetSafetyLabelType
|
|
||||||
case object NsfwCardImage extends TweetSafetyLabelType
|
|
||||||
case object NsfwHighRecall extends TweetSafetyLabelType
|
|
||||||
case object NsfwHighPrecision extends TweetSafetyLabelType
|
|
||||||
case object NsfwVideo extends TweetSafetyLabelType
|
|
||||||
case object Pdna extends TweetSafetyLabelType
|
|
||||||
|
|
||||||
case object RecommendationsLowQuality extends TweetSafetyLabelType
|
|
||||||
case object SearchBlacklist extends TweetSafetyLabelType
|
|
||||||
case object Spam extends TweetSafetyLabelType
|
|
||||||
case object SpamHighRecall extends TweetSafetyLabelType
|
|
||||||
case object UntrustedUrl extends TweetSafetyLabelType
|
|
||||||
case object HighToxicityScore extends TweetSafetyLabelType
|
|
||||||
case object HighPblockScore extends TweetSafetyLabelType
|
|
||||||
case object SearchBlacklistHighRecall extends TweetSafetyLabelType
|
|
||||||
case object ForEmergencyUseOnly extends TweetSafetyLabelType
|
|
||||||
case object HighProactiveTosScore extends TweetSafetyLabelType
|
|
||||||
case object SafetyCrisis extends TweetSafetyLabelType
|
|
||||||
case object MisinfoCivic extends TweetSafetyLabelType
|
|
||||||
case object MisinfoCrisis extends TweetSafetyLabelType
|
|
||||||
case object MisinfoGeneric extends TweetSafetyLabelType
|
|
||||||
case object MisinfoMedical extends TweetSafetyLabelType
|
|
||||||
case object AdsManagerDenyList extends TweetSafetyLabelType
|
|
||||||
case object GoreAndViolenceHighPrecision extends TweetSafetyLabelType
|
|
||||||
case object NsfwReportedHeuristics extends TweetSafetyLabelType
|
|
||||||
case object GoreAndViolenceReportedHeuristics extends TweetSafetyLabelType
|
|
||||||
case object HighPSpammyTweetScore extends TweetSafetyLabelType
|
|
||||||
case object DoNotAmplify extends TweetSafetyLabelType
|
|
||||||
case object HighlyReportedTweet extends TweetSafetyLabelType
|
|
||||||
case object AgathaSpam extends TweetSafetyLabelType
|
|
||||||
case object SmyteSpamTweet extends TweetSafetyLabelType
|
|
||||||
case object SemanticCoreMisinformation extends TweetSafetyLabelType
|
|
||||||
case object HighPReportedTweetScore extends TweetSafetyLabelType
|
|
||||||
case object HighSpammyTweetContentScore extends TweetSafetyLabelType
|
|
||||||
case object GoreAndViolenceTopicHighRecall extends TweetSafetyLabelType
|
|
||||||
case object CopypastaSpam extends TweetSafetyLabelType
|
|
||||||
case object ExperimentalSensitiveIllegal2 extends TweetSafetyLabelType
|
|
||||||
case object DownrankSpamReply extends TweetSafetyLabelType
|
|
||||||
case object NsfwText extends TweetSafetyLabelType
|
|
||||||
case object HighlyReportedAndMidhighToxicityScore extends TweetSafetyLabelType
|
|
||||||
case object DynamicProductAd extends TweetSafetyLabelType
|
|
||||||
case object TombstoneDevelopmentOnly extends TweetSafetyLabelType
|
|
||||||
case object TweetContainsHatefulConductSlurHighSeverity extends TweetSafetyLabelType
|
|
||||||
case object TweetContainsHatefulConductSlurMediumSeverity extends TweetSafetyLabelType
|
|
||||||
case object TweetContainsHatefulConductSlurLowSeverity extends TweetSafetyLabelType
|
|
||||||
case object RitoActionedTweet extends TweetSafetyLabelType
|
|
||||||
case object ExperimentalNudge extends TweetSafetyLabelType
|
|
||||||
case object PNegMultimodalHighPrecision extends TweetSafetyLabelType
|
|
||||||
case object PNegMultimodalHighRecall extends TweetSafetyLabelType
|
|
||||||
case object BrandSafetyNsfaAggregate extends TweetSafetyLabelType
|
|
||||||
case object HighCryptospamScore extends TweetSafetyLabelType
|
|
||||||
case object IpiDevelopmentOnly extends TweetSafetyLabelType
|
|
||||||
case object BounceEdits extends TweetSafetyLabelType
|
|
||||||
case object UnsafeUrl extends TweetSafetyLabelType
|
|
||||||
case object InterstitialDevelopmentOnly extends TweetSafetyLabelType
|
|
||||||
case object EdiDevelopmentOnly extends TweetSafetyLabelType
|
|
||||||
case object NsfwTextHighPrecision extends TweetSafetyLabelType
|
|
||||||
case object HatefulConduct extends TweetSafetyLabelType
|
|
||||||
case object HatefulConductViolentThreat extends TweetSafetyLabelType
|
|
||||||
case object NsfaHighPrecision extends TweetSafetyLabelType
|
|
||||||
case object BrandSafetyExperimental1 extends TweetSafetyLabelType
|
|
||||||
case object BrandSafetyExperimental2 extends TweetSafetyLabelType
|
|
||||||
case object BrandSafetyExperimental3 extends TweetSafetyLabelType
|
|
||||||
case object BrandSafetyExperimental4 extends TweetSafetyLabelType
|
|
||||||
|
|
||||||
case object FosnrHatefulConduct extends TweetSafetyLabelType
|
|
||||||
case object FosnrHatefulConductLowSeveritySlur extends TweetSafetyLabelType
|
|
||||||
|
|
||||||
case object Deprecated extends TweetSafetyLabelType
|
|
||||||
case object Unknown extends TweetSafetyLabelType
|
|
||||||
|
|
||||||
def fromThrift(safetyLabelType: s.SafetyLabelType): TweetSafetyLabelType =
|
|
||||||
thriftToModelMap.get(safetyLabelType) match {
|
|
||||||
case Some(tweetSafetyLabelType) => tweetSafetyLabelType
|
|
||||||
case _ =>
|
|
||||||
safetyLabelType match {
|
|
||||||
case s.SafetyLabelType.EnumUnknownSafetyLabelType(DeprecatedEnumValue) => Deprecated
|
|
||||||
case _ =>
|
|
||||||
Unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def toThrift(safetyLabelType: TweetSafetyLabelType): s.SafetyLabelType = {
|
|
||||||
modelToThriftMap.getOrElse(safetyLabelType, UnknownThriftSafetyLabelType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object TweetSafetyLabel {
|
|
||||||
def fromThrift(safetyLabelValue: s.SafetyLabelValue): TweetSafetyLabel =
|
|
||||||
fromTuple(safetyLabelValue.labelType, safetyLabelValue.label)
|
|
||||||
|
|
||||||
def fromTuple(
|
|
||||||
safetyLabelType: s.SafetyLabelType,
|
|
||||||
safetyLabel: s.SafetyLabel
|
|
||||||
): TweetSafetyLabel = {
|
|
||||||
TweetSafetyLabel(
|
|
||||||
labelType = TweetSafetyLabelType.fromThrift(safetyLabelType),
|
|
||||||
source = safetyLabel.source.flatMap(LabelSource.fromString),
|
|
||||||
safetyLabelSource = safetyLabel.safetyLabelSource,
|
|
||||||
applicableUsers = safetyLabel.applicableUsers
|
|
||||||
.map { perspectivalUsers =>
|
|
||||||
(perspectivalUsers map {
|
|
||||||
_.userId
|
|
||||||
}).toSet
|
|
||||||
}.getOrElse(Set.empty),
|
|
||||||
score = safetyLabel.score,
|
|
||||||
modelMetadata = safetyLabel.modelMetadata.flatMap(TweetModelMetadata.fromThrift)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
def toThrift(tweetSafetyLabel: TweetSafetyLabel): s.SafetyLabelValue = {
|
|
||||||
s.SafetyLabelValue(
|
|
||||||
labelType = TweetSafetyLabelType.toThrift(tweetSafetyLabel.labelType),
|
|
||||||
label = s.SafetyLabel(
|
|
||||||
applicableUsers = if (tweetSafetyLabel.applicableUsers.nonEmpty) {
|
|
||||||
Some(tweetSafetyLabel.applicableUsers.toSeq.map {
|
|
||||||
s.PerspectivalUser(_)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
source = tweetSafetyLabel.source.map(_.name),
|
|
||||||
score = tweetSafetyLabel.score,
|
|
||||||
modelMetadata = tweetSafetyLabel.modelMetadata.map(TweetModelMetadata.toThrift)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,16 +0,0 @@
|
|||||||
package com.twitter.visibility.models
|
|
||||||
|
|
||||||
trait UnitOfDiversion {
|
|
||||||
|
|
||||||
def apply: (String, Any)
|
|
||||||
}
|
|
||||||
|
|
||||||
object UnitOfDiversion {
|
|
||||||
case class ConversationId(conversationId: Long) extends UnitOfDiversion {
|
|
||||||
override def apply: (String, Any) = ("conversation_id", conversationId)
|
|
||||||
}
|
|
||||||
|
|
||||||
case class TweetId(tweetId: Long) extends UnitOfDiversion {
|
|
||||||
override def apply: (String, Any) = ("tweet_id", tweetId)
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user