anonymous クロスマーケット2のワールドをマイリストへ一括登録するブックマークレットです。
Mozilla Public v 2.0 JavaScript
2021年02月23日
Copy Clone
javascript:
/**
 * @file クロスマーケット2のワールドをマイリストへ一括登録するブックマークレットです。
 * 登録完了まで数秒ほどかかり、完了したことを通知するダイアログが表示されます。
 * @version 1.0.0
 * @license MPL-2.0
 * @author 100の人
 * @see {@link https://twitter.com/esperecyan}
 */

(async function () {
'use strict';

/**
 * 何番目のマイリストへ登録するかを 1 〜 4 で指定。
 * @constant {number}
 */
const NUMBER = 4;

/**
 * 登録するワールドID。
 * @constant {string[]}
 */
const WORLD_IDS = [
	'wrld_3aaf1c48-05bf-4765-a100-8315b1c11ca4',
	'wrld_54396534-69ad-4b1f-b9e2-48debe2025f1',
	'wrld_00dc34ea-ffe3-483a-98a6-2f364d160432',
	'wrld_eb1e11d2-28d2-468b-b913-be84f311225a',
	'wrld_b64f814d-afe5-493d-8030-7e24435027c7',
	'wrld_1a32bc38-ecf5-43c8-bd87-0cbe85ab3127',
	'wrld_c187fd45-a9a5-44c8-87d2-8bc83389b8f8',
	'wrld_f1061d17-ac82-4fce-b205-e71df5a1e3e8',
	'wrld_abb15d41-df9a-4bd7-9292-f1f95572b9c2',
	'wrld_18d313cf-2e96-45fb-84a6-c6954d7063af',
	'wrld_89f83bfb-5651-451d-81c6-dd691d630fb7',
	'wrld_f02d9269-200c-4b2f-9d2f-bc1eb361cbd9',
	'wrld_c78bd309-b349-4e49-b1e4-33d8f006e517',
];

/**
 * マイリストのグループ名。インデックス順。
 * @constant {string[]}
 */
const MYLIST_NAMES = ['worlds0', 'worlds2', 'worlds3', 'worlds4'];

/**
 * 一つのブックマークグループの最大登録数。
 * @constant {number}
 */
const MAX_FAVORITES_COUNT_PER_GROUP = 32;

/**
 * 一度に取得できる最大の要素数。
 * @constant {number}
 */
const MAX_ITEMS_COUNT = 100;

/**
 * JSONファイルをオブジェクトとして取得します。
 * @param {RequestInfo} url
 * @param {RequestInit}
 * @returns {Promise.<(Object|Array)>} OKステータスでなければ失敗します。
 */
async function fetchJSON(input, requestInit = {credentials: 'same-origin'})
{
	const response = await fetch(input, requestInit);
	return response.ok
		? response.json()
		: Promise.reject(new Error(`${response.status}  ${response.statusText}\n${await response.text()}`));
}

/**
 * マイリストを全件取得します。
 * @see [List Favorites — VRChat API Documentation]{@link https://vrchatapi.github.io/#/FavoritesAPI/ListAllFavorites}
 * @returns {Promise.<(string|string[])>} ワールドID一覧の配列の、マイリスト順の配列。
 */
async function fetchMylistIndexWorldIdsPairs()
{
	const mylistIndexWorldIdsPairs = [[], [], [], []];
	let offset = 0;
	while (true) {
		const favorites = await fetchJSON(`/api/1/favorites/?type=world&n=${MAX_ITEMS_COUNT}&offset=${offset}`);

		for (const favorite of favorites) {
			mylistIndexWorldIdsPairs[MYLIST_NAMES.indexOf(favorite.tags[0])].push(favorite);
		}

		if (favorites.length < MAX_ITEMS_COUNT) {
			break;
		}

		offset += favorites.length;
	}
	return mylistIndexWorldIdsPairs;
}

/**
 * マイリストへワールドを追加します。
 * @see [Add Favorite — VRChat API Documentation]{@link https://vrchatapi.github.io/#/FavoritesAPI/AddFavorite}
 * @param {number} mylistIndex - 何番目のマイリストへ追加するか、0から始まるインデックス。
 * @param {string} worldId
 * @returns {Promise.<void>}
 */
async function addWorldToMylist(mylistIndex, worldId)
{
	await fetch('/api/1/favorites', {
		method: 'POST',
		headers: { 'content-type': 'application/json' },
		credentials: 'same-origin',
		body: JSON.stringify({ type: 'world', favoriteId: worldId, tags: [ MYLIST_NAMES[mylistIndex] ] }),
	});
}

const mylistIndexWorldIdsPairs = await fetchMylistIndexWorldIdsPairs();
const mylistWorldIds = mylistIndexWorldIdsPairs.flat();

const worldIds = WORLD_IDS.filter(id => !mylistWorldIds.includes(id));
if (worldIds.length > MAX_FAVORITES_COUNT_PER_GROUP - mylistIndexWorldIdsPairs[NUMBER - 1].length) {
	alert(`${NUMBER}番目のマイリストには空きが不足しています。`);
	return;
}

for (const worldId of worldIds.reverse()) {
	await addWorldToMylist(NUMBER - 1, worldId);
}

alert(`${NUMBER}番目のマイリストへの登録が完了しました。`);

})().catch(function (exception) {
	console.error(exception);
	alert('エラーが発生しました: ' + exception + ('stack' in exception ? '\n\n' + exception.stack : ''));
});
javascript:
/**
 * @file クロスマーケット2のワールドをマイリストへ一括登録するブックマークレットです。
 * 登録完了まで数秒ほどかかり、完了したことを通知するダイアログが表示されます。
 * @version 1.0.0
 * @license MPL-2.0
 * @author 100の人
 * @see {@link https://twitter.com/esperecyan}
 */

(async function () {
'use strict';

/**
 * 何番目のマイリストへ登録するかを 1 〜 4 で指定。
 * @constant {number}
 */
const NUMBER = 4;

/**
 * 登録するワールドID。
 * @constant {string[]}
 */
const WORLD_IDS = [
	'wrld_3aaf1c48-05bf-4765-a100-8315b1c11ca4',
	'wrld_54396534-69ad-4b1f-b9e2-48debe2025f1',
	'wrld_00dc34ea-ffe3-483a-98a6-2f364d160432',
	'wrld_eb1e11d2-28d2-468b-b913-be84f311225a',
	'wrld_b64f814d-afe5-493d-8030-7e24435027c7',
	'wrld_1a32bc38-ecf5-43c8-bd87-0cbe85ab3127',
	'wrld_c187fd45-a9a5-44c8-87d2-8bc83389b8f8',
	'wrld_f1061d17-ac82-4fce-b205-e71df5a1e3e8',
	'wrld_abb15d41-df9a-4bd7-9292-f1f95572b9c2',
	'wrld_18d313cf-2e96-45fb-84a6-c6954d7063af',
	'wrld_89f83bfb-5651-451d-81c6-dd691d630fb7',
	'wrld_f02d9269-200c-4b2f-9d2f-bc1eb361cbd9',
	'wrld_c78bd309-b349-4e49-b1e4-33d8f006e517',
];

/**
 * マイリストのグループ名。インデックス順。
 * @constant {string[]}
 */
const MYLIST_NAMES = ['worlds0', 'worlds2', 'worlds3', 'worlds4'];

/**
 * 一つのブックマークグループの最大登録数。
 * @constant {number}
 */
const MAX_FAVORITES_COUNT_PER_GROUP = 32;

/**
 * 一度に取得できる最大の要素数。
 * @constant {number}
 */
const MAX_ITEMS_COUNT = 100;

/**
 * JSONファイルをオブジェクトとして取得します。
 * @param {RequestInfo} url
 * @param {RequestInit}
 * @returns {Promise.<(Object|Array)>} OKステータスでなければ失敗します。
 */
async function fetchJSON(input, requestInit = {credentials: 'same-origin'})
{
	const response = await fetch(input, requestInit);
	return response.ok
		? response.json()
		: Promise.reject(new Error(`${response.status}  ${response.statusText}\n${await response.text()}`));
}

/**
 * マイリストを全件取得します。
 * @see [List Favorites — VRChat API Documentation]{@link https://vrchatapi.github.io/#/FavoritesAPI/ListAllFavorites}
 * @returns {Promise.<(string|string[])>} ワールドID一覧の配列の、マイリスト順の配列。
 */
async function fetchMylistIndexWorldIdsPairs()
{
	const mylistIndexWorldIdsPairs = [[], [], [], []];
	let offset = 0;
	while (true) {
		const favorites = await fetchJSON(`/api/1/favorites/?type=world&n=${MAX_ITEMS_COUNT}&offset=${offset}`);

		for (const favorite of favorites) {
			mylistIndexWorldIdsPairs[MYLIST_NAMES.indexOf(favorite.tags[0])].push(favorite);
		}

		if (favorites.length < MAX_ITEMS_COUNT) {
			break;
		}

		offset += favorites.length;
	}
	return mylistIndexWorldIdsPairs;
}

/**
 * マイリストへワールドを追加します。
 * @see [Add Favorite — VRChat API Documentation]{@link https://vrchatapi.github.io/#/FavoritesAPI/AddFavorite}
 * @param {number} mylistIndex - 何番目のマイリストへ追加するか、0から始まるインデックス。
 * @param {string} worldId
 * @returns {Promise.<void>}
 */
async function addWorldToMylist(mylistIndex, worldId)
{
	await fetch('/api/1/favorites', {
		method: 'POST',
		headers: { 'content-type': 'application/json' },
		credentials: 'same-origin',
		body: JSON.stringify({ type: 'world', favoriteId: worldId, tags: [ MYLIST_NAMES[mylistIndex] ] }),
	});
}

const mylistIndexWorldIdsPairs = await fetchMylistIndexWorldIdsPairs();
const mylistWorldIds = mylistIndexWorldIdsPairs.flat();

const worldIds = WORLD_IDS.filter(id => !mylistWorldIds.includes(id));
if (worldIds.length > MAX_FAVORITES_COUNT_PER_GROUP - mylistIndexWorldIdsPairs[NUMBER - 1].length) {
	alert(`${NUMBER}番目のマイリストには空きが不足しています。`);
	return;
}

for (const worldId of worldIds.reverse()) {
	await addWorldToMylist(NUMBER - 1, worldId);
}

alert(`${NUMBER}番目のマイリストへの登録が完了しました。`);

})().catch(function (exception) {
	console.error(exception);
	alert('エラーが発生しました: ' + exception + ('stack' in exception ? '\n\n' + exception.stack : ''));
});
登録完了まで数秒ほどかかり、完了したことを通知するダイアログが表示されます。
No one still commented. Please first comment.
Output