mirror of
https://github.com/lklynet/hypermind.git
synced 2026-10-09 05:11:24 +00:00
feat: implement 90s screenname generator and update ID display
This commit is contained in:
1 parent
eb884e7643
commit
f150e39092
9 files changed
+157
-8
No files matched your search
+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) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
["aback","abaft","abandoned","abashed","aberrant","abhorrent","abiding","abject","ablaze","able","abnormal","aboard","aboriginal","abortive","abounding","abrasive","abrupt","absent","absorbed","absorbing","abstracted","absurd","abundant","abusive","acceptable","accessible","accidental","accurate","acid","acidic","acoustic","acrid","actually","ad","hoc","adamant","adaptable","addicted","adhesive","adjoining","adorable","adventurous","afraid","aggressive","agonizing","agreeable","ahead","ajar","alcoholic","alert","alike","alive","alleged","alluring","aloof","amazing","ambiguous","ambitious","amuck","amused","amusing","ancient","angry","animated","annoyed","annoying","anxious","apathetic","aquatic","aromatic","arrogant","ashamed","aspiring","assorted","astonishing","attractive","auspicious","automatic","available","average","awake","aware","awesome","awful","axiomatic","bad","barbarous","bashful","bawdy","beautiful","befitting","belligerent","beneficial","bent","berserk","best","better","bewildered","big","billowy","bite-sized","bitter","bizarre","black","black-and-white","bloody","blue","blue-eyed","blushing","boiling","boorish","bored","boring","bouncy","boundless","brainy","brash","brave","brawny","breakable","breezy","brief","bright","bright","broad","broken","brown","bumpy","burly","bustling","busy","cagey","calculating","callous","calm","capable","capricious","careful","careless","caring","cautious","ceaseless","certain","changeable","charming","cheap","cheerful","chemical","chief","childlike","chilly","chivalrous","chubby","chunky","clammy","classy","clean","clear","clever","cloistered","cloudy","closed","clumsy","cluttered","coherent","cold","colorful","colossal","combative","comfortable","common","complete","complex","concerned","condemned","confused","conscious","cooing","cool","cooperative","coordinated","courageous","cowardly","crabby","craven","crazy","creepy","crooked","crowded","cruel","cuddly","cultured","cumbersome","curious","curly","curved","curvy","cut","cute","cute","cynical","daffy","daily","damaged","damaging","damp","dangerous","dapper","dark","dashing","dazzling","dead","deadpan","deafening","dear","debonair","decisive","decorous","deep","deeply","defeated","defective","defiant","delicate","delicious","delightful","demonic","delirious","dependent","depressed","deranged","descriptive","deserted","detailed","determined","devilish","didactic","different","difficult","diligent","direful","dirty","disagreeable","disastrous","discreet","disgusted","disgusting","disillusioned","dispensable","distinct","disturbed","divergent","dizzy","domineering","doubtful","drab","draconian","dramatic","dreary","drunk","dry","dull","dusty","dynamic","dysfunctional","eager","early","earsplitting","earthy","easy","eatable","economic","educated","efficacious","efficient","eight","elastic","elated","elderly","electric","elegant","elfin","elite","embarrassed","eminent","empty","enchanted","enchanting","encouraging","endurable","energetic","enormous","entertaining","enthusiastic","envious","equable","equal","erect","erratic","ethereal","evanescent","evasive","even excellent excited","exciting exclusive","exotic","expensive","extra-large extra-small exuberant","exultant","fabulous","faded","faint fair","faithful","fallacious","false familiar famous","fanatical","fancy","fantastic","far"," far-flung"," fascinated","fast","fat faulty","fearful fearless","feeble feigned","female fertile","festive","few fierce","filthy","fine","finicky","first"," five"," fixed"," flagrant","flaky","flashy","flat","flawless","flimsy"," flippant","flowery","fluffy","fluttering"," foamy","foolish","foregoing","forgetful","fortunate","four frail","fragile","frantic","free"," freezing"," frequent"," fresh"," fretful","friendly","frightened frightening full fumbling functional","funny","furry furtive","future futuristic","fuzzy ","gabby","gainful","gamy","gaping","garrulous","gaudy","general gentle","giant","giddy","gifted","gigantic","glamorous","gleaming","glib","glistening glorious","glossy","godly","good","goofy","gorgeous","graceful","grandiose","grateful gratis","gray greasy great","greedy","green grey grieving","groovy","grotesque","grouchy","grubby gruesome","grumpy","guarded","guiltless","gullible gusty","guttural H habitual","half","hallowed","halting","handsome","handsomely","handy","hanging","hapless","happy","hard","hard-to-find","harmonious","harsh","hateful","heady","healthy","heartbreaking","heavenly heavy hellish","helpful","helpless","hesitant","hideous high","highfalutin","high-pitched","hilarious","hissing","historical","holistic","hollow","homeless","homely","honorable","horrible","hospitable","hot huge","hulking","humdrum","humorous","hungry","hurried","hurt","hushed","husky","hypnotic","hysterical","icky","icy","idiotic","ignorant","ill","illegal","ill-fated","ill-informed","illustrious","imaginary","immense","imminent","impartial","imperfect","impolite","important","imported","impossible","incandescent","incompetent"Line truncated
|
||||
@@ -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 };
|
||||
@@ -0,0 +1 @@
|
||||
["accelerator", "accordion", "account", "accountant", "acknowledgment", "acoustic", "acrylic", "act", "action", "active", "activity", "actor", "actress", "adapter", "addition", "address", "adjustment", "adult", "advantage", "advertisement", "advice", "afghanistan", "africa", "aftermath", "afternoon", "aftershave", "afterthought", "age", "agenda", "agreement", "air", "airbus", "airmail", "airplane", "airport", "airship", "alarm", "albatross", "alcohol", "algebra", "algeria", "alibi", "alley", "alligator", "alloy", "almanac", "alphabet", "alto", "aluminium", "aluminum", "ambulance", "america", "amount", "amusement", "anatomy", "anethesiologist", "anger", "angle", "angora", "animal", "anime", "ankle", "answer", "ant", "antarctica", "anteater", "antelope", "anthony", "anthropology", "apartment", "apology", "apparatus", "apparel", "appeal", "appendix", "apple", "appliance", "approval", "april", "aquarius", "arch", "archaeology", "archeology", "archer", "architecture", "area", "argentina", "argument", "aries", "arithmetic", "arm", "armadillo", "armchair", "armenian", "army", "arrow", "art", "ash", "ashtray", "asia", "asparagus", "asphalt", "asterisk", "astronomy", "athlete", "atm", "atom", "attack", "attempt", "attention", "attic", "attraction", "august", "aunt", "australia", "australian", "author", "authorisation", "authority", "authorization", "avenue", "babies", "baboon", "baby", "back", "backbone", "bacon", "badge", "badger", "bag", "bagel", "bagpipe", "bail", "bait", "baker", "bakery", "balance", "balinese", "ball", "balloon", "bamboo", "banana", "band", "bandana", "bangladesh", "bangle", "banjo", "bank", "bankbook", "banker", "bar", "barbara", "barber", "barge", "baritone", "barometer", "base", "baseball", "basement", "basin", "basket", "basketball", "bass", "bassoon", "bat", "bath", "bathroom", "bathtub", "battery", "battle", "bay", "beach", "bead", "beam", "bean", "bear", "beard", "beast", "beat", "beautician", "beauty", "beaver", "bed", "bedroom", "bee", "beech", "beef", "beer", "beet", "beetle", "beggar", "beginner", "begonia", "behavior", "belgian", "belief", "believe", "bell", "belt", "bench", "bengal", "beret", "berry", "bestseller", "betty", "bibliography", "bicycle", "bike", "bill", "billboard", "biology", "biplane", "birch", "bird", "birth", "birthday", "bit", "bite", "black", "bladder", "blade", "blanket", "blinker", "blizzard", "block", "blood", "blouse", "blow", "blowgun", "blue", "board", "boat", "bobcat", "body", "bolt", "bomb", "bomber", "bone", "bongo", "bonsai", "book", "bookcase", "booklet", "boot", "border", "botany", "bottle", "bottom", "boundary", "bow", "bowl", "bowling", "box", "boy", "bra", "brace", "bracket", "brain", "brake", "branch", "brand", "brandy", "brass", "brazil", "bread", "break", "breakfast", "breath", "brian", "brick", "bridge", "british", "broccoli", "brochure", "broker", "bronze", "brother", "brother-in-law", "brow", "brown", "brush", "bubble", "bucket", "budget", "buffer", "buffet", "bugle", "building", "bulb", "bull", "bulldozer", "bumper", "bun", "burglar", "burma", "burn", "burst", "bus", "bush", "business", "butane", "butcher", "butter", "button", "buzzard", "c-clamp", "cabbage", "cabinet", "cable", "cactus", "cafe", "cake", "calculator", "calculus", "calendar", "calf", "call", "camel", "camera", "camp", "can", "canada", "canadian", "cancer", "candle", "cannon", "canoe", "canvas", "cap", "capital", "cappelletti", "capricorn", "captain", "caption", "car", "caravan", "carbon", "card", "cardboard", "cardigan", "care", "carnation", "carol", "carp", "carpenter", "carriage", "carrot", "cart", "cartoon", "case", "cast", "castanet", "cat", "catamaran", "caterpillar", "cathedral", "catsup", "cattle", "cauliflower", "cause", "caution", "cave", "cd", "ceiling", "celery", "celeste", "cell", "cellar", "cello", "celsius", "cement", "cemetery", "cent", "centimeter", "century", "ceramic", "cereal", "certification", "chain", "chair", "chalk", "chance", "change", "channel", "character", "chard", "charles", "chauffeur", "check", "cheek", "cheese", "cheetah", "chef", "chemistry", "cheque", "cherries", "cherry", "chess", "chest", "chick", "chicken", "chicory", "chief", "child", "children", "chill", "chime", "chimpanzee", "chin", "china", "chinese", "chive", "chocolate", "chord", "christmas", "christopher", "chronometer", "church", "cicada", "cinema", "circle", "circulation", "cirrus", "citizenship", "city", "clam", "clarinet", "class", "claus", "clave", "clef", "clerk", "click", "client", "climb", "clipper", "cloakroom", "clock", "close", "closet", "cloth", "cloud", "cloudy", "clover", "club", "clutch", "coach", "coal", "coast", "coat", "cobweb", "cockroach", "cocktail", "cocoa", "cod", "coffee", "coil", "coin", "coke", "cold", "collar", "college", "collision", "colombia", "colon", "colony", "color", "colt", "column", "columnist", "comb", "comfort", "comic", "comma", "command", "commission", "committee", "community", "company", "comparison", "competition", "competitor", "composer",Line truncated
|
||||
+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