Compare commits

...

10 Commits

40 changed files with 1659 additions and 187 deletions

37
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,37 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-docstring-first
- id: debug-statements
- id: double-quote-string-fixer
- id: requirements-txt-fixer
- id: check-added-large-files
- id: check-ast
- id: check-builtin-literals
- id: check-case-conflict
- id: destroyed-symlinks
- id: check-merge-conflict
- id: debug-statements
- id: detect-private-key
- id: fix-byte-order-marker
- id: mixed-line-ending
- id: file-contents-sorter
- repo: https://github.com/hhatto/autopep8
rev: v2.1.0
hooks:
- id: autopep8
- repo: https://github.com/asottile/add-trailing-comma
rev: v3.1.0
hooks:
- id: add-trailing-comma
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.2
hooks:
- id: pyupgrade
- repo: https://github.com/abdelrahman0w/ouro
rev: v0.2.0
hooks:
- id: ouro

View File

@ -1,7 +1,66 @@
# BillPay
# <img src="https://git.cubable.date/BillPay/app/~raw/dev/assets/icon.png" alt="icon" width="45" height="45"/> BillPay (WIP)
To run the app:
A fully Independent Application for cross platform
```
flet run [app_directory]
```
[![wakatime](https://wakatime.com/badge/user/198abec6-27b2-419b-87cf-60c4107b01be/project/018ef6c9-2350-4cfa-a556-ccadc51e0aa9.svg)](https://wakatime.com/badge/user/198abec6-27b2-419b-87cf-60c4107b01be/project/018ef6c9-2350-4cfa-a556-ccadc51e0aa9)
## 🎯 Purpose for BillPay
It has been difficult for me to manage my bills by having multiple apps installed on my mobile device, and I am sure many other people do too. There are many services here that provide all in one bill pay but they are so locked out we have to login to pay, and to pay we have to communicate with a third party bank account to send it credit so we can process our payments. If those services go down there is no possibility that we can further process our bills through those platforms.
So what could be better than having a fully independent App where YOU control YOUR bills and payments without having to rely on a thirdparty service.
## 💧 Features
**🈚 Independent:**
Application doesnt rely on any thirdparty service to process any bill payments other than banks which are included by the vendor
**🧩 Plugins:**
Apps can be pluggable, if user dont want to see them they can turn it off
***♻️ Backup & Restore:***
Backup Payment History and Restore them on any device
## ⏬ Downloads
- Grab your own copy from [Builds](https://git.cubable.date/BillPay/app/~builds)
## 🦮 Build Guide
### 📜 Requirements
- flutter
- python 3.11.1
- android studio (for android)
- Visual Studio (for Windows)
- Xcode (for IOS)
- Chrome (for Web)
### 🛠️ Build Steps
- Create a python virtualenv: `python -m venv venv`
- Activate virtualenv:
- Windows: `.\venv\Scripts\activate`
- Linux/MacOS: `source venv/bin/activate`
- Install requirements: `pip install -r requirements.txt`
- Run interactive for debugging:
- `flet run --<platform>` where the platform is either `ios` `android` `web`
- To Build Release:
- `flet build <platform> --verbose` where `<platform>` is either `ios` `android` `web`
### Developed with ❤️:
<a href="https://penpot.app">
<img src="https://www.svgrepo.com/download/432263/penpot.svg" alt="penpot" width="50" height="50" class="penpot-color"/>
<style>
.penpot-color{
filter: invert(40%) sepia(0%) saturate(2476%);
}
</style>
</a>
<a href="https://python.org">
<img src="https://asset.brandfetch.io/idbpOFBgcc/idcTemqrrW.svg" alt="python" width="50" height="50"/>
</a>
<a href="https://flutter.dev">
<img src="https://asset.brandfetch.io/idEMZUpQF7/idvArxAqXg.svg" alt="drawing" width="50" height="50"/>
</a>
<a href="https://flet.dev">
<img src="https://github.com/flet-dev/flet/blob/main/media/logo/Icon-192.png?raw=true" alt="drawing" width="50" height="50"/>
</a>

5
apps/__init__.py Normal file
View File

@ -0,0 +1,5 @@
from .apps import Apps
__all__ = [
'Apps',
]

448
apps/apps.py Normal file
View File

@ -0,0 +1,448 @@
import flet as ft
from utils import AppView, Title
from modules.settings import config
from modules.home import AppRow
def Apps(page: ft.Page):
AppRow.controls = []
appsContainer = ft.ListView(
expand=True,
)
appList = ft.Container(
content=ft.Column(
controls=[],
),
)
appsContainer.controls.append(
appList,
)
if config.getint('apps', 'dhiraagu') == 1:
appList.content.controls.append(
ft.Card(
content=ft.Container(
ink=True,
content=ft.Column(
[
ft.ListTile(
leading=ft.Image(
border_radius=ft.border_radius.all(10),
fit=ft.ImageFit.CONTAIN,
src='https://asset.brandfetch.io/idZJsIaold/id9-VJM_HU.png',
),
title=ft.Text('Dhiraagu'),
subtitle=ft.Text(
'An ISP Service.',
),
),
],
),
padding=10,
on_click=lambda _: page.go('/apps/dhiraagu'),
),
),
)
AppRow.controls.append(
ft.Text('Dhiraagu'),
)
AppRow.controls.append(
ft.Container(
margin=10,
padding=10,
bgcolor=ft.colors.with_opacity(0.1, '#000000'),
border_radius=10,
content=ft.Row(
[
ft.Column(
controls=[
ft.Container(
content=ft.Icon(
ft.icons.ACCOUNT_BOX,
color=ft.colors.WHITE,
),
margin=10,
padding=10,
alignment=ft.alignment.center,
bgcolor=ft.colors.GREY_700,
width=60,
height=60,
border_radius=10,
ink=True,
on_click=lambda _: page.go(
'/apps/dhiraagu',
),
),
ft.Container(
content=ft.Text(
'Gift',
text_align=ft.TextAlign.CENTER,
),
alignment=ft.alignment.center,
width=80,
),
],
),
ft.Column(
controls=[
ft.Container(
content=ft.Icon(
ft.icons.ELECTRIC_BOLT,
color=ft.colors.WHITE,
),
margin=10,
padding=10,
alignment=ft.alignment.center,
bgcolor=ft.colors.GREY_700,
width=60,
height=60,
border_radius=10,
ink=True,
on_click=lambda _: page.go(
'/apps/dhiraagu',
),
),
ft.Container(
content=ft.Text(
'Reload',
text_align=ft.TextAlign.CENTER,
),
alignment=ft.alignment.center,
width=80,
),
],
),
ft.Column(
controls=[
ft.Container(
content=ft.Icon(
ft.icons.DASHBOARD,
color=ft.colors.WHITE,
),
margin=10,
padding=10,
alignment=ft.alignment.center,
bgcolor=ft.colors.GREY_700,
width=60,
height=60,
border_radius=10,
ink=True,
on_click=lambda _: page.go(
'/apps/dhiraagu',
),
),
ft.Container(
content=ft.Text(
'Usage',
text_align=ft.TextAlign.CENTER,
),
alignment=ft.alignment.center,
width=80,
),
],
),
ft.Column(
controls=[
ft.Container(
content=ft.Icon(
ft.icons.RECEIPT,
color=ft.colors.WHITE,
),
margin=10,
padding=10,
alignment=ft.alignment.center,
bgcolor=ft.colors.GREY_700,
width=60,
height=60,
border_radius=10,
ink=True,
on_click=lambda _: page.go(
'/apps/dhiraagu',
),
),
ft.Container(
content=ft.Text(
'Bills',
text_align=ft.TextAlign.CENTER,
),
alignment=ft.alignment.center,
width=80,
),
],
),
],
alignment=ft.MainAxisAlignment.CENTER,
),
),
)
if config.getint('apps', 'ooredoo') == 1:
appList.content.controls.append(
ft.Card(
content=ft.Container(
ink=True,
content=ft.Column(
[
ft.ListTile(
leading=ft.Image(
border_radius=ft.border_radius.all(10),
fit=ft.ImageFit.CONTAIN,
src='https://asset.brandfetch.io/idR1xFKHLD/idu6J-WQsm.jpeg',
),
title=ft.Text('Ooredoo'),
subtitle=ft.Text(
'An ISP Service.',
),
),
],
),
padding=10,
on_click=lambda _: page.go('/apps/ooredoo'),
),
),
)
AppRow.controls.append(
ft.Text('Ooredoo'),
)
AppRow.controls.append(
ft.Container(
margin=10,
padding=10,
bgcolor=ft.colors.with_opacity(0.1, '#000000'),
border_radius=10,
content=ft.Row(
[
ft.Column(
controls=[
ft.Container(
content=ft.Icon(
ft.icons.SETTINGS_PHONE,
color=ft.colors.WHITE,
),
margin=10,
padding=10,
alignment=ft.alignment.center,
bgcolor=ft.colors.GREY_700,
width=60,
height=60,
border_radius=10,
ink=True,
on_click=lambda _: page.go(
'/apps/ooredoo',
),
),
ft.Container(
content=ft.Text(
'Add-ons',
text_align=ft.TextAlign.CENTER,
),
alignment=ft.alignment.center,
width=80,
),
],
),
ft.Column(
controls=[
ft.Container(
content=ft.Icon(
ft.icons.ELECTRIC_BOLT,
color=ft.colors.WHITE,
),
margin=10,
padding=10,
alignment=ft.alignment.center,
bgcolor=ft.colors.GREY_700,
width=60,
height=60,
border_radius=10,
ink=True,
on_click=lambda _: page.go(
'/apps/ooredoo',
),
),
ft.Container(
content=ft.Text(
'Raastas',
text_align=ft.TextAlign.CENTER,
),
alignment=ft.alignment.center,
width=80,
),
],
),
ft.Column(
controls=[
ft.Container(
content=ft.Icon(
ft.icons.DASHBOARD,
color=ft.colors.WHITE,
),
margin=10,
padding=10,
alignment=ft.alignment.center,
bgcolor=ft.colors.GREY_700,
width=60,
height=60,
border_radius=10,
ink=True,
on_click=lambda _: page.go(
'/apps/ooredoo',
),
),
ft.Container(
content=ft.Text(
'Balance',
text_align=ft.TextAlign.CENTER,
),
alignment=ft.alignment.center,
width=80,
),
],
),
ft.Column(
controls=[
ft.Container(
content=ft.Icon(
ft.icons.RECEIPT,
color=ft.colors.WHITE,
),
margin=10,
padding=10,
alignment=ft.alignment.center,
bgcolor=ft.colors.GREY_700,
width=60,
height=60,
border_radius=10,
ink=True,
on_click=lambda _: page.go(
'/apps/ooredoo',
),
),
ft.Container(
content=ft.Text(
'Bills',
text_align=ft.TextAlign.CENTER,
),
alignment=ft.alignment.center,
width=80,
),
],
),
],
alignment=ft.MainAxisAlignment.CENTER,
),
),
)
if config.getint('apps', 'mwsc') == 1:
appList.content.controls.append(
ft.Card(
content=ft.Container(
ink=True,
content=ft.Column(
[
ft.ListTile(
leading=ft.Image(
border_radius=ft.border_radius.all(10),
fit=ft.ImageFit.CONTAIN,
src='https://asset.brandfetch.io/idOZWUOUm-/idNastd9Bg.jpeg',
),
title=ft.Text('MWSC'),
subtitle=ft.Text(
'Water Bills',
),
),
],
),
padding=10,
on_click=lambda _: page.go('/apps/mwsc'),
),
),
)
if config.getint('apps', 'stelco') == 1:
appList.content.controls.append(
ft.Card(
content=ft.Container(
ink=True,
content=ft.Column(
[
ft.ListTile(
leading=ft.Image(
border_radius=ft.border_radius.all(10),
fit=ft.ImageFit.CONTAIN,
src='https://www.stelco.com.mv/wp-content/uploads/2022/02/unnamed.png',
),
title=ft.Text('STELCO'),
subtitle=ft.Text(
'Electricity Bills',
),
),
],
),
padding=10,
on_click=lambda _: page.go('/apps/stelco'),
),
),
)
if config.getint('apps', 'medianet') == 1:
appList.content.controls.append(
ft.Card(
content=ft.Container(
ink=True,
content=ft.Column(
[
ft.ListTile(
leading=ft.Image(
border_radius=ft.border_radius.all(10),
fit=ft.ImageFit.CONTAIN,
src='https://asset.brandfetch.io/idLMZnv5SC/iduyX4keYN.jpeg',
),
title=ft.Text('Dhiraagu'),
subtitle=ft.Text(
'Online Television Service',
),
),
],
),
padding=10,
on_click=lambda _: page.go('/apps/medianet'),
),
),
)
if not AppRow.controls:
AppRow.controls.append(
ft.Container(
content=ft.Card(
content=ft.Container(
content=ft.Container(
ft.Text(
'Add a module from settings to see shortcuts here.',
text_align=ft.TextAlign.CENTER,
),
),
padding=10,
on_click=None,
),
),
),
)
if not appList.content.controls:
appList.content.controls.append(
ft.Text(
'Add a module from settings to see shortcuts here.',
text_align=ft.TextAlign.CENTER,
),
)
appList.alignment = ft.alignment.center
appList.expand = True
return AppView(
'/apps',
[
ft.AppBar(
leading=ft.IconButton(
ft.icons.ARROW_BACK_IOS_NEW_ROUNDED,
on_click=lambda _: page.go('/'),
),
title=ft.Text(Title(str(__file__))),
bgcolor=ft.colors.TRANSPARENT,
),
appsContainer,
],
)

13
apps/plugins/__init__.py Normal file
View File

@ -0,0 +1,13 @@
from .dhiraagu import Dhiraagu
from .ooredoo import Ooredoo
from .medianet import Medianet
from .mwsc import Mwsc
from .stelco import Stelco
__all__ = [
'Dhiraagu',
'Ooredoo',
'Medianet',
'Stelco',
'Mwsc',
]

View File

@ -0,0 +1,5 @@
from .app import App as Dhiraagu
__all__ = [
'Dhiraagu',
]

View File

@ -0,0 +1,18 @@
import flet as ft
from utils import AppView
def App(page: ft.Page):
return AppView(
'/apps/dhiraagu',
[
ft.AppBar(
leading=ft.IconButton(
ft.icons.ARROW_BACK_IOS_NEW_ROUNDED,
on_click=lambda _: page.go('/apps'),
),
title=ft.Text('Dhiraagu'),
bgcolor=ft.colors.TRANSPARENT,
),
],
)

View File

@ -0,0 +1,5 @@
from .app import App as Medianet
__all__ = [
'Medianet',
]

View File

@ -0,0 +1,18 @@
import flet as ft
from utils import AppView
def App(page: ft.Page):
return AppView(
'/apps/medianet',
[
ft.AppBar(
leading=ft.IconButton(
ft.icons.ARROW_BACK_IOS_NEW_ROUNDED,
on_click=lambda _: page.go('/apps'),
),
title=ft.Text('Medianet'),
bgcolor=ft.colors.TRANSPARENT,
),
],
)

View File

@ -0,0 +1,5 @@
from .app import App as Mwsc
__all__ = [
'Mwsc',
]

18
apps/plugins/mwsc/app.py Normal file
View File

@ -0,0 +1,18 @@
import flet as ft
from utils import AppView
def App(page: ft.Page):
return AppView(
'/apps/mwsc',
[
ft.AppBar(
leading=ft.IconButton(
ft.icons.ARROW_BACK_IOS_NEW_ROUNDED,
on_click=lambda _: page.go('/apps'),
),
title=ft.Text('MWSC'),
bgcolor=ft.colors.TRANSPARENT,
),
],
)

View File

@ -0,0 +1,5 @@
from .app import App as Ooredoo
__all__ = [
'Ooredoo',
]

View File

@ -0,0 +1,18 @@
import flet as ft
from utils import AppView
def App(page: ft.Page):
return AppView(
'/apps/ooredoo',
[
ft.AppBar(
leading=ft.IconButton(
ft.icons.ARROW_BACK_IOS_NEW_ROUNDED,
on_click=lambda _: page.go('/apps'),
),
title=ft.Text('Ooredoo'),
bgcolor=ft.colors.TRANSPARENT,
),
],
)

View File

@ -0,0 +1,5 @@
from .app import App as Stelco
__all__ = [
'Stelco',
]

View File

@ -0,0 +1,18 @@
import flet as ft
from utils import AppView
def App(page: ft.Page):
return AppView(
'/apps/stelco',
[
ft.AppBar(
leading=ft.IconButton(
ft.icons.ARROW_BACK_IOS_NEW_ROUNDED,
on_click=lambda _: page.go('/apps'),
),
title=ft.Text('STELCO'),
bgcolor=ft.colors.TRANSPARENT,
),
],
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

51
main.py
View File

@ -1,26 +1,61 @@
import flet as ft
from utils import views_handler
from modules.settings.functions import config
from utils import checkInternet
def main(page: ft.Page):
page.title = "BillPay"
# print("Initial route:", page.route)
def route_change(e):
# print("Route change:", e.route)
page.title = 'BillPay'
print('Initial route:', page.route)
config.save('general', 'ignore_network', '0')
def route_change(e: ft.ControlEvent):
print('Route change:', e.route)
page.views.clear()
page.views.append(
views_handler(page)[page.route]
views_handler(page)[page.route],
)
page.update()
def view_pop(e):
# print("View pop:", e.view)
if not config.getint('general', 'ignore_network') and not checkInternet():
dlg = ft.CupertinoActionSheet(
title=ft.Text('No Internet'),
message=ft.Text(
'Please check your network connectivity and try again',
),
cancel=ft.CupertinoActionSheetAction(
content=ft.Text('Okay'),
on_click=lambda _: (
page.close_bottom_sheet() if checkInternet(
) else None, page.update(), None if e.route == '/' else page.go('/'),
),
),
actions=[
ft.CupertinoActionSheetAction(
content=ft.Text('Ignore Completely'),
is_destructive_action=True,
on_click=lambda e: (
page.close_bottom_sheet(), config.save(
'general', 'ignore_network', '1',
),
),
),
],
)
page.show_bottom_sheet(ft.CupertinoBottomSheet(dlg))
def view_pop(e: ft.ControlEvent):
print('View pop:', e.view)
page.views.pop()
top_view = page.views[-1]
page.go(top_view.route)
if config.get('general', 'theme') == 'dark':
page.theme_mode = ft.ThemeMode.DARK
else:
page.theme_mode = ft.ThemeMode.LIGHT
page.on_route_change = route_change
page.on_view_pop = view_pop
page.go(page.route)
ft.app(target=main, assets_dir="assets")
ft.app(target=main, assets_dir='assets')

View File

@ -2,4 +2,4 @@ from .home import Home
from .settings import Settings
__all__ = ["Home", "Settings"]
__all__ = ['Home', 'Settings', 'Apps']

View File

@ -1,33 +1,181 @@
import flet as ft
from utils import AppView, Title
from modules.settings import config
from datetime import datetime
def greet(time: datetime):
if 5 <= time.hour < 12:
return 'Goodmorning,'
elif 12 <= time.hour < 18:
return 'Goodafternoon,'
else:
return 'Goodevening,'
AppRow = ft.ResponsiveRow(
alignment=ft.MainAxisAlignment.CENTER,
)
def Home(page: ft.Page):
return AppView(
"/",
[
ft.AppBar(
title=ft.Text(Title(__file__)),
actions=[
ft.IconButton(
icon=ft.icons.SETTINGS,
on_click=lambda _: page.go("/settings"),
)
]
),
ft.Container(
height=100,
contentColumn = ft.Container(
padding=10,
content=ft.Column(
controls=[
ft.Text(
"Goodmorning,"
greet(datetime.now()),
size=50,
weight=ft.FontWeight.W_500,
),
ft.Text(
config.get("general", "name")
)
config.get('general', 'name').split(' ')[0],
size=40,
weight=ft.FontWeight.W_300,
),
ft.Divider(),
AppRow,
ft.Divider(),
ft.Container(
margin=10,
padding=10,
bgcolor=ft.colors.with_opacity(0.1, '#000000'),
border_radius=10,
content=ft.Row(
[
ft.Column(
controls=[
ft.Container(
content=ft.Icon(
ft.icons.APPS,
color=ft.colors.WHITE,
),
margin=10,
padding=10,
alignment=ft.alignment.center,
bgcolor=ft.colors.GREY_700,
width=60,
height=60,
border_radius=10,
ink=True,
on_click=lambda _: page.go('/apps'),
),
ft.Container(
content=ft.Text(
'Apps',
text_align=ft.TextAlign.CENTER,
),
alignment=ft.alignment.center,
width=80,
),
],
),
ft.Column(
controls=[
ft.Container(
content=ft.Icon(
ft.icons.HISTORY,
color=ft.colors.WHITE,
),
margin=10,
padding=10,
alignment=ft.alignment.center,
bgcolor=ft.colors.GREY_700,
width=60,
height=60,
border_radius=10,
ink=True,
on_click=lambda e: print(
'Clickable with Ink clicked!',
),
),
ft.Container(
content=ft.Text(
'History',
text_align=ft.TextAlign.CENTER,
),
alignment=ft.alignment.center,
width=80,
),
],
),
ft.Column(
controls=[
ft.Container(
content=ft.Icon(
ft.icons.LIST,
color=ft.colors.WHITE,
),
margin=10,
padding=10,
alignment=ft.alignment.center,
bgcolor=ft.colors.GREY_700,
width=60,
height=60,
border_radius=10,
ink=True,
on_click=lambda e: print(
'Clickable with Ink clicked!',
),
),
ft.Container(
content=ft.Text(
'Misc.',
text_align=ft.TextAlign.CENTER,
),
alignment=ft.alignment.center,
width=80,
),
],
),
ft.Column(
controls=[
ft.Container(
content=ft.Icon(
ft.icons.SETTINGS,
color=ft.colors.WHITE,
),
margin=10,
padding=10,
alignment=ft.alignment.center,
bgcolor=ft.colors.GREY_700,
width=60,
height=60,
border_radius=10,
ink=True,
on_click=lambda _: page.go(
'/settings',
),
),
ft.Container(
content=ft.Text(
'Settings',
text_align=ft.TextAlign.CENTER,
),
alignment=ft.alignment.center,
width=80,
),
],
),
],
alignment=ft.MainAxisAlignment.CENTER,
),
),
],
),
)
return AppView(
'/',
[
ft.AppBar(
title=ft.Text(Title(__file__)),
# actions=[
# ft.IconButton(
# icon=ft.icons.SETTINGS,
# on_click=lambda _: page.go('/settings'),
# ),
# ],
),
contentColumn,
],
)

View File

@ -1,4 +1,4 @@
from .settings import Settings
from .functions import config
__all__ = ["Settings", "config"]
__all__ = ['Settings', 'config']

View File

@ -0,0 +1,13 @@
from .dhiraagu_settings import Settings as DhiraaguSettings
from .medianet_settings import Settings as MedianetSettings
from .mwsc_settings import Settings as MwscSettings
from .ooredoo_settings import Settings as OoredooSettings
from .stelco_settings import Settings as StelcoSettings
__all__ = [
'DhiraaguSettings',
'MedianetSettings',
'MwscSettings',
'OoredooSettings',
'StelcoSettings',
]

View File

@ -0,0 +1,117 @@
import flet as ft
from utils import AppView, Title
from ..functions import config
def Settings(page: ft.Page):
settingsContainer = ft.ListView(
expand=True,
)
settingsContainer.controls.append(ft.Text('General'))
def _on_enable_change(e: ft.ControlEvent):
if config.getint('apps', 'dhiraagu'):
config.save('apps', 'dhiraagu', '0')
else:
config.save('apps', 'dhiraagu', '1')
page.update()
generalSettings = ft.Container(
content=ft.Column(
controls=[
ft.Card(
content=ft.Container(
content=ft.Container(
ft.Switch(
label=' Enable this module',
on_change=_on_enable_change,
value=False,
),
),
padding=10,
on_click=None,
),
),
],
),
)
if config.getint('apps', 'dhiraagu') == 1:
generalSettings.content.controls[0].content.content.content.value = True
settingsContainer.controls.append(
generalSettings,
)
settingsContainer.controls.append(
ft.Divider(),
)
# main settings
settingsContainer.controls.append(ft.Text('Account'))
loginForm = ft.Column()
phoneInput = ft.Container(
content=ft.TextField(
config.get('dhiraagu', 'phone'),
label='Phone',
adaptive=True,
),
padding=10,
)
loginForm.controls.append(phoneInput)
otpInput = ft.Container(
content=ft.TextField(
config.get('dhiraagu', 'otp'),
label='OTP',
adaptive=True,
),
padding=10,
)
loginForm.controls.append(otpInput)
sendOTP = ft.CupertinoButton(
content=ft.Text('SEND OTP'),
opacity_on_click=0.3,
on_click=lambda e: print('clicked!'),
)
if config.get('dhiraagu', 'token'):
phoneInput.disabled = True
otpInput.disabled = True
sendOTP.disabled = True
log = ft.CupertinoButton(
content=ft.Text('LOG OUT'),
opacity_on_click=0.3,
on_click=lambda e: print('clicked!'),
)
else:
log = ft.CupertinoButton(
content=ft.Text('LOG IN'),
opacity_on_click=0.3,
on_click=lambda e: print('clicked!'),
)
buttons = ft.Row(
controls=[
sendOTP,
],
)
buttons.controls.append(
log,
)
loginForm.controls.append(
buttons,
)
settingsContainer.controls.append(
loginForm,
)
return AppView(
'/settings/dhiraagu',
[
ft.AppBar(
leading=ft.IconButton(
ft.icons.ARROW_BACK_IOS_NEW_ROUNDED,
on_click=lambda _: page.go('/settings'),
),
title=ft.Text(Title(str(__file__))),
bgcolor=ft.colors.TRANSPARENT,
),
settingsContainer,
],
)

View File

@ -0,0 +1,56 @@
import flet as ft
from utils import AppView, Title
from ..functions import config
def Settings(page: ft.Page):
settingsContainer = ft.ListView(
expand=True,
)
settingsContainer.controls.append(ft.Text('General'))
def _on_enable_change(e: ft.ControlEvent):
if config.getint('apps', 'medianet'):
config.save('apps', 'medianet', '0')
else:
config.save('apps', 'medianet', '1')
page.update()
generalSettings = ft.Container(
content=ft.Column(
controls=[
ft.Card(
content=ft.Container(
content=ft.Container(
ft.Switch(
label=' Enable this module',
on_change=_on_enable_change,
value=False,
),
),
padding=10,
on_click=None,
),
),
],
),
)
if config.getint('apps', 'medianet') == 1:
generalSettings.content.controls[0].content.content.content.value = True
settingsContainer.controls.append(
generalSettings,
)
return AppView(
'/settings/medianet',
[
ft.AppBar(
leading=ft.IconButton(
ft.icons.ARROW_BACK_IOS_NEW_ROUNDED,
on_click=lambda _: page.go('/settings'),
),
title=ft.Text(Title(str(__file__))),
bgcolor=ft.colors.TRANSPARENT,
),
settingsContainer,
],
)

View File

@ -0,0 +1,58 @@
import flet as ft
from utils import AppView, Title
from ..functions import config
ft.Control
def Settings(page: ft.Page):
settingsContainer = ft.ListView(
expand=True,
)
settingsContainer.controls.append(ft.Text('General'))
def _on_enable_change(e: ft.ControlEvent):
if config.getint('apps', 'mwsc'):
config.save('apps', 'mwsc', '0')
else:
config.save('apps', 'mwsc', '1')
page.update()
generalSettings = ft.Container(
content=ft.Column(
controls=[
ft.Card(
content=ft.Container(
content=ft.Container(
ft.Switch(
label=' Enable this module',
on_change=_on_enable_change,
value=False,
),
),
padding=10,
on_click=None,
),
),
],
),
)
if config.getint('apps', 'mwsc') == 1:
generalSettings.content.controls[0].content.content.content.value = True
settingsContainer.controls.append(
generalSettings,
)
return AppView(
'/settings/mwsc',
[
ft.AppBar(
leading=ft.IconButton(
ft.icons.ARROW_BACK_IOS_NEW_ROUNDED,
on_click=lambda _: page.go('/settings'),
),
title=ft.Text(Title(str(__file__))),
bgcolor=ft.colors.TRANSPARENT,
),
settingsContainer,
],
)

View File

@ -0,0 +1,56 @@
import flet as ft
from utils import AppView, Title
from ..functions import config
def Settings(page: ft.Page):
settingsContainer = ft.ListView(
expand=True,
)
settingsContainer.controls.append(ft.Text('General'))
def _on_enable_change(e: ft.ControlEvent):
if config.getint('apps', 'ooredoo'):
config.save('apps', 'ooredoo', '0')
else:
config.save('apps', 'ooredoo', '1')
page.update()
generalSettings = ft.Container(
content=ft.Column(
controls=[
ft.Card(
content=ft.Container(
content=ft.Container(
ft.Switch(
label=' Enable this module',
on_change=_on_enable_change,
value=False,
),
),
padding=10,
on_click=None,
),
),
],
),
)
if config.getint('apps', 'ooredoo') == 1:
generalSettings.content.controls[0].content.content.content.value = True
settingsContainer.controls.append(
generalSettings,
)
return AppView(
'/settings/ooredoo',
[
ft.AppBar(
leading=ft.IconButton(
ft.icons.ARROW_BACK_IOS_NEW_ROUNDED,
on_click=lambda _: page.go('/settings'),
),
title=ft.Text(Title(str(__file__))),
bgcolor=ft.colors.TRANSPARENT,
),
settingsContainer,
],
)

View File

@ -0,0 +1,56 @@
import flet as ft
from utils import AppView, Title
from ..functions import config
def Settings(page: ft.Page):
settingsContainer = ft.ListView(
expand=True,
)
settingsContainer.controls.append(ft.Text('General'))
def _on_enable_change(e: ft.ControlEvent):
if config.getint('apps', 'stelco'):
config.save('apps', 'stelco', '0')
else:
config.save('apps', 'stelco', '1')
page.update()
generalSettings = ft.Container(
content=ft.Column(
controls=[
ft.Card(
content=ft.Container(
content=ft.Container(
ft.Switch(
label=' Enable this module',
on_change=_on_enable_change,
value=False,
),
),
padding=10,
on_click=None,
),
),
],
),
)
if config.getint('apps', 'stelco') == 1:
generalSettings.content.controls[0].content.content.content.value = True
settingsContainer.controls.append(
generalSettings,
)
return AppView(
'/settings/stelco',
[
ft.AppBar(
leading=ft.IconButton(
ft.icons.ARROW_BACK_IOS_NEW_ROUNDED,
on_click=lambda _: page.go('/settings'),
),
title=ft.Text(Title(str(__file__))),
bgcolor=ft.colors.TRANSPARENT,
),
settingsContainer,
],
)

View File

@ -1,3 +1,4 @@
from .config import config
from .theme import Theme
__all__ = ['config', 'Theme']

View File

@ -2,24 +2,26 @@ from configparser import ConfigParser
import os
static = """
[general]
name = CustomIcon
name =
phone =
email =
nid =
theme = light
ignore_network = 0
[apps]
dhiraagu = true
ooredoo = true
hdc = true
mwsc = true
stelco = true
medianet = true
dhiraagu = 0
ooredoo = 0
mwsc = 0
stelco = 0
medianet = 0
[dhiraagu]
phone =
otp =
cookie =
token =
account =
[ooredoo]
phone =
@ -43,18 +45,26 @@ bill_no =
account_no =
nid =
phone =
"""
class Config(ConfigParser):
def __init__(self) -> None:
super().__init__()
if not os.path.exists("config.ini"):
if not os.path.exists('config.ini'):
self.initialize(content=static)
self.read("config.ini")
self.read('config.ini')
def initialize(self, content):
with open("config.ini", 'a+') as file:
with open('config.ini', 'a+') as file:
file.write(content)
def save(self, section, key, value):
config.set(section, key, value)
with open('config.ini', 'w') as configfile:
self.write(configfile)
self.read('config.ini')
config = Config()

View File

@ -1,5 +1,6 @@
import flet as ft
class Theme:
def __init__(self, page: ft.Page) -> None:
self.page = page
@ -7,10 +8,4 @@ class Theme:
def change(self, theme: ft.ThemeMode = None):
if theme:
self.page.theme_mode = theme
else:
self.page.theme_mode = (
ft.ThemeMode.DARK
if self.page.theme_mode == ft.ThemeMode.LIGHT
else ft.ThemeMode.LIGHT
)
self.page.update()

View File

@ -1,12 +1,158 @@
import flet as ft
from utils import AppView, Title
from .functions import Theme
from .functions import Theme, config
def Settings(page: ft.Page):
settingsContainer = ft.ListView(
expand=True
expand=True,
)
settingsContainer.controls.append(ft.Text("General"))
settingsContainer.controls.append(ft.Text('General'))
def _on_name_change(e: ft.ControlEvent):
newName = ft.CupertinoTextField(value=config.get('general', 'name'))
dlg = ft.CupertinoAlertDialog(
modal=True,
title=ft.Text('Enter your Full name'),
content=newName,
actions=[
ft.CupertinoButton(
'Save',
on_click=lambda _: (
setattr(
dlg,
'open',
False,
),
config.save(
'general',
'name',
newName.value,
),
page.update(),
),
),
ft.CupertinoButton(
'Close',
on_click=lambda _: (
setattr(dlg, 'open', False),
page.update(),
),
),
],
)
page.dialog = dlg
dlg.open = True
page.update()
def _on_phone_change(e: ft.ControlEvent):
newPhone = ft.CupertinoTextField(value=config.get('general', 'phone'))
dlg = ft.CupertinoAlertDialog(
modal=True,
title=ft.Text('Enter your Mobile'),
content=newPhone,
actions=[
ft.CupertinoButton(
'Save',
on_click=lambda _: (
setattr(
dlg,
'open',
False,
),
config.save(
'general',
'phone',
newPhone.value,
),
page.update(),
),
),
ft.CupertinoButton(
'Close',
on_click=lambda _: (
setattr(dlg, 'open', False),
page.update(),
),
),
],
)
page.dialog = dlg
dlg.open = True
page.update()
def _on_email_change(e: ft.ControlEvent):
newEmail = ft.CupertinoTextField(value=config.get('general', 'email'))
dlg = ft.CupertinoAlertDialog(
modal=True,
title=ft.Text('Enter your Email'),
content=newEmail,
actions=[
ft.CupertinoButton(
'Save',
on_click=lambda _: (
setattr(
dlg,
'open',
False,
),
config.save(
'general',
'email',
newEmail.value,
),
page.update(),
),
),
ft.CupertinoButton(
'Close',
on_click=lambda _: (
setattr(dlg, 'open', False),
page.update(),
),
),
],
)
page.dialog = dlg
dlg.open = True
page.update()
def _on_nid_change(e: ft.ControlEvent):
newNID = ft.CupertinoTextField(value=config.get('general', 'nid'))
dlg = ft.CupertinoAlertDialog(
modal=True,
title=ft.Text('Enter your NID'),
content=newNID,
actions=[
ft.CupertinoButton(
'Save',
on_click=lambda _: (
setattr(
dlg,
'open',
False,
),
config.save(
'general',
'nid',
newNID.value,
),
page.update(),
),
),
ft.CupertinoButton(
'Close',
on_click=lambda _: (
setattr(dlg, 'open', False),
page.update(),
),
),
],
)
page.dialog = dlg
dlg.open = True
page.update()
generalSettings = ft.Container(
content=ft.Column(
controls=[
@ -17,16 +163,16 @@ def Settings(page: ft.Page):
[
ft.ListTile(
leading=ft.Icon(ft.icons.PERSON),
title=ft.Text("Username"),
title=ft.Text('Username'),
subtitle=ft.Text(
"The greeting message"
'The greeting message',
),
),
]
],
),
padding=10,
on_click=lambda _: print("clicked username"),
)
on_click=_on_name_change,
),
),
ft.Card(
content=ft.Container(
@ -35,16 +181,16 @@ def Settings(page: ft.Page):
[
ft.ListTile(
leading=ft.Icon(ft.icons.PHONE),
title=ft.Text("Phone"),
title=ft.Text('Phone'),
subtitle=ft.Text(
"Personal Phone number"
'Personal Phone number',
),
),
]
],
),
padding=10,
on_click=lambda _: print("clicked phone"),
)
on_click=_on_phone_change,
),
),
ft.Card(
content=ft.Container(
@ -53,16 +199,16 @@ def Settings(page: ft.Page):
[
ft.ListTile(
leading=ft.Icon(ft.icons.EMAIL),
title=ft.Text("Email"),
title=ft.Text('Email'),
subtitle=ft.Text(
"Personal Email"
'Personal Email',
),
),
]
],
),
padding=10,
on_click=lambda _: print("clicked email"),
)
on_click=_on_email_change,
),
),
ft.Card(
content=ft.Container(
@ -71,54 +217,54 @@ def Settings(page: ft.Page):
[
ft.ListTile(
leading=ft.Icon(ft.icons.CARD_TRAVEL),
title=ft.Text("NID"),
title=ft.Text('NID'),
subtitle=ft.Text(
"National Idenity Number"
'National Idenity Number',
),
),
]
],
),
padding=10,
on_click=lambda _: print("clicked email"),
)
on_click=_on_nid_change,
),
),
],
),
]
)
)
settingsContainer.controls.append(
generalSettings
generalSettings,
)
# settingsContainer.controls.append(ft.Divider())
# settingsContainer.controls.append(ft.Text('Notifications'))
# notificationsSettings = ft.Container(
# content=ft.Column(
# controls=[
# ft.Card(
# content=ft.Container(
# ink=True,
# content=ft.Column(
# [
# ft.ListTile(
# leading=ft.Icon(ft.icons.TELEGRAM),
# title=ft.Text('Telegram'),
# subtitle=ft.Text(
# 'Telegram Notifications for reminders and Bill pays',
# ),
# ),
# ],
# ),
# padding=10,
# on_click=lambda _: print('clicked username'),
# ),
# ),
# ],
# ),
# )
# settingsContainer.controls.append(
# notificationsSettings,
# )
settingsContainer.controls.append(ft.Divider())
settingsContainer.controls.append(ft.Text("Notifications"))
notificationsSettings = ft.Container(
content=ft.Column(
controls=[
ft.Card(
content=ft.Container(
ink=True,
content=ft.Column(
[
ft.ListTile(
leading=ft.Icon(ft.icons.TELEGRAM),
title=ft.Text("Telegram"),
subtitle=ft.Text(
"Telegram Notifications for reminders and Bill pays"
),
),
]
),
padding=10,
on_click=lambda _: print("clicked username"),
)
),
]
)
)
settingsContainer.controls.append(
notificationsSettings
)
settingsContainer.controls.append(ft.Divider())
settingsContainer.controls.append(ft.Text("Apps"))
settingsContainer.controls.append(ft.Text('Apps'))
appsSettings = ft.Container(
content=ft.Column(
controls=[
@ -131,18 +277,18 @@ def Settings(page: ft.Page):
leading=ft.Image(
border_radius=ft.border_radius.all(10),
fit=ft.ImageFit.CONTAIN,
src="https://asset.brandfetch.io/idZJsIaold/id9-VJM_HU.png"
src='https://asset.brandfetch.io/idZJsIaold/id9-VJM_HU.png',
),
title=ft.Text("Dhiraagu"),
title=ft.Text('Dhiraagu'),
subtitle=ft.Text(
"An ISP Service."
'An ISP Service.',
),
),
]
],
),
padding=10,
on_click=lambda _: print("clicked dhiraagu"),
)
on_click=lambda _: page.go('/settings/dhiraagu'),
),
),
ft.Card(
content=ft.Container(
@ -153,18 +299,18 @@ def Settings(page: ft.Page):
leading=ft.Image(
border_radius=ft.border_radius.all(10),
fit=ft.ImageFit.CONTAIN,
src="https://asset.brandfetch.io/idR1xFKHLD/idu6J-WQsm.jpeg"
src='https://asset.brandfetch.io/idR1xFKHLD/idu6J-WQsm.jpeg',
),
title=ft.Text("Ooredoo"),
title=ft.Text('Ooredoo'),
subtitle=ft.Text(
"An ISP Service."
'An ISP Service.',
),
),
]
],
),
padding=10,
on_click=lambda _: print("clicked ooredoo"),
)
on_click=lambda _: page.go('/settings/ooredoo'),
),
),
ft.Card(
content=ft.Container(
@ -175,18 +321,18 @@ def Settings(page: ft.Page):
leading=ft.Image(
border_radius=ft.border_radius.all(10),
fit=ft.ImageFit.CONTAIN,
src="https://www.stelco.com.mv/wp-content/uploads/2022/02/unnamed.png"
src='https://www.stelco.com.mv/wp-content/uploads/2022/02/unnamed.png',
),
title=ft.Text("STELCO"),
title=ft.Text('STELCO'),
subtitle=ft.Text(
"Electricity Bills"
'Electricity Bills',
),
),
]
],
),
padding=10,
on_click=lambda _: print("clicked stelco"),
)
on_click=lambda _: page.go('/settings/stelco'),
),
),
ft.Card(
content=ft.Container(
@ -197,18 +343,18 @@ def Settings(page: ft.Page):
leading=ft.Image(
border_radius=ft.border_radius.all(10),
fit=ft.ImageFit.CONTAIN,
src="https://asset.brandfetch.io/idOZWUOUm-/idNastd9Bg.jpeg"
src='https://asset.brandfetch.io/idOZWUOUm-/idNastd9Bg.jpeg',
),
title=ft.Text("MWSC"),
title=ft.Text('MWSC'),
subtitle=ft.Text(
"Water Bills"
'Water Bills',
),
),
]
],
),
padding=10,
on_click=lambda _: print("clicked mwsc"),
)
on_click=lambda _: page.go('/settings/mwsc'),
),
),
ft.Card(
content=ft.Container(
@ -219,27 +365,41 @@ def Settings(page: ft.Page):
leading=ft.Image(
border_radius=ft.border_radius.all(10),
fit=ft.ImageFit.CONTAIN,
src="https://asset.brandfetch.io/idLMZnv5SC/iduyX4keYN.jpeg"
src='https://asset.brandfetch.io/idLMZnv5SC/iduyX4keYN.jpeg',
),
title=ft.Text("Medianet"),
title=ft.Text('Medianet'),
subtitle=ft.Text(
"Online Television Service"
'Online Television Service',
),
),
]
],
),
padding=10,
on_click=lambda _: print("clicked medianet"),
)
on_click=lambda _: page.go('/settings/medianet'),
),
),
],
),
]
)
)
settingsContainer.controls.append(appsSettings)
settingsContainer.controls.append(ft.Divider())
settingsContainer.controls.append(ft.Text("Theme"))
def _on_theme_change(e):
Theme(page).change()
settingsContainer.controls.append(ft.Text('Theme'))
def _on_theme_change(e: ft.ControlEvent):
if page.theme_mode == ft.ThemeMode.LIGHT:
e.value = False
config.save('general', 'theme', 'dark')
else:
e.value = True
config.save('general', 'theme', 'light')
Theme(page).change(
(
ft.ThemeMode.DARK
if page.theme_mode == ft.ThemeMode.LIGHT
else ft.ThemeMode.LIGHT
),
)
themeSettings = ft.Container(
content=ft.Column(
controls=[
@ -247,30 +407,30 @@ def Settings(page: ft.Page):
content=ft.Container(
content=ft.Container(
ft.Switch(
label=' Switch to dark theme',
on_change=_on_theme_change,
value=True,
label=" Switch to dark theme",
on_change=_on_theme_change
),
),
padding=10,
on_click=None,
)
),
]
)
),
],
),
)
settingsContainer.controls.append(themeSettings)
return AppView(
"/settings",
'/settings',
[
ft.AppBar(
leading=ft.IconButton(
ft.icons.ARROW_BACK_IOS_NEW_ROUNDED,
on_click=lambda _: page.go("/")
on_click=lambda _: page.go('/'),
),
title=ft.Text(Title(str(__file__))),
bgcolor=ft.colors.TRANSPARENT
bgcolor=ft.colors.TRANSPARENT,
),
settingsContainer
settingsContainer,
],
)

View File

@ -1 +1,2 @@
flet==0.22.*
requests

View File

@ -1,9 +1,11 @@
from .view import AppView
from .title import Title
from .views import views_handler
from .network import checkInternet
__all__ = [
"AppView",
"Title",
"views_handler"
'AppView',
'Title',
'views_handler',
'checkInternet',
]

11
utils/network.py Normal file
View File

@ -0,0 +1,11 @@
import requests
def checkInternet():
try:
requests.get(
'https://rest.ensembl.org/info/ping?content-type=application/json',
)
return True
except requests.ConnectionError:
return False

View File

@ -1,4 +1,5 @@
import os
def Title(title: str):
return str(os.path.basename(title)).replace('.py', '').title()
return str(os.path.basename(title)).split('.')[0].title().replace('_', ' ')

View File

@ -1,5 +1,6 @@
import flet as ft
class AppView(ft.View):
def __init__(self, route, controls):
super().__init__()

View File

@ -1,7 +1,33 @@
from modules import Home, Settings
from apps import Apps
from apps.plugins import Ooredoo, Dhiraagu, Medianet, Mwsc, Stelco
from modules.settings.appSettings import (
DhiraaguSettings,
MedianetSettings,
StelcoSettings,
OoredooSettings,
MwscSettings,
)
def views_handler(page):
return {
'/': Home(page),
'/settings': Settings(page)
}
views = {}
# root
views['/'] = Home(page)
# settings
views['/settings'] = Settings(page)
views['/settings/dhiraagu'] = DhiraaguSettings(page)
views['/settings/ooredoo'] = OoredooSettings(page)
views['/settings/mwsc'] = MwscSettings(page)
views['/settings/medianet'] = MedianetSettings(page)
views['/settings/stelco'] = StelcoSettings(page)
# apps
views['/apps'] = Apps(page)
views['/apps/dhiraagu'] = Dhiraagu(page)
views['/apps/ooredoo'] = Ooredoo(page)
views['/apps/medianet'] = Medianet(page)
views['/apps/mwsc'] = Mwsc(page)
views['/apps/stelco'] = Stelco(page)
return views

5
wrappers/__init__.py Normal file
View File

@ -0,0 +1,5 @@
from .dhiraagu import DhiraaguAPI
__all__ = [
'DhiraaguAPI',
]

43
wrappers/dhiraagu.py Normal file
View File

@ -0,0 +1,43 @@
import requests
class DhiraaguAPI:
def __init__(
self,
phone: str = None,
otp: str = None,
token: str = None,
account: str = None,
) -> None:
self.phone = phone
self.otp = otp
self.token = token
self.account = account
self.headers = {
'Host': 'app-production.dhiraagu.com.mv',
'Content-Type': 'application/json',
'Connection': 'keep-alive',
'Accept': '*/*',
'User-Agent': 'MyDhiraagu/5.0 (mv.com.dhiraagu.app; build:1; iOS 17.5.0) Alamofire/5.6.1',
'Accept-Language': 'en-US;q=1.0, dv-MV;q=0.9, ar-MV;q=0.8',
# 'Content-Length': '33',
# 'Accept-Encoding': 'br;q=1.0, gzip;q=0.9, deflate;q=0.8',
}
if self.token:
self.headers['Authorization'] = f'Bearer {self.token}'
def login(self):
data = requests.post(
'https://app-production.dhiraagu.com.mv/auth',
json={
'number': self.phone,
'otp': self.otp,
},
)
return data.json()
def bills(self):
return requests.get(
f'https://app-production.dhiraagu.com.mv/io/v1/info/subscribers/{self.account}/liberate/billinfo',
headers=self.headers,
).json()