diff --git a/extensions/yearnstudio/one.js b/extensions/yearnstudio/one.js new file mode 100644 index 0000000000..a367c63d70 --- /dev/null +++ b/extensions/yearnstudio/one.js @@ -0,0 +1,295 @@ +(function (Scratch) { + "use strict"; + if (!Scratch.extensions.unsandboxed) { + throw new Error("You can not run in sandbox"); + } + const SHANGCLOUD_ORIGIN = "https://api.yearnstudio.cn"; + let client_id = ""; + let client_id_verified = false; + let accessToken = ""; + let refreshToken = ""; + let uid = 0; + let nickname = ""; + class ShangCloudExtension { + getInfo() { + return { + id: "shangcloud", + name: Scratch.translate("shangcloud.name"), + color1: "#4CAF50", + color2: "#388E3C", + blocks: [ + { + opcode: "setClientID", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("shangcloud.setClientID"), + arguments: { + CLIENT_ID: { type: Scratch.ArgumentType.STRING, default: "" }, + }, + }, + { + opcode: "verifyClientID", + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate("shangcloud.verifyClientID"), + arguments: {}, + }, + { + opcode: "tryLogin", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("shangcloud.tryLogin"), + arguments: { + CLIENT_ID: { type: Scratch.ArgumentType.STRING, default: "" }, + }, + }, + { + opcode: "loginStatusBool", + blockType: Scratch.BlockType.BOOLEAN, + disableMonitor: true, + text: Scratch.translate("shangcloud.loginStatusBool"), + }, + { + opcode: "isTokenValid", + blockType: Scratch.BlockType.BOOLEAN, + disableMonitor: true, + text: Scratch.translate("shangcloud.isTokenValid"), + arguments: {}, + }, + { + opcode: "refreshToken", + blockType: Scratch.BlockType.BOOLEAN, + disableMonitor: true, + text: Scratch.translate("shangcloud.refreshToken"), + arguments: {}, + }, + + "---", + + { + blockType: Scratch.BlockType.LABEL, + text: Scratch.translate("shangcloud.label.getUserInfo"), + }, + { + opcode: "getUserInfo", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("shangcloud.getUserInfo"), + arguments: { + CLIENT_ID: { type: Scratch.ArgumentType.STRING, default: "" }, + }, + }, + { + opcode: "userUid", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("shangcloud.userUid"), + }, + { + opcode: "userNickname", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("shangcloud.userNickname"), + }, + + "---", + + { + blockType: Scratch.BlockType.LABEL, + text: Scratch.translate("shangcloud.label.cloudOperation"), + }, + { + opcode: "writeVar", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("shangcloud.writeVar"), + arguments: { + KEY: { type: Scratch.ArgumentType.STRING, default: "temp" }, + VALUE: { type: Scratch.ArgumentType.STRING, default: "1" }, + }, + }, + { + opcode: "getVar", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("shangcloud.getVar"), + arguments: { + KEY: { type: Scratch.ArgumentType.STRING, default: "temp" }, + }, + }, + { + opcode: "delVar", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("shangcloud.delVar"), + arguments: { + KEY: { type: Scratch.ArgumentType.STRING, default: "temp" }, + }, + }, + ], + }; + } + loginStatusBool() { + return accessToken ? true : false; + } + userUid() { + return uid || ""; + } + userNickname() { + return nickname || ""; + } + async isTokenValid() { + if (!client_id_verified) { + return false; + } + if (!accessToken) return false; + try { + const res = await Scratch.fetch(SHANGCLOUD_ORIGIN + "/api/token/test", { + method: "POST", + headers: { + Authorization: "Bearer " + accessToken, + }, + }); + if (res.status === 200) { + const data = await res.json(); + if (data.msg === "ok" || data.msg === "OK") { + return true; + } + } + return false; + } catch (error) { + return false; + } + } + async delVar(args) { + if (!accessToken) return; + const key = args.KEY; + await Scratch.fetch(SHANGCLOUD_ORIGIN + "/api/var/" + key, { + method: "DELETE", + headers: { + Authorization: "Bearer " + accessToken, + }, + }); + } + async getVar(args) { + if (!accessToken) return; + const key = args.KEY; + const res = await Scratch.fetch(SHANGCLOUD_ORIGIN + "/api/var/" + key, { + method: "GET", + headers: { + Authorization: "Bearer " + accessToken, + }, + }); + const data = await res.json(); + if (data.msg === "success") { + return data.data.value; + } + return ""; + } + async writeVar(args) { + if (!accessToken) return; + const key = args.KEY; + const value = args.VALUE; + const params = new URLSearchParams(); + params.append("value", value); + await Scratch.fetch(SHANGCLOUD_ORIGIN + "/api/var/" + key, { + method: "PUT", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Authorization: "Bearer " + accessToken, + }, + body: params, + }); + } + async setClientID(args) { + let temp_client_id = args.CLIENT_ID; + if (Scratch.vm.runtime.scratchblocks) { + client_id = temp_client_id; + return true; + } + let params = new URLSearchParams(); + params.append("client_id", temp_client_id); + const res = await Scratch.fetch( + SHANGCLOUD_ORIGIN + "/api/verify/client_id", + { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: params, + credentials: "include", + } + ); + const data = await res.json(); + if (data.code === 200) { + client_id_verified = true; + client_id = temp_client_id; + return true; + } + return false; + } + tryLogin() { + return new Promise((resolve) => { + if (!client_id) { + alert("请先填写应用ID"); + return; + } + const loginWindow = Scratch.openWindow( + SHANGCLOUD_ORIGIN + + "/auth/window?ui=1&client_id=" + + encodeURIComponent(client_id) + + "&origin=" + + encodeURIComponent(location.origin) + + "&scope=user:basic%20var:io", + "loginWindow", + "width=600,height=400" + ); + window.addEventListener("message", function (event) { + if (event.origin !== SHANGCLOUD_ORIGIN) { + return resolve(false); + } + if (event.data.type === "login-success") { + const { token } = event.data.payload; + let token_json = JSON.parse(token); + if (token_json) { + accessToken = token_json.access_token; + } + if (token_json.refresh_token) { + refreshToken = token_json.refresh_token; + } + loginWindow.close(); + resolve(true); + } + }); + }); + } + async refreshToken() { + if (!refreshToken) return false; + let param = new URLSearchParams(); + param.append("client_id", client_id); + param.append("refresh_token", refreshToken); + const res = await Scratch.fetch(SHANGCLOUD_ORIGIN + "/oauth/token", { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: param, + credentials: "include", + }); + const data = await res.json(); + if (data) { + accessToken = data.access_token; + refreshToken = data.refresh_token; + return true; + } + return false; + } + async getUserInfo() { + const res = await Scratch.fetch(SHANGCLOUD_ORIGIN + "/api/info/me", { + method: "POST", + credentials: "include", + }); + const data = await res.json(); + if (!data.data) { + return; + } + uid = data.data.uid; + nickname = data.data.nickname; + } + verifyClientID() { + return !!client_id; + } + } + Scratch.extensions.register(new ShangCloudExtension()); +})(Scratch);