無になろうとしても頭は、色々湧いてくるとか 自分の意志とかいうのは曖昧である
黒猫P@受験生
kur0nekop0315@yabu.me
npub1tcqt...q0l9
hello
最近欲しいものは、 自由で旧武蔵野国在住
中学生です。
信条:
1.完全に安全なシステムはない。
2.不可能に挑め。
3.現実と電子世界を楽しめ
今の世界から救われたくてキリストにすがるくらいなら
自分の力で変えてみせろよとか思う
p2p形式のゲームマーケット考えてたけど、自分のウォレットの秘密鍵をキーに指定してそこからの送信チェーンで検証すればいい事に気づいた。
秘密鍵なら予よき
そもそも部屋のドアがないんだよなぁ
a
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nostr Full Client (NIP-19)</title>
<style>
body { font-family: sans-serif; display:flex; margin:20px; }
#main { width:70%; }
#side { width:30%; padding-left:20px; }
.post {
border:1px solid #999;
padding:10px;
margin-bottom:10px;
background:#fafafa;
}
textarea { width:100%; height:100px; resize:vertical; }
button { margin:5px 0; }
input { width:100%; margin-top:5px; }
</style>
</head>
<body>
<div id="main">
<h2>Nostr Client (NIP-19 Full)</h2>
<button id="setKey">秘密鍵(nsec)設定</button>
<h3>投稿</h3>
<textarea id="text"></textarea>
<button id="send">投稿</button>
<h3>Timeline</h3>
<div id="timeline"></div>
<button id="more">もっと読む</button>
</div>
<div id="side">
<h3>Relays</h3>
<input id="relayInput" placeholder="wss://relay">
<button id="addRelay">追加</button>
<div id="relayList"></div>
</div>
<script>
// =======================================================
// 1. NIP-19 (完全内蔵 bech32)
// =======================================================
const ALPHABET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
function polymod(values){
const GEN=[0x3b6a57b2,0x26508e6d,0x1ea119fa,0x3d4233dd,0x2a1462b3];
let chk=1;
for(let v of values){
let top=chk>>25;
chk=(chk&0x1ffffff)<<5 ^ v;
for(let i=0;i<5;i++){
if((top>>i)&1) chk^=GEN[i];
}
}
return chk;
}
function hrpExpand(hrp){
let out=[];
for(let i=0;i<hrp.length;i++) out.push(hrp.charCodeAt(i)>>5);
out.push(0);
for(let i=0;i<hrp.length;i++) out.push(hrp.charCodeAt(i)&31);
return out;
}
function verify(hrp,data){
return polymod(hrpExpand(hrp).concat(data))===1;
}
function decodeBech32(str){
str=str.toLowerCase();
let pos=str.lastIndexOf("1");
let hrp=str.slice(0,pos);
let data=str.slice(pos+1);
let words=[];
for(let c of data){
let v=ALPHABET.indexOf(c);
if(v===-1) throw "invalid char";
words.push(v);
}
if(!verify(hrp,words)) throw "checksum error";
return {hrp, words:words.slice(0,-6)};
}
function fromWords(words){
let bits=0,val=0,out=[];
for(let w of words){
val=(val<<5)|w;
bits+=5;
while(bits>=8){
out.push((val>>(bits-8))&255);
bits-=8;
}
}
return out;
}
// =======================================================
// 2. secp256k1(署名)
// =======================================================
import('https://cdn.jsdelivr.net/npm/@noble/secp256k1@2.0.0/+esm').then(secp=>window.secp=secp);
// =======================================================
// 3. 状態
// =======================================================
let priv=null;
let pub=null;
let relays=["wss://yabu.me"];
let cache=[];
let shown=0;
let wsMap={};
// =======================================================
// 4. utils
// =======================================================
function hex(b){
return [...b].map(x=>x.toString(16).padStart(2,"0")).join("");
}
function sha256(data){
return secp.utils.sha256(data);
}
function escape(s){
return s.replace(/[&<>"']/g,c=>({
"&":"&","<":"<",">":">","\"":""","'":"'"
}[c]));
}
// =======================================================
// 5. NIP-19 decode
// =======================================================
function decodeNsec(nsec){
let {hrp,words}=decodeBech32(nsec.trim());
if(hrp!=="nsec") throw "not nsec";
let data=fromWords(words);
if(data.length!==32) throw "bad key";
return hex(data);
}
function decodeNpub(npub){
let {hrp,words}=decodeBech32(npub.trim());
if(hrp!=="npub") throw "not npub";
return hex(fromWords(words));
}
// =======================================================
// 6. event id
// =======================================================
async function makeId(ev){
let arr=[
0,
ev.pubkey,
ev.created_at,
ev.kind,
ev.tags,
ev.content
];
let enc=new TextEncoder().encode(JSON.stringify(arr));
let hash=await sha256(enc);
return hex(hash);
}
// =======================================================
// 7. connect relay
// =======================================================
function connect(r){
if(wsMap[r]) return;
let ws=new WebSocket(r);
wsMap[r]=ws;
ws.onopen=()=>{
ws.send(JSON.stringify(["REQ","sub",{kinds:[1],limit:100}]));
};
ws.onmessage=(e)=>{
let d=JSON.parse(e.data);
if(d[0]==="EVENT"){
let ev=d[2];
ev.content=escape(ev.content);
if(!cache.find(x=>x.id===ev.id)){
cache.push(ev);
draw();
}
}
};
}
// =======================================================
// 8. draw
// =======================================================
function draw(){
let div=document.getElementById("timeline");
div.innerHTML="";
let slice=cache.slice().reverse().slice(0,shown+100);
for(let ev of slice){
let d=document.createElement("div");
d.className="post";
d.innerHTML=`
<b>${ev.pubkey.slice(0,16)}...</b><br>
${ev.created_at}<br><br>
${ev.content}
`;
div.appendChild(d);
}
shown+=100;
}
// =======================================================
// 9. send
// =======================================================
async function send(){
if(!priv) return alert("no key");
let text=document.getElementById("text").value;
if(!text.trim()) return alert("empty");
let ev={
kind:1,
created_at:Math.floor(Date.now()/1000),
tags:[],
content:text,
pubkey:pub
};
ev.id=await makeId(ev);
ev.sig=await secp.schnorr.sign(ev.id,priv);
for(let r of relays){
let ws=new WebSocket(r);
ws.onopen=()=>ws.send(JSON.stringify(["EVENT",ev]));
}
}
// =======================================================
// 10. UI
// =======================================================
document.getElementById("setKey").onclick=()=>{
let nsec=prompt("nsec");
try{
priv=decodeNsec(nsec);
pub=hex(secp.getPublicKey(priv,true).slice(1));
alert("OK");
}catch(e){
alert("invalid key");
}
};
document.getElementById("send").onclick=send;
document.getElementById("addRelay").onclick=()=>{
let v=document.getElementById("relayInput").value;
if(v && !relays.includes(v)){
relays.push(v);
connect(v);
}
};
// init
relays.forEach(connect);
</script>
</body>
</html>
だれか、リアタイ更新/投稿に対応させる方法教えてクレメンス


メモWeb
メモWeb
メモWebはブラウザ上のメモ帳です。ブラウザがあれば、自分だけのメモを保存できます。スマートフォンやパ...
あめちゃん?
超てんちゃん?
nostterで、プロフィールだけ見れるの作る
のすた
は?


は? 完全に動作するのを作れと言ったよな?


いま、gptにクライアント作らせてる
hoge_1_500000

nostrってマスコットキャラいないの?
何の話ししてる?