From 1542009560886ff443f9762a522f5522219c8ecb Mon Sep 17 00:00:00 2001 From: Jeremie Miller Date: Wed, 4 Dec 2013 11:01:17 -0700 Subject: [PATCH] new version includes the field test app and more docs --- .gitignore | 5 +- README.md | 8 +- fieldtest/README.md | 6 + fieldtest/{rooms.md => groups.md} | 0 fieldtest/tft.js | 216 +++++++++++++++++++++--------- package.json | 7 +- seed/README.md | 13 +- 7 files changed, 170 insertions(+), 85 deletions(-) rename fieldtest/{rooms.md => groups.md} (100%) diff --git a/.gitignore b/.gitignore index 2f16ee4..00c8bd7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ node_modules npm-debug.log -seed/*.json -fieldtest/*.json -fieldtest/debug.log +*.json +*.log diff --git a/README.md b/README.md index f42bf23..b4e1af7 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,13 @@ This module presents a simple high-level API for using [telehash](https://github # Seeds -Telehash apps always need one or more seeds to bootstrap from, the default ones are in [seeds.json](seeds.json). You can run your own seed via: - -`node seed/seed.js` +Telehash apps always need one or more seeds to bootstrap from, the default ones are in [seeds.json](seeds.json). You can run your own seed via `npm start` or manually via `node seed/seed.js`. Take the output JSON, put it in an array and in your own seeds.json file, then load it with `.addSeeds("./seeds.json")`. # "Field Test" Utility -A way to explore telehash is using the field test app which provides a command line of utilities to explore the DHT and connect to other hashnames: - -`node fieldtest/tft.js` +There is a field test command line utility included to explore the DHT and connect to other hashnames, just run `node fieldtest/tft.js`. # Library Interface diff --git a/fieldtest/README.md b/fieldtest/README.md index e69de29..b9ae4a2 100644 --- a/fieldtest/README.md +++ b/fieldtest/README.md @@ -0,0 +1,6 @@ +Command-line telehash fieldtest utility +======================================= + +Just run `node tft.js` and the first time it runs it will ask you for a nickname and store your identity in ./id.json (which can be overridden with `--id ./foo.json`). + +Once running you will have a command line to look at the DHT, seek, ping, send test messages to other instances, create groups, etc. Type `help` to see a list of commands. All debug output is saved in ./debug.log. \ No newline at end of file diff --git a/fieldtest/rooms.md b/fieldtest/groups.md similarity index 100% rename from fieldtest/rooms.md rename to fieldtest/groups.md diff --git a/fieldtest/tft.js b/fieldtest/tft.js index d201346..24ee4eb 100755 --- a/fieldtest/tft.js +++ b/fieldtest/tft.js @@ -6,10 +6,11 @@ var tele = require("../index"); var argv = require("optimist") .usage("Usage: $0 --id id.json --seeds seeds.json") .default("id", "./id.json") + .default("v", "./debug.log") .argv; // write all debug output to a log -var vlog = fs.createWriteStream(path.join(__dirname,"debug.log"), {flags:"a"}); +var vlog = fs.createWriteStream(path.join(process.cwd(),argv.v), {flags:"a"}); vlog.write("starting with "+JSON.stringify(argv)+"\n"); tele.debug(function(){ var args = arguments; @@ -20,16 +21,22 @@ tele.debug(function(){ // set up our readline interface rl = require("readline").createInterface(process.stdin, process.stdout, null); -function log(line){ +function log(){ // hacks! rl.output.write("\x1b[2K\r"); - console.log(line); + var args = arguments; + args = Object.keys(arguments).map(function(k){return args[k]}); + console.log(args.join(" ")); rl._refreshLine() } +process.stdin.on("keypress", function(s, key){ + if(key && key.ctrl && key.name == "c") process.exit(0); + if(key && key.ctrl && key.name == "d") process.exit(0); +}) // load or generate our crypto id var id; -var idfile = path.join(__dirname, argv.id); +var idfile = path.join(process.cwd(),argv.id); if(fs.existsSync(idfile)) { id = require(idfile); @@ -51,35 +58,44 @@ var groups = { get:function(id){ if(groups.ids[id]) return groups.ids[id]; var group = groups.ids[id] = {id:id,members:{}}; - group.add = function(chan){ chan.group = group; }; + group.add = function(chan){ + chan.group = group; + group.members[chan.hashname] = chan; + }; + return group; } }; -var members = {};// TODO finish converting -var chat; + +var me; function init() { rl.setPrompt(id.nick+"> "); rl.prompt(); - chat = tele.hashname(id); - if(argv.seeds) chat.addSeeds(argv.seeds); + me = tele.hashname(id); + if(argv.seeds) me.addSeeds(argv.seeds); - chat.online(function(err){ - log((err?err:"online as "+chat.hashname)); - if(err) process.exit(0); + me.online(function(err){ + log((err?err:"online as "+me.hashname)); }); - chat.listen("chat", function(err, arg, chan, cb){ - if(arg.js.group) groups.get(arg.js.group).add(chan); - handshake(false, arg, chan, cb); + me.listen("message", function(err, arg, chan, cb){ + messageInit(false, arg, chan, cb); chan.send({js:{nick:id.nick}}); }); - chat.listen("members", function(err, arg, chan, cb){ + me.listen("group", function(err, arg, chan, cb){ + if(!arg.js.group) return log("missing group error from",chan.hashname); + groups.get(arg.js.group).add(chan); + groupInit(arg, chan); + chan.send({js:{nick:id.nick}}); + cb(); + }); + me.listen("members", function(err, arg, chan, cb){ // send members in chunks cb(); var group = groups.get(arg.js.group); var mlist = Object.keys(group.members); - mlist.push(chat.hashname); + mlist.push(me.hashname); // always include yourself while(mlist.length > 0) { var chunk = mlist.slice(0, 10); @@ -90,84 +106,150 @@ function init() }); } -function memberMesh(err, arg, chan, cb) +// intitial incoming or answer to outgoing message channel +var nicks = {}; +function messageInit(err, arg, chan, cb) { - if(err && err !== true) return log("error fetching members: "+err); - if(Array.isArray(arg.js.members)) arg.js.members.forEach(function(member){ - if(members[member]) return; - if(member == chat.hashname) return; - var hn = chat.whois(member); - if(hn) hn.start("chat", {js:{nick:id.nick, room:room}}, handshake); - }); + if(err) return log("message handshake err",err); + chan.nick = (arg.js.nick) ? arg.js.nick : chan.hashname.substr(0,6); + nicks[chan.nick] = chan; + log("m["+chan.nick+"] connected"); + chan.callback = function(err, arg, chan, cbMessage){ + if(arg && arg.js.message) log("m["+chan.nick+"]:",arg.js.message); + if(err){ + log("m["+chan.nick+"] disconnected",err); + delete nicks[chan.nick]; + } + cbMessage(); + }; cb(); } -// intitial incoming or answer to outgoing chats -var nicks = {}; -function handshake(err, arg, chan, cb) +// configure a channel for group mode +function groupInit(arg, chan) { - if(err) return console.log("handshake err",err); chan.nick = (arg.js.nick) ? arg.js.nick : chan.hashname.substr(0,6); - nicks[chan.nick] = chan.hashname; - if(!members[chan.hashname]) log(chan.nick+" joined"); - members[chan.hashname] = chan; + log("g["+chan.group.id+"/"+chan.nick+"] joined"); chan.callback = function(err, arg, chan, cbMessage){ - if(arg && arg.js.message) log("["+chan.nick+"] "+arg.js.message); + if(arg && arg.js.message) log("g["+chan.group.id+"/"+chan.nick+"]: "+arg.js.message); if(err) { - var msg = (err !== true)?" ("+err+")":""; - log(chan.nick+" left"+msg); - delete members[chan.hashname]; + log("g["+chan.group.id+"/"+chan.nick+"] left",err); + delete chan.group.members[chan.hashname]; } cbMessage(); }; - cb(); -} - -function blast(msg) -{ - Object.keys(members).forEach(function(member){ - members[member].send({js:{"message":msg}}); - }); } // our chat handler rl.on('line', function(line) { - if(line.indexOf("/") == 0) { - var parts = line.split(" "); - var cmd = parts.shift().substr(1); - if(cmds[cmd]) cmds[cmd](parts.join(" ")); - else log("I don't know how to "+cmd); - }else if(line != "") blast(line); + var parts = line.split(" "); + var cmd = parts.shift(); + if(cmds[cmd]) cmds[cmd](parts); + else log("I don't know how to "+cmd); rl.prompt(); }); var cmds = {}; -cmds.nick = function(nick){ - id.nick = nick; - blast(false, nick); - rl.setPrompt(id.nick+"> "); - rl.prompt(); +cmds.help = cmds["?"] = function(arg){ + log("'quit|done'","exit the app"); + log("'whoami'","your info"); + log("'seek hashname'","look for that hashname in the DHT"); + log("'ping hashname'","try to connect to and get response from that hashname"); + log("'a|all'","show all connected hashnames"); + log("'add hashname'","add a hashname to send messages to"); + log("'m|message nick'","send a message to the nickname"); + log("'w|who'","which nicknames are attached"); + log("'join group'","create a group that others can join"); + log("'join group hashname'","join a group that exists via that hashname"); + log("'gw|gwho group'","see who's in the group"); + log("'gm group'","send a message to the group"); } -cmds.quit = function(err){ - log(err||"poof"); +cmds.quit = cmds.exit = function(arg){ + if(arg[0]) log(arg[0]); process.exit(); } cmds.whoami = function(){ - log(room+"@"+chat.hashname); -} -cmds.who = cmds.whois = function(arg){ - if(!arg) return Object.keys(members).forEach(cmds.who); - if(nicks[arg]) log(arg+" is "+nicks[arg]); - if(members[arg]) log(arg+" is "+members[arg].nick); + log("I am",id.nick,me.address); } cmds["42"] = function(){ log("I hash, therefore I am."); } +cmds.add = function(arg){ + var host = me.whois(arg[0]); + if(!host) return log("invalid hashname",arg[0]); + log("adding",host.hashname); + host.start("message", {js:{nick:id.nick}}, messageInit); +} +cmds.message = cmds.m = function(arg){ + if(!nicks[arg[0]]) return log("unknown recipient",arg[0]); + nicks[arg.shift()].send({js:{message:arg.join(" ")}}); +} +cmds.who = cmds.w = function() +{ + Object.keys(nicks).forEach(function(nick){ + log(nick,nicks[nick].hashname); + }); +} +cmds.all = cmds.a = function() +{ + Object.keys(me.lines).forEach(function(line){ + var hn = me.lines[line]; + log(hn.address,Object.keys(hn.chans).length); + }); +} +cmds.gw = cmds.gwho = function(arg){ + var group = groups.get(arg.shift()); + Object.keys(group.members).forEach(function(member){ + log(group.members[member].nick,group.members[member].hashname); + }); +} +cmds.g = function(arg){ + var group = groups.get(arg.shift()); + Object.keys(group.members).forEach(function(member){ + group.members[member].send({js:{message:arg.join(" ")}}); + }); +} cmds.join = function(arg) { - var parts = arg.split("@"); - var host = chat.whois(parts[1]); - if(!host) return log("invalid id to join"); - host.start("members", {js:{group:parts[0]}}, memberMesh); + var group = groups.get(arg[0]); + if(!arg[1]) return log("g["+group.id+"] created"); + var host = me.whois(arg[1]); + if(!host) return log("invalid group hashname",arg[1]); + log("g["+group.id+"] fetching members"); + host.start("members", {js:{group:group.id}}, function(err, arg, chan, cb) + { + if(err && err !== true) return log("group",group.id,"error fetching members",err); + if(Array.isArray(arg.js.members)) arg.js.members.forEach(function(member){ + if(group.members[member]) return; + if(member == me.hashname) return; + var hn = me.whois(member); + if(!hn) return log("g["+group.id+"] invalid member",member); + hn.start("group", {js:{nick:id.nick, group:group.id}}, function(err, arg, chan, cb){ + if(err) return log("message handshake err",err); + group.add(chan); + groupInit(arg, chan); + cb(); + }); + }); + cb(); + }); +} +cmds.seek = function(arg) +{ + var hn = me.whois(arg[0]); + if(!hn) return log("invalid hashname",arg[0]); + me.seek(hn, function(err){ + if(err) return log("seek failed",hn.hashname,err); + log("seek",hn.hashname,JSON.stringify(hn.vias)); + }); +} +cmds.ping = function(arg) +{ + var hn = me.whois(arg[0]); + if(!hn) return log("invalid hashname",arg[0]); + hn.seekping(function(err){ + if(err) return log("ping failed",hn.hashname,err); + log("ping",hn.address); + }); } diff --git a/package.json b/package.json index 887c419..20223f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "telehash", - "version": "0.1.3", + "version": "0.1.4", "dependencies": { "node-forge": "0.2.x", "thjs":"0.0.x" @@ -16,6 +16,11 @@ "tseed": "./seed/seed.js", "tft": "./fieldtest/tft.js" }, + "scripts": { + "start":"./seed/seed.js", + "seed":"./seed/seed.js", + "tft":"./fieldtest/tft.js" + }, "keywords": [ "telehash", "crypto", diff --git a/seed/README.md b/seed/README.md index 1ed145b..8119baf 100644 --- a/seed/README.md +++ b/seed/README.md @@ -1,11 +1,8 @@ -hash-seed -======= +A minimal telehash seed +======================= -A minimal [telehash](http://telehash.org) seed. +Just run `node seed.js` to start it, and it will give you a JSON entry of it's information. -``` -npm install -npm start -``` +To be a seed you need a well-known / accessible IP address and port, so it tries to guess your network address and use that by default. To manually set either use `--ip 1.2.3.4 --port 5678`. -You can also do a `node seed.js -v` to see debug output, and `--seeds ./seeds.json` to have this seed connect/mesh with others. \ No newline at end of file +You can also do a `-v` to see debug output.