From 64cf9e83c4b7478e92192442e3d91fd7656bb247 Mon Sep 17 00:00:00 2001 From: programORdie2 Date: Mon, 24 Jun 2024 11:37:13 +0200 Subject: [PATCH 1/6] Added readme.md --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++++++ img/logowithtext.svg | 1 + 2 files changed, 47 insertions(+) create mode 100644 README.md create mode 100644 img/logowithtext.svg diff --git a/README.md b/README.md new file mode 100644 index 0000000..1913a71 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +## BlockLive: Real-Time Collaboration for Scratch + +BlockLive logo + +**BlockLive** is a Chrome extension that lets you work together with friends on Scratch projects in real-time! No more tedious remixing and sharing - BlockLive keeps everyone on the same page with instant synchronization. + +**Features:** + +* **Real-time Collaboration:** See changes made by your collaborators instantly, from block edits to asset uploads and project name changes. +* **Easy Setup:** Install the extension, open a Scratch project, and invite your friends using the BlockLive interface. +* **Collaborative Editing:** Everyone can work on the project simultaneously, fostering creative teamwork. +* **Websocket Powered:** BlockLive uses websockets for efficient and low-latency communication. + +**Installation:** + +1. Visit [the Chrome Webstore](https://chromewebstore.google.com/detail/blocklive-scratch-realtim/gelkmljpoacdjkjkcfekkmgkpnmeomlk). +2. Click "Add to Chrome" and follow the on-screen instructions. + +**Getting Started:** + +1. **Create a new project or open an existing project**: + - Start a new project or open a project you want to collaborate on. + +2. **Enable BlockLive**: + - Click on the BlockLive Share button in the Scratch interface to enable real-time collaboration. + +3. **Invite collaborators**: + - Invite your friends to the project using the menu in the popup. + +4. **Collaborate in real-time**: + - Work together with your collaborators, see their changes live, and communicate using the built-in chat feature. + +**Important Notes:** + +* **Allowlist:** For security reasons, you need to add collaborators to your allowlist before they can join your project. +* **Development:** BlockLive is still under development. You may encounter bugs or limitations. + +**Contributing:** + +We welcome contributions from the community! If you'd like to help improve BlockLive, you can find the source code on [GitHub](https://github.com/BlockliveScratch/blocklive). + +**Support the Project:** + +If you find BlockLive useful, consider supporting the development by donating us on [buymeacoffee](https://buymeacoffee.com/ilhp10). + +**We hope you enjoy collaborating on Scratch with BlockLive!** diff --git a/img/logowithtext.svg b/img/logowithtext.svg new file mode 100644 index 0000000..46ef0f5 --- /dev/null +++ b/img/logowithtext.svg @@ -0,0 +1 @@ +Blocklive \ No newline at end of file From b9349dbb0fb95638d430d6d87e13a5f7952c149b Mon Sep 17 00:00:00 2001 From: programORdie2 Date: Tue, 25 Jun 2024 08:45:00 +0200 Subject: [PATCH 2/6] fixed typo --- extension/scripts/editor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extension/scripts/editor.js b/extension/scripts/editor.js index 57cb4f9..0943ac3 100755 --- a/extension/scripts/editor.js +++ b/extension/scripts/editor.js @@ -147,7 +147,7 @@ async function startBlocklive(creatingNew) { }) } if(creatingNew) { - addToCredits('Get BIocklive for Live Collabs #bl') + addToCredits('Get Blocklive for Live Collabs #bl') } } @@ -2780,7 +2780,7 @@ let blActivateClick = async ()=>{ await refreshShareModal() // add blocklive ref in instructions credits - addToCredits('Get BIocklive for Live Collabs #bl') + addToCredits('Get Blocklive for Live Collabs #bl') // stop spinny document.querySelector('loader.blockliveloader').style.display = 'none' From d71ed132c60a1512e06dcdcbdb19372a5eba549d Mon Sep 17 00:00:00 2001 From: programORdie2 Date: Wed, 26 Jun 2024 09:57:58 +0200 Subject: [PATCH 3/6] Added cloud timeout (IMPORTANT!) and made basic stuff for comment verification --- backend/scratch-auth.js | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/backend/scratch-auth.js b/backend/scratch-auth.js index 1e62cdf..a397be8 100644 --- a/backend/scratch-auth.js +++ b/backend/scratch-auth.js @@ -6,7 +6,9 @@ export const freePassesPath = 'storage/freePasses.json' export const failedAuthLog = {} export const secondTimeSuccessAuthLog = {}; - +let cachedComments = []; +let lastTimeCheckedComments = 0; +const commentsRateLimit = 1000 * 5; function logAuth(username, success, word, info) { if (!username) { return; } @@ -43,6 +45,12 @@ function getAuthProjectId() { return authProjects[idIndex]; } +async function getAuthProjectData() { + const projectId = getAuthProjectId(); + const data = await (await fetch(`https://api.scratch.mit.edu/projects/${projectId}`)).json(); + return { projectId, projectUsername: data.author.username }; +} + let userManager let sessionManager export function setPaths(app, userManagerr, sessionManagerr) { @@ -70,6 +78,8 @@ export function setPaths(app, userManagerr, sessionManagerr) { res.send({ err: 'client code not found', clientCode }); return; } + + await sleep(CLOUD_WAIT) // ! THIS IS REALLY IMPORTANT ! let cloud = await getVerificationCloud(tempCode) if (!cloud || cloud?.err) { @@ -157,6 +167,30 @@ async function getVerificationCloud(tempCode) { return cloud; } +async function checkComments() { + if (lastTimeCheckedComments + commentsRateLimit > Date.now()) { + return cachedComments; + } + + let { projectUsername, projectId } = await getAuthProjectData(); + + const data = await (await fetch(`https://api.scratch.mit.edu/users/${projectUsername}/projects/${projectId}/comments?offset=0&limit=40&rand=${Math.random()}`)).json(); + cachedComments = data; + lastTimeCheckedComments = Date.now(); + return data; +} + +async function verifyCommentCode(code, retried=false) { + if (retried) { await sleep(commentsRateLimit); } + const data = await checkComments(); + const comment = data?.comments?.filter(c => c.content == code).reverse()[0]; + if (!comment) { + if (!retried) { return await verifyCommentCode(code, true) } + return null; + } + return comment; +} + // export let freePasses = {} // username : passtime From ca162f9507c2d27737339751ab7acd5121ea087a Mon Sep 17 00:00:00 2001 From: programORdie2 Date: Thu, 27 Jun 2024 12:30:12 +0200 Subject: [PATCH 4/6] speed up cloud --- backend/scratch-auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/scratch-auth.js b/backend/scratch-auth.js index a397be8..d5be7db 100644 --- a/backend/scratch-auth.js +++ b/backend/scratch-auth.js @@ -79,7 +79,7 @@ export function setPaths(app, userManagerr, sessionManagerr) { return; } - await sleep(CLOUD_WAIT) // ! THIS IS REALLY IMPORTANT ! + await sleep(500) // Makes sure cloud is ready to be called and we don't need to spend extra time on retrying let cloud = await getVerificationCloud(tempCode) if (!cloud || cloud?.err) { From 6243700c01c07aeb3dce396d4e46ee918961e5e7 Mon Sep 17 00:00:00 2001 From: programORdie2 Date: Thu, 27 Jun 2024 22:30:57 +0200 Subject: [PATCH 5/6] display online users direct after project loads --- extension/scripts/editor.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extension/scripts/editor.js b/extension/scripts/editor.js index 0943ac3..23c51f8 100755 --- a/extension/scripts/editor.js +++ b/extension/scripts/editor.js @@ -214,6 +214,8 @@ async function joinExistingBlocklive(id) { liveMessage({meta:"joinSession"}) // join sessionManager session readyToRecieveChanges = true pauseEventHandling = false; + + reloadOnlineUsers(); // hackyRefreshFlyoutVariables() setTimeout(BL_UTILS.refreshFlyout,100) // todo figure way other than timeout From 822eca4c4bfd47fa8ee232449f0a9414b1a27cd6 Mon Sep 17 00:00:00 2001 From: programORdie2 Date: Fri, 19 Jul 2024 00:11:24 +0200 Subject: [PATCH 6/6] Speed up auth and hide login banner when bypass enabled --- backend/scratch-auth.js | 3 +++ extension/scripts/mystuff.js | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/backend/scratch-auth.js b/backend/scratch-auth.js index 7f00d99..612cf5b 100644 --- a/backend/scratch-auth.js +++ b/backend/scratch-auth.js @@ -70,6 +70,9 @@ export function setPaths(app, userManagerr, sessionManagerr) { const CLOUD_WAIT = 1000 * 5; app.get('/verify/userToken', async (req, res) => { // ?code=000000&method=cloud|CLOUDs + if (bypassUserAuth) { + return res.send({ freepass: true }) + } try { let clientCode = req.query.code if (!clientCode) { res.send({ err: 'no client code included' }); return } diff --git a/extension/scripts/mystuff.js b/extension/scripts/mystuff.js index fadcc13..a18a8f6 100755 --- a/extension/scripts/mystuff.js +++ b/extension/scripts/mystuff.js @@ -400,21 +400,28 @@ chrome.runtime.sendMessage(exId, { meta: 'getUsernamePlus' }, async (userData) = }) - + let shouldShowBanner = true; + async function updateShouldHideBanner() { + const res = await fetch('https://spore.us.to/api/verify/bypass'); + const bypass = (await res.text()) === 'true'; + shouldShowBanner = !bypass; + } + updateShouldHideBanner(); chrome.runtime.sendMessage(exId, { meta: 'verifying' }, (verifying) => { console.log('vf',verifying) console.log('verifying',verifying) - if (verifying=='nocon'){ + if (verifying=='nocon' && shouldShowBanner) { // defaultAddHideBlockliveButton() document.querySelector('.box-head').insertAdjacentHTML('afterend', `
⚠️ Cant connect to blocklive servers at blocklivecollab.com Check Uptime or Dont show this message again
`) } - else if (verifying) { + else if (verifying && shouldShowBanner) { defaultAddHideBlockliveButton() document.querySelector('#verifying')?.remove() document.querySelector('.box-head').insertAdjacentHTML('afterend', `
Blocklive is verifying your account ...
`) } else { if (newVerified) { return } + if (!shouldShowBanner) { return } defaultAddHideBlockliveButton() document.querySelector('.box-head').insertAdjacentHTML('afterend', `
⚠️ Blocklive could not verify your account. Reload the tab in a few seconds. If this issue continues, contact @ilhp10 or @rgantzos
`) }