Skip to content

Commit

Permalink
Fix and add more API
Browse files Browse the repository at this point in the history
  • Loading branch information
zvyap committed May 11, 2023
1 parent d5c09ab commit a7aca0b
Show file tree
Hide file tree
Showing 13 changed files with 698 additions and 37 deletions.
37 changes: 24 additions & 13 deletions src/main/java/com/zvyap/hoyoapi/APIEnvironment.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@

public enum APIEnvironment {
OVERSEA(AccountAPIConstant.builder()
.userGameRoleEndpoint("https://api-account-os.hoyoverse.com/binding/api/getUserGameRolesByLtoken")
.userAccountInfoEndpoint("https://api-account-os.hoyolab.com/auth/api/getUserAccountInfoByLToken")
.forumFullUserInfoEndpoint("https://bbs-api-os.hoyolab.com/community/user/wapi/getUserFullInfo")
.userGameRoleEndpoint("https://api-account-os.hoyoverse.com/binding/api/getUserGameRolesByLtoken")
.userAccountInfoEndpoint("https://api-account-os.hoyolab.com/auth/api/getUserAccountInfoByLToken")
.forumFullUserInfoEndpoint("https://bbs-api-os.hoyolab.com/community/user/wapi/getUserFullInfo")
.gamesDetailsEndpoint("https://bbs-api-os.hoyolab.com/community/painter/wapi/circle/info")
.build(),
GameAPIConstant.builder()
.gameType(GameType.GENSHIN_IMPACT)
.name("Genshin Impact")
.apiUrl("https://hk4e-api-os.hoyoverse.com")
.dailyCheckInApiEndpoint("https://hk4e-api-os.hoyoverse.com/event/sol")
.dailyCheckInActId("e202102251931481")
.gameBiz("hk4e_global")
.build(),
GameAPIConstant.builder()
.name("Honkai Impact 3rd")
.gameType(GameType.HONKAI_IMPACT_3RD)
.apiUrl("https://sg-public-api.hoyolab.com")
.dailyCheckInApiEndpoint("https://sg-public-api.hoyolab.com/event/mani")
Expand All @@ -25,50 +28,58 @@ public enum APIEnvironment {
.build(),
GameAPIConstant.builder()
.gameType(GameType.HONKAI_STAR_RAIL)
.name("Honkai: Star Rail")
.apiUrl("https://sg-public-api.hoyolab.com")
.dailyCheckInApiEndpoint("https://sg-public-api.hoyolab.com/event/luna/os")
.dailyCheckInActId("e202303301540311")
.gameBiz("hkrpg_global")
.build(),
GameAPIConstant.builder()
.gameType(GameType.TEARS_OF_THEMIS)
.name("Tears of Themis")
.apiUrl("https://sg-public-api.hoyolab.com")
.dailyCheckInApiEndpoint("https://sg-public-api.hoyolab.com/event/luna/os")
.dailyCheckInActId("e202202281857121")
.gameBiz("nxx_global")
.build()),
//Zenless Zone Zero id - 8

CHINA(AccountAPIConstant.builder()
.userGameRoleEndpoint("https://api-takumi.mihoyo.com/binding/api/getUserGameRolesByLtoken")
.userGameRoleEndpoint("https://api-takumi.mihoyo.com/auth/api/getUserAccountInfoByLToken")
.forumFullUserInfoEndpoint("https://bbs-api.miyoushe.com/user/wapi/getUserFullInfo")
.userGameRoleEndpoint("https://api-takumi.mihoyo.com/binding/api/getUserGameRolesByLtoken")
.userGameRoleEndpoint("https://api-takumi.mihoyo.com/auth/api/getUserAccountInfoByLToken")
.forumFullUserInfoEndpoint("https://bbs-api.miyoushe.com/user/wapi/getUserFullInfo")
.navigatorsEndpoint("https://bbs-api.miyoushe.com/misc/wapi/getUniversalNavigators")
.build(),
GameAPIConstant.builder()
.gameType(GameType.GENSHIN_IMPACT)
.name("原神")
.apiUrl("https://api-takumi.mihoyo.com")
.dailyCheckInApiEndpoint("https://api-takumi.mihoyo.com/event/bbs_sign_reward/")
.dailyCheckInApiEndpoint("https://api-takumi.mihoyo.com/event/bbs_sign_reward")
.dailyCheckInActId("e202009291139501")
.gameBiz("hk4e_cn")
.build(),
GameAPIConstant.builder()
.gameType(GameType.HONKAI_IMPACT_3RD)
.name("崩坏3")
.apiUrl("")
.dailyCheckInApiEndpoint("")
.dailyCheckInActId("")
.dailyCheckInApiEndpoint("https://api-takumi.mihoyo.com/event/luna")
.dailyCheckInActId("e202207181446311")
.gameBiz("bh3_cn")
.build(),
GameAPIConstant.builder()
.gameType(GameType.HONKAI_STAR_RAIL)
.name("崩坏:星穹铁道")
.apiUrl("")
.dailyCheckInApiEndpoint("")
.dailyCheckInActId("")
.dailyCheckInApiEndpoint("https://api-takumi.mihoyo.com/event/luna")
.dailyCheckInActId("e202304121516551")
.gameBiz("hkrpg_cn")
.build(),
GameAPIConstant.builder()
.gameType(GameType.TEARS_OF_THEMIS)
.name("未定事件簿")
.apiUrl("")
.dailyCheckInApiEndpoint("")
.dailyCheckInActId("")
.dailyCheckInApiEndpoint("https://api-takumi.mihoyo.com/event/luna")
.dailyCheckInActId("e202202251749321")
.gameBiz("nxx_cn")
.build());

Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/zvyap/hoyoapi/AccountAPIConstant.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.zvyap.hoyoapi;

import com.zvyap.hoyoapi.annotation.OnlyAPI;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@Builder(access = AccessLevel.PACKAGE)
@NotNull
Expand All @@ -13,4 +15,12 @@ public class AccountAPIConstant {
String userAccountInfoEndpoint;

String forumFullUserInfoEndpoint;

@Nullable
@OnlyAPI(env = APIEnvironment.OVERSEA)
String gamesDetailsEndpoint;

@Nullable
@OnlyAPI(env = APIEnvironment.CHINA)
String navigatorsEndpoint;
}
1 change: 1 addition & 0 deletions src/main/java/com/zvyap/hoyoapi/GameAPIConstant.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
public class GameAPIConstant {

private GameType gameType;
private String name;
private String apiUrl;

private String dailyCheckInApiEndpoint;
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/com/zvyap/hoyoapi/GameType.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
package com.zvyap.hoyoapi;

public enum GameType {
GENSHIN_IMPACT, HONKAI_IMPACT_3RD, HONKAI_STAR_RAIL, TEARS_OF_THEMIS;
GENSHIN_IMPACT(2),
HONKAI_IMPACT_3RD(1),
HONKAI_STAR_RAIL(6),
TEARS_OF_THEMIS(4),
ZENLESS_ZONE_ZERO(8); //unimplemented yet

private final int gameId;

GameType(int gameId) {
this.gameId = gameId;
}

public int getGameId() {
return gameId;
}
}
58 changes: 54 additions & 4 deletions src/main/java/com/zvyap/hoyoapi/HoyoverseAPI.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.zvyap.hoyoapi;

import com.zvyap.hoyoapi.annotation.OnlyAPI;
import com.zvyap.hoyoapi.exception.HoyoverseAPIException;
import com.zvyap.hoyoapi.exception.HoyoverseLoginFailedException;
import com.zvyap.hoyoapi.exception.HoyoverseRequestFailedException;
Expand All @@ -8,10 +9,8 @@
import com.zvyap.hoyoapi.http.ContentType;
import com.zvyap.hoyoapi.http.HttpMethod;
import com.zvyap.hoyoapi.http.JsonBodyHandler;
import com.zvyap.hoyoapi.response.HoyoAPIResponse;
import com.zvyap.hoyoapi.response.HoyoGetForumFullUserResponse;
import com.zvyap.hoyoapi.response.HoyoGetUserAccountInfoResponse;
import com.zvyap.hoyoapi.response.HoyoGetUserGameRolesResponse;
import com.zvyap.hoyoapi.http.JsonPublisher;
import com.zvyap.hoyoapi.response.*;
import com.zvyap.hoyoapi.util.Utils;
import lombok.Getter;
import lombok.extern.java.Log;
Expand All @@ -31,6 +30,7 @@
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -259,6 +259,56 @@ public HoyoGetUserAccountInfoResponse getAccountInfo(HoyoToken token) {
JsonBodyHandler.of(HoyoGetUserAccountInfoResponse.class)).body().get();
}

/**
* Get game basic details, such as: icon, name(follow {@link APILocale})
*
* @return A list of GameDetails
*/
@NotNull
@OnlyAPI(env = APIEnvironment.OVERSEA)
public List<HoyoGamesDetailsResponse.GameDetails> getGameDetails() {
return makeRequest(null,
buildRequest(URI.create(APIEnvironment.OVERSEA.getAccountAPIConstant().getGamesDetailsEndpoint()),
HttpMethod.GET, ContentType.HOYO_API,
"x-rpc-client_type", "4",
"x-rpc-language", lang),
JsonBodyHandler.of(HoyoGamesDetailsResponse.class)).body().get().getData().getGameList();
}

/**
* Get game basic details, such as: icon, name(follow {@link APILocale})
*
* @param type The GameType for filter game you want
* @return GameDetails
*/
@NotNull
@OnlyAPI(env = APIEnvironment.OVERSEA)
public HoyoGamesDetailsResponse.GameDetails getGameDetails(GameType type) {
for (HoyoGamesDetailsResponse.GameDetails details : getGameDetails()) {
if (details.getId().equals(String.valueOf(type.getGameId()))) {
return details;
}
}
return null; //Should not be happened if game already released
}

/**
* Get Miyoushe tools navigators
*
* @param type The GameType for filter game you want
* @return GameDetails
*/
@NotNull
@OnlyAPI(env = APIEnvironment.CHINA)
public MYSNavigatorsResponse.Data getNavigators(GameType type) {
return makeRequest(null,
buildRequest(URI.create(APIEnvironment.CHINA.getAccountAPIConstant().getNavigatorsEndpoint()),
HttpMethod.POST, ContentType.HOYO_API,
JsonPublisher.of(Map.of("game_id", type.getGameId(), "types", List.of(4, 5))),
"x-rpc-client_type", "4"),
JsonBodyHandler.of(MYSNavigatorsResponse.class)).body().get().getData();
}

@NotNull
public HttpRequest.Builder buildRequest(@NotNull URI uri, @NotNull HttpMethod method, @NotNull ContentType contentType, String... headers) {
return buildRequest(uri, method, contentType, null, headers);
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/com/zvyap/hoyoapi/annotation/OnlyAPI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.zvyap.hoyoapi.annotation;

import com.zvyap.hoyoapi.APIEnvironment;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR, ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
public @interface OnlyAPI {
/**
* API only contain at which environment
*
* @return APIEnvironment
*/
APIEnvironment env();

/**
* Is this API only can run in that environment
*
* @return Boolean
*/
boolean isStrict() default false;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.zvyap.hoyoapi.feature.daily;

import com.zvyap.hoyoapi.APIEnvironment;
import com.zvyap.hoyoapi.GameAPIConstant;
import com.zvyap.hoyoapi.GameType;
import com.zvyap.hoyoapi.HoyoToken;
Expand All @@ -16,6 +17,7 @@
import com.zvyap.hoyoapi.response.HoyoDailyCheckInRewardResponse;
import com.zvyap.hoyoapi.response.HoyoDailyCheckInSignResponse;
import com.zvyap.hoyoapi.util.HttpUtils;
import com.zvyap.hoyoapi.util.Utils;
import org.jetbrains.annotations.NotNull;

import java.net.http.HttpResponse;
Expand All @@ -33,41 +35,61 @@ public DailyCheckInFeature(HoyoverseAPI api) {
private <T> T fetchDailyEndpoint(HoyoToken token, HttpMethod method, GameType type, String endpoint, Class<T> responseClass) {
GameAPIConstant constant = api.getEnvironment().getAPIConstant(type);
Map<String, String> data = Map.of("act_id", constant.getDailyCheckInActId(), "lang", api.getLang());
HttpResponse<Supplier<T>> response = api.makeRequest(token,
HttpResponse<Supplier<T>> response = api.makeRequest(token,
api.buildRequest(
HttpUtils.createURI(constant.getDailyCheckInApiEndpoint(), endpoint, data),
method,
ContentType.HOYO_API,
method == HttpMethod.POST ? JsonPublisher.of(data) : null,
"Accept-Language", "en-US,en;q=0.5",
"Origin", "https://webstatic-sea.mihoyo.com", "Connection", "keep-alive",
"Referer", "https://webstatic-sea.mihoyo.com/ys/event/signin-sea/index.html?act_id={config[\"ACT_ID\"]}&lang=en-us",
"Cache-Control", "max-age=0"),
headers()),
new JsonBodyHandler<>(responseClass));

if(response.statusCode() != 200) {
if (response.statusCode() != 200) {
throw new HoyoverseHttpRequestException(response.statusCode());
}

T body = response.body().get();
if(body instanceof HoyoAPIResponse) {
if (body instanceof HoyoAPIResponse) {
HoyoAPIResponse r = (HoyoAPIResponse) body;
switch (r.getRetcode()) {
case -5003:
throw new AlreadyCheckInException(r.getRetcode(), r.getMessage());
case -10002:
throw new GameProfileNotFoundException(r.getRetcode(),r.getMessage());
throw new GameProfileNotFoundException(r.getRetcode(), r.getMessage());
}
}

return body;
}

private String[] headers() {
if (api.getEnvironment() == APIEnvironment.OVERSEA) {
return new String[]{"Accept-Language", "en-US,en;q=0.5",
"Origin", "https://webstatic-sea.mihoyo.com", "Connection", "keep-alive",
"Referer", "https://webstatic-sea.mihoyo.com/ys/event/signin-sea/index.html?act_id={config[\"ACT_ID\"]}&lang=en-us",
"Cache-Control", "max-age=0"};
} else {
return new String[]{
"Origin", "https://webstatic.mihoyo.com",
"X_Requested_With", "com.mihoyo.hyperion",
"Sec_Fetch_Site", "same-site",
"Sec_Fetch_Mode", "cors",
"Sec_Fetch_Dest", "empty",
"Accept_Encoding", "gzip,deflate",

"x-rpc-client_type", "5",
"Referer", "https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?bbs_auth_required=true&act_id=e202009291139501&utm_source=bbs&utm_medium=mys&utm_campaign=icon",
"x-rpc-app_version", Utils.MYS_VERSION,
"DS", Utils.getDS()
};
}
}

/**
* Get daily information of the account, contains day checked in, day missing etc
* Check out - {@link HoyoDailyCheckInInfoResponse}
*
* @param type GameType
* @param type GameType
* @param token HoyoToken
* @return The information of daily status
*/
Expand All @@ -78,7 +100,7 @@ public HoyoDailyCheckInInfoResponse getDailyInfo(@NotNull GameType type, @NotNul
/**
* Check in daily
*
* @param type GameType
* @param type GameType
* @param token HoyoToken
* @return The response of official API
*/
Expand Down
8 changes: 6 additions & 2 deletions src/main/java/com/zvyap/hoyoapi/response/HoyoAPIResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ public class HoyoAPIResponse {
@JsonProperty("retcode")
protected int retcode;

public String getMessage(){
public String getMessage() {
return message;
}

public int getRetcode(){
public int getRetcode() {
return retcode;
}

public boolean isSuccess() {
return retcode >= 0;
}
}
Loading

0 comments on commit a7aca0b

Please sign in to comment.