import mute list to Rabbit

  • /*
     * @title import mute list to Rabbit
     * @description リレーからkind10000イベントを取得してRabbitにミュートリストをインポートする
     * @include https://rabbit.syusui.net/*
     * @license CC0 1.0
     * @require
     */
    
    (async () => {
    	if (typeof window === 'undefined') {
    		await import('websocket-polyfill');
    	}
    
    	let relayUrl = 'wss://relay-jp.nostr.wirednet.jp';
    
    	const getPubkey = async () => {
    		let pubkey; //Node.jsでデバッグする際はここに初期値を入れてください。無ければ最新の誰かのイベントで代用します。
    		const nostr = globalThis.window?.nostr;
    		if (nostr?.getPublicKey) {
    			try {
    				pubkey = await nostr.getPublicKey();
    			} catch (error) {
    				console.warn(error);
    			}
    		}
    		return pubkey;
    	};
    
    	const getEvent10000 = async (pubkey) => {
    		return new Promise((resolve) => {
    			const ws = new WebSocket(relayUrl);
    			const subscription_id = 'getmutelistinrabbit';
    			let res;
    			ws.onopen = () => {
    				const req = ['REQ', subscription_id, pubkey ? { kinds: [10000], authors: [pubkey] } : { kinds: [10000], limit: 1 }];
    				ws.send(JSON.stringify(req));
    			};
    			ws.onmessage = (e) => {
    				const msg = JSON.parse(e.data);
    				switch (msg[0]) {
    					case 'EVENT':
    						res = 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 getDecrypt = (content) => {
    		const nostr = globalThis.window?.nostr;
    		const isNIP04 = content.includes('?iv=');
    		if (isNIP04 && nostr?.nip04?.decrypt !== undefined) {
    			return nostr.nip04.decrypt;
    		} else if (nostr?.nip44?.decrypt !== undefined) {
    			return nostr.nip44.decrypt;
    		}
    		return null;
    	};
    
    	const getMuteList = async (event, pubkey) => {
    		let mutedPubkeys = event.tags.filter((tag) => tag.length >= 2 && tag[0] === 'p').map((tag) => tag[1]) ?? [];
    		let mutedChannels = event.tags.filter((tag) => tag.length >= 2 && tag[0] === 'e').map((tag) => tag[1]) ?? [];
    		let mutedWords = event.tags.filter((tag) => tag.length >= 2 && tag[0] === 'word').map((tag) => tag[1]) ?? [];
    		let mutedHashtags = event.tags.filter((tag) => tag.length >= 2 && tag[0] === 't').map((tag) => tag[1]) ?? [];
    		if (event.content.length > 0) {
    			const decrypt = getDecrypt(event.content);
    			if (decrypt !== null) {
    				try {
    					const content = await decrypt(pubkey, event.content);
    					const list = JSON.parse(content);
    					mutedPubkeys = mutedPubkeys.concat(list.filter((tag) => tag.length >= 2 && tag[0] === 'p').map((tag) => tag[1]));
    					mutedChannels = mutedChannels.concat(list.filter((tag) => tag.length >= 2 && tag[0] === 'e').map((tag) => tag[1]));
    					mutedWords = mutedWords.concat(list.filter((tag) => tag.length >= 2 && tag[0] === 'word').map((tag) => tag[1]));
    					mutedHashtags = mutedHashtags.concat(list.filter((tag) => tag.length >= 2 && tag[0] === 't').map((tag) => tag[1]));
    				} catch (error) {
    					console.warn(error);
    				}
    			}
    		}
    		return [mutedPubkeys, Array.from(new Set(mutedWords.concat(mutedHashtags.map((t) => '#' + t)))), mutedChannels];
    	};
    
    	const main = async () => {
    		relayUrl = window.prompt('Input relay URL.', relayUrl);
    		if (!URL.canParse(relayUrl)) {
    			console.warn(`Invalid URL: ${relayUrl}`);
    			return;
    		}
    		let pubkey;
    		try {
    			pubkey = await getPubkey();
    		} catch (error) {
    			console.warn(error);
    			return;
    		}
    		if (globalThis.window && !pubkey) {
    			console.warn('pubkey is empty.');
    			return;
    		}
    		let ev10000;
    		try {
    			ev10000 = await getEvent10000(pubkey);
    		} catch (error) {
    			console.warn(error);
    			return;
    		}
    		const [mutedPubkeysList, mutedKeywordsList, mutedChannelList] = await getMuteList(ev10000, pubkey);
    		if (globalThis.window) {
    			const config = JSON.parse(localStorage['RabbitConfig']);
    			config.mutedPubkeys = Array.from(new Set([...config.mutedPubkeys, ...mutedPubkeysList]));
    			config.mutedKeywords = Array.from(new Set([...config.mutedKeywords, ...mutedKeywordsList]));
    			config.mutedThreads = Array.from(new Set([...config.mutedThreads, ...mutedChannelList]));
    			localStorage['RabbitConfig'] = JSON.stringify(config);
    			alert('Complete. Please reload by yourself.');
    		} else {
    			console.log({ mutedPubkeysList, mutedKeywordsList });
    			console.log('Complete.');
    		}
    	};
    
    	main();
    })();
    
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。

History

  1. 2025/08/21 08:18:27 - 08/21
  2. 2024/11/09 17:29:02 - 2024-11-09
  3. 2024/08/08 19:45:30 - 2024-08-08
  4. 2024/03/06 09:56:51 - 2024-03-06
  5. 2024/03/06 09:14:31 - 2024-03-06
  6. 2024/03/05 22:45:41 - 2024-03-05
  7. 2024/03/05 22:45:07 - 2024-03-05
  8. 2024/03/05 19:59:02 - 2024-03-05
  9. 2024/03/05 19:56:08 - 2024-03-05