This commit is contained in:
Yorgei
2026-01-13 17:52:57 +10:00
commit fee8ab1a0c
4 changed files with 371 additions and 0 deletions

BIN
.gitea/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,112 @@
name: Auto Release
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
jobs:
update-and-release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: pip install requests
- name: Configure Git
run: |
git config user.name "Gitea Actions"
git config user.email "actions@gitea.local"
- name: Get file hash before update
id: before
run: |
if [ -f discord-quest.js ]; then
echo "hash=$(sha256sum discord-quest.js | cut -d' ' -f1)" >> $GITHUB_OUTPUT
else
echo "hash=" >> $GITHUB_OUTPUT
fi
- name: Run update script
id: update
env:
GITEA_ACTIONS: true
run: python main.py || true
- name: Get file hash after update
id: after
run: |
if [ -f discord-quest.js ]; then
echo "hash=$(sha256sum discord-quest.js | cut -d' ' -f1)" >> $GITHUB_OUTPUT
else
echo "hash=" >> $GITHUB_OUTPUT
fi
- name: Check for changes
id: changes
run: |
if [ "${{ steps.before.outputs.hash }}" != "${{ steps.after.outputs.hash }}" ]; then
echo "changed=true" >> $GITHUB_OUTPUT
else
echo "changed=false" >> $GITHUB_OUTPUT
fi
- name: Commit and push changes
if: steps.changes.outputs.changed == 'true'
run: |
if [ -n "$(git status --porcelain discord-quest.js)" ]; then
git add discord-quest.js
git commit -m "Update discord-quest.js from upstream gist"
git push origin main
fi
- name: Get latest tag
if: steps.changes.outputs.changed == 'true'
id: tag
run: |
latest_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
echo "latest=$latest_tag" >> $GITHUB_OUTPUT
version=$(echo $latest_tag | sed 's/v//')
IFS='.' read -r major minor patch <<< "$version"
patch=$((patch + 1))
new_tag="v${major}.${minor}.${patch}"
echo "new=$new_tag" >> $GITHUB_OUTPUT
- name: Create and push tag
if: steps.changes.outputs.changed == 'true'
run: |
git tag ${{ steps.tag.outputs.new }}
git push origin ${{ steps.tag.outputs.new }}
- name: Create Gitea release
if: steps.changes.outputs.changed == 'true'
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_URL: ${{ secrets.GITEA_URL || github.server_url }}
run: |
REPO_OWNER=$(echo ${{ github.repository }} | cut -d'/' -f1)
REPO_NAME=$(echo ${{ github.repository }} | cut -d'/' -f2)
API_URL="${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases"
curl -X POST "${API_URL}" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"tag_name\": \"${{ steps.tag.outputs.new }}\",
\"name\": \"Release ${{ steps.tag.outputs.new }}\",
\"body\": \"Automated release: Updated discord-quest.js from upstream gist\",
\"draft\": false,
\"prerelease\": false
}"

166
discord-quest.js Normal file
View File

@@ -0,0 +1,166 @@
delete window.$;
let wpRequire = webpackChunkdiscord_app.push([[Symbol()], {}, r => r]);
webpackChunkdiscord_app.pop();
let ApplicationStreamingStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.__proto__?.getStreamerActiveStreamMetadata).exports.Z;
let RunningGameStore = Object.values(wpRequire.c).find(x => x?.exports?.ZP?.getRunningGames).exports.ZP;
let QuestsStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.__proto__?.getQuest).exports.Z;
let ChannelStore = Object.values(wpRequire.c).find(x => x?.exports?.Z?.__proto__?.getAllThreadsForParent).exports.Z;
let GuildChannelStore = Object.values(wpRequire.c).find(x => x?.exports?.ZP?.getSFWDefaultChannel).exports.ZP;
let FluxDispatcher = Object.values(wpRequire.c).find(x => x?.exports?.Z?.__proto__?.flushWaitQueue).exports.Z;
let api = Object.values(wpRequire.c).find(x => x?.exports?.tn?.get).exports.tn;
const supportedTasks = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP", "PLAY_ACTIVITY", "WATCH_VIDEO_ON_MOBILE"]
let quests = [...QuestsStore.quests.values()].filter(x => x.userStatus?.enrolledAt && !x.userStatus?.completedAt && new Date(x.config.expiresAt).getTime() > Date.now() && supportedTasks.find(y => Object.keys((x.config.taskConfig ?? x.config.taskConfigV2).tasks).includes(y)))
let isApp = typeof DiscordNative !== "undefined"
if(quests.length === 0) {
console.log("You don't have any uncompleted quests!")
} else {
let doJob = function() {
const quest = quests.pop()
if(!quest) return
const pid = Math.floor(Math.random() * 30000) + 1000
const applicationId = quest.config.application.id
const applicationName = quest.config.application.name
const questName = quest.config.messages.questName
const taskConfig = quest.config.taskConfig ?? quest.config.taskConfigV2
const taskName = supportedTasks.find(x => taskConfig.tasks[x] != null)
const secondsNeeded = taskConfig.tasks[taskName].target
let secondsDone = quest.userStatus?.progress?.[taskName]?.value ?? 0
if(taskName === "WATCH_VIDEO" || taskName === "WATCH_VIDEO_ON_MOBILE") {
const maxFuture = 10, speed = 7, interval = 1
const enrolledAt = new Date(quest.userStatus.enrolledAt).getTime()
let completed = false
let fn = async () => {
while(true) {
const maxAllowed = Math.floor((Date.now() - enrolledAt)/1000) + maxFuture
const diff = maxAllowed - secondsDone
const timestamp = secondsDone + speed
if(diff >= speed) {
const res = await api.post({url: `/quests/${quest.id}/video-progress`, body: {timestamp: Math.min(secondsNeeded, timestamp + Math.random())}})
completed = res.body.completed_at != null
secondsDone = Math.min(secondsNeeded, timestamp)
}
if(timestamp >= secondsNeeded) {
break
}
await new Promise(resolve => setTimeout(resolve, interval * 1000))
}
if(!completed) {
await api.post({url: `/quests/${quest.id}/video-progress`, body: {timestamp: secondsNeeded}})
}
console.log("Quest completed!")
doJob()
}
fn()
console.log(`Spoofing video for ${questName}.`)
} else if(taskName === "PLAY_ON_DESKTOP") {
if(!isApp) {
console.log("This no longer works in browser for non-video quests. Use the discord desktop app to complete the", questName, "quest!")
} else {
api.get({url: `/applications/public?application_ids=${applicationId}`}).then(res => {
const appData = res.body[0]
const exeName = appData.executables.find(x => x.os === "win32").name.replace(">","")
const fakeGame = {
cmdLine: `C:\\Program Files\\${appData.name}\\${exeName}`,
exeName,
exePath: `c:/program files/${appData.name.toLowerCase()}/${exeName}`,
hidden: false,
isLauncher: false,
id: applicationId,
name: appData.name,
pid: pid,
pidPath: [pid],
processName: appData.name,
start: Date.now(),
}
const realGames = RunningGameStore.getRunningGames()
const fakeGames = [fakeGame]
const realGetRunningGames = RunningGameStore.getRunningGames
const realGetGameForPID = RunningGameStore.getGameForPID
RunningGameStore.getRunningGames = () => fakeGames
RunningGameStore.getGameForPID = (pid) => fakeGames.find(x => x.pid === pid)
FluxDispatcher.dispatch({type: "RUNNING_GAMES_CHANGE", removed: realGames, added: [fakeGame], games: fakeGames})
let fn = data => {
let progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.PLAY_ON_DESKTOP.value)
console.log(`Quest progress: ${progress}/${secondsNeeded}`)
if(progress >= secondsNeeded) {
console.log("Quest completed!")
RunningGameStore.getRunningGames = realGetRunningGames
RunningGameStore.getGameForPID = realGetGameForPID
FluxDispatcher.dispatch({type: "RUNNING_GAMES_CHANGE", removed: [fakeGame], added: [], games: []})
FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
doJob()
}
}
FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
console.log(`Spoofed your game to ${applicationName}. Wait for ${Math.ceil((secondsNeeded - secondsDone) / 60)} more minutes.`)
})
}
} else if(taskName === "STREAM_ON_DESKTOP") {
if(!isApp) {
console.log("This no longer works in browser for non-video quests. Use the discord desktop app to complete the", questName, "quest!")
} else {
let realFunc = ApplicationStreamingStore.getStreamerActiveStreamMetadata
ApplicationStreamingStore.getStreamerActiveStreamMetadata = () => ({
id: applicationId,
pid,
sourceName: null
})
let fn = data => {
let progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.STREAM_ON_DESKTOP.value)
console.log(`Quest progress: ${progress}/${secondsNeeded}`)
if(progress >= secondsNeeded) {
console.log("Quest completed!")
ApplicationStreamingStore.getStreamerActiveStreamMetadata = realFunc
FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
doJob()
}
}
FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn)
console.log(`Spoofed your stream to ${applicationName}. Stream any window in vc for ${Math.ceil((secondsNeeded - secondsDone) / 60)} more minutes.`)
console.log("Remember that you need at least 1 other person to be in the vc!")
}
} else if(taskName === "PLAY_ACTIVITY") {
const channelId = ChannelStore.getSortedPrivateChannels()[0]?.id ?? Object.values(GuildChannelStore.getAllGuilds()).find(x => x != null && x.VOCAL.length > 0).VOCAL[0].channel.id
const streamKey = `call:${channelId}:1`
let fn = async () => {
console.log("Completing quest", questName, "-", quest.config.messages.questName)
while(true) {
const res = await api.post({url: `/quests/${quest.id}/heartbeat`, body: {stream_key: streamKey, terminal: false}})
const progress = res.body.progress.PLAY_ACTIVITY.value
console.log(`Quest progress: ${progress}/${secondsNeeded}`)
await new Promise(resolve => setTimeout(resolve, 20 * 1000))
if(progress >= secondsNeeded) {
await api.post({url: `/quests/${quest.id}/heartbeat`, body: {stream_key: streamKey, terminal: true}})
break
}
}
console.log("Quest completed!")
doJob()
}
fn()
}
}
doJob()
}

93
main.py Normal file
View File

@@ -0,0 +1,93 @@
import requests
import re
import os
import subprocess
import sys
# Configuration
URL = "https://gist.githubusercontent.com/aamiaa/204cd9d42013ded9faf646fae7f89fbb/raw"
OUTPUT_FILENAME = "discord-quest.js"
REPO_REMOTE = "origin" # Change if your remote is named differently
REPO_BRANCH = "main" # Change if your branch is named differently
def fetch_and_extract():
print(f"Fetching content from {URL}...")
try:
response = requests.get(URL)
response.raise_for_status()
content = response.text
except Exception as e:
print(f"Error downloading content: {e}")
sys.exit(1)
# Regex to find markdown code blocks (``` ... ```)
# This looks for blocks optionally specified as javascript/js
code_blocks = re.findall(r'```(?:javascript|js)?\s*(.*?)```', content, re.DOTALL)
if not code_blocks:
print("No code blocks found in the fetched text.")
sys.exit(1)
# Heuristic: Find the block that looks like the actual script.
# Based on the known content, the script usually contains 'webpackChunkdiscord_app'
# If we can't find that specific keyword, we default to the longest block.
target_code = None
for block in code_blocks:
if "webpackChunkdiscord_app" in block:
target_code = block
break
if not target_code:
print("Could not identify the specific Discord script block. Using the longest code block found.")
target_code = max(code_blocks, key=len)
return target_code.strip()
def has_changes(filename):
# Check if the file is modified in git
result = subprocess.run(["git", "status", "--porcelain", filename], capture_output=True, text=True)
return bool(result.stdout.strip())
def git_commit_and_push(filename):
try:
print("Changes detected. Committing...")
subprocess.run(["git", "add", filename], check=True)
subprocess.run(["git", "commit", "-m", "Update discord-quest.js from upstream gist"], check=True)
print("Pushing to Gitea...")
subprocess.run(["git", "push", REPO_REMOTE, REPO_BRANCH], check=True)
print("Successfully pushed changes.")
except subprocess.CalledProcessError as e:
print(f"Git operation failed: {e}")
sys.exit(1)
def main():
# 1. Extract new code
new_code = fetch_and_extract()
# 2. Read existing file to compare (avoids git modifying timestamp if identical)
if os.path.exists(OUTPUT_FILENAME):
with open(OUTPUT_FILENAME, 'r', encoding='utf-8') as f:
current_code = f.read().strip()
else:
current_code = None
# 3. Write file only if content is different
if new_code != current_code:
print(f"Updating {OUTPUT_FILENAME}...")
with open(OUTPUT_FILENAME, 'w', encoding='utf-8') as f:
f.write(new_code + '\n') # Add newline at end of file
else:
print("No changes in content detected. Exiting.")
sys.exit(0)
# 4. Check Git status and Push (skip in CI - workflow handles it)
if os.getenv("CI") or os.getenv("GITHUB_ACTIONS") or os.getenv("GITEA_ACTIONS"):
print("Running in CI environment. Skipping git operations - workflow will handle commit/push.")
elif has_changes(OUTPUT_FILENAME):
git_commit_and_push(OUTPUT_FILENAME)
else:
print("File updated but git status shows no changes (clean).")
if __name__ == "__main__":
main()