import emoji list to Rabbit
    
  
    
  
  by 
Nikola
  2024-12-20 [2024/12/20 06:26:28]
  
  リレーからkind10030,kind30030イベントを取得してRabbitにカスタム絵文字リストをインポートする
 
  - 
      
  
 
  /*
 * @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] !== undefined &&
						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 です。