Skip to content

Commit

Permalink
feat: add helix raids endpoints in open beta (#587)
Browse files Browse the repository at this point in the history
  • Loading branch information
iProdigy committed Jun 18, 2022
1 parent e5981b1 commit 2cd17f5
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 0 deletions.
Expand Up @@ -24,6 +24,7 @@ public enum TwitchScopes {
HELIX_CHANNEL_POLLS_READ("channel:read:polls"),
HELIX_CHANNEL_PREDICTIONS_MANAGE("channel:manage:predictions"),
HELIX_CHANNEL_PREDICTIONS_READ("channel:read:predictions"),
HELIX_CHANNEL_RAIDS_MANAGE("channel:manage:raids"),
HELIX_CHANNEL_REDEMPTIONS_READ("channel:read:redemptions"),
HELIX_CHANNEL_STREAM_KEY_READ("channel:read:stream_key"),
HELIX_CHANNEL_SUBSCRIPTIONS_READ("channel:read:subscriptions"),
Expand Down
Expand Up @@ -1626,6 +1626,43 @@ HystrixCommand<PredictionsList> endPrediction(
Prediction prediction
);

/**
* Raid another channel by sending the broadcaster’s viewers to the targeted channel.
* <p>
* This endpoint triggers a 90-second cooldown before the raid is executed (or the broadcaster can manually press "Raid now").
*
* @param authToken Broadcaster user access token with the channel:manage:raids scope.
* @param fromBroadcasterId The ID of the broadcaster that’s sending the raiding party.
* @param toBroadcasterId The ID of the broadcaster to raid.
* @return RaidRequestList
* @see com.github.twitch4j.auth.domain.TwitchScopes#HELIX_CHANNEL_RAIDS_MANAGE
*/
@Unofficial // currently in open beta
@RequestLine("POST /raids?from_broadcaster_id={from_broadcaster_id}&to_broadcaster_id={to_broadcaster_id}")
@Headers("Authorization: Bearer {token}")
HystrixCommand<RaidRequestList> startRaid(
@Param("token") String authToken,
@Param("from_broadcaster_id") String fromBroadcasterId,
@Param("to_broadcaster_id") String toBroadcasterId
);

/**
* Cancel a pending raid.
* <p>
* You can cancel a raid at any point up until the broadcaster clicks Raid Now in the Twitch UX or the 90 seconds countdown expires.
*
* @param authToken Broadcaster user access token with the channel:manage:raids scope.
* @param broadcasterId The ID of the broadcaster that sent the raiding party.
* @return 204 No Content upon a successful raid cancel call
*/
@Unofficial // currently in open beta
@RequestLine("DELETE /raids?broadcaster_id={broadcaster_id}")
@Headers("Authorization: Bearer {token}")
HystrixCommand<Void> cancelRaid(
@Param("token") String authToken,
@Param("broadcaster_id") String broadcasterId
);

/**
* Gets games sorted by number of current viewers on Twitch, most popular first.
* Using user-token or app-token to increase rate limits.
Expand Down
Expand Up @@ -28,6 +28,11 @@ public class BanUsersResult {
*/
private String userId;

/**
* The UTC date and time (in RFC3999 format) when the ban was created.
*/
private Instant createdAt;

/**
* The UTC date and time (in RFC3339 format) that the timeout will end.
* Is null if the user was banned instead of put in a timeout.
Expand Down
Expand Up @@ -34,6 +34,11 @@ public class BannedUser {
*/
private Instant expiresAt;

/**
* The UTC date and time (in RFC3999 format) when the ban was created.
*/
private Instant createdAt;

/**
* The reason for the ban if provided by the moderator.
*/
Expand Down
@@ -0,0 +1,27 @@
package com.github.twitch4j.helix.domain;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;
import lombok.experimental.Accessors;

import java.time.Instant;

@Data
@Setter(AccessLevel.PRIVATE)
public class RaidRequest {

/**
* The UTC date and time, in RFC3339 format, when the raid request was created.
*/
private Instant createdAt;

/**
* Whether the channel being raided contains mature content.
*/
@Accessors(fluent = true)
@JsonProperty("is_mature")
private Boolean isMature;

}
@@ -0,0 +1,18 @@
package com.github.twitch4j.helix.domain;

import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;

import java.util.List;

@Data
@Setter(AccessLevel.PRIVATE)
public class RaidRequestList {

/**
* A list of raids. The list will contain the single raid that this request created.
*/
private List<RaidRequest> data;

}
Expand Up @@ -99,6 +99,17 @@ private Response delegatedExecute(Request request, Request.Options options) thro
return executeAgainstBucket(clipBucket, () -> client.execute(request, options));
}

// Raids API: startRaid and cancelRaid have a stricter bucket that applies per channel id
if (templatePath.endsWith("/raids")) {
// Obtain the channel id
String param = request.httpMethod() == Request.HttpMethod.POST ? "from_broadcaster_id" : "broadcaster_id";
String channelId = request.requestTemplate().queries().getOrDefault(param, Collections.emptyList()).iterator().next();

// Conform to endpoint-specific bucket
Bucket raidBucket = rateLimitTracker.getRaidsBucket(channelId);
return executeAgainstBucket(raidBucket, () -> client.execute(request, options));
}

// no endpoint-specific rate limiting was needed; simply perform network request now
return client.execute(request, options);
}
Expand Down
Expand Up @@ -19,6 +19,11 @@
@SuppressWarnings("ConstantConditions")
public final class TwitchHelixRateLimitTracker {

/**
* Officially documented rate limit for {@link com.github.twitch4j.helix.TwitchHelix#startRaid(String, String, String)} and {@link com.github.twitch4j.helix.TwitchHelix#cancelRaid(String, String)}
*/
private static final Bandwidth RAIDS_BANDWIDTH = Bandwidth.simple(10, Duration.ofMinutes(10));

/**
* Empirically determined rate limit on helix bans and unbans, per channel
*/
Expand All @@ -44,6 +49,13 @@ public final class TwitchHelixRateLimitTracker {
.expireAfterAccess(1, TimeUnit.MINUTES)
.build();

/**
* Raids API: start and cancel raid rate limit buckets per channel
*/
private final Cache<String, Bucket> raidsByChannelId = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build();

/**
* Moderation API: ban and unban rate limit buckets per channel
*/
Expand Down Expand Up @@ -98,6 +110,11 @@ String getPrimaryBucketKey(@NotNull OAuth2Credential credential) {
* Secondary (endpoint-specific) rate limit buckets
*/

@NotNull
Bucket getRaidsBucket(@NotNull String channelId) {
return raidsByChannelId.get(channelId, k -> BucketUtils.createBucket(RAIDS_BANDWIDTH));
}

@NotNull
@Unofficial
Bucket getModerationBucket(@NotNull String channelId) {
Expand Down

0 comments on commit 2cd17f5

Please sign in to comment.