cohost.py/cohost/models/user.py
Juhani Krekelä c10e54dd34
Some checks failed
Python package / build (3.10) (push) Has been cancelled
Python package / build (3.11) (push) Has been cancelled
Python package / build (3.9) (push) Has been cancelled
Make cohost.py run with Python 3.6 without external dependencies
2024-09-30 22:17:19 +00:00

173 lines
5 KiB
Python

import base64
from hashlib import pbkdf2_hmac
from typing import Any, Optional
from cohost.models.notification import buildFromNotifList
from cohost.models.project import EditableProject
from cohost.network import fetch, fetchTrpc, generate_login_cookies
class User:
def __init__(self, cookie):
self.cookie = cookie
def __str__(self):
return "user_{}".format(self.userId)
@property
def email(self):
return self.userInfo['email']
@property
def userId(self):
return self.userInfo['userId']
@property
def modMode(self):
return self.userInfo['modMode']
@property
def activated(self):
return self.userInfo['activated']
@property
def readOnly(self):
return self.userInfo['readOnly']
@property
def defaultProject(self):
from cohost.models.project import EditableProject
return EditableProject(self, self.userInfo['projectId'])
@property
def userInfo(self):
return fetchTrpc('login.loggedIn', self.cookie)['result']['data']
"""Fetch data from the API about projets the user can edit
Returns:
dict: Project dictionaries
"""
@property
def editedProjectsRaw(self):
rawResp = fetchTrpc('projects.listEditedProjects', self.cookie)
return rawResp['result']['data']['projects']
"""Fetch data from the API about projects the user can edit
Returns:
list[Project]: Project objects
"""
@property
def editedProjects(self):
rawP = self.editedProjectsRaw
projects = []
for project in rawP:
projects.append(EditableProject(self, project['projectId']))
return projects
"""Get a salt for an email - for use in logging in
Returns:
str: Base64 encoded salt
"""
@staticmethod
def getSalt(email):
salt = fetch(
"GET",
"/login/salt",
{'email': email}
)
return salt
"""Create a user object from a login and password"""
@staticmethod
def login(email, password):
# base64 terribleness
salt = fetch("GET", "/login/salt", {"email": email})['salt']
# explanation for whatever this is by @iliana
# https://cohost.org/iliana/post/180187-eggbug-rs-v0-1-3-d
salt = salt.replace('-', 'A')
salt = salt.replace('_', 'A')
salt = salt + "=="
saltDecoded = base64.b64decode(salt.encode("ascii"))
# generating the hash
# the 200000 is the magic iter number - Use This, or login will break
hash = pbkdf2_hmac("sha384", password.encode("utf-8"), saltDecoded,
200000, 128)
clientHash = base64.b64encode(hash).decode("ascii")
# getting cookie
res = fetch("POST", "/login",
{"email": email, "clientHash": clientHash}, complex=True)
sessionCookie = (res['headers']['set-cookie'].
split(";")[0].split("=")[1])
u = User(sessionCookie)
# if no error we're good
u.userInfo
return u
"""Create a user object from a cookie"""
@staticmethod
def loginWithCookie(cookie):
# First, let's create a user
u = User(cookie)
# Now, we need to validate functions are working
# We can do this by getting the user's info
u.userInfo
# If this didn't error out, we're good!
return u
def getProject(self, handle):
# Retrieve a project that you can edit
projects = self.editedProjects
for project in projects:
if project.handle == handle:
return project
return None
def resolveSecondaryProject(self, projectData):
from cohost.models.project import Project
editableProjects = self.editedProjects
for project in editableProjects:
if project.projectId == projectData['projectId']:
return project
return Project(self, projectData)
@property
def notifications(self):
return self.notificationsPagified(notificationsPerPage=10)
@property
def allNotifications(self):
page = 0
notifsPerPage = 100
notifs = self.notificationsPagified(
page=page,
notificationsPerPage=notifsPerPage
)
newNotifs = notifs
while len(newNotifs) == notifsPerPage:
page += 1
newNotifs = self.notificationsPagified(
page=page,
notificationsPerPage=notifsPerPage
)
for n in newNotifs:
notifs.append(n)
return notifs
def notificationsPagified(self, page=0, notificationsPerPage=10):
nJson = self.notificationsRaw(page, notificationsPerPage)
return buildFromNotifList(nJson, self)
def notificationsRaw(self, page=0, notificationsPerPage=10):
return fetch('GET', 'notifications/list', {
'offset': page * notificationsPerPage,
'limit': notificationsPerPage
}, generate_login_cookies(self.cookie))