import emoji list to Rabbit

    @@ -71,12 +71,16 @@ const ary = atag.split(':'); filters.push({ kinds: [parseInt(ary[0])], authors: [ary[1]], '#d': [ary[2]] }); } + const sliceByNumber = (array, number) => { + const length = Math.ceil(array.length / number); + return new Array(length).fill(undefined).map((_, i) => array.slice(i * number, (i + 1) * number)); + }; let emojiMap = new Map(); - for (const filter of filters) { - await getEvent30030(filter); + for (const filterGroup of sliceByNumber(filters, 10)) { + await getEvent30030(filterGroup); } return emojiMap; - async function getEvent30030(filter) { + async function getEvent30030(filters) { return new Promise((resolve) => { const ws = new WebSocket(relayUrl); const subscription_id = 'getemojiinrabbit'; @@ -84,7 +88,7 @@ const req = [ 'REQ', subscription_id, - filter + ...filters ]; ws.send(JSON.stringify(req)); }; @@ -172,4 +176,4 @@ }; main(); -})(); +})();
  • /*
     * @title import emoji list to Rabbit
     * @description リレーからkind10030,kind30030イベントを取得して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 getEvent10030 = async (pubkey) => {
    		return new Promise((resolve) => {
    			const ws = new WebSocket(relayUrl);
    			const subscription_id = 'getemojilistinrabbit';
    			let res;
    			ws.onopen = () => {
    				const req = [
    					'REQ',
    					subscription_id,
    					pubkey ? { 'kinds': [10030], 'authors': [pubkey] } : { 'kinds': [10030], '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 getEmojiList = async (ev10030) => {
    		const atags = ev10030.tags.filter(tag => tag.length >= 2 && tag[0] === 'a').map(tag => tag[1]);
    		if (atags.length === 0) {
    			return [];
    		}
    		const filters = [];
    		for (const atag of atags) {
    			const ary = atag.split(':');
    			filters.push({ kinds: [parseInt(ary[0])], authors: [ary[1]], '#d': [ary[2]] });
    		}
    		const sliceByNumber = (array, number) => {
    			const length = Math.ceil(array.length / number);
    			return new Array(length).fill(undefined).map((_, i) => array.slice(i * number, (i + 1) * number));
    		};
    		let emojiMap = new Map();
    		for (const filterGroup of sliceByNumber(filters, 10)) {
    			await getEvent30030(filterGroup);
    		}
    		return emojiMap;
    		async function getEvent30030(filters) {
    			return new Promise((resolve) => {
    				const ws = new WebSocket(relayUrl);
    				const subscription_id = 'getemojiinrabbit';
    				ws.onopen = () => {
    					const req = [
    						'REQ',
    						subscription_id,
    						...filters
    					];
    					ws.send(JSON.stringify(req));
    				};
    				ws.onmessage = (e) => {
    					const msg = JSON.parse(e.data);
    					switch ( msg[0] ) {
    					case 'EVENT':
    						for (const tag of msg[2].tags.filter(tag => tag.length >= 2 && tag[0] === 'emoji')) {
    							let key = tag[1];
    							while (emojiMap.has(key)) {
    								key += '_';
    							}
    							key = key.replaceAll('-', '_');
    							if (/\W/.test(key))
    								continue;
    							emojiMap.set(key, tag[2]);
    						}
    						break;
    					case 'EOSE':
    						ws.send(JSON.stringify(['CLOSE', subscription_id]));
    						ws.close();
    						resolve();
    						break;
    					default:
    						console.log(msg);
    						break;
    					}
    				};
    				ws.onerror = () => {
    					console.error('failed to connect');
    				}
    			});
    		};
    	};
    
    	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 ev10030;
    		try {
    			ev10030 = await getEvent10030(pubkey);
    		} catch (error) {
    			console.warn(error);
    			return;
    		}
    		if (!ev10030) {
    			return;
    		}
    		const emojiMap = await getEmojiList(ev10030);
    		if (globalThis.window) {
    			const config = JSON.parse(localStorage['RabbitConfig']);
    			for (const [key, value] of emojiMap) {
    				if (config.customEmojis[key] === undefined) {
    					config.customEmojis[key] = { shortcode: key, url: value };
    				}
    				else if (config.customEmojis[key].shortcode === key && config.customEmojis[key].url !== value) {
    					let newkey = key;
    					while (config.customEmojis[newkey].shortcode === newkey && config.customEmojis[newkey].url !== value) {
    						newkey += '_';
    					}
    					config.customEmojis[newkey] = { shortcode: newkey, url: value };
    				}
    			}
    			localStorage['RabbitConfig'] = JSON.stringify(config);
    			alert('Complete. Please reload by yourself.');
    		}
    		else {
    			console.log(emojiMap);
    			console.log('Complete.');
    		}
    	};
    
    	main();
    })();
    
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。