See #1
This commit is contained in:
@@ -11,12 +11,17 @@ import org.java_websocket.client.WebSocketClient;
|
|||||||
import org.java_websocket.handshake.ServerHandshake;
|
import org.java_websocket.handshake.ServerHandshake;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@@ -24,12 +29,26 @@ public class BukkitstrPlugin extends JavaPlugin implements Listener {
|
|||||||
|
|
||||||
private NostrWebSocketClient webSocketClient;
|
private NostrWebSocketClient webSocketClient;
|
||||||
private final Set<Player> authenticatedPlayers = new HashSet<>();
|
private final Set<Player> authenticatedPlayers = new HashSet<>();
|
||||||
|
private final Map<UUID, PendingAuth> pendingAuths = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// Class to hold pending authentication data
|
||||||
|
private static class PendingAuth {
|
||||||
|
final Player player;
|
||||||
|
final String pubkey;
|
||||||
|
String minecraftUsername;
|
||||||
|
boolean metadataVerified = false;
|
||||||
|
|
||||||
|
PendingAuth(Player player, String pubkey) {
|
||||||
|
this.player = player;
|
||||||
|
this.pubkey = pubkey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
getLogger().info("BukkitstrPlugin enabled!");
|
getLogger().info("BukkitstrPlugin enabled!");
|
||||||
try {
|
try {
|
||||||
URI uri = new URI("wss://"); // Replace with your Nostr relay URL
|
URI uri = new URI("ws://192.168.1.14:8888"); // Replace with your Nostr relay URL
|
||||||
webSocketClient = new NostrWebSocketClient(uri, getLogger());
|
webSocketClient = new NostrWebSocketClient(uri, getLogger());
|
||||||
webSocketClient.connect();
|
webSocketClient.connect();
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
@@ -48,23 +67,139 @@ public class BukkitstrPlugin extends JavaPlugin implements Listener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
if (sender instanceof Player) {
|
if (!(sender instanceof Player)) {
|
||||||
Player player = (Player) sender;
|
sender.sendMessage("This command can only be used by players");
|
||||||
player.sendMessage("Waiting for Nostr event...");
|
|
||||||
|
|
||||||
webSocketClient.waitForEvent(content -> handleSpecificEvent(content, player));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Player player = (Player) sender;
|
||||||
|
|
||||||
|
if (command.getName().equalsIgnoreCase("nostrauth")) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
player.sendMessage("Usage: /nostrauth <pubkey>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String pubkey = args[0];
|
||||||
|
player.sendMessage("Starting Nostr authentication for pubkey: " + pubkey);
|
||||||
|
|
||||||
|
// Create a unique ID for this authentication attempt
|
||||||
|
UUID authId = UUID.randomUUID();
|
||||||
|
String metadataSubId = "meta_" + authId.toString().replace("-", "");
|
||||||
|
String authSubId = "auth_" + authId.toString().replace("-", "");
|
||||||
|
|
||||||
|
// Store the pending authentication
|
||||||
|
PendingAuth pendingAuth = new PendingAuth(player, pubkey);
|
||||||
|
pendingAuths.put(authId, pendingAuth);
|
||||||
|
|
||||||
|
// First step: Check for metadata (kind 30078)
|
||||||
|
player.sendMessage("§6Checking your Nostr profile...");
|
||||||
|
webSocketClient.subscribeToParameterizedReplaceable(metadataSubId, 30078, pubkey, "mcauth", authId,
|
||||||
|
(success, username) -> {
|
||||||
|
if (success && username != null) {
|
||||||
|
pendingAuth.metadataVerified = true;
|
||||||
|
pendingAuth.minecraftUsername = username;
|
||||||
|
|
||||||
|
// Verify username matches
|
||||||
|
if (username.equalsIgnoreCase(player.getName())) {
|
||||||
|
player.sendMessage("§6Profile verified! Waiting for authentication event...");
|
||||||
|
|
||||||
|
// Now subscribe to auth events (kind 13378008)
|
||||||
|
webSocketClient.subscribeToAuthEvents(authSubId, 13378008, pubkey, authId);
|
||||||
|
} else {
|
||||||
|
player.sendMessage("§cAuthentication failed: The Minecraft username in your Nostr profile (" +
|
||||||
|
username + ") doesn't match your in-game name (" + player.getName() + ")");
|
||||||
|
webSocketClient.closeSubscription(metadataSubId);
|
||||||
|
pendingAuths.remove(authId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
player.sendMessage("§cCouldn't find a Nostr profile with the Minecraft username. " +
|
||||||
|
"Make sure you've created a kind 30078 event with d=mcauth containing your username.");
|
||||||
|
webSocketClient.closeSubscription(metadataSubId);
|
||||||
|
pendingAuths.remove(authId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSpecificEvent(String content, Player player) {
|
// Process an authentication event (kind 13378008)
|
||||||
if (content.equals(player.getName())) {
|
private void handleAuthEvent(UUID authId, JsonNode eventNode) {
|
||||||
authenticatedPlayers.add(player);
|
PendingAuth auth = pendingAuths.get(authId);
|
||||||
player.sendMessage("You are now authenticated.");
|
if (auth == null || !auth.metadataVerified) {
|
||||||
} else {
|
getLogger().warning("Received auth event for unknown auth ID or unverified metadata: " + authId);
|
||||||
player.sendMessage("Authentication failed. Username does not match.");
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player player = auth.player;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get event kind to verify it's 13378008
|
||||||
|
int kind = eventNode.path("kind").asInt();
|
||||||
|
if (kind != 13378008) {
|
||||||
|
getLogger().warning("Expected kind 13378008, got: " + kind);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the pubkey matches
|
||||||
|
String eventPubkey = eventNode.path("pubkey").asText("");
|
||||||
|
if (!eventPubkey.equals(auth.pubkey)) {
|
||||||
|
player.sendMessage("§cAuthentication failed: Event signed by wrong pubkey");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check tags for the d tag with "mcauth" value
|
||||||
|
boolean hasMcAuthTag = false;
|
||||||
|
JsonNode tagsNode = eventNode.path("tags");
|
||||||
|
if (tagsNode.isArray()) {
|
||||||
|
for (JsonNode tag : tagsNode) {
|
||||||
|
if (tag.isArray() && tag.size() > 1 &&
|
||||||
|
"d".equals(tag.get(0).asText()) &&
|
||||||
|
"mcauth".equals(tag.get(1).asText())) {
|
||||||
|
hasMcAuthTag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasMcAuthTag) {
|
||||||
|
player.sendMessage("§cAuthentication failed: Event is missing the d=mcauth tag");
|
||||||
|
getLogger().warning("Missing d=mcauth tag in auth event");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the content
|
||||||
|
String content = eventNode.path("content").asText("");
|
||||||
|
|
||||||
|
// Log the received event details
|
||||||
|
getLogger().info("Auth event received - Content: '" + content + "', Player: '" + player.getName() + "'");
|
||||||
|
|
||||||
|
// Verify content matches the player's name
|
||||||
|
if (content.equalsIgnoreCase(player.getName())) {
|
||||||
|
// Authentication successful!
|
||||||
|
authenticatedPlayers.add(player);
|
||||||
|
player.sendMessage("§a✓ Authentication successful! You can now move freely.");
|
||||||
|
player.sendMessage("§6 Please note, you'll need to reauth ~30 days");
|
||||||
|
getLogger().info("Player " + player.getName() + " authenticated successfully with pubkey " + auth.pubkey);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
String authSubId = "auth_" + authId.toString().replace("-", "");
|
||||||
|
String metaSubId = "meta_" + authId.toString().replace("-", "");
|
||||||
|
webSocketClient.closeSubscription(authSubId);
|
||||||
|
webSocketClient.closeSubscription(metaSubId);
|
||||||
|
pendingAuths.remove(authId);
|
||||||
|
} else {
|
||||||
|
player.sendMessage("§cAuthentication failed: Event content doesn't match your username");
|
||||||
|
getLogger().warning("Auth event content '" + content + "' doesn't match player name '" + player.getName() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
getLogger().severe("Error processing auth event: " + e.getMessage());
|
||||||
|
player.sendMessage("§cAn error occurred during authentication. Please try again.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +208,7 @@ public class BukkitstrPlugin extends JavaPlugin implements Listener {
|
|||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
if (!authenticatedPlayers.contains(player)) {
|
if (!authenticatedPlayers.contains(player)) {
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
player.sendMessage("You need to authenticate first!");
|
player.sendMessage("§cYou need to authenticate first with /nostrauth <pubkey>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +216,7 @@ public class BukkitstrPlugin extends JavaPlugin implements Listener {
|
|||||||
|
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
private Consumer<String> eventConsumer;
|
private final Map<String, UUID> subscriptionMap = new HashMap<>();
|
||||||
|
|
||||||
public NostrWebSocketClient(URI serverUri, Logger logger) {
|
public NostrWebSocketClient(URI serverUri, Logger logger) {
|
||||||
super(serverUri);
|
super(serverUri);
|
||||||
@@ -92,7 +227,6 @@ public class BukkitstrPlugin extends JavaPlugin implements Listener {
|
|||||||
@Override
|
@Override
|
||||||
public void onOpen(ServerHandshake handshakedata) {
|
public void onOpen(ServerHandshake handshakedata) {
|
||||||
logger.info("Connected to Nostr relay: " + getURI());
|
logger.info("Connected to Nostr relay: " + getURI());
|
||||||
sendInitialRequest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -114,37 +248,217 @@ public class BukkitstrPlugin extends JavaPlugin implements Listener {
|
|||||||
private void handleNostrEvent(String message) {
|
private void handleNostrEvent(String message) {
|
||||||
try {
|
try {
|
||||||
JsonNode jsonNode = objectMapper.readTree(message);
|
JsonNode jsonNode = objectMapper.readTree(message);
|
||||||
if (jsonNode.isArray() && jsonNode.size() > 2) {
|
|
||||||
|
// Log all messages for debugging
|
||||||
|
logger.info("Processing Nostr message: " + message);
|
||||||
|
|
||||||
|
// Check if this is an EVENT message
|
||||||
|
if (jsonNode.isArray() && jsonNode.size() >= 3 && "EVENT".equals(jsonNode.get(0).asText())) {
|
||||||
|
String subscriptionId = jsonNode.get(1).asText();
|
||||||
JsonNode eventNode = jsonNode.get(2);
|
JsonNode eventNode = jsonNode.get(2);
|
||||||
JsonNode tagsNode = eventNode.path("tags");
|
|
||||||
if (tagsNode.isArray()) {
|
// Log the event kind for debugging
|
||||||
for (JsonNode tag : tagsNode) {
|
int kind = eventNode.path("kind").asInt();
|
||||||
if (tag.isArray() && tag.size() > 1 && "d".equals(tag.get(0).asText())) {
|
logger.info("Received event with kind: " + kind + " for subscription: " + subscriptionId);
|
||||||
String content = tag.get(1).asText();
|
|
||||||
if (eventConsumer != null) {
|
// Figure out what kind of subscription this is
|
||||||
eventConsumer.accept(content);
|
if (subscriptionId.startsWith("meta_")) {
|
||||||
}
|
logger.info("Processing as metadata event");
|
||||||
|
handleMetadataResponse(subscriptionId, eventNode);
|
||||||
|
} else if (subscriptionId.startsWith("auth_")) {
|
||||||
|
logger.info("Processing as auth event");
|
||||||
|
handleAuthResponse(subscriptionId, eventNode);
|
||||||
|
}
|
||||||
|
} else if (jsonNode.isArray() && jsonNode.size() >= 3 && "EOSE".equals(jsonNode.get(0).asText())) {
|
||||||
|
// End of stored events
|
||||||
|
String subscriptionId = jsonNode.get(1).asText();
|
||||||
|
logger.info("Received EOSE for subscription: " + subscriptionId);
|
||||||
|
|
||||||
|
if (subscriptionId.startsWith("meta_")) {
|
||||||
|
UUID authId = getAuthIdFromSubscription(subscriptionId);
|
||||||
|
if (authId != null) {
|
||||||
|
PendingAuth auth = pendingAuths.get(authId);
|
||||||
|
if (auth != null && !auth.metadataVerified) {
|
||||||
|
// No metadata found by EOSE
|
||||||
|
auth.player.sendMessage("§cNo Nostr profile found with Minecraft authentication. " +
|
||||||
|
"Please set up your profile first.");
|
||||||
|
closeSubscription(subscriptionId);
|
||||||
|
pendingAuths.remove(authId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
logger.warning("No 'tags' array found in the event message.");
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
logger.warning("Event message is not in the expected format.");
|
|
||||||
}
|
}
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
logger.severe("Failed to parse Nostr event: " + e.getMessage());
|
logger.severe("Failed to parse Nostr event: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waitForEvent(Consumer<String> eventConsumer) {
|
private void handleMetadataResponse(String subscriptionId, JsonNode eventNode) {
|
||||||
this.eventConsumer = eventConsumer;
|
UUID authId = getAuthIdFromSubscription(subscriptionId);
|
||||||
|
if (authId == null) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if this is kind 30078
|
||||||
|
int kind = eventNode.path("kind").asInt();
|
||||||
|
if (kind != 30078) return;
|
||||||
|
|
||||||
|
// Look for d tag with "mcauth" value
|
||||||
|
String dValue = null;
|
||||||
|
JsonNode tagsNode = eventNode.path("tags");
|
||||||
|
if (tagsNode.isArray()) {
|
||||||
|
for (JsonNode tag : tagsNode) {
|
||||||
|
if (tag.isArray() && tag.size() > 1 &&
|
||||||
|
"d".equals(tag.get(0).asText())) {
|
||||||
|
dValue = tag.get(1).asText();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!"mcauth".equals(dValue)) {
|
||||||
|
logger.info("Skipping 30078 event with d=" + dValue + " (looking for d=mcauth)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse content for the Minecraft username
|
||||||
|
String content = eventNode.path("content").asText("");
|
||||||
|
String username = content.trim();
|
||||||
|
|
||||||
|
if (username.isEmpty()) {
|
||||||
|
logger.warning("Empty Minecraft username in 30078 event");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingAuth auth = pendingAuths.get(authId);
|
||||||
|
if (auth != null) {
|
||||||
|
// Callback with success
|
||||||
|
auth.player.sendMessage("§6Found Nostr profile with Minecraft username: " + username);
|
||||||
|
auth.metadataVerified = true;
|
||||||
|
auth.minecraftUsername = username;
|
||||||
|
|
||||||
|
// Check if username matches
|
||||||
|
if (username.equalsIgnoreCase(auth.player.getName())) {
|
||||||
|
auth.player.sendMessage("§6Username verified! Waiting for authentication event...");
|
||||||
|
|
||||||
|
// Now subscribe to auth events (kind 13378008)
|
||||||
|
String authSubId = "auth_" + authId.toString().replace("-", "");
|
||||||
|
subscribeToAuthEvents(authSubId, 13378008, auth.pubkey, authId);
|
||||||
|
} else {
|
||||||
|
auth.player.sendMessage("§cAuthentication failed: The Minecraft username in your Nostr profile (" +
|
||||||
|
username + ") doesn't match your in-game name (" + auth.player.getName() + ")");
|
||||||
|
closeSubscription(subscriptionId);
|
||||||
|
pendingAuths.remove(authId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.severe("Error handling metadata response: " + e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendInitialRequest() {
|
private void handleAuthResponse(String subscriptionId, JsonNode eventNode) {
|
||||||
String initialRequest = "[\"REQ\",\"mc_auth\",{\"kinds\":[13378008],\"limit\":0}]";
|
UUID authId = getAuthIdFromSubscription(subscriptionId);
|
||||||
send(initialRequest);
|
if (authId != null) {
|
||||||
logger.info("Sent initial request to Nostr relay: " + initialRequest);
|
// Log the event details for debugging
|
||||||
|
int kind = eventNode.path("kind").asInt();
|
||||||
|
String pubkey = eventNode.path("pubkey").asText("");
|
||||||
|
String content = eventNode.path("content").asText("");
|
||||||
|
|
||||||
|
logger.info("Auth event received - Kind: " + kind + ", Pubkey: " + pubkey + ", Content: '" + content + "'");
|
||||||
|
|
||||||
|
// Only process kind 13378008 events for authentication
|
||||||
|
if (kind == 13378008) {
|
||||||
|
logger.info("Processing kind 13378008 event for authentication");
|
||||||
|
handleAuthEvent(authId, eventNode);
|
||||||
|
} else {
|
||||||
|
logger.info("Ignoring event with kind " + kind + " (expecting 13378008)");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warning("Cannot find auth ID for subscription: " + subscriptionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UUID getAuthIdFromSubscription(String subscriptionId) {
|
||||||
|
// Extract authId from subscription ID
|
||||||
|
String idPart = subscriptionId.substring(subscriptionId.indexOf('_') + 1);
|
||||||
|
try {
|
||||||
|
return UUID.fromString(
|
||||||
|
idPart.replaceFirst(
|
||||||
|
"(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)",
|
||||||
|
"$1-$2-$3-$4-$5"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
logger.warning("Invalid UUID in subscription ID: " + idPart);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to parameterized replaceable events (for metadata)
|
||||||
|
public void subscribeToParameterizedReplaceable(String subscriptionId, int kind, String pubkey,
|
||||||
|
String dValue, UUID authId,
|
||||||
|
MetadataCallback callback) {
|
||||||
|
try {
|
||||||
|
// Create proper filter for Nostr - using array notation for kinds and correct #d tag format
|
||||||
|
String filterJson = "{" +
|
||||||
|
"\"kinds\":[" + kind + "]," +
|
||||||
|
"\"authors\":[\"" + pubkey + "\"]," +
|
||||||
|
"\"#d\":[\"" + dValue + "\"]" +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
JsonNode filter = objectMapper.readTree(filterJson);
|
||||||
|
String reqMessage = "[\"REQ\",\"" + subscriptionId + "\"," + filter.toString() + "]";
|
||||||
|
send(reqMessage);
|
||||||
|
logger.info("Sent metadata subscription: " + reqMessage);
|
||||||
|
|
||||||
|
// Store the subscription ID mapping
|
||||||
|
subscriptionMap.put(subscriptionId, authId);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.severe("Failed to create metadata subscription: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to authentication events
|
||||||
|
public void subscribeToAuthEvents(String subscriptionId, int kind, String pubkey, UUID authId) {
|
||||||
|
try {
|
||||||
|
// Create proper filter format for Nostr - using array notation for #d tag
|
||||||
|
String filterJson = "{" +
|
||||||
|
"\"kinds\":[" + kind + "]," +
|
||||||
|
"\"authors\":[\"" + pubkey + "\"]," +
|
||||||
|
"\"#d\":[\"mcauth\"]" +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
JsonNode filter = objectMapper.readTree(filterJson);
|
||||||
|
String reqMessage = "[\"REQ\",\"" + subscriptionId + "\"," + filter.toString() + "]";
|
||||||
|
send(reqMessage);
|
||||||
|
logger.info("Sent auth subscription: " + reqMessage);
|
||||||
|
|
||||||
|
// Store the subscription ID mapping
|
||||||
|
subscriptionMap.put(subscriptionId, authId);
|
||||||
|
|
||||||
|
// Notify player
|
||||||
|
PendingAuth auth = pendingAuths.get(authId);
|
||||||
|
if (auth != null) {
|
||||||
|
auth.player.sendMessage("§6Waiting for authentication event (kind 13378008)...");
|
||||||
|
auth.player.sendMessage("§6Please sign an event with your Nostr client.");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.severe("Failed to create auth subscription: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeSubscription(String subscriptionId) {
|
||||||
|
String closeMessage = "[\"CLOSE\",\"" + subscriptionId + "\"]";
|
||||||
|
send(closeMessage);
|
||||||
|
logger.info("Closed subscription: " + closeMessage);
|
||||||
|
subscriptionMap.remove(subscriptionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Interface for metadata callback
|
||||||
|
interface MetadataCallback {
|
||||||
|
void onResult(boolean success, String username);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user