Edhack

Oct 2019
HTMLCSSJavaScriptBrowser Extension

Are you tired of only getting a handful of views on your edstem posts?

Want to increase their perceived popularity among your peers?

Well, those are stupid things to want. Maybe focus on learning instead, you idiot.



Anyway, I built a view botter Chrome extension for edstem.org which can be kinda fun.

Demo GIF
Despite the seamless user experience, this is actually not a typical ed feature.

Note this is not just a client-side effect. Anybody else viewing the post will see it rise in views. Better yet, the view updates are streamed to other clients via websockets in real time, so they see the view count increase almost how it appears above.

The source code is available on GitHub. I have patched the extension on three separate occasions now after Edstem has pushed an update which breaks it. After a certain point I won't be able to fix it anymore as I will have left uni and won't have access to ed. 🥹

Technology

The actual view botting technique is nothing special.

I just noticed that the view updates were actually their own separate HTTP requests sent on the client-side to a special route, rather than being calculated server-side based on whenever post data was fetched.

This meant I could easily trigger a view by just copying this same request on the client.

The only painful part was automatically mocking the request as it needed a special session token provided by the server and I didn't want the user to have to open the network inspector to extract the token.

I achieved this programmatically by intercepting and reading the token from the first view request that is sent when you open a post.

Expand for details on how I intercepted the request.

The actual method I intercepted network requests with was stupid given there is a literal webRequest API for this purpose, which I guess I wasn't aware of at the time. So I've decided to hide these details.

But it's also so stupid that I want to write it up anyway. It also might be useful for other scenarios where you need to override a builtin object/method in the DOM.


To begin, note that JavaScript is insane so you can just override the default built-in fetch method which effectively lets you intercept every other network request sent with fetch.

const oldFetch = fetch; const newFetch = function(fetchParams) { // Insert custom code oldFetch(fetchParams); } fetch = newFetch;

Unfortunately, extensions exist with their own JavaScript execution environment that is separate from the DOM. This means the fetch method for the extension is actually different from the fetch method that is called when the page sends a view request.

This can be fixed because, once again, JavaScript is insane. From the extension, we simply put our desired JS code into a file, create a script element that sources that file, and append that script element to the DOM of our target JavaScript environment. The browser will then execute the code in the context of the DOM's JS environment, rather than the extension's.

// Create script element let injectedScript = document.createElement('script') // Assign element to a target source JavaScript file injectedScript.src = chrome.runtime.getURL('path/to/script.js') // Append script element to body of DOM document.body.appendChild(injectedScript)

Don't do this unless you have a reason to :)