This commit is contained in:
0x3C50 2022-04-28 02:52:56 +02:00
parent 4d10a1c9ce
commit c0de23593f
6 changed files with 185 additions and 23 deletions

View file

@ -43,6 +43,7 @@ import net.shadow.client.feature.command.impl.Kill;
import net.shadow.client.feature.command.impl.LinkWolf;
import net.shadow.client.feature.command.impl.LogFlood;
import net.shadow.client.feature.command.impl.MessageSpam;
import net.shadow.client.feature.command.impl.OnlineAPI;
import net.shadow.client.feature.command.impl.Panic;
import net.shadow.client.feature.command.impl.PermissionLevel;
import net.shadow.client.feature.command.impl.Poof;
@ -153,6 +154,7 @@ public class CommandRegistry {
vanillaCommands.add(new ForceOP());
vanillaCommands.add(new ServerCrash());
vanillaCommands.add(new RandomBook());
vanillaCommands.add(new OnlineAPI());
rebuildSharedCommands();
}

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) Shadow client, 0x150, Saturn5VFive 2022. All rights reserved.
*/
package net.shadow.client.feature.command.impl;
import net.shadow.client.ShadowMain;
import net.shadow.client.feature.command.Command;
import net.shadow.client.feature.command.coloring.ArgumentType;
import net.shadow.client.feature.command.coloring.PossibleArgument;
import net.shadow.client.feature.command.coloring.StaticArgumentServer;
import net.shadow.client.feature.command.exception.CommandException;
import net.shadow.client.feature.module.ModuleRegistry;
import net.shadow.client.feature.module.impl.misc.IRC;
import net.shadow.client.helper.ShadowAPIWrapper;
import net.shadow.client.helper.event.EventType;
import net.shadow.client.helper.event.Events;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.nio.charset.StandardCharsets;
public class OnlineAPI extends Command {
static final File SESSION_KEY_FILE = new File(ShadowMain.BASE, "session.sip");
public OnlineAPI() {
super("OnlineAPI", "Manage your API connection to the mothership","onlineapi","shadowapi","onlineservices");
tryToRecoverSession();
Events.registerEventHandler(EventType.CONFIG_SAVE, event -> {
String authKey = ShadowAPIWrapper.getAuthKey();
if (authKey != null) {
try {
FileUtils.write(SESSION_KEY_FILE,authKey,StandardCharsets.UTF_8);
} catch (Exception e) {
System.out.println("failed to save session :(");
e.printStackTrace();
}
}
});
}
void tryToRecoverSession() {
if (SESSION_KEY_FILE.exists()) {
try {
String session = FileUtils.readFileToString(SESSION_KEY_FILE, StandardCharsets.UTF_8);
if (!session.isEmpty()) {
if (ShadowAPIWrapper.loginWithKey(session)) {
System.out.println("recovered previous session from backup file");
} else {
System.out.println("server said no to session recovery, moving on");
}
}
} catch (Exception e) {
System.out.println("failed to recover session :(");
e.printStackTrace();
}
}
}
@Override
public PossibleArgument getSuggestionsWithType(int index, String[] args) {
if (index == 0) return new PossibleArgument(ArgumentType.STRING, "login", "logout");
if (args[0].equalsIgnoreCase("login")) {
return StaticArgumentServer.serveFromStatic(index-1,new PossibleArgument(ArgumentType.STRING, "(username)"), new PossibleArgument(ArgumentType.STRING, "(password)"));
}
return super.getSuggestionsWithType(index, args);
}
@Override
public void onExecute(String[] args) throws CommandException {
validateArgumentsLength(args,1, "Need an action");
switch(args[0].toLowerCase()) {
case "login" -> {
if (ShadowAPIWrapper.getAuthKey() != null) {
error("You're already logged in!");
return;
}
validateArgumentsLength(args, 3, "Need action, username and password");
if (ShadowAPIWrapper.attemptLogin(args[1], args[2])) {
success("You're now logged in as "+args[1]+". Try using IRC or the item market ;)");
} else {
error("Failed to login. Check if username and password are correct");
}
}
case "logout" -> {
IRC irc = ModuleRegistry.getByClass(IRC.class);
if (irc.isEnabled()) irc.setEnabled(false);
ShadowAPIWrapper.logout();
success("Logged you out");
}
}
}
}

View file

@ -5,13 +5,6 @@
package net.shadow.client.feature.command.impl;
import net.shadow.client.feature.command.Command;
import net.shadow.client.feature.module.AddonModule;
import net.shadow.client.feature.module.Module;
import net.shadow.client.feature.module.ModuleRegistry;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
public class Test extends Command {
public Test() {
@ -20,16 +13,6 @@ public class Test extends Command {
@Override
public void onExecute(String[] args) {
StringBuilder sb = new StringBuilder();
for (Module module : ModuleRegistry.getModules()) {
if (module instanceof AddonModule) continue;
String cname = module.getClass().getSimpleName();
sb.append(String.format("registerModule(%s.class);", cname)).append("\n");
}
try {
Files.writeString(new File("bruh.txt").toPath(), sb.toString(), StandardOpenOption.CREATE);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View file

@ -9,6 +9,7 @@ import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket;
import net.shadow.client.feature.module.Module;
import net.shadow.client.feature.module.ModuleType;
import net.shadow.client.helper.IRCWebSocket;
import net.shadow.client.helper.ShadowAPIWrapper;
import net.shadow.client.helper.event.EventListener;
import net.shadow.client.helper.event.EventType;
import net.shadow.client.helper.event.events.PacketEvent;
@ -17,10 +18,8 @@ import net.shadow.client.helper.util.Utils;
import java.net.URI;
public class IRC extends Module {
static final String websocketUrl = "ws://localhost:8080/irc";
static String ircPrefix = "#";
IRCWebSocket wsS;
static String authToken = System.getenv("SHADOW_API_KEY");
public IRC() {
super("IRC", "Chat with others using the client", ModuleType.MISC);
}
@ -42,12 +41,12 @@ public class IRC extends Module {
@Override
public void enable() {
if (authToken == null) {
if (ShadowAPIWrapper.getAuthKey() == null) {
Utils.Logging.error("Cannot use IRC because you didn't use the launcher to launch shadow.");
setEnabled(false);
return;
}
this.wsS = new IRCWebSocket(URI.create(websocketUrl),authToken,()-> {
this.wsS = new IRCWebSocket(URI.create(ShadowAPIWrapper.BASE_WS+"/irc"),ShadowAPIWrapper.getAuthKey(),()-> {
this.wsS = null;
this.setEnabled(false);
});

View file

@ -80,6 +80,6 @@ public class IRCWebSocket extends WebSocketClient {
@Override
public void onError(Exception ex) {
ex.printStackTrace();
}
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) Shadow client, 0x150, Saturn5VFive 2022. All rights reserved.
*/
package net.shadow.client.helper;
import com.google.gson.Gson;
import net.minecraft.item.ItemStack;
import net.minecraft.util.registry.Registry;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
public class ShadowAPIWrapper {
public static final String BASE_DOMAIN = "api.shadowclient.cf";
public static final String BASE_URL = "https://"+BASE_DOMAIN;
public static final String BASE_WS = "wss://"+BASE_DOMAIN;
static String authKey = "";
static HttpClient client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build();
static Gson gson = new Gson();
static boolean currentUserIsAdmin = false;
public static String getAuthKey() {
if (authKey.isEmpty()) return null;
return authKey;
}
public static boolean isCurrentUserAdmin() {
return currentUserIsAdmin;
}
static HttpResponse<String> get(String path) {
return request(path, "GET", HttpRequest.BodyPublishers.noBody());
}
static HttpResponse<String> request(String path, String method, HttpRequest.BodyPublisher publisher) {
URI u = URI.create(BASE_URL+path);
HttpRequest request = HttpRequest.newBuilder().method(method, publisher).uri(u).header("Authorization", authKey).build();
try {
return client.send(request, HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
static HttpResponse<String> post(String path, String data) {
return request(path, "POST", HttpRequest.BodyPublishers.ofString(data));
}
public static void logout() {
authKey = "";
}
public static boolean loginWithKey(String session) {
String prevAuthKey = authKey;
authKey = session;
HttpResponse<String> resp = get("/users/current");
if (resp.statusCode() != 200) {
authKey = prevAuthKey;
return false;
}
Map<String, Object> info = gson.fromJson(resp.body(),Map.class);
currentUserIsAdmin = (boolean) info.getOrDefault("isAdmin", false);
return true;
}
public static boolean attemptLogin(String username, String password) {
String d = gson.toJson(Map.of(
"username", username,
"password", password
));
HttpResponse<String> uResp = post("/users/apiKeyForCreds", d);
if (uResp == null) return false;
System.out.println(uResp.body()+": "+d);
if (uResp.statusCode() != 200) return false;
return loginWithKey(uResp.body());
}
public static boolean putItem(ItemStack stack) {
HttpResponse<String> a = request("/items","PUT", HttpRequest.BodyPublishers.ofString(gson.toJson(Map.of(
"itemName", Registry.ITEM.getId(stack.getItem()).getPath(),
"itemNbt", new String(Base64.getEncoder().encode(stack.getOrCreateNbt().toString().getBytes(StandardCharsets.UTF_8)))
))));
if (a == null) return false;
System.out.println(a.body());
return a.statusCode() == 200;
}
}