您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
none
// ==UserScript== // @name 绳网跨域助手 // @namespace http://tampermonkey.net/ // @version 1.7.1 // @description none // @author claxmo // @license MIT // @run-at document-start // @match http://localhost:*/inter-knot/* // @match https://claxmo.github.io/inter-knot/* // @connect github.com // @connect api.github.com // @connect private-user-images.githubusercontent.com // @grant unsafeWindow // @grant GM_xmlhttpRequest // ==/UserScript== (function () { 'use strict'; const CLIENT_ID = "Ov23lifQzV3VA2p4vEmL"; const CLIENT_SECRET = "b9cab7872c74d1958582676e4393bc112cacee24"; const OWNER = "claxmo"; const REPO = "inter-knot"; const TIMEOUT = 5000; let accessToken = localStorage.getItem('access_token'); unsafeWindow.version = "1.7.1"; const request = async (method, url, data = null, timeout = TIMEOUT) => { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method, url, headers: { "Authorization": `Bearer ${accessToken}`, "Accept": "application/json" }, ...(data && { data: JSON.stringify(data) }), timeout, onload: (res) => { if (res.status === 401){ localStorage.removeItem("access_token"); accessToken = null; return authLogin(); }else{ resolve(JSON.parse(res.responseText)); } }, ontimeout: () => reject(new Error('request timeout')), onerror: () => reject(new Error('request error')) }); }); }; const getAccessToken = async (code, timeout = TIMEOUT) => { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "POST", url: "https://github.com/login/oauth/access_token", headers: { "Content-Type": "application/json", "Accept": "application/json" }, data: JSON.stringify({ client_id: CLIENT_ID, client_secret: CLIENT_SECRET, code, }), timeout, onload: (res) => { resolve(JSON.parse(res.responseText).access_token); }, onerror: () => reject(new Error('get access_token error')), ontimeout: () => reject(new Error ('get access_token timeout')), }); }); }; const authLogin = () => { window.location.href =`https://github.com/login/oauth/authorize?client_id=${CLIENT_ID}&scope=public_repo`; }; const graphql = async (query, variables) => { return await request("POST","https://api.github.com/graphql",{query,variables}); }; unsafeWindow.getBlob = async (url) => { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: url, responseType: 'blob', onload: (res) => { resolve(res.response); }, onerror: () => reject(new Error('get blob error: ' + url)), }); }); }; unsafeWindow.getUserProfile = async () => { return await request("GET","https://api.github.com/user"); }; unsafeWindow.searchDiscussion = async (query = '', cursor = null) => { return (await graphql( `query($query: String!, $cursor: String ) { search(type: DISCUSSION, query: $query, first: 20, after: $cursor ) { pageInfo { endCursor hasNextPage } nodes { ... on Discussion { id number title body bodyHTML bodyText author { login avatarUrl } category { id name emoji } createdAt updatedAt url comments { totalCount } locked viewerCanDelete viewerDidAuthor reactionGroups { content viewerHasReacted users { totalCount } } reactions { totalCount } upvoteCount } } } }`, { query: `repo:${OWNER}/${REPO} ${query}`.trim(), cursor } ) ).data.search; }; unsafeWindow.getDiscussionComments = async (discussionId, cursor = null, isAsc = true) => { return (await graphql( `query($id: ID!, $cursor: String) { node(id: $id) { ... on Discussion { comments(${isAsc ? 'first' : 'last'}: 20, ${isAsc ? 'after' : 'before'}: $cursor) { pageInfo { startCursor hasPreviousPage, endCursor, hasNextPage } totalCount nodes { author { login avatarUrl } authorAssociation body bodyHTML bodyText createdAt id updatedAt url viewerCanDelete viewerDidAuthor reactionGroups { content viewerHasReacted users { totalCount } } reactions { totalCount } upvoteCount } } } } }`, { id: discussionId, cursor } ) ).data.node.comments; }; unsafeWindow.deleteDiscussion = async (discussionId) => { return (await graphql( `mutation($discussionId: ID!) { deleteDiscussion(input: { id: $discussionId } ) { clientMutationId } }`, { discussionId } ) ).data.deleteDiscussion; }; unsafeWindow.deleteDiscussionComment = async (commentId) => { return (await graphql( `mutation($commentId: ID!) { deleteDiscussionComment(input: { id: $commentId } ) { clientMutationId } }`, { commentId } ) ).data.deleteDiscussionComment; }; unsafeWindow.addDiscussionComment = async (discussionId, body) => { return (await graphql( `mutation($discussionId: ID!, $body: String!) { addDiscussionComment(input: {discussionId: $discussionId, body: $body}) { comment { author { login avatarUrl } authorAssociation body bodyHTML bodyText createdAt id updatedAt url viewerCanDelete viewerDidAuthor } } } }`, { discussionId, body } ) ).data.addDiscussionComment.comment; }; unsafeWindow.getDiscussion = async (number) => { return (await graphql( `query getDiscussion($number: Int!) { repository(owner: "${OWNER}", name: "${REPO}") { discussion(number: $number ) { id number title body bodyHTML bodyText author { login avatarUrl } category { id name emoji } createdAt updatedAt url comments { totalCount } locked viewerCanDelete viewerDidAuthor reactionGroups { content viewerHasReacted users { totalCount } } reactions { totalCount } upvoteCount } } }`, { number } ) ).data.repository.discussion; }; unsafeWindow.addReaction = async (subjectId, content) => { return (await graphql( `mutation($subjectId: ID!, $content: ReactionContent!) { addReaction(input: {subjectId: $subjectId, content: $content}) {reaction { content } subject { id } } }`, { subjectId, content } ) ).data.addReaction.reaction; }; unsafeWindow.removeReaction = async (subjectId, content) => { return (await graphql( `mutation($subjectId: ID!, $content: ReactionContent!) { removeReaction(input: {subjectId: $subjectId, content: $content}) {reaction { content } subject { id } } }`, { subjectId, content } ) ).data.removeReaction.reaction; }; const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get("code"); if (code) { history.replaceState(null, '', window.location.origin + window.location.pathname); getAccessToken(code).then((token) => { accessToken = token; localStorage.setItem('access_token', accessToken); // console.log("Access Token:",accessToken); }); }else if(!accessToken){ authLogin(); } })();