show comment on this URL

    @@ -9,17 +9,13 @@ (() => { const relayUrl = 'wss://yabu.me/'; - const getEvent = (url) => { + const getEvent = (filter) => { return new Promise((resolve) => { const ws = new WebSocket(relayUrl); const subscription_id = 'getcommentofthispage'; let res = []; ws.onopen = () => { - const req = [ - 'REQ', - subscription_id, - { kinds: [1], '#r': [url], limit: 10, until: Math.floor(Date.now() / 1000) } - ]; + const req = ['REQ', subscription_id, filter]; ws.send(JSON.stringify(req)); }; ws.onmessage = (e) => { @@ -52,15 +48,35 @@ }; const main = async () => { - let events; + const url = location.href.replace(/#.*$/, ''); + const filter1 = { kinds: [1], '#r': [url], limit: 10, until: Math.floor(Date.now() / 1000) }; + let events1; try { - events = await getEvent(location.href.replace(/#.*$/, '')); + events1 = await getEvent(filter1); } catch (error) { console.warn(error); return; } - const comments = events.slice(0, 10).map((ev) => ev.content); - console.log(comments); + const pubkeys = events1.map((ev) => ev.pubkey); + const filter0 = { kinds: [0], authors: pubkeys, until: Math.floor(Date.now() / 1000) }; + let events0; + try { + events0 = await getEvent(filter0); + } catch (error) { + console.warn(error); + return; + } + const profs = new Map(); + for (const ev0 of events0) { + let prof; + try { + prof = JSON.parse(ev0.content); + } catch (error) { + console.warn(error); + continue; + } + profs.set(ev0.pubkey, prof); + } const div = document.createElement('div'); div.style.position = 'fixed'; div.style.top = '10px'; @@ -74,11 +90,31 @@ div.style.display = 'none'; }); document.body.append(div); - for (const comment of comments) { - const text = document.createTextNode(comment); + for (const ev of events1) { + const prof = profs.get(ev.pubkey); + const img = document.createElement('img'); + img.src = prof.picture ?? ''; + img.width = 32; + img.height = 32; + const time = new Date(1000 * ev.created_at).toLocaleString(); + const name = document.createTextNode(`${prof.display_name} @${prof.name} ${time}`); + const span = document.createElement('span'); + span.append(img); + span.append(name); + div.append( + tag( + 'div', + { + style: + 'max-width: 30em; margin-bottom: 2px; padding: 5px; background-color: rgba(50, 0, 50, 0.7); color: #fff; border-radius: 10px;' + }, + [span] + ) + ); + const text = document.createTextNode(ev.content); div.append( tag( - 'p', + 'div', { style: 'max-width: 30em; margin-bottom: 2px; padding: 5px; background-color: rgba(0, 0, 0, 0.7); color: #fff; border-radius: 10px;'
  • /*
     * @title show comment on this URL
     * @description このページに言及しているNostrのコメントを表示する
     * @include https://*
     * @license CC0 1.0
     * @require
     */
    
    (() => {
    	const relayUrl = 'wss://yabu.me/';
    
    	const getEvent = (filter) => {
    		return new Promise((resolve) => {
    			const ws = new WebSocket(relayUrl);
    			const subscription_id = 'getcommentofthispage';
    			let res = [];
    			ws.onopen = () => {
    				const req = ['REQ', subscription_id, filter];
    				ws.send(JSON.stringify(req));
    			};
    			ws.onmessage = (e) => {
    				const msg = JSON.parse(e.data);
    				switch (msg[0]) {
    					case 'EVENT':
    						res.push(msg[2]);
    						break;
    					case 'EOSE':
    						ws.send(JSON.stringify(['CLOSE', subscription_id]));
    						ws.close();
    						resolve(res);
    						break;
    					default:
    						console.log(msg);
    						break;
    				}
    			};
    			ws.onerror = () => {
    				console.error('failed to connect');
    			};
    		});
    	};
    
    	const tag = (name, props = {}, children = []) => {
    		const e = Object.assign(document.createElement(name), props);
    		if (typeof props.style === 'object') Object.assign(e.style, props.style);
    		(children.forEach ? children : [children]).forEach((c) => e.appendChild(c));
    		return e;
    	};
    
    	const main = async () => {
    		const url = location.href.replace(/#.*$/, '');
    		const filter1 = { kinds: [1], '#r': [url], limit: 10, until: Math.floor(Date.now() / 1000) };
    		let events1;
    		try {
    			events1 = await getEvent(filter1);
    		} catch (error) {
    			console.warn(error);
    			return;
    		}
    		const pubkeys = events1.map((ev) => ev.pubkey);
    		const filter0 = { kinds: [0], authors: pubkeys, until: Math.floor(Date.now() / 1000) };
    		let events0;
    		try {
    			events0 = await getEvent(filter0);
    		} catch (error) {
    			console.warn(error);
    			return;
    		}
    		const profs = new Map();
    		for (const ev0 of events0) {
    			let prof;
    			try {
    				prof = JSON.parse(ev0.content);
    			} catch (error) {
    				console.warn(error);
    				continue;
    			}
    			profs.set(ev0.pubkey, prof);
    		}
    		const div = document.createElement('div');
    		div.style.position = 'fixed';
    		div.style.top = '10px';
    		div.style.right = '10px';
    		div.style.whiteSpace = 'pre-wrap';
    		div.style.marginLeft = '10px';
    		div.style.paddingBottom = '100px';
    		div.style.maxHeight = '100%';
    		div.style.overflowY = 'scroll';
    		div.addEventListener('click', () => {
    			div.style.display = 'none';
    		});
    		document.body.append(div);
    		for (const ev of events1) {
    			const prof = profs.get(ev.pubkey);
    			const img = document.createElement('img');
    			img.src = prof.picture ?? '';
    			img.width = 32;
    			img.height = 32;
    			const time = new Date(1000 * ev.created_at).toLocaleString();
    			const name = document.createTextNode(`${prof.display_name} @${prof.name} ${time}`);
    			const span = document.createElement('span');
    			span.append(img);
    			span.append(name);
    			div.append(
    				tag(
    					'div',
    					{
    						style:
    							'max-width: 30em; margin-bottom: 2px; padding: 5px; background-color: rgba(50, 0, 50, 0.7); color: #fff; border-radius: 10px;'
    					},
    					[span]
    				)
    			);
    			const text = document.createTextNode(ev.content);
    			div.append(
    				tag(
    					'div',
    					{
    						style:
    							'max-width: 30em; margin-bottom: 2px; padding: 5px; background-color: rgba(0, 0, 0, 0.7); color: #fff; border-radius: 10px;'
    					},
    					[text]
    				)
    			);
    		}
    	};
    
    	main();
    })();
    
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。

History

  1. 2024/12/31 18:07:47 - 2024-12-31
  2. 2024/12/27 12:08:27 - 2024-12-27
  3. 2024/12/27 10:42:16 - 2024-12-27
  4. 2024/12/27 10:28:30 - 2024-12-27
  5. 2024/12/27 10:12:52 - 2024-12-27