Skip to content

Commit

Permalink
Merge pull request #10 from marvinscham/develop
Browse files Browse the repository at this point in the history
Removed SERVER_URL env, updated log outputs
  • Loading branch information
marvinscham authored Sep 21, 2023
2 parents 01db3f2 + c3b6494 commit 0cd4efc
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 36 deletions.
7 changes: 4 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ FROM python:3-alpine

WORKDIR /app

COPY requirements.txt duolingo.py duo_server.py exec.sh duo_main.py ./
COPY requirements.txt duolingo.py duo_server.py entrypoint.sh duo_main.py ./

RUN apk add --no-cache --virtual .build-deps gcc musl-dev shadow && \
pip install --no-cache-dir -U -r requirements.txt && \
apk del .build-deps && \
adduser -D duolingoapi && \
touch duo_server.log && \
chown -R duolingoapi:duolingoapi /app

USER duolingoapi

HEALTHCHECK --interval=5s --timeout=10s --retries=3 CMD curl -sS 127.0.0.1:7000/health || exit 1
HEALTHCHECK --interval=30s --timeout=10s --retries=3 --start-period=10s CMD wget -q --spider 127.0.0.1:7000/health || exit 1
EXPOSE 7000
CMD sh exec.sh
CMD sh entrypoint.sh
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Serves your Duolingo progress info as JSON.

Based on [KartikTalwar/Duolingo](https://github.com/KartikTalwar/Duolingo), utilizing modifications from [lidiaCirrone/pw-duolingo-data](https://github.com/lidiaCirrone/pw-duolingo-data).

![GitHub release (with filter)](https://img.shields.io/github/v/release/marvinscham/duolingo-api-dockerized)
![GitHub release](https://img.shields.io/github/v/release/marvinscham/duolingo-api-dockerized)
[![Quality Gate Status](https://sonar.ms-ds.org/api/project_badges/measure?project=Duolingo-API-Dockerized&metric=alert_status&token=sqb_d0fa0ab8bdae8eda3db5414f9483e03e91034da3)](https://sonar.ms-ds.org/dashboard?id=Duolingo-API-Dockerized)

</div>
Expand All @@ -22,7 +22,7 @@ In case you'd like to run this behind a nginx reverse proxy, you can use `nginx.
```yml
version: '3.2'

# e.g. swag reverse proxy network
# e.g. reverse proxy network
networks:
yourNetwork:
external: true
Expand All @@ -36,7 +36,6 @@ services:
- TIMEZONE=Europe/Berlin
- DUO_USERNAME=yourUsername
- DUO_JWT=yourJWT
- SERVER_URL=https://duo.your-domain.com
- XP_SUMMARY_DAYS=30
- UPDATE_INTERVAL=15
- MAX_RETRIES=3
Expand All @@ -57,9 +56,6 @@ This will serve your progress info at `https://duo.your-domain.com/duo_user_info
- Required for login
- `DUO_JWT`
- Login token (**Not your password!** Info on obtaining this is in the following segment)
- `SERVER_URL`
- Used for connectivity self check
- Example: `https://your-domain.com` → without trailing slash!
- `XP_SUMMARY_DAYS`
- Number of past days to get data from. _Might stop working properly if > 300_
- Default: `30`
Expand All @@ -72,7 +68,7 @@ This will serve your progress info at `https://duo.your-domain.com/duo_user_info

## Grabbing your JWT

Login on [Duolingo](duolingo.com) and run the following JavaScript in your browser console. The returned string is your JWT.
Login on [Duolingo](duolingo.com) and run the following JavaScript in your browser console. The returned string is your JWT. Do not share this token as it will allow anyone to access your Duolingo account.

```js
document.cookie.match(new RegExp('(^| )jwt_token=([^;]+)'))[0].slice(11);
Expand Down
43 changes: 29 additions & 14 deletions duo_main.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import os, json, time, logging, schedule, requests, math
import json
import logging
import os
import time
from datetime import datetime

import pytz
import schedule

import duolingo

duo_user_name = os.getenv("DUO_USERNAME")
duo_user_jwt = os.getenv("DUO_JWT")
server_url = os.getenv("SERVER_URL")

timezone = os.getenv("TIMEZONE", "Europe/Berlin")
xp_summary_days = int(os.getenv("XP_SUMMARY_DAYS", 30))
Expand All @@ -19,16 +25,20 @@
)
log.addHandler(handler)

if not duo_user_name or not duo_user_jwt or not server_url:
raise KeyError("Incorrect setup: username, jwt or server url missing.")
if not duo_user_name or not duo_user_jwt:
raise KeyError("Incorrect setup: username, jwt missing.")

log.info("I'm alive!")


def connectivity_handler():
page = requests.get(server_url + "/duo_user_info.json")
if page.status_code != 200:
log.error("Server cannot be reached.")
def convert_timestamp(timestamp, timezone):
dt = datetime.utcfromtimestamp(timestamp)
dt_utc = pytz.utc.localize(dt)
tz = pytz.timezone(timezone)
dt_local = dt_utc.astimezone(tz)

formatted_date = dt_local.strftime("%Y-%m-%d %H:%M:%S")
return formatted_date


def job(retries=max_retries):
Expand Down Expand Up @@ -57,16 +67,19 @@ def job(retries=max_retries):
date_format
)

language_progress = duo_user.get_language_progress(learning_language_abbr)
language_progress = duo_user.get_language_progress(
learning_language_abbr)

streak_info = duo_user.get_streak_info()

xp_summary_start = datetime.fromtimestamp(
time.time() - (60 * 60 * 24 * (xp_summary_days - 1))
).strftime(date_format)
xp_summary_end = datetime.fromtimestamp(time.time()).strftime(date_format)
xp_summary_end = datetime.fromtimestamp(
time.time()).strftime(date_format)

lang_data = duo_user.get_all_languages()
timestamp = str(int(time.time()))

user_object = {
"username": username,
Expand All @@ -75,7 +88,7 @@ def job(retries=max_retries):
"creation_date": user_date_str,
"learning_language": learning_language_abbr,
"streak_today": streak_info["streak_extended_today"],
"timestamp": str(int(time.time())),
"timestamp": timestamp,
"xp_summary_timezone": timezone,
"xp_summary_count": xp_summary_days,
"xp_summary": duo_user.get_xp_summaries(
Expand All @@ -95,10 +108,11 @@ def job(retries=max_retries):
f.write(str_user)
f.close()

log.info("Successfully updated info")
timestamp_date = convert_timestamp(int(timestamp), timezone)
log.info(
f"Updated {username}'s info: {user_total_info['totalXp']} XP @ {timestamp_date}")

time.sleep(2)
connectivity_handler()
except Exception as e:
log.warning(e)

Expand All @@ -108,7 +122,8 @@ def job(retries=max_retries):
log.error("Out of retries. Waiting for next execution.")
return

log.info("Attempt {}, retrying in 60 seconds".format(max_retries - retries))
log.info("Attempt {}, retrying in 60 seconds".format(
max_retries - retries))
time.sleep(60)
job(retries)

Expand Down
19 changes: 12 additions & 7 deletions duo_server.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
import sys

hostName = "0.0.0.0"
Expand All @@ -13,16 +14,25 @@ def do_GET(self):
self.end_headers()
with open("duo_user_info.json", "rb") as file:
self.wfile.write(file.read())

elif self.path == "/health":
self.send_response(418)
self.send_header("Content-type", "beverage/tee")
self.send_response(200)
self.send_header("Content-type", "application/json")
self.end_headers()
response_content = {
"code": 200,
"message": "I'm alive!"
}
response_json = json.dumps(response_content)
self.wfile.write(response_json.encode("utf-8"))

else:
self.send_response(403)
self.end_headers()


def run_server():
print("Server started.")
web_server = HTTPServer((hostName, serverPort), MyServer)

try:
Expand All @@ -34,9 +44,4 @@ def run_server():
print("Server stopped.")


logfile = open("duoserver.log", "w")
sys.stdout = logfile
sys.stdin = logfile
sys.stderr = logfile

run_server()
3 changes: 3 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
python3 duo_server.py > /dev/null 2>&1 &
python3 duo_main.py &
wait
1 change: 0 additions & 1 deletion example.docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ services:
- TIMEZONE=Europe/Berlin
- DUO_USERNAME=yourUsername
- DUO_JWT=yourJWT
- SERVER_URL=https://duo.your-domain.com
- XP_SUMMARY_DAYS=30
- UPDATE_INTERVAL=15
- MAX_RETRIES=3
Expand Down
3 changes: 0 additions & 3 deletions exec.sh

This file was deleted.

3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
datetime
pytz
schedule
requests
requests

0 comments on commit 0cd4efc

Please sign in to comment.