2019-07-06 09:34:24 +00:00
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
2019-07-07 12:01:07 +00:00
from apscheduler . schedulers . background import BackgroundScheduler
2019-07-06 09:34:24 +00:00
import os
2019-07-07 12:01:07 +00:00
import atexit
2019-07-11 13:11:26 +00:00
import logging
logging . basicConfig ( filename = ' example.log ' , level = logging . DEBUG )
logging . debug ( " Start " )
2019-07-06 09:34:24 +00:00
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 )
2019-07-09 09:23:51 +00:00
UPDATE_INTERVAL = int ( os . getenv ( " FUNK_UPDATE_INTERVAL " , 60 * 10 ) )
2019-07-06 09:34:24 +00:00
2019-07-07 12:01:07 +00:00
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 } )
2019-07-11 12:07:42 +00:00
plans . append ( { " name " : " Unlimit - Immidiately " , " number " : " 43 " , " canBeBooked " : False } )
2019-07-07 12:01:07 +00:00
2019-07-06 09:34:24 +00:00
#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
2019-07-07 12:01:07 +00:00
def function_cron ( ) :
print ( " Run Cron " )
with app . app_context ( ) :
2019-07-09 09:23:51 +00:00
print ( " Debug Mode: " + str ( int ( app . debug ) ) )
if app . debug == False :
function_updateAccountsAtFunk ( )
else :
print ( " Cron don ' t run in debug mode " )
2019-07-07 12:01:07 +00:00
def function_updateAllAccounts ( ) :
accounts = function_getAccounts ( )
for account in accounts :
function_updateAccount ( account [ " number " ] )
def function_updateAccount ( number , api = None ) :
2019-07-06 09:34:24 +00:00
cur = get_db ( ) . cursor ( )
2019-07-07 12:01:07 +00:00
if api == None :
cur . execute ( " SELECT mail, password FROM accounts WHERE `number` = ' %s ' " % ( number ) )
res = cur . fetchone ( )
api = FunkAPI ( res [ 0 ] , res [ 1 ] )
2019-07-06 09:34:24 +00:00
usage = api . getData ( ) [ " data " ] [ " me " ] [ " customerProducts " ] [ 0 ] [ " mobileNumbers " ] [ 0 ] [ " usage " ] [ " usedDataPercentage " ]
2019-07-11 08:37:22 +00:00
plan = getCurrentPlan ( api )
if plan == None :
plan = 42 ;
else :
plan = plan [ " productServiceId " ]
2019-07-06 09:34:24 +00:00
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 ] )
2019-07-07 12:16:11 +00:00
day [ " plan " ] = function_getPlanName ( r [ 3 ] )
2019-07-06 09:34:24 +00:00
days . append ( day )
return days
2019-07-07 12:01:07 +00:00
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- %d T % 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
2019-07-06 09:34:24 +00:00
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 ]
2019-07-07 12:01:07 +00:00
account [ " defaultPlan " ] = function_getPlanName ( accountData [ 4 ] )
account [ " currentPlan " ] = function_getPlanName ( lastUpdate [ 1 ] )
2019-07-06 09:34:24 +00:00
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 ]
2019-07-07 12:01:07 +00:00
account [ " defaultPlan " ] = function_getPlanName ( accountData [ 4 ] )
account [ " currentPlan " ] = function_getPlanName ( lastUpdate [ 1 ] )
2019-07-06 09:34:24 +00:00
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 ]
2019-07-07 12:01:07 +00:00
def function_updateAccountsAtFunk ( ) :
2019-07-11 13:11:26 +00:00
logging . debug ( " Send Updates to Func " )
2019-07-07 12:01:07 +00:00
accounts = function_getAccounts ( True )
tomorrow = datetime . datetime . now ( datetime . timezone . utc ) + datetime . timedelta ( days = 1 )
2019-07-11 11:54:06 +00:00
today = datetime . datetime . now ( datetime . timezone . utc ) + datetime . timedelta ( days = 1 )
2019-07-07 12:01:07 +00:00
for account in accounts :
2019-07-11 13:11:26 +00:00
logging . debug ( " Update for Account: " + str ( account [ " number " ] ) )
2019-07-07 12:01:07 +00:00
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 )
2019-07-11 11:54:06 +00:00
currentPlan = getCurrentPlan ( api )
2019-07-11 12:07:42 +00:00
if currentPlan == 42 : # Wenn aktuell eine pause ist darf das update erst am selben tag erfolgen wie der neue Plan gültug ist
2019-07-11 13:11:26 +00:00
logging . debug ( " Account is in a Break, updates will be called at the day the new Plan should work " )
2019-07-11 11:54:06 +00:00
planedPlan = function_getPlanForDay ( account [ " number " ] , today . year , today . month , today . day )
2019-07-11 13:17:59 +00:00
if ( planedPlan == 42 ) :
logging . debug ( " Today Day is already at the currect plan: " + str ( planedPlan ) )
2019-07-11 13:11:26 +00:00
print ( " Nothing to Change, plan already set " )
2019-07-07 12:01:07 +00:00
else :
2019-07-11 13:11:26 +00:00
logging . debug ( " Next day should be updatedt " )
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 " )
2019-07-11 11:54:06 +00:00
else :
2019-07-11 13:11:26 +00:00
logging . debug ( " Account it not on break, next Day can be set " )
2019-07-11 11:54:06 +00:00
if ( plan [ " productServiceId " ] == planedPlan ) :
2019-07-11 13:11:26 +00:00
logging . debug ( " Next Day is already at the currect plan: " + str ( planedPlan ) )
2019-07-11 11:54:06 +00:00
print ( " Nothing to Change, plan already set " )
else :
2019-07-11 13:11:26 +00:00
logging . debug ( " Next day should be updatedt " )
2019-07-11 11:54:06 +00:00
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 " )
2019-07-07 12:01:07 +00:00
2019-07-06 09:34:24 +00:00
#Routes
@app.route ( " / " )
@requires_auth
def hello ( ) :
2019-07-09 13:32:37 +00:00
resData = function_getAccounts ( )
plans = { }
plans [ " 1 GB " ] = 0 ;
plans [ " Unlimit " ] = 0 ;
plans [ " Unlimit - First Day " ] = 0 ;
2019-07-11 13:11:26 +00:00
plans [ " Unlimit - Immidiately " ] = 0 ;
2019-07-09 13:32:37 +00:00
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 )
2019-07-06 09:34:24 +00:00
@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
2019-07-11 11:58:05 +00:00
@app.route ( ' /sendToFunk ' , methods = [ ' GET ' ] )
@requires_auth
def sendToFunkByGui ( ) :
function_updateAccountsAtFunk ( )
return " Update send to funk "
2019-07-06 09:34:24 +00:00
@app.route ( " /accounts " )
@requires_auth
def gui_liste ( ) :
2019-07-09 09:23:51 +00:00
return render_template ( " list.html " , debug = app . debug , accounts = function_getAccounts ( ) , currentTime = int ( time . time ( ) ) )
2019-07-06 09:34:24 +00:00
@app.route ( " /account/<number> " )
@requires_auth
def gui_account ( number ) :
account = function_getAccount ( number )
special_days = function_speacialDays ( number )
2019-07-09 09:23:51 +00:00
return render_template ( " account.html " , debug = app . debug , account = account , plans = plans , special_days = special_days , currentTime = int ( time . time ( ) ) )
2019-07-06 09:34:24 +00:00
2019-07-09 10:15:53 +00:00
@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 )
2019-07-06 09:34:24 +00:00
@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 )
2019-07-07 12:11:43 +00:00
day [ " plan " ] = function_getPlanName ( function_getPlanForDay ( number , date . year , date . month , date . day ) )
2019-07-06 09:34:24 +00:00
dayList . append ( day )
2019-07-09 09:23:51 +00:00
return render_template ( " list_days.html " , debug = app . debug , dayList = dayList )
2019-07-06 09:34:24 +00:00
@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 )
2019-07-11 13:11:26 +00:00
@app.route ( " /log " , methods = [ ' GET ' ] )
@requires_auth
def log_show_gui ( ) :
f = open ( " example.log " )
content = f . readlines ( )
f . close ( )
return render_template ( " log.html " , content = content , debug = app . debug )
2019-07-06 09:34:24 +00:00
# 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 )
2019-07-07 12:01:07 +00:00
plan = getCurrentPlan ( api ) [ " productServiceId " ]
2019-07-06 09:34:24 +00:00
name = personalData [ " firstName " ] + " " + personalData [ " lastName " ]
2019-07-07 12:01:07 +00:00
defaultPlan = 42
for plan in plans :
if plan [ " number " ] == plan :
defaultPlan = plan
2019-07-06 09:34:24 +00:00
# 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 ,
2019-07-07 12:01:07 +00:00
defaultPlan
2019-07-06 09:34:24 +00:00
) )
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 ( )
2019-07-07 12:01:07 +00:00
atexit . register ( lambda : scheduler . shutdown ( ) )
def print_date_time ( ) :
print ( time . strftime ( " % A, %d . % B % Y % I: % M: % S % p " ) )
2019-07-09 09:23:51 +00:00
if app . debug == False :
print ( " Start cron " ) ;
2019-07-11 11:58:05 +00:00
print ( " Cron interval: " + str ( UPDATE_INTERVAL ) )
2019-07-09 09:23:51 +00:00
scheduler = BackgroundScheduler ( )
scheduler . add_job ( func = function_cron , trigger = " interval " , seconds = ( UPDATE_INTERVAL ) )
scheduler . start ( )
else :
print ( " Dont start crons in Debug Mode! " )
2019-07-07 12:01:07 +00:00
2019-07-06 09:34:24 +00:00
if __name__ == ' __main__ ' :
2019-07-11 12:15:13 +00:00
app . run ( debug = False , host = ' 0.0.0.0 ' )
2019-07-09 09:23:51 +00:00