import emoji list to Rabbit

    
      
  • /*
     * @title import emoji list to Rabbit
     * @description リレーからkind10030,kind30030イベントを取得してRabbitにカスタム絵文字リストをインポートする
     * @include https://rabbit.syusui.net/*
     * @license CC0 1.0
     * @require 
     */
    
    (() => {
    	const relayUrl = 'wss://relay-jp.nostr.wirednet.jp'; 
    
    	const getPubkey = async () => {
    		let pubkey;
    		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,
    					{ 'kinds': [10030], 'authors': [pubkey] }
    				];
    				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]));
    					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.lengt === 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]] });
    		}
    		let emojiMap = new Map();
    		for (const filter of filters) {
    			await getEvent30030(filter);
    		}
    		return emojiMap;
    		async function getEvent30030(filter) {
    			return new Promise((resolve) => {
    				const ws = new WebSocket(relayUrl);
    				const subscription_id = 'getemojiinrabbit';
    				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':
    						for (const tag of msg[2].tags.filter(tag => tag.length >= 2 && tag[0] === 'emoji')) {
    							emojiMap.set(tag[1], tag[2]);
    						}
    						break;
    					case 'EOSE':
    						ws.send(JSON.stringify(['CLOSE', subscription_id]));
    						resolve();
    						break;
    					default:
    						console.log(msg);
    						break;
    					}
    				};
    				ws.onerror = () => {
    					console.error('failed to connect');
    				}
    			});
    		};
    	};
    
    	const main = async () => {
    		let pubkey
    		try {
    			pubkey = await getPubkey();
    		} catch (error) {
    			console.warn(error);
    			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) {
    				config.customEmojis[key] = { shortcode: key, url: value };
    			}
    			localStorage['RabbitConfig'] = JSON.stringify(config);
    		}
    	};
    
    	main();
    })();
    
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。