Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add helix raids endpoints in open beta #587

Merged
merged 1 commit into from Jun 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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