mirror of
https://github.com/lklynet/hypermind.git
synced 2026-05-03 09:30:36 +00:00
feat: implement 90s screenname generator and update ID display
This commit is contained in:
+24
-3
@@ -220,7 +220,8 @@ const updateMap = async (peers) => {
|
||||
fillOpacity: 0.15
|
||||
}).addTo(map);
|
||||
|
||||
marker.bindPopup(`<b>Node</b> ${peer.id.slice(-8)}<br>${loc.city}, ${loc.country}`);
|
||||
const peerName = window.generateScreenname ? window.generateScreenname(peer.id) : peer.id.slice(-8);
|
||||
marker.bindPopup(`<b>${peerName}</b><br>${loc.city}, ${loc.country}`);
|
||||
peerMarkers[peer.id] = marker;
|
||||
}
|
||||
}
|
||||
@@ -319,7 +320,16 @@ const appendMessage = (msg) => {
|
||||
|
||||
if (msg.type === 'CHAT') {
|
||||
const senderColor = getColorFromId(msg.sender);
|
||||
const senderName = msg.sender === myId ? 'You' : msg.sender.slice(-4);
|
||||
|
||||
let senderName = 'Unknown';
|
||||
if (window.generateScreenname) {
|
||||
senderName = window.generateScreenname(msg.sender);
|
||||
} else {
|
||||
senderName = msg.sender.slice(-8);
|
||||
}
|
||||
|
||||
if (msg.sender === myId) senderName = 'You';
|
||||
|
||||
const scopeLabel = msg.scope === 'GLOBAL' ? '[GLOBAL] ' : '';
|
||||
|
||||
const senderSpan = document.createElement('span');
|
||||
@@ -430,7 +440,18 @@ evtSource.onmessage = (event) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (data.id) myId = data.id;
|
||||
if (data.id) {
|
||||
myId = data.id;
|
||||
const diagIdEl = document.getElementById('diag-id');
|
||||
if (diagIdEl && diagIdEl.innerText === '{{FULL_ID}}') {
|
||||
diagIdEl.innerText = data.id;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.screenname) {
|
||||
const screennameEl = document.getElementById('my-screenname');
|
||||
if (screennameEl) screennameEl.innerText = data.screenname;
|
||||
}
|
||||
|
||||
if (data.peers) {
|
||||
lastPeerData = data.peers;
|
||||
|
||||
+7
-1
@@ -7,6 +7,8 @@
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script src="/js/lists.js"></script>
|
||||
<script src="/js/screenname.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="network" data-visual-limit="{{VISUAL_LIMIT}}"></canvas>
|
||||
@@ -17,7 +19,7 @@
|
||||
powered by <a href="https://github.com/lklynet/hypermind" target="_blank">hypermind</a>
|
||||
</div>
|
||||
<div class="debug">
|
||||
ID: {{ID}}<br>
|
||||
ID: <span id="my-screenname">{{ID}}</span><br>
|
||||
Direct Connections: <span id="direct">{{DIRECT}}</span><br>
|
||||
<span class="debug-link" onclick="openDiagnostics()">diagnostics</span>
|
||||
<span id="map-container" class="{{MAP_CLASS}}"> | <span class="debug-link" id="map-link" onclick="openMap()">map</span></span>
|
||||
@@ -35,6 +37,10 @@
|
||||
<div class="modal-content">
|
||||
<button class="close-btn" onclick="closeDiagnostics()">×</button>
|
||||
<div class="modal-title">Diagnostics</div>
|
||||
<div class="stat-row">
|
||||
<span class="stat-label">ID</span>
|
||||
<span class="stat-value" id="diag-id" style="font-size: 0.8em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; direction: rtl; max-width: 300px; display: block;">{{FULL_ID}}</span>
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<span class="stat-label">Heartbeats Received</span>
|
||||
<span class="stat-value" id="diag-heartbeats-rx">0</span>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
const crypto = require("crypto");
|
||||
const { MY_POW_PREFIX } = require("../config/constants");
|
||||
const { generateScreenname } = require("../utils/name-generator");
|
||||
|
||||
const generateIdentity = () => {
|
||||
const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519");
|
||||
const id = publicKey.export({ type: "spki", format: "der" }).toString("hex");
|
||||
const screenname = generateScreenname(id);
|
||||
|
||||
let nonce = 0;
|
||||
while (true) {
|
||||
@@ -15,7 +17,7 @@ const generateIdentity = () => {
|
||||
nonce++;
|
||||
}
|
||||
|
||||
return { publicKey, privateKey, id, nonce };
|
||||
return { publicKey, privateKey, id, nonce, screenname };
|
||||
}
|
||||
|
||||
module.exports = { generateIdentity };
|
||||
|
||||
@@ -6,6 +6,7 @@ const {
|
||||
const crypto = require("crypto");
|
||||
const { MAX_RELAY_HOPS, ENABLE_CHAT, CHAT_RATE_LIMIT } = require("../config/constants");
|
||||
const { BloomFilterManager } = require("../state/bloom");
|
||||
const { generateScreenname } = require("../utils/name-generator");
|
||||
|
||||
class MessageHandler {
|
||||
constructor(
|
||||
@@ -93,7 +94,7 @@ class MessageHandler {
|
||||
if (ENABLE_CHAT && this.chatSystemFn && hops === 0) {
|
||||
this.chatSystemFn({
|
||||
type: "SYSTEM",
|
||||
content: `Connection established with Node ...${id.slice(-8)}`,
|
||||
content: `Connection established with Node [${generateScreenname(id)}]`,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
@@ -134,7 +135,7 @@ class MessageHandler {
|
||||
if (ENABLE_CHAT && this.chatSystemFn && hops === 0) {
|
||||
this.chatSystemFn({
|
||||
type: "SYSTEM",
|
||||
content: `Node ...${id.slice(-8)} disconnected.`,
|
||||
content: `Node [${generateScreenname(id)}] disconnected.`,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const Hyperswarm = require("hyperswarm");
|
||||
const { signMessage } = require("../core/security");
|
||||
const { TOPIC, TOPIC_NAME, HEARTBEAT_INTERVAL, MAX_CONNECTIONS, CONNECTION_ROTATION_INTERVAL, ENABLE_CHAT } = require("../config/constants");
|
||||
const { generateScreenname } = require("../utils/name-generator");
|
||||
|
||||
class SwarmManager {
|
||||
constructor(identity, peerManager, diagnostics, messageHandler, relayFn, broadcastFn, chatSystemFn) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,91 @@
|
||||
const adjectives = require("./adjectives.json");
|
||||
const nouns = require("./nouns.json");
|
||||
|
||||
const hashStr = (str) => {
|
||||
let hash = 5381;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
hash = ((hash << 5) + hash) + str.charCodeAt(i);
|
||||
}
|
||||
return hash >>> 0;
|
||||
};
|
||||
|
||||
class SeededRandom {
|
||||
constructor(seedStr) {
|
||||
this.seed = hashStr(seedStr);
|
||||
}
|
||||
|
||||
next() {
|
||||
const x = Math.sin(this.seed++) * 10000;
|
||||
return x - Math.floor(x);
|
||||
}
|
||||
|
||||
range(min, max) {
|
||||
return Math.floor(this.next() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
bool(p = 0.5) {
|
||||
return this.next() < p;
|
||||
}
|
||||
|
||||
pick(array) {
|
||||
return array[this.range(0, array.length - 1)];
|
||||
}
|
||||
}
|
||||
|
||||
const toLeet = (str, rng) => {
|
||||
const map = {
|
||||
'a': '4', 'e': '3', 'i': '1', 'o': '0', 's': '5', 't': '7'
|
||||
};
|
||||
|
||||
return str.split('').map(char => {
|
||||
const lower = char.toLowerCase();
|
||||
|
||||
if (map[lower] && rng.bool(0.5)) {
|
||||
return map[lower];
|
||||
}
|
||||
|
||||
if (rng.bool(0.3)) {
|
||||
return char.toUpperCase();
|
||||
}
|
||||
return char;
|
||||
}).join('');
|
||||
};
|
||||
|
||||
const generateScreenname = (id) => {
|
||||
if (!id) return "Guest_" + Math.floor(Math.random() * 1000);
|
||||
|
||||
const rng = new SeededRandom(id);
|
||||
|
||||
const adj = rng.pick(adjectives);
|
||||
const noun = rng.pick(nouns);
|
||||
|
||||
let name = "";
|
||||
|
||||
const strategy = rng.range(0, 2);
|
||||
|
||||
if (strategy === 0) {
|
||||
name = `${adj}_${noun}`;
|
||||
} else if (strategy === 1) {
|
||||
name = `${adj}${noun.charAt(0).toUpperCase() + noun.slice(1)}`;
|
||||
} else {
|
||||
name = `${adj.toUpperCase()}_${noun}`;
|
||||
}
|
||||
|
||||
if (rng.bool(0.4)) {
|
||||
name = toLeet(name, rng);
|
||||
}
|
||||
|
||||
const wrapStrategy = rng.range(0, 4);
|
||||
if (wrapStrategy === 0) {
|
||||
name = `xX${name}Xx`;
|
||||
} else if (wrapStrategy === 1) {
|
||||
name = `_${name}_`;
|
||||
} else if (wrapStrategy === 2) {
|
||||
|
||||
name = `${name}${rng.range(1, 999)}`;
|
||||
}
|
||||
|
||||
return name;
|
||||
};
|
||||
|
||||
module.exports = { generateScreenname };
|
||||
File diff suppressed because one or more lines are too long
+26
-1
@@ -10,16 +10,39 @@ const HTML_TEMPLATE = fs.readFileSync(
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
// Pre-load lists and generator logic
|
||||
const adjectives = fs.readFileSync(path.join(__dirname, "../utils/adjectives.json"), "utf-8");
|
||||
const nouns = fs.readFileSync(path.join(__dirname, "../utils/nouns.json"), "utf-8");
|
||||
const generatorLogic = fs.readFileSync(path.join(__dirname, "../utils/name-generator.js"), "utf-8");
|
||||
|
||||
const setupRoutes = (app, identity, peerManager, swarm, sseManager, diagnostics) => {
|
||||
app.use(express.json());
|
||||
|
||||
// Serve lists
|
||||
app.get("/js/lists.js", (req, res) => {
|
||||
res.setHeader("Content-Type", "application/javascript");
|
||||
res.send(`window.ADJECTIVES = ${adjectives}; window.NOUNS = ${nouns};`);
|
||||
});
|
||||
|
||||
// Serve generator
|
||||
app.get("/js/screenname.js", (req, res) => {
|
||||
res.setHeader("Content-Type", "application/javascript");
|
||||
// Adapt CommonJS module to Browser script
|
||||
const browserLogic = generatorLogic
|
||||
.replace('const adjectives = require("./adjectives.json");', 'const adjectives = window.ADJECTIVES;')
|
||||
.replace('const nouns = require("./nouns.json");', 'const nouns = window.NOUNS;')
|
||||
.replace('module.exports = { generateScreenname };', 'window.generateScreenname = generateScreenname;');
|
||||
res.send(browserLogic);
|
||||
});
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
const count = peerManager.size;
|
||||
const directPeers = swarm.getSwarm().connections.size;
|
||||
|
||||
const html = HTML_TEMPLATE
|
||||
.replace(/\{\{COUNT\}\}/g, count)
|
||||
.replace(/\{\{ID\}\}/g, "..." + identity.id.slice(-8))
|
||||
.replace(/\{\{ID\}\}/g, identity.screenname || "Unknown")
|
||||
.replace(/\{\{FULL_ID\}\}/g, identity.id)
|
||||
.replace(/\{\{DIRECT\}\}/g, directPeers)
|
||||
.replace(/\{\{MAP_CLASS\}\}/g, ENABLE_MAP ? '' : 'hidden')
|
||||
.replace(/\{\{VISUAL_LIMIT\}\}/g, VISUAL_LIMIT);
|
||||
@@ -40,6 +63,7 @@ const setupRoutes = (app, identity, peerManager, swarm, sseManager, diagnostics)
|
||||
totalUnique: peerManager.totalUniquePeers,
|
||||
direct: swarm.getSwarm().connections.size,
|
||||
id: identity.id,
|
||||
screenname: identity.screenname,
|
||||
diagnostics: diagnostics.getStats(),
|
||||
chatEnabled: ENABLE_CHAT,
|
||||
peers: peerManager.getPeersWithIps()
|
||||
@@ -57,6 +81,7 @@ const setupRoutes = (app, identity, peerManager, swarm, sseManager, diagnostics)
|
||||
totalUnique: peerManager.totalUniquePeers,
|
||||
direct: swarm.getSwarm().connections.size,
|
||||
id: identity.id,
|
||||
screenname: identity.screenname,
|
||||
diagnostics: diagnostics.getStats(),
|
||||
chatEnabled: ENABLE_CHAT,
|
||||
peers: peerManager.getPeersWithIps()
|
||||
|
||||
Reference in New Issue
Block a user