All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.twitch4j.TwitchClientPoolBuilder Maven / Gradle / Ivy

There is a newer version: 1.23.0
Show newest version
package com.github.twitch4j;

import com.github.philippheuer.credentialmanager.CredentialManager;
import com.github.philippheuer.credentialmanager.CredentialManagerBuilder;
import com.github.philippheuer.credentialmanager.domain.OAuth2Credential;
import com.github.philippheuer.events4j.api.service.IEventHandler;
import com.github.philippheuer.events4j.core.EventManager;
import com.github.philippheuer.events4j.simple.SimpleEventHandler;
import com.github.twitch4j.auth.TwitchAuth;
import com.github.twitch4j.auth.providers.TwitchIdentityProvider;
import com.github.twitch4j.chat.ITwitchChat;
import com.github.twitch4j.chat.TwitchChat;
import com.github.twitch4j.chat.TwitchChatBuilder;
import com.github.twitch4j.chat.TwitchChatConnectionPool;
import com.github.twitch4j.chat.util.TwitchChatLimitHelper;
import com.github.twitch4j.client.websocket.WebsocketConnectionConfig;
import com.github.twitch4j.common.annotation.Unofficial;
import com.github.twitch4j.common.config.ProxyConfig;
import com.github.twitch4j.common.config.Twitch4JGlobal;
import com.github.twitch4j.common.util.BucketUtils;
import com.github.twitch4j.common.util.EventManagerUtils;
import com.github.twitch4j.common.util.ThreadUtils;
import com.github.twitch4j.eventsub.socket.IEventSubSocket;
import com.github.twitch4j.eventsub.socket.TwitchEventSocket;
import com.github.twitch4j.eventsub.socket.TwitchEventSocketPool;
import com.github.twitch4j.extensions.TwitchExtensions;
import com.github.twitch4j.extensions.TwitchExtensionsBuilder;
import com.github.twitch4j.graphql.TwitchGraphQL;
import com.github.twitch4j.graphql.TwitchGraphQLBuilder;
import com.github.twitch4j.helix.TwitchHelix;
import com.github.twitch4j.helix.TwitchHelixBuilder;
import com.github.twitch4j.internal.ChatCommandHelixForwarder;
import com.github.twitch4j.kraken.TwitchKraken;
import com.github.twitch4j.kraken.TwitchKrakenBuilder;
import com.github.twitch4j.pubsub.ITwitchPubSub;
import com.github.twitch4j.pubsub.TwitchPubSub;
import com.github.twitch4j.pubsub.TwitchPubSubBuilder;
import com.github.twitch4j.pubsub.TwitchPubSubConnectionPool;
import com.github.twitch4j.tmi.TwitchMessagingInterface;
import com.github.twitch4j.tmi.TwitchMessagingInterfaceBuilder;
import feign.Logger;
import io.github.bucket4j.Bandwidth;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.With;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.jetbrains.annotations.NotNull;

import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;

/**
 * Builder to get a TwitchClientPool Instance by provided various options, to provide the user with a lot of customizable options.
 */
@Slf4j
@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class TwitchClientPoolBuilder {

    /**
     * Client Id
     */
    @With
    private String clientId = Twitch4JGlobal.clientId;

    /**
     * Client Secret
     */
    @With
    private String clientSecret = Twitch4JGlobal.clientSecret;

    /**
     * User Agent
     */
    @With
    private String userAgent = Twitch4JGlobal.userAgent;

    /**
     * HTTP Request Queue Size
     */
    @With
    private Integer requestQueueSize = -1;

    /**
     * Redirect Url
     */
    @With
    private String redirectUrl = "http://localhost";

    /**
     * Default Timeout
     */
    @With
    private Integer timeout = 5000;

    /**
     * Enabled: Extensions
     *
     * @see Twitch Shutdown Announcement
     * @deprecated the Extensions API traditionally uses the decommissioned Kraken API. While the module now forwards calls to Helix, please migrate to using Helix directly as this module will be removed in the future.
     */
    @With
    @Deprecated
    private Boolean enableExtensions = false;

    /**
     * Enabled: Helix
     */
    @With
    private Boolean enableHelix = false;

    /**
     * Enabled: Kraken
     *
     * @deprecated Twitch shutdown the Kraken API in 2022.
     */
    @With
    @Deprecated
    private Boolean enableKraken = false;

    /**
     * Enabled: TMI
     *
     * @deprecated All of the {@link TwitchMessagingInterfaceBuilder} endpoints have been (or will be) decommissioned by Twitch.
     */
    @With
    @Deprecated
    private Boolean enableTMI = false;

    /**
     * Enabled: Chat
     */
    @With
    private Boolean enableChat = false;

    /**
     * Enabled: EventSub over WebSocket
     */
    @With
    private Boolean enableEventSocket = false;

    @With
    private Boolean enableChatPool = false;

    @With
    private int maxChannelsPerChatInstance = 100;

    /**
     * User IDs of Bot Owners for applying {@link com.github.twitch4j.common.enums.CommandPermission#OWNER}
     */
    @Setter
    @Accessors(chain = true)
    protected Collection botOwnerIds = new HashSet<>();

    /**
     * IRC Command Handlers
     */
    protected Set commandPrefixes = new HashSet<>();

    /**
     * Enabled: PubSub
     */
    @With
    private Boolean enablePubSub = false;

    @With
    private Boolean enablePubSubPool = false;

    @With
    private int maxTopicsPerPubSubInstance = 50;

    /**
     * Enabled: GraphQL
     * 

* This is an unofficial API that is not intended for third-party use. Use at your own risk. Methods could change or stop working at any time. */ @With @Unofficial private Boolean enableGraphQL = false; /** * Chat Account */ @With private OAuth2Credential chatAccount; /** * EventManager */ @With private EventManager eventManager = null; /** * EventManager */ @With private Class defaultEventHandler = SimpleEventHandler.class; /** * Size of the ChatQueue */ @With protected Integer chatQueueSize = 200; /** * Custom RateLimit for ChatMessages */ @With protected Bandwidth chatRateLimit = TwitchChatLimitHelper.USER_MESSAGE_LIMIT; /** * Custom RateLimit for Whispers * * @deprecated Twitch will decommission whispers over IRC on February 18, 2023; * please migrate to TwitchHelix#sendWhisper and TwitchLimitRegistry#setLimit */ @With @Deprecated protected Bandwidth[] chatWhisperLimit = TwitchChatLimitHelper.USER_WHISPER_LIMIT.toArray(new Bandwidth[2]); /** * Custom RateLimit for JOIN/PART */ @With protected Bandwidth chatJoinLimit = TwitchChatLimitHelper.USER_JOIN_LIMIT; /** * Custom RateLimit for AUTH */ @With protected Bandwidth chatAuthLimit = TwitchChatLimitHelper.USER_AUTH_LIMIT; /** * Custom RateLimit for Messages per Channel *

* For example, this can restrict messages per channel at 100/30 (for a verified bot that has a global 7500/30 message limit). */ @With protected Bandwidth chatChannelMessageLimit = BucketUtils.simple(100, Duration.ofSeconds(30), "per-channel-limit"); /** * Wait time for taking items off chat queue in milliseconds. Default recommended */ @With private long chatQueueTimeout = 1000L; /** * The maximum number of retries to make for joining each channel, with exponential backoff. * Set to zero or a negative value to disable this feature. */ @With private int chatMaxJoinRetries = 7; /** * Sets the default server used for chat *

* Defaults to TwitchChat.TWITCH_WEB_SOCKET_SERVER, you can use TwitchChat.FDGT_TEST_SOCKET_SERVER for testing */ @With private String chatServer = TwitchChat.TWITCH_WEB_SOCKET_SERVER; /** * The base URL to use for Helix API calls. *

* Can be adjusted to point to the Twitch CLI Mock API, for example. * * @see TwitchHelixBuilder#OFFICIAL_BASE_URL * @see TwitchHelixBuilder#MOCK_BASE_URL */ @With private String helixBaseUrl = TwitchHelixBuilder.OFFICIAL_BASE_URL; /** * CredentialManager */ @With @NotNull private CredentialManager credentialManager = CredentialManagerBuilder.builder().build(); /** * Scheduler Thread Pool Executor */ @With private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = null; /** * Millisecond Delay for Client Helper Thread */ @With private long helperThreadDelay = 10000L; /** * Default Auth Token for API Requests */ @With private OAuth2Credential defaultAuthToken = null; /** * Default First-Party OAuth Token for GraphQL calls */ @With private OAuth2Credential defaultFirstPartyToken = null; /** * Proxy Configuration */ @With private ProxyConfig proxyConfig = null; /** * you can overwrite the feign loglevel to print the full requests + responses if needed */ @With private Logger.Level feignLogLevel = Logger.Level.NONE; /** * WebSocket RFC Ping Period in ms (0 = disabled) */ @With private int wsPingPeriod = 15_000; /** * Websocket Close Delay in ms (0 = minimum) * * @see WebsocketConnectionConfig#closeDelay() */ @With private int wsCloseDelay = 1_000; /** * Whether chat commands should be executed via the Helix API, if possible. *

* Must have {@link #withEnableHelix(Boolean)} set to true. * Must have {@link #withEnableChat(Boolean)} or {@link #withEnableChatPool(Boolean)} set to true. *

* Must have {@link #withChatAccount(OAuth2Credential)} or {@link #withDefaultAuthToken(OAuth2Credential)} specified. */ @With private boolean chatCommandsViaHelix = Instant.now().isAfter(ChatCommandHelixForwarder.ENABLE_AFTER); /** * The per-channel rate limit at which chat commands forwarded to helix should be executed. *

* This prevents commands to a single channel from consuming the entire helix rate limit bucket. * As such, this can be restricted further or loosened, depending on how many channels the bot serves. *

* This has no effect unless {@link #isChatCommandsViaHelix()} is true. *

* Users should migrate to manual helix calls (with whatever throttling they desire) instead. */ @With(onMethod_ = { @Deprecated }) private Bandwidth forwardedChatCommandHelixLimitPerChannel = TwitchChatLimitHelper.MOD_MESSAGE_LIMIT; /** * With a Bot Owner's User ID * * @param userId the user id * @return TwitchClientPoolBuilder */ public TwitchClientPoolBuilder withBotOwnerId(String userId) { this.botOwnerIds.add(userId); return this; } /** * With a CommandTrigger * * @param commandTrigger Command Trigger (Prefix) * @return TwitchClientPoolBuilder */ public TwitchClientPoolBuilder withCommandTrigger(String commandTrigger) { this.commandPrefixes.add(commandTrigger); return this; } /** * With a base thread delay for API calls by {@link TwitchClientHelper} *

* Note: the method name has been a misnomer as it has always set the delay rather than a rate. * One can change the rate at any time via {@link TwitchClientHelper#setThreadRate(long)}. * * @param helperThreadDelay TwitchClientHelper Base Thread Delay * @return TwitchClientPoolBuilder * @deprecated in favor of withHelperThreadDelay */ @Deprecated public TwitchClientPoolBuilder withHelperThreadRate(long helperThreadDelay) { return this.withHelperThreadDelay(helperThreadDelay); } /** * Initialize the builder * * @return Twitch Client Pool Builder */ public static TwitchClientPoolBuilder builder() { return new TwitchClientPoolBuilder(); } /** * Initialize * * @return {@link TwitchClientPool} initialized class */ @SuppressWarnings("deprecation") public TwitchClientPool build() { log.debug("TwitchClientPool: Initializing ErrorTracking ..."); // Module: Auth (registers Twitch Identity Providers) TwitchAuth.registerIdentityProvider(credentialManager, getClientId(), getClientSecret(), redirectUrl, TwitchHelixBuilder.MOCK_BASE_URL.equals(helixBaseUrl)); // Initialize/Check EventManager eventManager = EventManagerUtils.validateOrInitializeEventManager(eventManager, defaultEventHandler); // Determinate required threadPool size int poolSize = TwitchClientHelper.REQUIRED_THREAD_COUNT; if (enableChat || enableChatPool) poolSize = poolSize + TwitchChat.REQUIRED_THREAD_COUNT; if (enablePubSub || enablePubSubPool) poolSize = poolSize + TwitchPubSub.REQUIRED_THREAD_COUNT; if (enableEventSocket) poolSize = poolSize + TwitchEventSocket.REQUIRED_THREAD_COUNT; // check a provided threadPool or initialize a default one if (scheduledThreadPoolExecutor != null && scheduledThreadPoolExecutor.getCorePoolSize() < poolSize) { log.warn("Twitch4J requires a scheduledThreadPoolExecutor with at least {} threads to be fully functional! Some features may not work as expected.", poolSize); } if (scheduledThreadPoolExecutor == null) { if (enableChatPool || enablePubSubPool) poolSize = Math.max(poolSize, Runtime.getRuntime().availableProcessors()); scheduledThreadPoolExecutor = ThreadUtils.getDefaultScheduledThreadPoolExecutor("twitch4j-" + RandomStringUtils.random(4, true, true), poolSize); } // Module: Extensions TwitchExtensions extensions = null; if (this.enableExtensions) { extensions = TwitchExtensionsBuilder.builder() .withClientId(clientId) .withClientSecret(clientSecret) .withUserAgent(userAgent) .withRequestQueueSize(requestQueueSize) .withTimeout(timeout) .withProxyConfig(proxyConfig) .withLogLevel(feignLogLevel) .build(); } // Module: Helix TwitchHelix helix; if (this.enableHelix) { helix = TwitchHelixBuilder.builder() .withBaseUrl(helixBaseUrl) .withClientId(clientId) .withClientSecret(clientSecret) .withRedirectUrl(redirectUrl) .withCredentialManager(credentialManager) .withUserAgent(userAgent) .withDefaultAuthToken(defaultAuthToken) .withRequestQueueSize(requestQueueSize) .withTimeout(timeout) .withProxyConfig(proxyConfig) .withLogLevel(feignLogLevel) .build(); } else { helix = null; } // Module: Kraken TwitchKraken kraken = null; if (this.enableKraken) { kraken = TwitchKrakenBuilder.builder() .withClientId(clientId) .withClientSecret(clientSecret) .withUserAgent(userAgent) .withRequestQueueSize(requestQueueSize) .withTimeout(timeout) .withProxyConfig(proxyConfig) .withLogLevel(feignLogLevel) .build(); } // Module: TMI TwitchMessagingInterface tmi = null; if (this.enableTMI) { tmi = TwitchMessagingInterfaceBuilder.builder() .withClientId(clientId) .withClientSecret(clientSecret) .withUserAgent(userAgent) .withRequestQueueSize(requestQueueSize) .withTimeout(timeout) .withProxyConfig(proxyConfig) .withLogLevel(feignLogLevel) .build(); } // Module: Chat ITwitchChat chat = null; ChatCommandHelixForwarder forwarder = chatCommandsViaHelix && enableHelix && (enableChat || enableChatPool) && (chatAccount != null || defaultAuthToken != null) ? new ChatCommandHelixForwarder( helix, chatAccount != null ? chatAccount : defaultAuthToken, credentialManager.getIdentityProviderByName(TwitchIdentityProvider.PROVIDER_NAME, TwitchIdentityProvider.class).orElse(null), scheduledThreadPoolExecutor, forwardedChatCommandHelixLimitPerChannel ) : null; if (this.enableChatPool) { chat = TwitchChatConnectionPool.builder() .eventManager(eventManager) .chatAccount(() -> chatAccount) .chatRateLimit(chatRateLimit) .whisperRateLimit(chatWhisperLimit) .joinRateLimit(chatJoinLimit) .authRateLimit(chatAuthLimit) .perChannelRateLimit(chatChannelMessageLimit) .executor(() -> scheduledThreadPoolExecutor) .proxyConfig(() -> proxyConfig) .maxSubscriptionsPerConnection(maxChannelsPerChatInstance) .advancedConfiguration(builder -> builder.withCredentialManager(credentialManager) .withChatQueueSize(chatQueueSize) .withBaseUrl(chatServer) .withChatQueueTimeout(chatQueueTimeout) .withMaxJoinRetries(chatMaxJoinRetries) .withOutboundCommandFilter(forwarder) .withWsPingPeriod(wsPingPeriod) .withWsCloseDelay(wsCloseDelay) .setCommandPrefixes(commandPrefixes) .setBotOwnerIds(botOwnerIds) ) .build(); } else if (this.enableChat) { chat = TwitchChatBuilder.builder() .withEventManager(eventManager) .withCredentialManager(credentialManager) .withChatAccount(chatAccount) .withChatQueueSize(chatQueueSize) .withChatRateLimit(chatRateLimit) .withWhisperRateLimit(chatWhisperLimit) .withJoinRateLimit(chatJoinLimit) .withAuthRateLimit(chatAuthLimit) .withPerChannelRateLimit(chatChannelMessageLimit) .withScheduledThreadPoolExecutor(scheduledThreadPoolExecutor) .withBaseUrl(chatServer) .withChatQueueTimeout(chatQueueTimeout) .withProxyConfig(proxyConfig) .withMaxJoinRetries(chatMaxJoinRetries) .withOutboundCommandFilter(forwarder) .setBotOwnerIds(botOwnerIds) .setCommandPrefixes(commandPrefixes) .withWsPingPeriod(wsPingPeriod) .withWsCloseDelay(wsCloseDelay) .build(); } // Module: EventSub over WebSocket IEventSubSocket eventSocket = null; if (this.enableEventSocket) { eventSocket = TwitchEventSocketPool.builder() .eventManager(eventManager) .executor(scheduledThreadPoolExecutor) .fallbackToken(defaultAuthToken) .helix(helix) .identityProvider( credentialManager.getIdentityProviderByName(TwitchIdentityProvider.PROVIDER_NAME, TwitchIdentityProvider.class) .orElseGet(() -> new TwitchIdentityProvider(clientId, clientSecret, redirectUrl)) ) .advancedConfiguration(builder -> builder.proxyConfig(() -> proxyConfig) ) .build(); } // Module: PubSub ITwitchPubSub pubSub = null; if (this.enablePubSubPool) { pubSub = TwitchPubSubConnectionPool.builder() .eventManager(eventManager) .executor(() -> scheduledThreadPoolExecutor) .proxyConfig(() -> proxyConfig) .advancedConfiguration(builder -> builder.withWsPingPeriod(wsPingPeriod) .withWsCloseDelay(wsCloseDelay) .setBotOwnerIds(botOwnerIds) ) .build(); } else if (this.enablePubSub) { pubSub = TwitchPubSubBuilder.builder() .withEventManager(eventManager) .withScheduledThreadPoolExecutor(scheduledThreadPoolExecutor) .withProxyConfig(proxyConfig) .withWsPingPeriod(wsPingPeriod) .withWsCloseDelay(wsCloseDelay) .setBotOwnerIds(botOwnerIds) .build(); } // Module: GraphQL TwitchGraphQL graphql = null; if (this.enableGraphQL) { graphql = TwitchGraphQLBuilder.builder() .withEventManager(eventManager) .withDefaultFirstPartyToken(defaultFirstPartyToken) .withProxyConfig(proxyConfig) .withTimeout(timeout) .build(); } // Module: TwitchClientPool & ClientHelper final TwitchClientPool client = new TwitchClientPool(eventManager, extensions, helix, kraken, tmi, chat, pubSub, graphql, eventSocket, scheduledThreadPoolExecutor, credentialManager, defaultAuthToken); client.getClientHelper().setThreadDelay(helperThreadDelay); // Return new Client Instance return client; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy