Skip to content

Instantly share code, notes, and snippets.

@danthemango
Last active October 18, 2024 19:05
Show Gist options
  • Save danthemango/0fa0a5f9fd0b3b79059a26f67155317b to your computer and use it in GitHub Desktop.
Save danthemango/0fa0a5f9fd0b3b79059a26f67155317b to your computer and use it in GitHub Desktop.
Detect and send message to your program on Duolingo challenge completed
  • open chrome://extensions/ and turn on "Developer Mode" on the top right
  • install tampermonkey: https://chromewebstore.google.com/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo
  • click on the extension, then click "create a new script"
  • in the editor, copy the userscript.js file contents
  • install python, flask, and run server.py
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/data', methods=['POST'])
def receive_data():
# Get the JSON data sent from Tampermonkey
data = request.get_json()
print(f"Received data: {data}")
# Do something with the data, e.g., save it, process it, etc.
# Return a response back to Tampermonkey
return jsonify({"message": "Data received successfully", "received": data})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
// ==UserScript==
// @name Duolingo Lesson Completion Detection
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Detect Duolingo lesson completion by intercepting requests and checking body content
// @author Daniel Guenther
// @match https://www.duolingo.com/learn
// @grant GM_xmlhttpRequest
// @connect localhost
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
console.log('lesson completed detector running...');
// Save the original XMLHttpRequest open and send methods
const originalXhrOpen = window.XMLHttpRequest.prototype.open;
const originalXhrSend = window.XMLHttpRequest.prototype.send;
// Override the open method to inspect request details
window.XMLHttpRequest.prototype.open = function(method, url) {
this._url = url; // Save the request URL for later use
originalXhrOpen.apply(this, arguments); // Call the original open method
};
// Override the send method to inspect the request body
window.XMLHttpRequest.prototype.send = function(body) {
if (this._url.includes('https://excess.duolingo.com/challenge_response/batch')) { // Check if this is the challenge response request
console.log("Intercepted XHR request to Duolingo challenge_response API!");
// Parse the request body (assuming it's JSON format)
const requestBody = JSON.parse(body);
// Check for the conditions `correct: true` and `skipped: false`
const challengeCompleted = requestBody.some(entry => entry.correct === true && entry.skipped === false);
if (challengeCompleted) {
console.log("Duolingo challenge completed successfully! Sending signal to server...");
sendChallengeCompleted();
}
}
// Call the original send method
originalXhrSend.apply(this, arguments);
};
// Function to send data to the server when a lesson is completed
function sendChallengeCompleted() {
GM_xmlhttpRequest({
method: "POST",
url: "http://localhost:5000/data", // Your server endpoint
data: JSON.stringify({
message: "Challeneg completed!",
timestamp: new Date().toISOString()
}),
headers: {
"Content-Type": "application/json"
},
onload: function(response) {
console.log("Response from server:", response.responseText);
},
onerror: function(error) {
console.error("Error:", error);
}
});
}
})();
@danthemango
Copy link
Author

Partly generated with chatGPT, tested locally on 2024-10-18

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment