show comment on this URL
by
Nikola
2024-12-31 [2024/12/31 18:07:47]
このページに言及しているNostrのコメントを表示する
@@ -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 です。