Skip to content

Commit 7396fb8

Browse files
committed
Add automatic following and unfollowing
Inspired by https://github.com/minerj101/FriendConnect
1 parent 784418f commit 7396fb8

6 files changed

Lines changed: 154 additions & 82 deletions

File tree

bootstrap/standalone/src/main/java/com/rtm516/mcxboxbroadcast/bootstrap/standalone/StandaloneConfig.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,26 @@
44
import com.rtm516.mcxboxbroadcast.core.SessionInfo;
55

66
public class StandaloneConfig {
7-
@JsonProperty("update-interval")
8-
public int updateInterval;
9-
@JsonProperty("query-server")
10-
public boolean queryServer;
11-
@JsonProperty("session-info")
12-
public SessionInfo sessionInfo;
7+
@JsonProperty("session")
8+
public SessionConfig sessionConfig;
9+
@JsonProperty("friend-sync")
10+
public FriendSyncConfig friendSyncConfig;
11+
12+
public static class SessionConfig {
13+
@JsonProperty("update-interval")
14+
public int updateInterval;
15+
@JsonProperty("query-server")
16+
public boolean queryServer;
17+
@JsonProperty("session-info")
18+
public SessionInfo sessionInfo;
19+
}
20+
21+
public static class FriendSyncConfig {
22+
@JsonProperty("update-interval")
23+
public int updateInterval;
24+
@JsonProperty("auto-follow")
25+
public boolean autoFollow;
26+
@JsonProperty("auto-unfollow")
27+
public boolean autoUnfollow;
28+
}
1329
}

bootstrap/standalone/src/main/java/com/rtm516/mcxboxbroadcast/bootstrap/standalone/StandaloneMain.java

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,15 @@
99
import com.rtm516.mcxboxbroadcast.core.SessionInfo;
1010
import com.rtm516.mcxboxbroadcast.core.SessionManager;
1111
import com.rtm516.mcxboxbroadcast.core.exceptions.SessionUpdateException;
12+
import com.rtm516.mcxboxbroadcast.core.exceptions.XboxFriendsException;
13+
import com.rtm516.mcxboxbroadcast.core.models.FollowerResponse;
1214
import org.java_websocket.util.NamedThreadFactory;
1315

1416
import java.io.File;
1517
import java.io.FileOutputStream;
16-
import java.io.FileWriter;
1718
import java.io.IOException;
1819
import java.io.InputStream;
1920
import java.net.InetSocketAddress;
20-
import java.nio.file.FileSystem;
21-
import java.nio.file.Files;
2221
import java.util.concurrent.ExecutionException;
2322
import java.util.concurrent.Executors;
2423
import java.util.concurrent.ScheduledExecutorService;
@@ -31,7 +30,7 @@ public class StandaloneMain {
3130
public static void main(String[] args) throws Exception {
3231
logger = new GenericLoggerImpl();
3332

34-
ScheduledExecutorService scheduledThread = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Scheduled Thread"));
33+
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2, new NamedThreadFactory("Scheduled Thread"));
3534

3635
SessionManager sessionManager = new SessionManager("./cache", logger);
3736

@@ -64,7 +63,7 @@ public static void main(String[] args) throws Exception {
6463
return;
6564
}
6665

67-
SessionInfo sessionInfo = config.sessionInfo;
66+
SessionInfo sessionInfo = config.sessionConfig.sessionInfo;
6867

6968
// Sync the session info from the server if needed
7069
updateSessionInfo(sessionInfo);
@@ -75,7 +74,7 @@ public static void main(String[] args) throws Exception {
7574

7675
logger.info("Created session!");
7776

78-
scheduledThread.scheduleWithFixedDelay(() -> {
77+
scheduledThreadPool.scheduleWithFixedDelay(() -> {
7978
updateSessionInfo(sessionInfo);
8079

8180
try {
@@ -84,11 +83,33 @@ public static void main(String[] args) throws Exception {
8483
} catch (SessionUpdateException e) {
8584
logger.error("Failed to update session", e);
8685
}
87-
}, config.updateInterval, config.updateInterval, TimeUnit.SECONDS);
86+
}, config.sessionConfig.updateInterval, config.sessionConfig.updateInterval, TimeUnit.SECONDS);
87+
88+
if (config.friendSyncConfig.autoFollow || config.friendSyncConfig.autoUnfollow) {
89+
scheduledThreadPool.scheduleWithFixedDelay(() -> {
90+
try {
91+
for (FollowerResponse.Person person : sessionManager.getXboxFriends(true, true)) {
92+
// Follow the person back
93+
if (config.friendSyncConfig.autoFollow && person.isFollowingCaller && !person.isFollowedByCaller) {
94+
logger.info("Added " + person.displayName + " (" + person.xuid + ") as a friend");
95+
sessionManager.addXboxFriend(person.xuid);
96+
}
97+
98+
// Unfollow the person
99+
if (config.friendSyncConfig.autoUnfollow && !person.isFollowingCaller && person.isFollowedByCaller) {
100+
logger.info("Removed " + person.displayName + " (" + person.xuid + ") as a friend");
101+
sessionManager.removeXboxFriend(person.xuid);
102+
}
103+
}
104+
} catch (XboxFriendsException e) {
105+
logger.error("Failed to sync friends", e);
106+
}
107+
}, config.friendSyncConfig.updateInterval, config.friendSyncConfig.updateInterval, TimeUnit.SECONDS);
108+
}
88109
}
89110

90111
private static void updateSessionInfo(SessionInfo sessionInfo) {
91-
if (config.queryServer) {
112+
if (config.sessionConfig.queryServer) {
92113
BedrockClient client = null;
93114
try {
94115
InetSocketAddress bindAddress = new InetSocketAddress("0.0.0.0", 0);
Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
1-
# The amount of time in seconds to update session information and
2-
# sync other data
3-
update-interval: 30
1+
# Core session settings
2+
session:
3+
# The amount of time in seconds to update session information
4+
update-interval: 30
45

5-
# Should we query the bedrock server to sync the session information
6-
query-server: true
6+
# Should we query the bedrock server to sync the session information
7+
query-server: true
78

8-
# The data to broadcast over xbox live
9-
session-info:
10-
host-name: "Geyser Test Server"
11-
world-name: "\u00A7aGeyserMC \u00A7bDemo \u00A7f& \u00A7cTest \u00A7fServer"
12-
version: 1.19.2
13-
protocol: 527
14-
players: 0
15-
max-players: 20
16-
ip: 51.210.124.95
17-
port: 19132
9+
# The data to broadcast over xbox live, this is the default if querying is enabled
10+
session-info:
11+
host-name: "Geyser Test Server"
12+
world-name: "\u00A7aGeyserMC \u00A7bDemo \u00A7f& \u00A7cTest \u00A7fServer"
13+
version: 1.19.2
14+
protocol: 527
15+
players: 0
16+
max-players: 20
17+
ip: 51.210.124.95
18+
port: 19132
19+
20+
# Friend/follower list sync settings
21+
friend-sync:
22+
# The amount of time in seconds to check for follower changes
23+
# This can be no lower than 10 due to xbox rate limits
24+
update-interval: 10
25+
26+
# Should we automatically follow people that follow us
27+
auto-follow: true
28+
29+
# Should we automatically unfollow people that no longer follow us
30+
auto-unfollow: true

core/src/main/java/com/rtm516/mcxboxbroadcast/core/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ public class Constants {
2525

2626
public static final String PEOPLE = "https://social.xboxlive.com/users/me/people";
2727
public static final URI FOLLOWERS = URI.create("https://peoplehub.xboxlive.com/users/me/people/followers");
28+
public static final URI SOCIAL = URI.create("https://peoplehub.xboxlive.com/users/me/people/social");
2829
}

core/src/main/java/com/rtm516/mcxboxbroadcast/core/SessionManager.java

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -262,14 +262,14 @@ public void checkConnection() {
262262
*
263263
* @param includeFollowing Include users that are following us and not full friends
264264
* @param includeFollowedBy Include users that we are following and not full friends
265-
* @return A list of XUIDs of your friends
265+
* @return A list of {@link FollowerResponse.Person} of your friends
266266
* @throws XboxFriendsException If there was an error getting friends from Xbox Live
267267
*/
268-
public List<String> getXboxFriends(boolean includeFollowing, boolean includeFollowedBy) throws XboxFriendsException {
269-
List<String> xuids = new ArrayList<>();
268+
public List<FollowerResponse.Person> getXboxFriends(boolean includeFollowing, boolean includeFollowedBy) throws XboxFriendsException {
269+
List<FollowerResponse.Person> people = new ArrayList<>();
270270

271-
// Create the request for getting the users friends
272-
HttpRequest xboxFollowerRequest = HttpRequest.newBuilder()
271+
// Create the request for getting the people following us and friends
272+
HttpRequest xboxFollowersRequest = HttpRequest.newBuilder()
273273
.uri(Constants.FOLLOWERS)
274274
.header("Authorization", getTokenHeader())
275275
.header("x-xbl-contract-version", "5")
@@ -279,29 +279,55 @@ public List<String> getXboxFriends(boolean includeFollowing, boolean includeFoll
279279

280280
try {
281281
// Get the list of friends from the api
282-
FollowerResponse xboxFollowerResponse = Constants.OBJECT_MAPPER.readValue(httpClient.send(xboxFollowerRequest, HttpResponse.BodyHandlers.ofString()).body(), FollowerResponse.class);
282+
FollowerResponse xboxFollowerResponse = Constants.OBJECT_MAPPER.readValue(httpClient.send(xboxFollowersRequest, HttpResponse.BodyHandlers.ofString()).body(), FollowerResponse.class);
283283

284284
// Parse through the returned list to make sure we are friends and
285285
// add them to the list to return
286286
for (FollowerResponse.Person person : xboxFollowerResponse.people) {
287287
// Make sure they are full friends
288288
if ((person.isFollowedByCaller && person.isFollowingCaller)
289-
|| (includeFollowing && person.isFollowingCaller)
290-
|| (includeFollowedBy && person.isFollowedByCaller)) {
291-
xuids.add(person.xuid);
289+
|| (includeFollowing && person.isFollowingCaller)) {
290+
people.add(person);
292291
}
293292
}
294293
} catch (IOException | InterruptedException e) {
295294
throw new XboxFriendsException(e.getMessage());
296295
}
297296

298-
return xuids;
297+
if (includeFollowedBy) {
298+
// Create the request for getting the people we are following and friends
299+
HttpRequest xboxSocialRequest = HttpRequest.newBuilder()
300+
.uri(Constants.SOCIAL)
301+
.header("Authorization", getTokenHeader())
302+
.header("x-xbl-contract-version", "5")
303+
.header("accept-language", "en-GB")
304+
.GET()
305+
.build();
306+
307+
try {
308+
// Get the list of people we are following from the api
309+
FollowerResponse xboxSocialResponse = Constants.OBJECT_MAPPER.readValue(httpClient.send(xboxSocialRequest, HttpResponse.BodyHandlers.ofString()).body(), FollowerResponse.class);
310+
311+
// Parse through the returned list to make sure we are following them and
312+
// add them to the list to return
313+
for (FollowerResponse.Person person : xboxSocialResponse.people) {
314+
// Make sure we are following them
315+
if (person.isFollowedByCaller) {
316+
people.add(person);
317+
}
318+
}
319+
} catch (IOException | InterruptedException e) {
320+
throw new XboxFriendsException(e.getMessage());
321+
}
322+
}
323+
324+
return people;
299325
}
300326

301327
/**
302328
* @see #getXboxFriends(boolean, boolean)
303329
*/
304-
public List<String> getXboxFriends() throws XboxFriendsException {
330+
public List<FollowerResponse.Person> getXboxFriends() throws XboxFriendsException {
305331
return getXboxFriends(false, false);
306332
}
307333

core/src/main/java/com/rtm516/mcxboxbroadcast/core/models/FollowerResponse.java

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,69 +4,64 @@
44
import java.util.List;
55

66
public class FollowerResponse {
7-
public Object accountLinkDetails;
8-
public Object friendFinderState;
97
public List<Person> people;
108
public Object recommendationSummary;
11-
12-
public static class Follower {
13-
public Date followedDateTime;
14-
public String text;
15-
}
9+
public Object friendFinderState;
10+
public Object accountLinkDetails;
1611

1712
public static class LinkedAccount {
18-
public Object deeplink;
19-
public String displayName;
20-
public boolean isFamilyFriendly;
2113
public String networkName;
14+
public String displayName;
2215
public boolean showOnProfile;
16+
public boolean isFamilyFriendly;
17+
public Object deeplink;
2318
}
2419

2520
public static class Person {
26-
public Object addedDateTimeUtc;
27-
public Object avatar;
28-
public Object broadcast;
29-
public String colorTheme;
30-
public Object communityManagerTitles;
31-
public Object detail;
21+
public String xuid;
22+
public boolean isFavorite;
23+
public boolean isFollowingCaller;
24+
public boolean isFollowedByCaller;
25+
public boolean isIdentityShared;
26+
public Date addedDateTimeUtc;
3227
public String displayName;
28+
public String realName;
3329
public String displayPicRaw;
34-
public Follower follower;
35-
public String gamerScore;
30+
public String showUserAsAvatar;
3631
public String gamertag;
32+
public String gamerScore;
33+
public String modernGamertag;
34+
public String modernGamertagSuffix;
35+
public String uniqueModernGamertag;
36+
public String xboxOneRep;
37+
public String presenceState;
38+
public String presenceText;
39+
public Object presenceDevices;
3740
public boolean isBroadcasting;
3841
public Object isCloaked;
39-
public boolean isFavorite;
40-
public boolean isFollowedByCaller;
41-
public boolean isFollowingCaller;
42-
public boolean isIdentityShared;
4342
public boolean isQuarantined;
4443
public boolean isXbox360Gamerpic;
4544
public Date lastSeenDateTimeUtc;
46-
public List<LinkedAccount> linkedAccounts;
47-
public String modernGamertag;
48-
public String modernGamertagSuffix;
49-
public Object multiplayerSummary;
50-
public Object preferredColor;
51-
public String preferredFlag;
52-
public List<String> preferredPlatforms;
53-
public Object presenceDetails;
54-
public Object presenceDevices;
55-
public String presenceState;
56-
public String presenceText;
57-
public Object presenceTitleIds;
58-
public String realName;
59-
public Object recentPlayer;
45+
public Object suggestion;
6046
public Object recommendation;
6147
public Object search;
62-
public String showUserAsAvatar;
63-
public Object socialManager;
64-
public Object suggestion;
6548
public Object titleHistory;
49+
public Object multiplayerSummary;
50+
public Object recentPlayer;
51+
public Object follower;
52+
public Object preferredColor;
53+
public Object presenceDetails;
6654
public Object titlePresence;
6755
public Object titleSummaries;
68-
public String uniqueModernGamertag;
69-
public String xboxOneRep;
70-
public String xuid;
56+
public Object presenceTitleIds;
57+
public Object detail;
58+
public Object communityManagerTitles;
59+
public Object socialManager;
60+
public Object broadcast;
61+
public Object avatar;
62+
public List<LinkedAccount> linkedAccounts;
63+
public String colorTheme;
64+
public String preferredFlag;
65+
public List<String> preferredPlatforms;
7166
}
7267
}

0 commit comments

Comments
 (0)