commit 12e5e43f5c01ab66bd37274a22130edfab1ba49c Author: Kekskurse Date: Sat Jul 6 11:34:24 2019 +0200 First Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b63411e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +__pycache__ +*.pyc +database.db \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8452775 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3.7.3 + +COPY ./ ./ +RUN pip install --no-cache-dir -r requirements.txt + +CMD [ "python", "./server.py" ] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b0698dc --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +freenet-funk-api==0.1.4 +Flask==1.1.0 \ No newline at end of file diff --git a/server.py b/server.py new file mode 100644 index 0000000..70e93ef --- /dev/null +++ b/server.py @@ -0,0 +1,332 @@ +import sqlite3 +from flask import g +from flask import Flask +app = Flask(__name__) +from functools import wraps +from flask import request, Response +from flask import render_template, redirect +from funkapi import FunkAPI +import json +import time +import datetime +from dateutil.relativedelta import relativedelta +import os + + +DATABASE = os.getenv("FUNK_DATABASE", 'database.db') +USERNAME = os.getenv("FUNK_USER", 'admin') +PASSWORD = os.getenv("FUNK_PASS", 'geheim') +ENABLED_AUTH = os.getenv("FUNK_AUTH", False) + +#Helper +def get_db(): + db = getattr(g, '_database', None) + if db is None: + db = g._database = sqlite3.connect(DATABASE) + return db + +def check_auth(username, password): + """This function is called to check if a username / + password combination is valid. + """ + return username == USERNAME and password == PASSWORD + +def authenticate(): + """Sends a 401 response that enables basic auth""" + return Response( + 'Could not verify your access level for that URL.\n' + 'You have to login with proper credentials', 401, + {'WWW-Authenticate': 'Basic realm="Login Required"'}) + +def requires_auth(f): + @wraps(f) + def decorated(*args, **kwargs): + if not ENABLED_AUTH: + return f(*args, **kwargs) + auth = request.authorization + if not auth or not check_auth(auth.username, auth.password): + return authenticate() + return f(*args, **kwargs) + return decorated + +# Functions +def function_updateAccount(number): + cur = get_db().cursor() + cur.execute("SELECT mail, password FROM accounts WHERE `number` = '%s'" % (number)) + res = cur.fetchone() + api = FunkAPI(res[0], res[1]) + + plan = api.getCurrentPlan()["productServiceInfo"]["marketingInfo"]["name"] + usage = api.getData()["data"]["me"]["customerProducts"][0]["mobileNumbers"][0]["usage"]["usedDataPercentage"] + + cur.execute("INSERT INTO updates VALUES (%s, %s, '%s', '%s')" % ( + number, + int(time.time()), + plan, + usage + )) + + get_db().commit() + +def function_speacialDays(number): + cur = get_db().cursor() + cur.execute("SELECT day, month, year, plan FROM special WHERE number = %s ORDER BY year, month, day" % number) + res = cur.fetchall() + days = [] + for r in res: + day = {} + day["day"] = str(r[0])+"."+str(r[1])+"."+str(r[2]) + day["plan"] = r[3] + days.append(day) + return days + + + +def function_getAccounts(includePassword = False): + cur = get_db().cursor() + cur.execute("SELECT number, mail, password, owner, defaultPlan FROM accounts") + res = cur.fetchall() + accounts = [] + for accountData in res: + lastUpdate = cur.execute("SELECT updateTime, plan, dataUsed FROM updates WHERE number = '%s' ORDER BY updateTime DESC LIMIT 1" % (accountData[0])).fetchone() + account = {} + account["number"] = accountData[0] + account["mail"] = accountData[1] + if includePassword: + account["password"] = accountData[2] + account["owner"] = accountData[3] + account["defaultPlan"] = accountData[4] + account["currentPlan"] = lastUpdate[1] + account["dataUsed"] = lastUpdate[2] + account["lastUpdate"] = lastUpdate[0] + accounts.append(account) + + return accounts + +def function_getAccount(number, includePassword = False): + cur = get_db().cursor() + cur.execute("SELECT number, mail, password, owner, defaultPlan FROM accounts WHERE number = %s" % (number)) + accountData = cur.fetchone() + lastUpdate = cur.execute("SELECT updateTime, plan, dataUsed FROM updates WHERE number = '%s' ORDER BY updateTime DESC LIMIT 1" % (accountData[0])).fetchone() + account = {} + account["number"] = accountData[0] + account["mail"] = accountData[1] + if includePassword: + account["password"] = accountData[2] + account["owner"] = accountData[3] + account["defaultPlan"] = accountData[4] + account["currentPlan"] = lastUpdate[1] + account["dataUsed"] = lastUpdate[2] + account["lastUpdate"] = lastUpdate[0] + return account + +def function_getPlanForDay(number, year, month, day): + cur = get_db().cursor() + dayPlan = cur.execute("SELECT plan FROM special WHERE number = %s AND day = %s AND month = %s AND year = %s" % (number, day, month, year)).fetchone() + if dayPlan: + return dayPlan[0] + else: + defaultPlan = cur.execute("SELECT defaultPlan FROM accounts WHERE number = %s" % (number)).fetchone() + return defaultPlan[0] + +@app.route("/updateAll") +@requires_auth +def function_updateAccountsAtFunk(): + cur = get_db().cursor() + today = datetime.datetime.now() + done = cur.execute("SELECT count(*) FROM run_actions WHERE year = %s AND month = %s AND day = %s" % (today.year, today.month, today.day)).fetchone() + + if done[0] == 0: + numbers = cur.execute("SELECT number FROM accounts").fetchall() + tomorrow = datetime.date.today() + datetime.timedelta(days=1) + + for number in numbers: + print("Update Account: "+str(number[0])) + #function_updateAccount(number[0]) + account = function_getAccount(number[0], True) + print(account) + nextPlan = function_getPlanForDay(number[0], tomorrow.year, tomorrow.month, tomorrow.day) + if account["currentPlan"] == nextPlan: + print("Plan match, nothing to do") + else: + api = FunkAPI(account["mail"], account["password"]) + if nextPlan == "1 GB": + api.order1GBPlan() + elif nextPlan == "unlimited": + api.orderUnlimitedPlan() + elif nextPlan == "Break": + api.startPause() + else: + print("SOMETHING GO WRONG") + + cur.execute("INSERT INTO run_actions VALUES (%s, %s, %s) " % (today.year, today.month, today.day)) + get_db().commit() + + return "OK"; + + + + else: + return "Done for today" + + + + +#Routes +@app.route("/") +@requires_auth +def hello(): + return render_template("base.html") + +@app.route('/setup', methods=['GET']) +@requires_auth +def setupDB(): + cur = get_db().cursor() + cur.execute('''CREATE TABLE accounts (number integer, mail text, password text, owner text, defaultPlan text)''') + cur.execute('''CREATE TABLE updates (number integer, updateTime integer, plan text, dataUsed integer)''') + cur.execute('''CREATE TABLE special (number integer, day integer, month integer, year integer, plan text)''') + cur.execute('''CREATE TABLE run_actions (year integer, month integer, day integer)''') + get_db().commit() + return "Accounts" + +# GUI +@app.route("/accounts") +@requires_auth +def gui_liste(): + return render_template("list.html", accounts=function_getAccounts(), currentTime=int(time.time())) + +@app.route("/account/") +@requires_auth +def gui_account(number): + account = function_getAccount(number) + special_days = function_speacialDays(number) + + return render_template("account.html", account=account, special_days=special_days, currentTime=int(time.time())) + +@app.route("/account//special", methods=['POST']) +@requires_auth +def special_days(number): + start = datetime.datetime(int(request.form["from_year"]), int(request.form["from_month"]), int(request.form["from_day"]), 0, 0, 0) + ende = datetime.datetime(int(request.form["to_year"]), int(request.form["to_month"]), int(request.form["to_day"]), 0, 0, 0) + delta = ende - start + + cur = get_db().cursor() + for i in range(delta.days + 1): + date = start + datetime.timedelta(days=i) + if request.form["plan"] == "Default": + cur.execute("DELETE FROM special WHERE number = %s AND year = %s AND month = %s AND day = %s" % (number, date.year, date.month, date.day)) + else: + cur.execute("DELETE FROM special WHERE number = %s AND year = %s AND month = %s AND day = %s" % (number, date.year, date.month, date.day)) + cur.execute("INSERT INTO special VALUES (%s, %s, %s, %s, '%s')" % (number, date.day, date.month, date.year, request.form["plan"])) + + get_db().commit(); + + return redirect("/account/"+str(number), code=302) + +@app.route("/account//days") +@requires_auth +def list_days(number): + start = datetime.datetime.now() + ende = start + relativedelta(months=2) + + delta = ende - start + dayList = [] + + for i in range(delta.days + 1): + day = {} + date = start + datetime.timedelta(days=i) + day["date"] = str(date.day)+"."+str(date.month)+"."+str(date.year) + day["plan"] = function_getPlanForDay(number, date.year, date.month, date.day) + dayList.append(day) + + + return render_template("list_days.html", dayList=dayList) + +@app.route("/account/add", methods=['GET']) +@requires_auth +def add_account_gui(): + return render_template("account_add.html") + +@app.route("/account/add", methods=['POST']) +@requires_auth +def add_account_gui_save(): + r = addAccount() + return redirect("/accounts", code=302) + + +# API +@app.route('/api/accounts', methods=['GET']) +@requires_auth +def listAccounts(): + resData = function_getAccounts() + response = {"success": True, "data": resData} + return json.dumps(response) + +@app.route('/api/account//update', methods=['GET']) +@requires_auth +def updateAccount(number): + function_updateAccount(number) + response = {"success": True} + return json.dumps(response) + +@app.route('/api/account//changeDafault', methods=['POST']) +@requires_auth +def changeDefault(number): + cur = get_db().cursor() + default = request.form["plan"] + cur.execute("UPDATE accounts SET `defaultPlan` = '%s' WHERE `number` = %s" % (default, number)) + get_db().commit(); + response = {"success": True} + return json.dumps(response) + +@app.route('/api/accounts', methods=['POST']) +@requires_auth +def addAccount(): + cur = get_db().cursor() + mail = request.form["mail"] + password = request.form["password"] + response = {} + response["success"] = False + response["msg"] = "" + + try: + api = FunkAPI(mail, password) + personalData = api.getPersonalInfo() + except Exception: + response["msg"] = "Autherized failed, wrong mail/password?" + return json.dumps(response) + + plan = api.getCurrentPlan()["productServiceInfo"]["marketingInfo"]["name"] + name = personalData["firstName"]+" "+personalData["lastName"] + + # getUsage + data = api.getData() + number = data["data"]["me"]["customerProducts"][0]["mobileNumbers"][0]["number"] + + + #number integer, mail text, password text, owner text, defaultPlan text + cur.execute("INSERT INTO accounts VALUES ('%s', '%s', '%s', '%s', '%s')" % ( + number, + mail, + password, + name, + plan + )) + + get_db().commit() + function_updateAccount(number) + + response["success"] = True + response["data"] = {"name": name} + + return json.dumps(response) +#SQLITE + +@app.teardown_appcontext +def close_connection(exception): + db = getattr(g, '_database', None) + if db is not None: + db.close() + +if __name__ == '__main__': + app.run(debug=True,host='0.0.0.0') \ No newline at end of file diff --git a/templates/account.html b/templates/account.html new file mode 100644 index 0000000..73049ec --- /dev/null +++ b/templates/account.html @@ -0,0 +1,205 @@ +{% extends "base.html" %} +{% block content %} +

Accounts {{ account["mail"] }}

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Owner{{ account["owner"] }}
Mail{{ account["mail"] }}
Number{{ account["number"] }}
Current Plan{{ account["currentPlan"] }}
Default Plan{{ account["defaultPlan"] }}
Updatet min ago + {{ ((currentTime - account["lastUpdate"]) /60)|round(1) }} +
Data Usage{{ account["dataUsed"] }}%
+Update now +
+

Update Default Plan

+

The Default Plan will be set if no other Rule is enabled for the Day.

+
+ + +
+

Special Days

+

Add some Special Days

+From +
+
+
+ +
+
+ +
+
+ +
+
+To +
+
+ +
+
+ +
+
+ +
+
+Plan +
+ +
+
+

Next Special Days

+ + {% for day in special_days %} + + + + + {% endfor %} +
{{ day["day"] }} {{ day["plan"] }}
+See next 2 Month + +{% endblock %} \ No newline at end of file diff --git a/templates/account_add.html b/templates/account_add.html new file mode 100644 index 0000000..4198e4d --- /dev/null +++ b/templates/account_add.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} +{% block content %} +

Add Account

+
+ + + +
+ +
+{% endblock %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..fc46b9b --- /dev/null +++ b/templates/base.html @@ -0,0 +1,35 @@ + + FUNK Manager + + + + + + + +
+ +
+
+ {% block content %}{% endblock %} +
+
+
+ + \ No newline at end of file diff --git a/templates/list.html b/templates/list.html new file mode 100644 index 0000000..6fe4d37 --- /dev/null +++ b/templates/list.html @@ -0,0 +1,28 @@ +{% extends "base.html" %} +{% block content %} +

FUNK Accounts

+ + + + + + + + + + {% for account in accounts %} + + + + + + + + + {% endfor %} +
NumberData UsageCurrent PlanDefault PlanLast UpdateActions
+{{ account["number"] }} +
+
+
+
{{ account["currentPlan"] }} {{ account["defaultPlan"] }} {{ ((currentTime - account["lastUpdate"]) /60)|round(1) }} min Edit
+{% endblock %} \ No newline at end of file diff --git a/templates/list_days.html b/templates/list_days.html new file mode 100644 index 0000000..cd17475 --- /dev/null +++ b/templates/list_days.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} +{% block content %} +

Days

+ + + + + + {% for day in dayList %} + + + + + {% endfor %} +
DayPlan
{{ day["date"] }}{{ day["plan"] }}
+{% endblock %} \ No newline at end of file