438 lines
No EOL
15 KiB
Python
438 lines
No EOL
15 KiB
Python
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
|
|
from apscheduler.schedulers.background import BackgroundScheduler
|
|
import os
|
|
import atexit
|
|
|
|
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)
|
|
UPDATE_INTERVAL = int(os.getenv("FUNK_UPDATE_INTERVAL", 60*10))
|
|
|
|
|
|
plans = []
|
|
plans.append({"name": "Unlimit", "number": "8", "canBeBooked": True})
|
|
plans.append({"name": "1 GB", "number": "9", "canBeBooked": True})
|
|
plans.append({"name": "Break", "number": "42", "canBeBooked": True})
|
|
plans.append({"name": "Unlimit - First Day", "number": "40", "canBeBooked": False})
|
|
plans.append({"name": "Unlimit - Immidiately", "number": "43", "canBeBooked": 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_cron():
|
|
print("Run Cron")
|
|
with app.app_context():
|
|
print("Debug Mode:" + str(int(app.debug)))
|
|
if app.debug == False:
|
|
function_updateAccountsAtFunk()
|
|
else:
|
|
print("Cron don't run in debug mode")
|
|
def function_updateAllAccounts():
|
|
accounts = function_getAccounts()
|
|
for account in accounts:
|
|
function_updateAccount(account["number"])
|
|
def function_updateAccount(number, api = None):
|
|
cur = get_db().cursor()
|
|
|
|
if api == None:
|
|
cur.execute("SELECT mail, password FROM accounts WHERE `number` = '%s'" % (number))
|
|
res = cur.fetchone()
|
|
api = FunkAPI(res[0], res[1])
|
|
|
|
usage = api.getData()["data"]["me"]["customerProducts"][0]["mobileNumbers"][0]["usage"]["usedDataPercentage"]
|
|
plan = getCurrentPlan(api)
|
|
if plan == None:
|
|
plan = 42;
|
|
else:
|
|
plan = plan["productServiceId"]
|
|
|
|
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"] = function_getPlanName(r[3])
|
|
days.append(day)
|
|
return days
|
|
|
|
def getCurrentPlan(api, now = datetime.datetime.now(datetime.timezone.utc)):
|
|
currentPlan = None
|
|
for plan in api.getData(refresh=False)["data"]["me"]["customerProducts"][0]["tariffs"]:
|
|
planStart = datetime.datetime.strptime(plan["starts"], "%Y-%m-%dT%H:%M:%S.%f%z")
|
|
if planStart > now:
|
|
continue
|
|
currentPlan = plan
|
|
|
|
return currentPlan
|
|
|
|
def function_getPlanName(planNumber):
|
|
for plan in plans:
|
|
if plan["number"] == planNumber:
|
|
return plan
|
|
return False
|
|
|
|
|
|
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"] = function_getPlanName(accountData[4])
|
|
account["currentPlan"] = function_getPlanName(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"] = function_getPlanName(accountData[4])
|
|
account["currentPlan"] = function_getPlanName(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]
|
|
|
|
def function_updateAccountsAtFunk():
|
|
print("Send Updates to Funk")
|
|
accounts = function_getAccounts(True)
|
|
tomorrow = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=1)
|
|
today = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=1)
|
|
for account in accounts:
|
|
api = FunkAPI(account["mail"], account["password"])
|
|
function_updateAccount(account["number"], api) # We already have the API with the Data, so we can write them in the update table
|
|
planedPlan = function_getPlanForDay(account["number"], tomorrow.year, tomorrow.month, tomorrow.day)
|
|
plan = getCurrentPlan(api, tomorrow)
|
|
currentPlan = getCurrentPlan(api)
|
|
if currentPlan == 42: # Wenn aktuell eine pause ist darf das update erst am selben tag erfolgen wie der neue Plan gültug ist
|
|
planedPlan = function_getPlanForDay(account["number"], today.year, today.month, today.day)
|
|
if planedPlan == "8": #Change to unlimit
|
|
api.orderUnlimitedPlan()
|
|
print("Switch to Unlimit")
|
|
elif planedPlan == "9": #Change to 1GB
|
|
api.order1GBPlan()
|
|
print("Switch to 1GB")
|
|
elif planedPlan == "42": #Change to break
|
|
api.startPause()
|
|
print("Switch to break")
|
|
else:
|
|
print("Cant update plan!!! ERROR")
|
|
else:
|
|
if(plan["productServiceId"] == planedPlan):
|
|
print("Nothing to Change, plan already set")
|
|
else:
|
|
print("Update")
|
|
if planedPlan == "8": #Change to unlimit
|
|
api.orderUnlimitedPlan()
|
|
print("Switch to Unlimit")
|
|
elif planedPlan == "9": #Change to 1GB
|
|
api.order1GBPlan()
|
|
print("Switch to 1GB")
|
|
elif planedPlan == "42": #Change to break
|
|
api.startPause()
|
|
print("Switch to break")
|
|
else:
|
|
print("Cant update plan!!! ERROR")
|
|
|
|
#Routes
|
|
@app.route("/")
|
|
@requires_auth
|
|
def hello():
|
|
resData = function_getAccounts()
|
|
plans = {}
|
|
plans["1 GB"] = 0;
|
|
plans["Unlimit"] = 0;
|
|
plans["Unlimit - First Day"] = 0;
|
|
plans["Break"] = 0
|
|
for account in resData:
|
|
plans[account["currentPlan"]["name"]] = plans[account["currentPlan"]["name"]] + 1;
|
|
|
|
return render_template("dashboard.html", debug=app.debug, plans=plans)
|
|
|
|
@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('/sendToFunk', methods=['GET'])
|
|
@requires_auth
|
|
def sendToFunkByGui():
|
|
function_updateAccountsAtFunk()
|
|
return "Update send to funk"
|
|
|
|
@app.route("/accounts")
|
|
@requires_auth
|
|
def gui_liste():
|
|
return render_template("list.html", debug=app.debug, accounts=function_getAccounts(), currentTime=int(time.time()))
|
|
|
|
@app.route("/account/<number>")
|
|
@requires_auth
|
|
def gui_account(number):
|
|
account = function_getAccount(number)
|
|
special_days = function_speacialDays(number)
|
|
|
|
return render_template("account.html", debug=app.debug, account=account, plans=plans, special_days=special_days, currentTime=int(time.time()))
|
|
|
|
@app.route("/account/<number>/statistics")
|
|
@requires_auth
|
|
def gui_statistic(number):
|
|
cur = get_db().cursor()
|
|
ende = datetime.datetime.now()
|
|
start = ende - relativedelta(months=1)
|
|
|
|
delta = ende - start
|
|
dayList = []
|
|
|
|
for i in range(delta.days + 1):
|
|
day = {}
|
|
date = start + datetime.timedelta(days=i)
|
|
dayStart = datetime.datetime(date.year, date.month, date.day, 0, 0, 0)
|
|
dayEnde = datetime.datetime(date.year, date.month, date.day, 23, 59, 59)
|
|
day["date"] = str(date.day)+"."+str(date.month)+"."+str(date.year)
|
|
lastUpdate = cur.execute("SELECT dataUsed, updateTime FROM `updates` WHERE `number` = %s AND `updateTime` > %s AND `updateTime` < %s ORDER BY `updateTime` DESC" % (number, int(dayStart.timestamp()), int(dayEnde.timestamp()))).fetchone()
|
|
if lastUpdate == None:
|
|
day["usage"] = 0
|
|
else:
|
|
day["usage"] = round(lastUpdate[0], 4);
|
|
dayList.append(day)
|
|
|
|
return render_template("statistics.html", debug=app.debug, dayList=dayList)
|
|
|
|
@app.route("/account/<number>/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/<number>/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_getPlanName(function_getPlanForDay(number, date.year, date.month, date.day))
|
|
dayList.append(day)
|
|
|
|
|
|
return render_template("list_days.html", debug=app.debug, 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/<number>/update', methods=['GET'])
|
|
@requires_auth
|
|
def updateAccount(number):
|
|
function_updateAccount(number)
|
|
response = {"success": True}
|
|
return json.dumps(response)
|
|
|
|
@app.route('/api/account/<number>/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 = getCurrentPlan(api)["productServiceId"]
|
|
name = personalData["firstName"]+" "+personalData["lastName"]
|
|
defaultPlan = 42
|
|
|
|
for plan in plans:
|
|
if plan["number"] == plan:
|
|
defaultPlan = plan
|
|
|
|
# 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,
|
|
defaultPlan
|
|
))
|
|
|
|
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()
|
|
|
|
atexit.register(lambda: scheduler.shutdown())
|
|
|
|
def print_date_time():
|
|
print(time.strftime("%A, %d. %B %Y %I:%M:%S %p"))
|
|
|
|
|
|
if app.debug == False:
|
|
print("Start cron");
|
|
print("Cron interval: "+str(UPDATE_INTERVAL))
|
|
scheduler = BackgroundScheduler()
|
|
scheduler.add_job(func=function_cron, trigger="interval", seconds=(UPDATE_INTERVAL))
|
|
scheduler.start()
|
|
else:
|
|
print("Dont start crons in Debug Mode!")
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
app.run(debug=False,host='0.0.0.0')
|
|
|
|
|