Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

js2py for amuselabs #144

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ Unidecode==1.3.6
pyyaml==6.0.1
xmltodict==0.13.0
lxml==4.9.2
Js2Py==0.74
96 changes: 11 additions & 85 deletions xword_dl/downloader/amuselabsdownloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from bs4 import BeautifulSoup
from html2text import html2text

import js2py

from .basedownloader import BaseDownloader
from ..util import *

Expand Down Expand Up @@ -86,92 +88,17 @@ def fetch_data(self, solver_url):
js_url_fragment = m1.groups()[0]
js_url = urllib.parse.urljoin(solver_url, js_url_fragment)

# get the "key" from the URL
# get the decryption function from the JS URL
res2 = requests.get(js_url)
js_text = res2.text
re_match = re.search(r'rawc\;try\{(var n=function.*?n.join\(""\)\})', js_text)
jsFunc = re_match.groups()[0]
context = js2py.EvalJs()
context.execute(jsFunc)

# matches a 7-digit hex string preceded by `="` and followed by `"`
m2 = re.search(r'="([0-9a-f]{7})"', res2.text)
if m2:
# in this format, add 2 to each digit
amuseKey = [int(c,16)+2 for c in m2.groups()[0]]
else:
# otherwise, grab the new format key and do not add 2
amuseKey = [int(x) for x in
re.findall(r'=\[\]\).push\(([0-9]{1,2})\)', res2.text)]

# But now that might not be the right key, and there's another one
# that we need to try!
# (current as of 10/26/2023)
key_2_order_regex = r'i=(\d+);i<t.length;i\+='
key_2_digit_regex = r't.length\?(\d+)'

key_digits = [int(x) for x in
re.findall(key_2_digit_regex, res2.text)]
key_orders = [int(x) for x in
re.findall(key_2_order_regex, res2.text)]

amuseKey2 = [x for x, _ in sorted(zip(key_digits, key_orders), key=lambda pair: pair[1])]


# helper function to decode rawc
# as occasionally it can be obfuscated
def load_rawc(rawc, amuseKey=None):
try:
# the original case is just base64'd JSON
return json.loads(base64.b64decode(rawc).decode("utf-8"))
except:
try:
# case 2 is the first obfuscation
E = rawc.split('.')
A = list(E[0])
H = E[1][::-1]
F = [int(A,16)+2 for A in H]
B, G = 0, 0
while B < len(A) - 1:
C = min(F[G % len(F)], len(A) - B)
for D in range(C//2):
A[B+D], A[B+C-D-1] = A[B+C-D-1], A[B+D]
B+=C
G+=1
newRawc=''.join(A)
return json.loads(base64.b64decode(newRawc).decode("utf-8"))
except:
# case 3 is the most recent obfuscation
def amuse_b64(e, amuseKey=None):
e = list(e)
H=amuseKey
E=[]
F=0

while F<len(H):
J=H[F]
E.append(J)
F+=1

A, G, I = 0, 0, len(e)-1
while A < I:
B = E[G]
L = I - A + 1
C = A
B = min(B, L)
D = A + B - 1
while C < D:
M = e[D]
e[D] = e[C]
e[C] = M
D -= 1
C += 1
A += B
G = (G + 1) % len(E)
return ''.join(e)
return json.loads(base64.b64decode(
amuse_b64(rawc, amuseKey)
).decode("utf-8"))

try:
xword_data = load_rawc(rawc, amuseKey=amuseKey)
except (UnicodeDecodeError, base64.binascii.Error):
xword_data = load_rawc(rawc, amuseKey=amuseKey2)
# Execute the function on our rawc
s1 = context.n(rawc)
xword_data = json.loads(base64.b64decode(s1).decode("utf-8"))

return xword_data

Expand Down Expand Up @@ -254,4 +181,3 @@ def pick_filename(self, puzzle, **kwargs):
if not self.date and self.id:
self.guess_date_from_id(self.id)
return super().pick_filename(puzzle, **kwargs)

2 changes: 1 addition & 1 deletion xword_dl/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2023.10.26
2023.11.05