package chatty.util.api;
import chatty.Helper;
import chatty.util.api.CommunitiesManager.Community;
import chatty.util.api.CommunitiesManager.CommunityListener;
import chatty.util.api.CommunitiesManager.CommunityPutListener;
import chatty.util.api.UserIDs.UserIdResult;
import java.util.*;
import java.util.logging.Logger;
/**
* Handles TwitchApi requests and responses.
*
* @author tduva
*/
public class TwitchApi {
private final static Logger LOGGER = Logger.getLogger(TwitchApi.class.getName());
/**
* How long the Emoticons can be cached in a file after they are updated
* from the API.
*/
public static final int CACHED_EMOTICONS_EXPIRE_AFTER = 60*60*24;
public static final int TOKEN_CHECK_DELAY = 600;
public enum RequestResultCode {
ACCESS_DENIED, SUCCESS, FAILED, NOT_FOUND, RUNNING_COMMERCIAL,
INVALID_CHANNEL, INVALID_STREAM_STATUS, UNKNOWN
}
private final TwitchApiResultListener resultListener;
protected final StreamInfoManager streamInfoManager;
protected final EmoticonManager emoticonManager;
protected final CheerEmoticonManager cheersManager;
protected final CheerEmoticonManager2 cheersManager2;
protected final FollowerManager followerManager;
protected final FollowerManager subscriberManager;
protected final BadgeManager badgeManager;
protected final UserIDs userIDs;
protected final ChannelInfoManager channelInfoManager;
protected final CommunitiesManager communitiesManager;
private volatile Long tokenLastChecked = Long.valueOf(0);
private volatile String defaultToken;
protected final Requests requests;
public TwitchApi(TwitchApiResultListener apiResultListener,
StreamInfoListener streamInfoListener) {
this.resultListener = apiResultListener;
this.streamInfoManager = new StreamInfoManager(this, streamInfoListener);
emoticonManager = new EmoticonManager(apiResultListener);
cheersManager = new CheerEmoticonManager(apiResultListener);
cheersManager2 = new CheerEmoticonManager2(this, resultListener);
followerManager = new FollowerManager(Follower.Type.FOLLOWER, this, resultListener);
subscriberManager = new FollowerManager(Follower.Type.SUBSCRIBER, this, resultListener);
badgeManager = new BadgeManager(this);
requests = new Requests(this, resultListener);
channelInfoManager = new ChannelInfoManager(this, resultListener);
userIDs = new UserIDs(this);
communitiesManager = new CommunitiesManager(this);
getCommunityTop(r -> {});
}
//=================
// Chat / Emoticons
//=================
public void requestCheerEmoticons(boolean forcedUpdate) {
if (forcedUpdate || !cheersManager.load(false)) {
requests.requestCheerEmoticons(forcedUpdate);
}
}
public void requestEmoticons(boolean forcedUpdate) {
if (forcedUpdate || !emoticonManager.load(false)) {
requests.requestEmoticons(forcedUpdate);
}
}
public void getGlobalBadges(boolean forceRefresh) {
badgeManager.requestGlobalBadges(forceRefresh);
}
public void getRoomBadges(String room, boolean forceRefresh) {
badgeManager.requestBadges(room, forceRefresh);
}
public void getCheers(String room, boolean forceRefresh) {
cheersManager2.request(room, forceRefresh);
}
//====================
// Channel Information
//====================
public void getChannelInfo(String stream) {
getChannelInfo(stream, null);
}
public void getChannelInfo(String stream, String id) {
if (id != null) {
requests.getChannelInfo(id, stream);
} else {
userIDs.getUserIDsAsap(r -> {
if (r.hasError()) {
resultListener.receivedChannelInfo(stream, null, TwitchApi.RequestResultCode.FAILED);
} else {
requests.getChannelInfo(r.getId(stream), stream);
}
}, stream);
}
}
public void getChatInfo(String stream) {
if (Helper.validateStreamStrict(stream)) {
requests.requestChatInfo(stream);
}
}
public void getFollowers(String stream) {
followerManager.request(stream);
}
public void getSubscribers(String stream) {
subscriberManager.request(stream);
}
/**
* Get ChannelInfo, if cached. This will *not* request missing ChannelInfo.
*
* @param stream
* @return
*/
public ChannelInfo getOnlyCachedChannelInfo(String stream) {
return channelInfoManager.getOnlyCachedChannelInfo(stream);
}
public ChannelInfo getCachedChannelInfo(String stream) {
return getCachedChannelInfo(stream, null);
}
/**
* Get ChannelInfo, which may be cached. This will request immediately if
* not cached.
*
* @param stream
* @param id
* @return
*/
public ChannelInfo getCachedChannelInfo(String stream, String id) {
return channelInfoManager.getCachedChannelInfo(stream, id);
}
//===================
// Stream Information
//===================
/**
* Get StreamInfo for the given stream. Always returns a StreamInfo object,
* which may however be marked as invalid if the stream is no valid stream
* name or does not exist or data hasn't been requested yet.
*
* The first request per stream is always invalid, because the info has
* to be requested from the server first. Further request return a cached
* version of the StreamInfo, until the info is marked as expired.
*
* @param stream
* @return The StreamInfo object
*/
public StreamInfo getStreamInfo(String stream, Set<String> streams) {
if (streams == null) {
streams = new HashSet<>();
}
return streamInfoManager.getStreamInfo(stream, streams);
}
public void getFollowedStreams(String token) {
streamInfoManager.getFollowedStreams(token);
}
public void manualRefreshStreams() {
streamInfoManager.manualRefresh();
}
//======
// Token
//======
public void setToken(String token) {
this.defaultToken = token;
}
/**
* When access was denied when doing an authenticated request. Check the
* token maybe subsequently.
*/
protected void accessDenied() {
resultListener.accessDenied();
}
/**
* Verifies token, but only once the delay has passed. For automatic checks
* instead of manual ones.
*
* @param token
*/
public void checkToken(String token) {
if (token != null && !token.isEmpty() &&
(System.currentTimeMillis() - tokenLastChecked) / 1000 > TOKEN_CHECK_DELAY) {
LOGGER.info("Checking token..");
tokenLastChecked = Long.valueOf(System.currentTimeMillis());
requests.verifyToken(token);
}
}
public void verifyToken(String token) {
requests.verifyToken(token);
}
public String getToken() {
return defaultToken;
}
//=========
// User IDs
//=========
public void setUserId(String userName, String userId) {
userIDs.setUserId(userName, userId);
}
public void waitForUserId(UserIDs.UserIdResultListener listener, String... names) {
userIDs.waitForUserIDs(listener, names);
}
/**
* @see UserIDs#getUserIDsAsap(chatty.util.api.UserIDs.UserIdResultListener, java.lang.String...)
*
* @param listener
* @param names
*/
public void getUserIdAsap(UserIDs.UserIdResultListener listener, String... names) {
userIDs.getUserIDsAsap(listener, names);
}
public void requestUserId(String... names) {
userIDs.requestUserIDs(names);
}
public void getUserIDsTest2(String usernames) {
UserIdResult result = userIDs.requestUserIDs(usernames.split(" "));
if (result != null) {
System.out.println(result.getValidIDs());
}
}
public void getUserIDsTest3(String usernames) {
userIDs.waitForUserIDs(r -> {
System.out.println(r.getValidIDs());
}, usernames.split(" "));
}
//================
// User Management
//================
public void followChannel(String user, String target) {
userIDs.getUserIDsAsap(r -> {
if (r.hasError()) {
resultListener.followResult("Couldn't follow '" + target + "' ("+r.getError()+")");
} else {
requests.followChannel(r.getId(user), r.getId(target), target, defaultToken);
}
}, user, target);
}
public void unfollowChannel(String user, String target) {
userIDs.getUserIDsAsap(r -> {
if (r.hasError()) {
resultListener.followResult("Couldn't unfollow '" + target + "' ("+r.getError()+")");
} else {
requests.unfollowChannel(r.getId(user), r.getId(target), target, defaultToken);
}
}, user, target);
}
//===================
// Admin / Moderation
//===================
public void putChannelInfo(ChannelInfo info) {
userIDs.getUserIDsAsap(r -> {
if (r.hasError()) {
resultListener.putChannelInfoResult(TwitchApi.RequestResultCode.FAILED);
} else {
requests.putChannelInfo(r.getId(info.name), info, defaultToken);
}
}, info.name);
}
public void performGameSearch(String search, GameSearchListener listener) {
requests.getGameSearch(search, listener);
}
public interface GameSearchListener {
public void result(Collection<String> result);
}
/**
* Gets Community by id, which is guaranteed to contain description/rules.
* Cached only, so it doesn't requests it if it's not in the cache.
*
* @param id
* @return
*/
public Community getCachedCommunityInfo(String id) {
return communitiesManager.getCachedByIdWithInfo(id);
}
/**
* Gets community by id, which may or may not contain description/rules.
* Requests it if not cached (but not again in case of error).
*
* @param id The community id
* @param listener
*/
public void getCommunity(String id, CommunityListener listener) {
if (id == null || id.isEmpty()) {
listener.received(null, "No community id.");
} else {
communitiesManager.getById(id, listener);
}
}
/**
* Requests the current top 100 communities.
*
* @param listener
*/
public void getCommunityTop(CommunitiesManager.CommunityTopListener listener) {
requests.getCommunitiesTop(listener);
}
/**
* Requests the community by name.
*
* @param name The name of the community
* @param listener
*/
public void getCommunityByName(String name, CommunityListener listener) {
if (name != null && !name.isEmpty()) {
requests.getCommunityByName(name, listener);
} else {
listener.received(null, "Invalid community name");
}
}
public void getCommunityById(String id, CommunityListener listener) {
requests.getCommunityById(id, listener);
}
public void setCommunity(String channelName, String communityId, CommunityPutListener listener) {
userIDs.getUserIDsAsap(r -> {
if (r.hasError()) {
listener.result("Failed getting user id");
} else {
String channelId = r.getId(channelName);
if (communityId != null) {
requests.setCommunity(channelId, communityId, defaultToken, listener);
} else {
requests.removeCommunity(channelId, defaultToken, listener);
}
}
}, channelName);
}
public void getCommunityForChannel(String channelName, CommunityListener listener) {
userIDs.getUserIDsAsap(r -> {
if (r.hasError()) {
listener.received(null, "Error resolving id.");
} else {
requests.getCommunity(r.getId(channelName), listener);
}
}, channelName);
}
public void runCommercial(String stream, int length) {
userIDs.getUserIDsAsap(r -> {
if (r.hasError()) {
resultListener.runCommercialResult(stream, "Failed to resolve id", RequestResultCode.UNKNOWN);
} else {
requests.runCommercial(r.getId(stream), stream, defaultToken, length);
}
}, stream);
}
public void autoMod(String action, String msgId) {
requests.autoMod(action, msgId, defaultToken);
}
}