2
0
mirror of https://github.com/ComradCollective/Comrad synced 2024-11-17 21:25:37 +00:00
Comrad/app/main.py

472 lines
14 KiB
Python
Raw Normal View History

2020-08-12 08:02:51 +00:00
## CONFIG
# change this to your external ip address for your server
#(needs to be external to allow tor routing)
2020-08-19 23:26:25 +00:00
DEFAULT_SCREEN='profile'
2020-08-19 19:33:25 +00:00
import random
2020-08-19 23:26:25 +00:00
HORIZONTAL = False #random.choice([True,True,True,False])
2020-08-19 19:33:25 +00:00
FACTOR=1
WINDOW_SIZE = (1136*FACTOR,640*FACTOR) if HORIZONTAL else (640*FACTOR,1136*FACTOR)
2020-08-20 15:09:43 +00:00
WINDOW_SIZE=1111,1111
2020-08-20 08:55:50 +00:00
BG_IMG='assets/bg-brown.png'
2020-08-19 23:26:25 +00:00
grass=(201,203,163)
russiangreen = (109,140,96)
huntergreen = (67,92,61)
kombugreen = (49,67,45)
pinetreegreen = (29,40,27)
junglegreen = (15, 21, 14)
browncoffee=(77, 42, 34)
rootbeer=(38, 7, 1)
blackbean=(61, 12, 2)
burntumber=(132, 55, 34)
brownsugar=(175, 110, 81)
antiquebrass= (198, 144, 118)
royalbrown=(94, 55, 46)
bole=(113, 65, 55)
liver= (110, 56, 31)
bistre=(58, 33, 14)
skin1=(89, 47, 42)
skin2=(80, 51, 53)
skin3=(40, 24, 26)
grullo=177, 158, 141
smokyblack=33, 14, 0
liverchestnut=148, 120, 96
ashgray=196, 199, 188
livchestnut2=156, 106, 73
beaver=165, 134, 110
rawumber=120, 95, 74
dutchwhite=229,219,181
COLOR_TOOLBAR= smokyblack #5,5,5 #russiangreen #pinetreegreen #kombugreen #(12,5,5) #russiangreen
COLOR_BG = (0,73,54)
# COLOR_ICON = (201,203,163)
2020-08-20 08:55:50 +00:00
COLOR_LOGO = grass#russiangreen #(0,0,0) #(0,0,0) #(151,177,140) #(132,162,118) #(109,140,106)
COLOR_ICON = grass#russiangreen #(0,0,0) #COLOR_LOGO
2020-08-19 23:26:25 +00:00
COLOR_TEXT =dutchwhite #(241,233,203) #COLOR_ICON #(207,219,204) #(239,235,206) # (194,211,187) # (171,189,163) # (222,224,198) # COLOR_LOGO #(223, 223, 212)
COLOR_CARD = smokyblack #skin2 #huntergreen #(30,23,20) #(51,73,45) # (67,92,61) #(12,9,10)
# COLOR_TOOLBAR = (8s9,59,43)
# COLOR_ICON = COLOR_LOGO = COLOR_TEXT
# COLOR_TEXT=tuple([x+50 for x in russiangreen]) #COLOR_TOOLBAR
# COLOR_ICON = COLOR_LOGO = grass
# COLOR_LOGO = junglegreen #(199,22,22)
# COLOR_ICON = COLOR_LOGO
COLOR_CARD_BORDER = rawumber
2020-08-12 08:02:51 +00:00
2020-08-19 13:01:37 +00:00
# monkeypatching the things that asyncio needs
import subprocess
subprocess.PIPE = -1 # noqa
subprocess.STDOUT = -2 # noqa
subprocess.DEVNULL = -3 # noqa
2020-08-18 17:25:15 +00:00
import asyncio
import os
2020-08-19 14:07:12 +00:00
os.environ['KIVY_EVENTLOOP'] = 'asyncio'
2020-08-18 17:25:15 +00:00
# loop = asyncio.get_event_loop()
# loop.set_debug(True)
2020-08-12 08:02:51 +00:00
# imports
2020-08-03 10:25:58 +00:00
from kivy.uix.screenmanager import Screen,ScreenManager
from kivymd.app import MDApp
2020-08-03 15:48:47 +00:00
from kivymd.uix.button import MDFillRoundFlatButton, MDIconButton
from kivymd.uix.toolbar import MDToolbar
from kivymd.uix.screen import MDScreen
2020-08-03 10:25:58 +00:00
from kivy.lang import Builder
2020-08-03 15:48:47 +00:00
from kivy.uix.boxlayout import BoxLayout
from kivymd.theming import ThemeManager
from kivy.properties import ObjectProperty,ListProperty
import time,os
2020-08-03 15:48:47 +00:00
from collections import OrderedDict
from functools import partial
from kivy.uix.screenmanager import NoTransition
from kivymd.uix.label import MDLabel
from kivy.uix.widget import Widget
2020-08-03 16:29:03 +00:00
from kivymd.uix.list import OneLineListItem
from kivymd.uix.card import MDCard, MDSeparator
2020-08-06 10:24:09 +00:00
from kivymd.uix.boxlayout import MDBoxLayout
2020-08-03 20:16:30 +00:00
from kivy.uix.gridlayout import GridLayout
from kivy.metrics import dp
from kivy.properties import NumericProperty
2020-08-03 21:20:38 +00:00
from kivymd.uix.list import * #MDList, ILeftBody, IRightBody, ThreeLineAvatarListItem, TwoLineAvatarListItem, BaseListItem, ImageLeftWidget
from kivy.uix.image import Image, AsyncImage
2020-08-06 08:46:51 +00:00
import requests,json
2020-08-06 09:43:13 +00:00
from kivy.storage.jsonstore import JsonStore
from kivy.core.window import Window
2020-08-09 10:32:42 +00:00
from kivy.core.text import LabelBase
2020-08-17 20:59:53 +00:00
import shutil,sys
2020-08-13 17:41:31 +00:00
from kivy.uix.image import Image
2020-08-18 06:43:50 +00:00
import sys
sys.path.append("..") # Adds higher directory to python modules path.
2020-08-17 13:33:26 +00:00
from p2p import p2p,crypto,api
from kivy.event import EventDispatcher
2020-08-19 13:01:37 +00:00
import threading,asyncio,sys
2020-08-11 13:01:48 +00:00
2020-08-13 07:55:59 +00:00
Window.size = WINDOW_SIZE
2020-08-09 10:32:42 +00:00
2020-08-17 16:23:40 +00:00
# with open('log.txt','w') as of:
# of.write('### LOG ###\n')
2020-08-19 23:26:25 +00:00
def rgb(r,g,b,a=1):
return (r/255,g/255,b/255,a)
2020-08-10 16:37:42 +00:00
2020-08-06 10:24:09 +00:00
class MyLayout(MDBoxLayout):
2020-08-03 15:48:47 +00:00
scr_mngr = ObjectProperty(None)
2020-08-10 20:38:00 +00:00
post_id = ObjectProperty()
2020-08-06 08:46:51 +00:00
2020-08-19 23:26:25 +00:00
def rgb(self,r,g,b,a=1):
return rgb(r,g,b,a=a)
2020-08-03 15:48:47 +00:00
def change_screen(self, screen, *args):
self.scr_mngr.current = screen
2020-08-10 20:38:00 +00:00
def view_post(self,post_id):
self.post_id=post_id
self.change_screen('view')
2020-08-03 15:48:47 +00:00
2020-08-03 20:16:30 +00:00
2020-08-06 10:24:09 +00:00
class MyBoxLayout(MDBoxLayout): pass
class MyLabel(MDLabel): pass
2020-08-03 20:16:30 +00:00
2020-08-19 23:26:25 +00:00
class MyToolbar(MDToolbar):
action_icon_color = ListProperty()
def update_action_bar(self, action_bar, action_bar_items):
action_bar.clear_widgets()
new_width = 0
for item in action_bar_items:
new_width += dp(48)
action_bar.add_widget(
MDIconButton(
icon=item[0],
on_release=item[1],
opposite_colors=True,
text_color=(self.specific_text_color if not self.action_icon_color else self.action_icon_color),
theme_text_color="Custom",
)
)
action_bar.width = new_width
def update_action_bar_text_colors(self, instance, value):
for child in self.ids["left_actions"].children:
if not self.action_icon_color:
child.text_color = self.specific_text_color
else:
child.text_color = self.action_icon_color
for child in self.ids["right_actions"].children:
if not self.action_icon_color:
child.text_color = self.specific_text_color
else:
child.text_color = self.action_icon_color
2020-08-09 10:32:42 +00:00
2020-08-03 16:29:03 +00:00
2020-08-06 08:46:51 +00:00
2020-08-09 10:32:42 +00:00
def get_tor_proxy_session():
session = requests.session()
# Tor uses the 9050 port as the default socks port
2020-08-13 19:41:19 +00:00
session.proxies = {'http': 'socks5://127.0.0.1:9150',
'https': 'socks5://127.0.0.1:9150'}
2020-08-09 10:32:42 +00:00
return session
def get_async_tor_proxy_session():
from requests_futures.sessions import FuturesSession
session = FuturesSession()
# Tor uses the 9050 port as the default socks port
2020-08-13 19:41:19 +00:00
session.proxies = {'http': 'socks5://127.0.0.1:9150',
'https': 'socks5://127.0.0.1:9150'}
return session
2020-08-09 10:32:42 +00:00
def get_tor_python_session():
from torpy.http.requests import TorRequests
with TorRequests() as tor_requests:
with tor_requests.get_session() as s:
return s
2020-08-13 17:41:31 +00:00
def draw_background(widget, img_fn='assets/bg.png'):
from kivy.core.image import Image as CoreImage
from kivy.graphics import Color, Rectangle
widget.canvas.before.clear()
with widget.canvas.before:
Color(.4, .4, .4, 1)
texture = CoreImage(img_fn).texture
texture.wrap = 'repeat'
nx = float(widget.width) / texture.width
ny = float(widget.height) / texture.height
Rectangle(pos=widget.pos, size=widget.size, texture=texture,
tex_coords=(0, 0, nx, 0, nx, ny, 0, ny))
2020-08-10 20:38:00 +00:00
#### LOOPER
2020-08-03 10:25:58 +00:00
class MainApp(MDApp):
2020-08-06 10:24:09 +00:00
title = 'Komrade'
2020-08-06 09:43:13 +00:00
logged_in=False
2020-08-19 20:15:38 +00:00
store = JsonStore('../p2p/.keys.json')
2020-08-20 01:39:26 +00:00
store_global = JsonStore('../p2p/.keys.global.json')
2020-08-06 09:43:13 +00:00
login_expiry = 60 * 60 * 24 * 7 # once a week
2020-08-13 17:41:31 +00:00
texture = ObjectProperty()
2020-08-17 13:33:26 +00:00
# def connect(self):
2020-08-16 14:59:36 +00:00
# # connect to kad?
2020-08-17 13:33:26 +00:00
# self.node = p2p.connect()
2020-08-19 23:26:25 +00:00
def rgb(self,*_): return rgb(*_)
2020-08-19 10:29:56 +00:00
@property
def logger(self):
if not hasattr(self,'_logger'):
import logging
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
self._logger = logger = logging.getLogger(self.title)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
return self._logger
def log(self,*args):
line = ' '.join(str(x) for x in args)
self.logger.debug(line)
2020-08-16 14:59:36 +00:00
def __init__(self, **kwargs):
super().__init__(**kwargs)
2020-08-19 10:29:56 +00:00
# start looping
2020-08-19 13:01:37 +00:00
# self.log('PATH',sys.path)
# sys.path.append('./p2p')
self.event_loop_worker = None
2020-08-18 17:25:15 +00:00
self.loop=asyncio.get_event_loop()
2020-08-19 10:29:56 +00:00
# load json storage
self.username=''
self.load_store()
# connect to API
self.api = api.Api(app=self)
2020-08-18 17:25:15 +00:00
2020-08-03 10:25:58 +00:00
2020-08-09 10:32:42 +00:00
def get_session(self):
# return get_async_tor_proxy_session()
2020-08-09 10:32:42 +00:00
return get_tor_proxy_session()
#return get_tor_python_session()
2020-08-12 06:49:14 +00:00
def get_username(self):
if hasattr(self,'username'): return self.username
self.load_store()
if hasattr(self,'username'): return self.username
return ''
2020-08-03 10:25:58 +00:00
def build(self):
2020-08-09 10:32:42 +00:00
# bind
2020-08-03 16:29:03 +00:00
global app,root
2020-08-03 15:48:47 +00:00
app = self
2020-08-17 16:23:40 +00:00
2020-08-19 10:29:56 +00:00
# load root
2020-08-10 21:16:36 +00:00
self.root = root = Builder.load_file('root.kv')
2020-08-13 17:41:31 +00:00
draw_background(self.root)
2020-08-06 12:55:26 +00:00
# edit logo
2020-08-19 23:26:25 +00:00
toolbar=root.ids.toolbar
toolbar.md_bg_color = root.rgb(*COLOR_TOOLBAR)
toolbar.action_icon_color=root.rgb(*COLOR_ICON)
logo=toolbar.ids.label_title
2020-08-06 12:55:26 +00:00
logo.font_name='assets/Strengthen.ttf'
logo.font_size='58dp'
logo.pos_hint={'center_y':0.43}
2020-08-19 23:26:25 +00:00
logo.text_color=root.rgb(*COLOR_LOGO)
2020-08-19 10:29:56 +00:00
# logged in?
2020-08-06 09:43:13 +00:00
if not self.is_logged_in():
self.root.change_screen('login')
else:
2020-08-12 19:23:23 +00:00
self.root.change_screen(DEFAULT_SCREEN)
2020-08-19 10:29:56 +00:00
2020-08-03 16:29:03 +00:00
return self.root
2020-08-03 10:25:58 +00:00
2020-08-12 06:49:14 +00:00
def load_store(self):
if not self.store.exists('user'): return
userd=self.store.get('user')
if not userd: userd={}
self.logged_in_when = userd.get('logged_in_when')
self.username = userd.get('username','')
def is_logged_in(self,just_check_timestamp=True, use_caching=True):
2020-08-17 20:40:48 +00:00
# self.username='root'
# return True
2020-08-06 09:43:13 +00:00
if self.logged_in: return True
2020-08-12 06:49:14 +00:00
if not use_caching: return False
###
2020-08-06 09:43:13 +00:00
if not self.store.exists('user'): return False
2020-08-12 06:49:14 +00:00
userd=self.store.get('user')
if not userd: userd={}
if userd.get('logged_in'):
un=userd.get('username')
timestamp=userd.get('logged_in_when')
# just a time check
if timestamp and just_check_timestamp:
if time.time() - timestamp < self.login_expiry:
self.logged_in=True
#self.username=un
return True
2020-08-06 09:43:13 +00:00
return False
2020-08-12 06:49:14 +00:00
def save_login(self,un):
2020-08-06 09:43:13 +00:00
self.logged_in=True
2020-08-12 06:49:14 +00:00
self.username=un
# self.store.put('username',un)
2020-08-13 17:41:31 +00:00
# self.store.put('user',username=un,logged_in=True,logged_in_when=time.time())
2020-08-06 12:55:26 +00:00
self.root.change_screen('feed')
2020-08-06 09:43:13 +00:00
2020-08-03 10:25:58 +00:00
2020-08-12 06:49:14 +00:00
def login(self,un=None,pw=None):
2020-08-19 19:33:25 +00:00
async def do():
dat = await self.api.login(un,pw)
self.log(dat)
if 'success' in dat:
self.save_login(un)
elif 'error' in dat:
self.root.ids.login_screen.login_status.text=dat['error']
2020-08-17 16:23:40 +00:00
return False
2020-08-19 19:33:25 +00:00
asyncio.create_task(do())
2020-08-06 08:46:51 +00:00
2020-08-19 19:33:25 +00:00
def register(self,un,pw):
async def do():
dat = await self.api.register(un,pw)
if 'success' in dat:
self.save_login(un)
return True
elif 'error' in dat:
self.root.ids.login_screen.login_status.text=dat['error']
return False
asyncio.create_task(do())
async def upload(self,filename,file_id=None):
2020-08-19 10:29:56 +00:00
self.log('uploading filename:',filename)
2020-08-19 19:33:25 +00:00
rdata=await self.api.upload(filename,file_id=file_id)
2020-08-19 10:29:56 +00:00
self.log('upload result:',rdata)
2020-08-17 20:40:48 +00:00
if rdata is not None:
rdata['success']='File uploaded'
return rdata
return {'error':'Upload failed'}
2020-08-12 19:23:23 +00:00
2020-08-19 19:33:25 +00:00
async def download(self,file_id,output_fn=None):
2020-08-19 10:29:56 +00:00
self.log('downloading:',file_id)
2020-08-19 19:33:25 +00:00
file_dat = await self.api.download(file_id)
2020-08-20 11:48:26 +00:00
self.log('file_dat =',file_dat)
if not output_fn:
file_id=file_dat['id']
file_ext=file_dat['ext']
output_fn=os.path.join('cache',file_id[:3]+'/'+file_id[3:]+'.'+file_ext)
2020-08-12 19:23:23 +00:00
output_dir=os.path.dirname(output_fn)
if not os.path.exists(output_dir): os.makedirs(output_dir)
with open(output_fn,'wb') as of:
for data_piece in file_dat['parts_data']:
if data_piece is not None:
of.write(data_piece)
2020-08-12 19:23:23 +00:00
2020-08-19 14:07:12 +00:00
async def post(self, content='', file_id=None, file_ext=None, anonymous=False):
#timestamp=time.time()
jsond={}
#jsond['timestamp']=
if content: jsond['content']=str(content)
if file_id: jsond['file_id']=str(file_id)
if file_ext: jsond['file_ext']=str(file_ext)
if not anonymous and self.username:
jsond['author']=self.username
2020-08-19 10:29:56 +00:00
self.log('posting:',jsond)
2020-08-19 14:07:12 +00:00
res=await self.api.post(jsond)
2020-08-17 20:40:48 +00:00
if 'success' in res:
self.root.change_screen('feed')
return {'post_id':res['post_id']}
2020-08-12 19:23:23 +00:00
2020-08-12 06:49:14 +00:00
2020-08-19 14:07:12 +00:00
async def get_post(self,post_id):
return await self.api.get_post(post_id)
2020-08-19 14:07:12 +00:00
async def get_posts(self):
return await self.api.get_posts()
2020-08-19 19:33:25 +00:00
async def get_my_posts(self):
return await self.api.get_posts('/author/'+self.username)
2020-08-11 13:01:48 +00:00
2020-08-19 19:33:25 +00:00
### SYNCHRONOUS?
2020-08-19 14:07:12 +00:00
def app_func(self):
'''This will run both methods asynchronously and then block until they
are finished
'''
# self.other_task = asyncio.ensure_future(self.waste_time_freely())
self.other_task = asyncio.ensure_future(self.api.connect_forever())
async def run_wrapper():
# we don't actually need to set asyncio as the lib because it is
# the default, but it doesn't hurt to be explicit
await self.async_run() #async_lib='asyncio')
print('App done')
self.other_task.cancel()
return asyncio.gather(run_wrapper(), self.other_task)
2020-08-06 08:46:51 +00:00
2020-08-19 14:07:12 +00:00
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(MainApp().app_func())
loop.close()
2020-08-19 10:29:56 +00:00
2020-08-18 17:25:15 +00:00
2020-08-19 14:07:12 +00:00
# def main():
# # start_logger()
# App = MainApp()
# App.run()
# if __name__ == '__main__':
# # loop = asyncio.get_event_loop()
# # asyncio.set_event_loop(loop)
# # loop.run_until_complete(main())
# # loop.close()
# main()