diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLinkMicArmiesEvent.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLinkMicArmiesEvent.java index 3345737..0a68ec9 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLinkMicArmiesEvent.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLinkMicArmiesEvent.java @@ -27,12 +27,12 @@ import io.github.jwdeveloper.tiktok.annotations.EventType; import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent; import io.github.jwdeveloper.tiktok.data.models.LinkMicArmy; import io.github.jwdeveloper.tiktok.data.models.Picture; +import io.github.jwdeveloper.tiktok.messages.enums.LinkMicBattleStatus; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkMicArmies; import lombok.Getter; import java.util.List; - /** * Triggered every time a battle participant receives points. Contains the current status of the battle and the army that suported the group. */ @@ -40,8 +40,10 @@ import java.util.List; @EventMeta(eventType = EventType.Message) public class TikTokLinkMicArmiesEvent extends TikTokHeaderEvent { private final Long battleId; - - private final Integer battleStatus; + /** + true if battle is finished otherwise false + */ + private final boolean finished; private final Picture picture; @@ -52,6 +54,6 @@ public class TikTokLinkMicArmiesEvent extends TikTokHeaderEvent { battleId = msg.getId(); armies = msg.getBattleItemsList().stream().map(LinkMicArmy::new).toList(); picture = Picture.map(msg.getImage()); - battleStatus = msg.getBattleStatus(); + finished = msg.getBattleStatus() == LinkMicBattleStatus.ARMY_FINISHED; } -} +} \ No newline at end of file diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLinkMicBattleEvent.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLinkMicBattleEvent.java index 62c28ad..1b55357 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLinkMicBattleEvent.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/events/TikTokLinkMicBattleEvent.java @@ -22,29 +22,50 @@ */ package io.github.jwdeveloper.tiktok.data.events; -import io.github.jwdeveloper.tiktok.annotations.EventMeta; -import io.github.jwdeveloper.tiktok.annotations.EventType; +import io.github.jwdeveloper.tiktok.annotations.*; import io.github.jwdeveloper.tiktok.data.events.common.TikTokHeaderEvent; -import io.github.jwdeveloper.tiktok.data.models.LinkMicBattleTeam; +import io.github.jwdeveloper.tiktok.data.models.battles.*; +import io.github.jwdeveloper.tiktok.messages.enums.LinkMicBattleStatus; import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkMicBattle; import lombok.Getter; -import java.util.List; +import java.util.*; /** - * Triggered every time a battle starts. + * Triggered every time a battle starts & ends */ @Getter @EventMeta(eventType = EventType.Message) -public class TikTokLinkMicBattleEvent extends TikTokHeaderEvent { - private final Long battleId; - private final List team1; - private final List team2; +public class TikTokLinkMicBattleEvent extends TikTokHeaderEvent +{ + private final Long battleId; + /** + true if battle is finished otherwise false + */ + private final boolean finished; + private final List teams; - public TikTokLinkMicBattleEvent(WebcastLinkMicBattle msg) { - super(msg.getCommon()); - battleId = msg.getId(); - team1 = msg.getTeams1List().stream().map(LinkMicBattleTeam::new).toList(); - team2 = msg.getTeams2List().stream().map(LinkMicBattleTeam::new).toList(); - } -} + public TikTokLinkMicBattleEvent(WebcastLinkMicBattle msg) { + super(msg.getCommon()); + battleId = msg.getId(); + finished = msg.getBattleStatus() == LinkMicBattleStatus.BATTLE_FINISHED; + teams = new ArrayList<>(); + if (msg.getHostTeamCount() == 2) { // 1v1 battle + teams.add(new Team1v1(msg.getHostTeam(0), msg)); + teams.add(new Team1v1(msg.getHostTeam(1), msg)); + } else { // 2v2 battle + if (isFinished()) { + teams.add(new Team2v2(msg.getHostData2V2List().stream().filter(data -> data.getTeamNumber() == 1).findFirst().orElse(null), msg)); + teams.add(new Team2v2(msg.getHostData2V2List().stream().filter(data -> data.getTeamNumber() == 2).findFirst().orElse(null), msg)); + } else { + teams.add(new Team2v2(msg.getHostTeam(0), msg.getHostTeam(1), msg)); + teams.add(new Team2v2(msg.getHostTeam(2), msg.getHostTeam(3), msg)); + } + } + + // Info: + // - msg.getDetailsList() & msg.getViewerTeamList() both only have content when battle is finished + // - msg.getDetailsCount() & msg.getViewerTeamCount() always is 2 only when battle is finished + // - msg.getHostTeamCount() always is 2 for 1v1 or 4 for 2v2 + } +} \ No newline at end of file diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/LinkMicArmy.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/LinkMicArmy.java index 0777172..c679c93 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/LinkMicArmy.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/LinkMicArmy.java @@ -46,4 +46,4 @@ public class LinkMicArmy { List Users; Integer Points; } -} +} \ No newline at end of file diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/LinkMicBattleTeam.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/LinkMicBattleTeam.java deleted file mode 100644 index 59141d8..0000000 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/LinkMicBattleTeam.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2023-2023 jwdeveloper jacekwoln@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.github.jwdeveloper.tiktok.data.models; - - -import io.github.jwdeveloper.tiktok.data.models.users.User; -import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkMicBattle; -import lombok.Value; - -import java.util.List; - -@Value -public class LinkMicBattleTeam { - Long teamId; - List users; - - public LinkMicBattleTeam(WebcastLinkMicBattle.LinkMicBattleTeam team) { - this.teamId = team.getId(); - this.users = team.getUsersList().stream().map(User::new).toList(); - } -} diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/Picture.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/Picture.java index 2f1ef99..c55a053 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/Picture.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/Picture.java @@ -44,11 +44,11 @@ public class Picture { } public static Picture map(io.github.jwdeveloper.tiktok.messages.data.Image profilePicture) { - var index = profilePicture.getUrlListCount() - 1; + var index = profilePicture.getUrlCount() - 1; if (index < 0) { return new Picture(""); } - var url = profilePicture.getUrlList(index); + var url = profilePicture.getUrl(index); return new Picture(url); } diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/battles/Team.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/battles/Team.java new file mode 100644 index 0000000..5b4e773 --- /dev/null +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/battles/Team.java @@ -0,0 +1,47 @@ +package io.github.jwdeveloper.tiktok.data.models.battles; + +public abstract class Team { + /** + * Provides a check for verifying if this team represents a 1v1 Team. + * @return true if this team is of type {@link Team1v1}, false otherwise. + */ + public boolean is1v1Team() { + return this instanceof Team1v1; + } + + /** + * Provides a check for verifying if this team represents a 1v1 Team. + * @return true if this team is of type {@link Team1v1}, false otherwise. + */ + public boolean is2v2Team() { + return this instanceof Team2v2; + } + + /** + * Convenience method to get this team as a {@link Team1v1}. If this team is of some + * other type, an {@link IllegalStateException} will result. Hence it is best to use this method + * after ensuring that this element is of the desired type by calling {@link #is1v1Team()} first. + * + * @return this team as a {@link Team1v1}. + * @throws IllegalStateException if this team is of another type. + */ + public Team1v1 getAs1v1Team() { + if (is1v1Team()) + return (Team1v1) this; + throw new IllegalStateException("Not a 1v1Team: " + this); + } + + /** + * Convenience method to get this team as a {@link Team2v2}. If this team is of some + * other type, an {@link IllegalStateException} will result. Hence it is best to use this method + * after ensuring that this element is of the desired type by calling {@link #is2v2Team()} first. + * + * @return this team as a {@link Team2v2}. + * @throws IllegalStateException if this team is of another type. + */ + public Team2v2 getAs2v2Team() { + if (is2v2Team()) + return (Team2v2) this; + throw new IllegalStateException("Not a 2v2Team: " + this); + } +} \ No newline at end of file diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/battles/Team1v1.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/battles/Team1v1.java new file mode 100644 index 0000000..647ab55 --- /dev/null +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/battles/Team1v1.java @@ -0,0 +1,26 @@ +package io.github.jwdeveloper.tiktok.data.models.battles; + +import io.github.jwdeveloper.tiktok.data.models.users.User; +import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkMicBattle; +import lombok.Getter; + +import java.util.*; + +@Getter +public class Team1v1 extends Team +{ + /** Value >= 0 when finished otherwise -1 */ + private final int totalPoints; + private final int winStreak; + private final User host; + private final List viewers; + + public Team1v1(WebcastLinkMicBattle.LinkMicBattleHost hostTeam, WebcastLinkMicBattle msg) { + long hostId = hostTeam.getId(); + this.winStreak = msg.getTeamDataList().stream().filter(data -> data.getTeamId() == hostId).map(data -> data.getData().getWinStreak()).findFirst().orElse(-1); + this.totalPoints = msg.getDetailsList().stream().filter(dets -> dets.getId() == hostId).map(dets -> dets.getSummary().getPoints()).findFirst().orElse(-1); + this.host = new User(hostTeam.getHostGroup(0).getHost(0)); + this.viewers = msg.getViewerTeamList().stream().filter(team -> team.getId() == hostId).findFirst().map(topViewers -> + topViewers.getViewerGroup(0).getViewerList().stream().map(Viewer::new).toList()).orElseGet(ArrayList::new); + } +} \ No newline at end of file diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/battles/Team2v2.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/battles/Team2v2.java new file mode 100644 index 0000000..374f579 --- /dev/null +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/battles/Team2v2.java @@ -0,0 +1,31 @@ +package io.github.jwdeveloper.tiktok.data.models.battles; + +import io.github.jwdeveloper.tiktok.data.models.users.User; +import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkMicBattle; +import lombok.Getter; + +import java.util.*; + +@Getter +public class Team2v2 extends Team { + /** Value >= 0 when finished otherwise -1 */ + private final int totalPoints; + + private final List hosts; + private final List viewers; + + public Team2v2(WebcastLinkMicBattle.LinkMicBattleHost hostTeam1, WebcastLinkMicBattle.LinkMicBattleHost hostTeam2, WebcastLinkMicBattle msg) { + this.totalPoints = -1; + this.hosts = List.of(new User(hostTeam1.getHostGroup(0).getHost(0)), new User(hostTeam2.getHostGroup(0).getHost(0))); + this.viewers = new ArrayList<>(); + } + + public Team2v2(WebcastLinkMicBattle.Host2v2Data hd, WebcastLinkMicBattle msg) { + this.totalPoints = hd.getTotalPoints(); + var host = new User(msg.getHostTeamList().stream().filter(data -> data.getId() == hd.getHostdata(0).getHostId()).findFirst().orElseThrow().getHostGroup(0).getHost(0)); + var cohost = new User(msg.getHostTeamList().stream().filter(data -> data.getId() == hd.getHostdata(1).getHostId()).findFirst().orElseThrow().getHostGroup(0).getHost(0)); + this.hosts = List.of(host, cohost); + this.viewers = msg.getViewerTeamList().stream().filter(team -> team.getId() == host.getId() || team.getId() == cohost.getId()).findFirst().map(topViewers -> + topViewers.getViewerGroup(0).getViewerList().stream().map(Viewer::new).toList()).orElseGet(ArrayList::new); + } +} \ No newline at end of file diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/battles/Viewer.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/battles/Viewer.java new file mode 100644 index 0000000..bf4b385 --- /dev/null +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/battles/Viewer.java @@ -0,0 +1,17 @@ +package io.github.jwdeveloper.tiktok.data.models.battles; + +import io.github.jwdeveloper.tiktok.data.models.Picture; +import io.github.jwdeveloper.tiktok.data.models.users.User; +import io.github.jwdeveloper.tiktok.messages.webcast.WebcastLinkMicBattle; +import lombok.Getter; + +@Getter +public class Viewer { + private final User user; + private final int points; + + public Viewer(WebcastLinkMicBattle.LinkMicBattleTopViewers.TopViewerGroup.TopViewer topViewer) { + this.user = new User(topViewer.getId(), null, topViewer.getProfileId(), Picture.map(topViewer.getImages(0))); + this.points = topViewer.getPoints(); + } +} \ No newline at end of file diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/users/User.java b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/users/User.java index 8916799..cab957b 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/users/User.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/data/models/users/User.java @@ -24,7 +24,7 @@ package io.github.jwdeveloper.tiktok.data.models.users; import io.github.jwdeveloper.tiktok.data.models.badges.Badge; import io.github.jwdeveloper.tiktok.data.models.Picture; -import io.github.jwdeveloper.tiktok.messages.webcast.WebcastEnvelopeMessage; +import io.github.jwdeveloper.tiktok.messages.webcast.*; import lombok.AccessLevel; import lombok.Getter; @@ -136,6 +136,14 @@ public class User { this.picture = picture; } + public User(long id, String name, String profileId, Picture picture) { + this(id, name, profileId, picture, 0, 0, List.of(Badge.empty())); + } + + public User(WebcastLinkMicBattle.LinkMicBattleHost.HostGroup.Host host) { + this(host.getId(), host.getName(), host.getProfileId(), Picture.map(host.getImages(0))); + } + public User(io.github.jwdeveloper.tiktok.messages.data.User user) { this(user.getId(), user.getDisplayId(), Picture.map(user.getAvatarThumb())); profileName = user.getNickname(); @@ -159,7 +167,6 @@ public class User { } } - public static User EMPTY = new User(0L, "", Picture.Empty(), @@ -209,4 +216,18 @@ public class User { 0, List.of(Badge.empty())); } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", name='" + name + "'" + + ", profileName='" + profileName + "'" + + ", picture=" + picture + + ", following=" + following + + ", followers=" + followers + + ", badges=" + badges + + ", attributes=" + attributes + + "}"; + } } \ No newline at end of file diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveRoomInfo.java b/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveRoomInfo.java index 0f624a5..b3df78c 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveRoomInfo.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/live/LiveRoomInfo.java @@ -47,7 +47,7 @@ public interface LiveRoomInfo String getRoomId(); String getHostName(); String getTitle(); - User getHostUser(); + User getHost(); List getUsersRanking(); ConnectionState getConnectionState(); } \ No newline at end of file diff --git a/API/src/main/proto/data.proto b/API/src/main/proto/data.proto index acea8e6..f82d407 100644 --- a/API/src/main/proto/data.proto +++ b/API/src/main/proto/data.proto @@ -102,7 +102,8 @@ message Text { // @Image message Image { - repeated string urlList = 1; + repeated string url = 1; + string extras = 2; bool isAnimated = 9; } diff --git a/API/src/main/proto/enums.proto b/API/src/main/proto/enums.proto index 8e132d0..4512837 100644 --- a/API/src/main/proto/enums.proto +++ b/API/src/main/proto/enums.proto @@ -35,6 +35,14 @@ enum LinkmicApplierSortSetting { LINKMIC_APPLIER_SORT_SETTING_BY_GIFT_SCORE = 1; } +enum LinkMicBattleStatus { + BATTLE_ARMY_UNKNOWN = 0; + ARMY_ONGOING = 1; + ARMY_FINISHED = 2; + BATTLE_ONGOING = 4; + BATTLE_FINISHED = 5; +} + enum HashtagNamespace { GLOBAL = 0; GAMING = 1; diff --git a/API/src/main/proto/webcast.proto b/API/src/main/proto/webcast.proto index d1fba54..4e97f26 100644 --- a/API/src/main/proto/webcast.proto +++ b/API/src/main/proto/webcast.proto @@ -521,7 +521,7 @@ message WebcastLinkMicArmies { uint64 id2 = 4; uint64 timeStamp1 = 5; uint64 timeStamp2 = 6; - int32 battleStatus = 7; // SHOULD BE AN ENUM + LinkMicBattleStatus battleStatus = 7; uint64 data1 = 8; uint64 data2 = 9; uint32 data3 = 10; @@ -574,11 +574,26 @@ message WebcastLinkMicBattle { Common common = 1; uint64 id = 2; LinkMicBattleConfig battleConfig = 3; - uint32 data2 = 4; + LinkMicBattleStatus battleStatus = 4; repeated LinkMicBattleDetails details = 5; - repeated LinkMicBattleTeam teams1 = 9; - repeated LinkMicBattleTeam teams2 = 10; + repeated LinkMicBattleTopViewers viewerTeam = 9; + repeated LinkMicBattleHost hostTeam = 10; repeated LinkMicBattleTeamData teamData = 13; + uint64 unknownData16 = 16; + repeated Host2v2Data hostData2v2 = 17; + + message Host2v2Data { + uint32 teamNumber = 1; + repeated HostData hostdata = 2; + uint32 unknownData3 = 3; + uint32 totalPoints = 4; + + message HostData { + uint64 hostId = 1; + uint32 points = 2; + string hostIdStr = 3; + } + } message LinkMicBattleConfig { uint64 id1 = 1; @@ -586,29 +601,69 @@ message WebcastLinkMicBattle { uint32 data1 = 3; uint64 id2 = 4; uint32 data2 = 5; + uint32 data3 = 6; + uint32 data4 = 8; + } + + message LinkMicBattleTeamData { + uint64 teamId = 1; + LinkMicBattleData data = 2; } message LinkMicBattleData { uint64 id = 1; uint32 data1 = 2; - uint32 data2 = 3; + uint32 winStreak = 3; uint32 data3 = 5; string url = 6; } message LinkMicBattleDetails { uint64 id = 1; - LinkMicBattleData details = 2; + LinkMicBattleDetailsSummary summary = 2; + + message LinkMicBattleDetailsSummary { + uint64 id = 1; + uint32 unknownData2 = 2; + uint32 points = 3; + } } - message LinkMicBattleTeam { + message LinkMicBattleTopViewers { uint64 id = 1; - repeated User users = 2; + repeated TopViewerGroup viewerGroup = 2; + + message TopViewerGroup { + repeated TopViewer viewer = 1; + uint32 points = 2; + string hostIdOrTeamNum = 3; // 1v1 Battle = HostId | 2v2 Battle = Team # - 1 & 2 + + message TopViewer { + uint64 id = 1; + uint32 points = 2; + string profileId = 3; + repeated Image images = 4; + string stringId = 6; + } + } } - message LinkMicBattleTeamData { - uint64 teamId = 1; - LinkMicBattleData data = 2; + message LinkMicBattleHost { + uint64 id = 1; + repeated HostGroup hostGroup = 2; + + message HostGroup { + repeated Host host = 1; + uint32 points = 2; + string hostId = 3; + + message Host { + uint64 id = 1; + string profileId = 2; + repeated Image images = 3; + string name = 4; + } + } } } @@ -792,6 +847,4 @@ message RoomVerifyMessage { string content = 3; int64 noticeType = 4; bool closeRoom = 5; -} - - +} \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokRoomInfo.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokRoomInfo.java index a2043a2..c02a7e3 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokRoomInfo.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokRoomInfo.java @@ -61,11 +61,6 @@ public class TikTokRoomInfo implements LiveRoomInfo { return connectionState == state; } - @Override - public User getHostUser() { - return host; - } - public void updateRanking(List rankingUsers) { usersRanking.clear(); usersRanking.addAll(rankingUsers);