您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Open Users or Notes on services that supports ActivityPub on your main Misskey server. Open the home page of this script and execute the user script command to set the main server.
当前为
// ==UserScript== // @name Fediverse Open on Main Server // @name:ja Fediverse メインサーバーで開く // @description Open Users or Notes on services that supports ActivityPub on your main Misskey server. Open the home page of this script and execute the user script command to set the main server. // @description:ja ActivityPubに対応しているサービスのUser、またはNoteを、メインで利用しているMisskeyサーバーで開きます。このスクリプトのホームページを開いて、ユーザースクリプトコマンドを実行して、メインサーバーを指定してください。 // @namespace https://gf.qytechs.cn/users/137 // @version 1.0.0 // @match https://gf.qytechs.cn/*/scripts/474630-* // @match https://mastodon.social/* // @match https://pawoo.net/* // @match https://mstdn.jp/* // @match https://misskey.io/* // @match https://mastodon.cloud/* // @match https://fedibird.com/* // @match https://nijimiss.moe/* // @match https://buicha.social/* // @match https://misskey.niri.la/* // @match https://vcasskey.net/* // @require https://gf.qytechs.cn/scripts/19616/code/utilities.js?version=895049 // @license MPL-2.0 // @contributionURL https://www.amazon.co.jp/registry/wishlist/E7PJ5C3K7AM2 // @compatible Edge // @compatible Firefox Firefoxを推奨 / Firefox is recommended // @compatible Opera // @compatible Chrome // @grant GM.registerMenuCommand // @grant GM.setValue // @grant GM.getValue // @grant GM.deleteValue // @grant GM_xmlhttpRequest // @run-at document-start // @noframes // @icon https://codeberg.org/fediverse/distributopia/raw/branch/main/all-logos-in-one-basket/public/basket/Fediverse_logo_proposal-1-min.svg // @author 100の人 // @homepageURL https://gf.qytechs.cn/scripts/474630 // ==/UserScript== /*global Gettext, _, h */ 'use strict'; // L10N Gettext.setLocalizedTexts({ /*eslint-disable quote-props, max-len */ 'ja': { 'Fediverse Open on Main Server': 'Fediverse メインサーバーで開く', 'Fediverse Set your main server': 'Fediverse メインサーバーの設定', 'Main server URL': 'メインサーバーのURL', 'Add the URLs of the main server and the server to which you want to add the user script command to the “User @match” in the user script settings in the format like “https://example.com/*”.': 'メインサーバー、およびユーザースクリプトコマンドを追加したいサーバーのURLを、「https://example.com/*」のような形式で、ユーザースクリプト設定の「ユーザー @match」へ追加してください。', 'Cancel': 'キャンセル', 'OK': 'OK', 'Failed to look up.': '照会に失敗しました。', 'Open the home page of this script and configure from the user script command.': '当スクリプトのホームページを開いて、ユーザースクリプトコマンドから設定を行ってください。', }, /*eslint-enable quote-props, max-len */ }); Gettext.originalLocale = 'en'; Gettext.setLocale(navigator.language); class HttpError extends Error { name = 'HttpError'; response; /** * @param {Response} response */ constructor(response) { super(response.status + ' ' + response.statusText + '\n\n'); this.response = response; response.text().then(body => { this.message += '\n\n' + body; }); } } /** * @param {string} serverURL * @returns {Promise.<string>} */ async function miAuth(serverURL) { const sessionId = crypto.randomUUID(); await Promise.all([ GM.setValue('miAuthSessionId', sessionId), GM.setValue('urlWaitingMiAuth', location.href) ]); location.assign(`${serverURL}/miauth/${sessionId}?${new URLSearchParams({ name: _('Fediverse Open on Main Server'), callback: serverURL, })}`); } /** * @param {string} accessToken * @param {string} url * @returns {Promise.<string>} */ async function lookUpOnMisskey(serverURL, accessToken, url) { let json; const apiURL = `${serverURL}/api/ap/show`; const requestInit = { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ i: accessToken, uri: url }), }; if (typeof GM_xmlhttpRequest !== 'undefined') { //eslint-disable-line camelcase // 通信がContent Security PolicyによってブロックされるViolemntmonkeyの不具合を回避 const response = await new Promise(function (resolve, reject) { GM_xmlhttpRequest(Object.assign({ //eslint-disable-line new-cap url: apiURL, data: requestInit.body, onload: resolve, onerror: reject, ontimeout: reject, }, requestInit)); }); if (response.status !== 200) { return Promise.reject(new HttpError(new Response(response.responseText, response))); } json = JSON.parse(response.responseText); } else { const response = await fetch(apiURL, requestInit); if (!response.ok) { return Promise.reject(new HttpError(response)); } json = await response.json(); } const { type, object: { username, host, id } } = json; switch (type) { case 'User': return serverURL + '/@' + username + (host ? '@' + host : ''); case 'Note': return serverURL + '/notes/' + id; } } switch (location.host) { case 'gf.qytechs.cn': { /** @type {HTMLDialogElement} */ let dialog, form; GM.registerMenuCommand(_('Fediverse Set your main server'), async function () { const [ url ] = await Promise.all([ 'url' ].map(name => GM.getValue(name, ''))); if (!dialog) { document.body.insertAdjacentHTML('beforeend', h`<dialog> <form method="dialog"> <input type="hidden" name="application" value="Misskey" /> <p><label> ${_('Main server URL')} <input type="url" name="url" placeholder="https://example.com" pattern="https?://[^\\/]+" /> </label></p> <p>${_('Add the URLs of the main server and the server to which you want to add the user script command to the “User @match” in the user script settings in the format like “https://example.com/*”.' /* eslint-disable-line max-len */)}</p> <button name="cancel">${_('Cancel')}</button> <button>${_('OK')}</button> </form> </dialog>`); dialog = document.body.lastElementChild; form = dialog.getElementsByTagName('form')[0]; form.url.addEventListener('change', function (event) { let url; try { url = new URL(event.target.value); } catch (exception) { if (exception.name !== 'TypeError') { throw exception; } } if (!url) { return; } event.target.value = url.origin; }); let chromium = false; form.addEventListener('submit', function (event) { if (event.submitter?.name === 'cancel') { event.preventDefault(); dialog.close(); } chromium = true; }); form.addEventListener('formdata', function (event) { chromium = false; if (event.formData.get('url') !== url) { GM.deleteValue('accessToken'); } for (const [ name, value ] of event.formData) { GM.setValue(name, value); } }); // Chromiumでformdataイベントが発生しない不具合の回避 dialog.addEventListener('close', function () { if (!chromium) { return; } form.dispatchEvent(new FormDataEvent('formdata', { formData: new FormData(form) })); }); } form.url.value = url; dialog.showModal(); }); break; } default: if (location.search.startsWith('?session=')) { // MiAuthで認可が終わった後のリダイレクトの可能性があれば Promise.all([ 'application', 'url', 'miAuthSessionId', 'urlWaitingMiAuth' ].map(name => GM.getValue(name))) .then(async function ([ application, serverURL, miAuthSessionId, urlWaitingMiAuth ]) { if (application !== 'Misskey' || location.origin !== serverURL) { return; } const session = new URLSearchParams(location.search).get('session'); if (session !== miAuthSessionId) { return; } await Promise.all([ 'miAuthSessionId', 'urlWaitingMiAuth' ].map(name => GM.deleteValue(name))); // アクセストークンを取得 const response = await fetch(`${serverURL}/api/miauth/${miAuthSessionId}/check`, { method: 'POST' }); if (!response.ok) { console.error(response); return; } const { ok, token } = await response.json(); if (!ok) { console.error(response); return; } await GM.setValue('accessToken', token); // 照会 let url; try { url = await lookUpOnMisskey(serverURL, token, urlWaitingMiAuth); } catch (exception) { if (exception.name !== 'HttpError') { throw exception; } switch (exception.response.status) { case 500: alert(_('Failed to look up.')); //eslint-disable-line no-alert return; default: throw exception; } } location.replace(url); }); } else { GM.registerMenuCommand(_('Fediverse Open on Main Server'), async function () { const [ application, serverURL, accessToken ] = await Promise.all([ 'application', 'url', 'accessToken' ].map(name => GM.getValue(name))); if (!application || !serverURL) { //eslint-disable-next-line no-alert alert(_('Open the home page of this script and configure from the user script command.')); return; } let url; switch (application) { case 'Misskey': { if (!accessToken) { await miAuth(serverURL); return; } try { url = await lookUpOnMisskey(serverURL, accessToken, location.href); } catch (exception) { if (exception.name !== 'HttpError') { throw exception; } switch (exception.response.status) { case 401: await miAuth(serverURL); return; case 500: break; default: throw exception; } } } } if (!url) { alert(_('Failed to look up.')); //eslint-disable-line no-alert return; } location.assign(url); }); } }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址