bacommon.cloud
Functionality related to cloud functionality.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Functionality related to cloud functionality.""" 4 5from __future__ import annotations 6 7from enum import Enum 8from dataclasses import dataclass, field 9from typing import TYPE_CHECKING, Annotated, override 10 11from efro.message import Message, Response 12from efro.dataclassio import ioprepped, IOAttrs 13from bacommon.securedata import SecureDataChecker 14from bacommon.transfer import DirectoryManifest 15from bacommon.login import LoginType 16 17if TYPE_CHECKING: 18 pass 19 20 21class WebLocation(Enum): 22 """Set of places we can be directed on ballistica.net.""" 23 24 ACCOUNT_EDITOR = 'e' 25 ACCOUNT_DELETE_SECTION = 'd' 26 27 28@ioprepped 29@dataclass 30class LoginProxyRequestMessage(Message): 31 """Request send to the cloud to ask for a login-proxy.""" 32 33 @override 34 @classmethod 35 def get_response_types(cls) -> list[type[Response] | None]: 36 return [LoginProxyRequestResponse] 37 38 39@ioprepped 40@dataclass 41class LoginProxyRequestResponse(Response): 42 """Response to a request for a login proxy.""" 43 44 # URL to direct the user to for sign in. 45 url: Annotated[str, IOAttrs('u')] 46 47 # URL to use for overlay-web-browser sign ins. 48 url_overlay: Annotated[str, IOAttrs('uo')] 49 50 # Proxy-Login id for querying results. 51 proxyid: Annotated[str, IOAttrs('p')] 52 53 # Proxy-Login key for querying results. 54 proxykey: Annotated[str, IOAttrs('k')] 55 56 57@ioprepped 58@dataclass 59class LoginProxyStateQueryMessage(Message): 60 """Soo.. how is that login proxy going?""" 61 62 proxyid: Annotated[str, IOAttrs('p')] 63 proxykey: Annotated[str, IOAttrs('k')] 64 65 @override 66 @classmethod 67 def get_response_types(cls) -> list[type[Response] | None]: 68 return [LoginProxyStateQueryResponse] 69 70 71@ioprepped 72@dataclass 73class LoginProxyStateQueryResponse(Response): 74 """Here's the info on that login-proxy you asked about, boss.""" 75 76 class State(Enum): 77 """States a login-proxy can be in.""" 78 79 WAITING = 'waiting' 80 SUCCESS = 'success' 81 FAIL = 'fail' 82 83 state: Annotated[State, IOAttrs('s')] 84 85 # On success, these will be filled out. 86 credentials: Annotated[str | None, IOAttrs('tk')] 87 88 89@ioprepped 90@dataclass 91class LoginProxyCompleteMessage(Message): 92 """Just so you know, we're done with this proxy.""" 93 94 proxyid: Annotated[str, IOAttrs('p')] 95 96 97@ioprepped 98@dataclass 99class PingMessage(Message): 100 """Standard ping.""" 101 102 @override 103 @classmethod 104 def get_response_types(cls) -> list[type[Response] | None]: 105 return [PingResponse] 106 107 108@ioprepped 109@dataclass 110class PingResponse(Response): 111 """pong.""" 112 113 114@ioprepped 115@dataclass 116class TestMessage(Message): 117 """Can I get some of that workspace action?""" 118 119 testfoo: Annotated[int, IOAttrs('f')] 120 121 @override 122 @classmethod 123 def get_response_types(cls) -> list[type[Response] | None]: 124 return [TestResponse] 125 126 127@ioprepped 128@dataclass 129class TestResponse(Response): 130 """Here's that workspace you asked for, boss.""" 131 132 testfoo: Annotated[int, IOAttrs('f')] 133 134 135@ioprepped 136@dataclass 137class SendInfoMessage(Message): 138 """User is using the send-info function""" 139 140 description: Annotated[str, IOAttrs('c')] 141 142 @override 143 @classmethod 144 def get_response_types(cls) -> list[type[Response] | None]: 145 return [SendInfoResponse] 146 147 148@ioprepped 149@dataclass 150class SendInfoResponse(Response): 151 """Response to sending into the server.""" 152 153 handled: Annotated[bool, IOAttrs('v')] 154 message: Annotated[str | None, IOAttrs('m', store_default=False)] = None 155 legacy_code: Annotated[str | None, IOAttrs('l', store_default=False)] = None 156 157 158@ioprepped 159@dataclass 160class WorkspaceFetchState: 161 """Common state data for a workspace fetch.""" 162 163 manifest: Annotated[DirectoryManifest, IOAttrs('m')] 164 iteration: Annotated[int, IOAttrs('i')] = 0 165 total_deletes: Annotated[int, IOAttrs('tdels')] = 0 166 total_downloads: Annotated[int, IOAttrs('tdlds')] = 0 167 total_up_to_date: Annotated[int | None, IOAttrs('tunmd')] = None 168 169 170@ioprepped 171@dataclass 172class WorkspaceFetchMessage(Message): 173 """Can I get some of that workspace action?""" 174 175 workspaceid: Annotated[str, IOAttrs('w')] 176 state: Annotated[WorkspaceFetchState, IOAttrs('s')] 177 178 @override 179 @classmethod 180 def get_response_types(cls) -> list[type[Response] | None]: 181 return [WorkspaceFetchResponse] 182 183 184@ioprepped 185@dataclass 186class WorkspaceFetchResponse(Response): 187 """Here's that workspace you asked for, boss.""" 188 189 state: Annotated[WorkspaceFetchState, IOAttrs('s')] 190 deletes: Annotated[list[str], IOAttrs('dlt', store_default=False)] = field( 191 default_factory=list 192 ) 193 downloads_inline: Annotated[ 194 dict[str, bytes], IOAttrs('dinl', store_default=False) 195 ] = field(default_factory=dict) 196 197 done: Annotated[bool, IOAttrs('d')] = False 198 199 200@ioprepped 201@dataclass 202class MerchAvailabilityMessage(Message): 203 """Can we show merch link?""" 204 205 @override 206 @classmethod 207 def get_response_types(cls) -> list[type[Response] | None]: 208 return [MerchAvailabilityResponse] 209 210 211@ioprepped 212@dataclass 213class MerchAvailabilityResponse(Response): 214 """About that merch...""" 215 216 url: Annotated[str | None, IOAttrs('u')] 217 218 219@ioprepped 220@dataclass 221class SignInMessage(Message): 222 """Can I sign in please?""" 223 224 login_type: Annotated[LoginType, IOAttrs('l')] 225 sign_in_token: Annotated[str, IOAttrs('t')] 226 227 # For debugging. Can remove soft_default once build 20988+ is ubiquitous. 228 description: Annotated[str, IOAttrs('d', soft_default='-')] 229 apptime: Annotated[float, IOAttrs('at', soft_default=-1.0)] 230 231 @override 232 @classmethod 233 def get_response_types(cls) -> list[type[Response] | None]: 234 return [SignInResponse] 235 236 237@ioprepped 238@dataclass 239class SignInResponse(Response): 240 """Here's that sign-in result you asked for, boss.""" 241 242 credentials: Annotated[str | None, IOAttrs('c')] 243 244 245@ioprepped 246@dataclass 247class ManageAccountMessage(Message): 248 """Message asking for a manage-account url.""" 249 250 weblocation: Annotated[WebLocation, IOAttrs('l')] = ( 251 WebLocation.ACCOUNT_EDITOR 252 ) 253 254 @override 255 @classmethod 256 def get_response_types(cls) -> list[type[Response] | None]: 257 return [ManageAccountResponse] 258 259 260@ioprepped 261@dataclass 262class ManageAccountResponse(Response): 263 """Here's that sign-in result you asked for, boss.""" 264 265 url: Annotated[str | None, IOAttrs('u')] 266 267 268@ioprepped 269@dataclass 270class StoreQueryMessage(Message): 271 """Message asking about purchasable stuff and store related state.""" 272 273 @override 274 @classmethod 275 def get_response_types(cls) -> list[type[Response] | None]: 276 return [StoreQueryResponse] 277 278 279@ioprepped 280@dataclass 281class StoreQueryResponse(Response): 282 """Here's that store info you asked for, boss.""" 283 284 class Result(Enum): 285 """Our overall result.""" 286 287 SUCCESS = 's' 288 ERROR = 'e' 289 290 @dataclass 291 class Purchase: 292 """Info about a purchasable thing.""" 293 294 purchaseid: Annotated[str, IOAttrs('id')] 295 296 # Overall result; all data is undefined if not SUCCESS. 297 result: Annotated[Result, IOAttrs('r')] 298 299 tokens: Annotated[int, IOAttrs('t')] 300 gold_pass: Annotated[bool, IOAttrs('g')] 301 302 available_purchases: Annotated[list[Purchase], IOAttrs('p')] 303 token_info_url: Annotated[str, IOAttrs('tiu')] 304 305 306@ioprepped 307@dataclass 308class SecureDataCheckMessage(Message): 309 """Was this data signed by the master-server?.""" 310 311 data: Annotated[bytes, IOAttrs('d')] 312 signature: Annotated[bytes, IOAttrs('s')] 313 314 @override 315 @classmethod 316 def get_response_types(cls) -> list[type[Response] | None]: 317 return [SecureDataCheckResponse] 318 319 320@ioprepped 321@dataclass 322class SecureDataCheckResponse(Response): 323 """Here's the result of that data check, boss.""" 324 325 # Whether the data signature was valid. 326 result: Annotated[bool, IOAttrs('v')] 327 328 329@ioprepped 330@dataclass 331class SecureDataCheckerRequest(Message): 332 """Can I get a checker over here?.""" 333 334 @override 335 @classmethod 336 def get_response_types(cls) -> list[type[Response] | None]: 337 return [SecureDataCheckerResponse] 338 339 340@ioprepped 341@dataclass 342class SecureDataCheckerResponse(Response): 343 """Here's that checker ya asked for, boss.""" 344 345 checker: Annotated[SecureDataChecker, IOAttrs('c')]
22class WebLocation(Enum): 23 """Set of places we can be directed on ballistica.net.""" 24 25 ACCOUNT_EDITOR = 'e' 26 ACCOUNT_DELETE_SECTION = 'd'
Set of places we can be directed on ballistica.net.
29@ioprepped 30@dataclass 31class LoginProxyRequestMessage(Message): 32 """Request send to the cloud to ask for a login-proxy.""" 33 34 @override 35 @classmethod 36 def get_response_types(cls) -> list[type[Response] | None]: 37 return [LoginProxyRequestResponse]
Request send to the cloud to ask for a login-proxy.
40@ioprepped 41@dataclass 42class LoginProxyRequestResponse(Response): 43 """Response to a request for a login proxy.""" 44 45 # URL to direct the user to for sign in. 46 url: Annotated[str, IOAttrs('u')] 47 48 # URL to use for overlay-web-browser sign ins. 49 url_overlay: Annotated[str, IOAttrs('uo')] 50 51 # Proxy-Login id for querying results. 52 proxyid: Annotated[str, IOAttrs('p')] 53 54 # Proxy-Login key for querying results. 55 proxykey: Annotated[str, IOAttrs('k')]
Response to a request for a login proxy.
58@ioprepped 59@dataclass 60class LoginProxyStateQueryMessage(Message): 61 """Soo.. how is that login proxy going?""" 62 63 proxyid: Annotated[str, IOAttrs('p')] 64 proxykey: Annotated[str, IOAttrs('k')] 65 66 @override 67 @classmethod 68 def get_response_types(cls) -> list[type[Response] | None]: 69 return [LoginProxyStateQueryResponse]
Soo.. how is that login proxy going?
72@ioprepped 73@dataclass 74class LoginProxyStateQueryResponse(Response): 75 """Here's the info on that login-proxy you asked about, boss.""" 76 77 class State(Enum): 78 """States a login-proxy can be in.""" 79 80 WAITING = 'waiting' 81 SUCCESS = 'success' 82 FAIL = 'fail' 83 84 state: Annotated[State, IOAttrs('s')] 85 86 # On success, these will be filled out. 87 credentials: Annotated[str | None, IOAttrs('tk')]
Here's the info on that login-proxy you asked about, boss.
77 class State(Enum): 78 """States a login-proxy can be in.""" 79 80 WAITING = 'waiting' 81 SUCCESS = 'success' 82 FAIL = 'fail'
States a login-proxy can be in.
90@ioprepped 91@dataclass 92class LoginProxyCompleteMessage(Message): 93 """Just so you know, we're done with this proxy.""" 94 95 proxyid: Annotated[str, IOAttrs('p')]
Just so you know, we're done with this proxy.
98@ioprepped 99@dataclass 100class PingMessage(Message): 101 """Standard ping.""" 102 103 @override 104 @classmethod 105 def get_response_types(cls) -> list[type[Response] | None]: 106 return [PingResponse]
Standard ping.
pong.
115@ioprepped 116@dataclass 117class TestMessage(Message): 118 """Can I get some of that workspace action?""" 119 120 testfoo: Annotated[int, IOAttrs('f')] 121 122 @override 123 @classmethod 124 def get_response_types(cls) -> list[type[Response] | None]: 125 return [TestResponse]
Can I get some of that workspace action?
128@ioprepped 129@dataclass 130class TestResponse(Response): 131 """Here's that workspace you asked for, boss.""" 132 133 testfoo: Annotated[int, IOAttrs('f')]
Here's that workspace you asked for, boss.
136@ioprepped 137@dataclass 138class SendInfoMessage(Message): 139 """User is using the send-info function""" 140 141 description: Annotated[str, IOAttrs('c')] 142 143 @override 144 @classmethod 145 def get_response_types(cls) -> list[type[Response] | None]: 146 return [SendInfoResponse]
User is using the send-info function
149@ioprepped 150@dataclass 151class SendInfoResponse(Response): 152 """Response to sending into the server.""" 153 154 handled: Annotated[bool, IOAttrs('v')] 155 message: Annotated[str | None, IOAttrs('m', store_default=False)] = None 156 legacy_code: Annotated[str | None, IOAttrs('l', store_default=False)] = None
Response to sending into the server.
159@ioprepped 160@dataclass 161class WorkspaceFetchState: 162 """Common state data for a workspace fetch.""" 163 164 manifest: Annotated[DirectoryManifest, IOAttrs('m')] 165 iteration: Annotated[int, IOAttrs('i')] = 0 166 total_deletes: Annotated[int, IOAttrs('tdels')] = 0 167 total_downloads: Annotated[int, IOAttrs('tdlds')] = 0 168 total_up_to_date: Annotated[int | None, IOAttrs('tunmd')] = None
Common state data for a workspace fetch.
171@ioprepped 172@dataclass 173class WorkspaceFetchMessage(Message): 174 """Can I get some of that workspace action?""" 175 176 workspaceid: Annotated[str, IOAttrs('w')] 177 state: Annotated[WorkspaceFetchState, IOAttrs('s')] 178 179 @override 180 @classmethod 181 def get_response_types(cls) -> list[type[Response] | None]: 182 return [WorkspaceFetchResponse]
Can I get some of that workspace action?
185@ioprepped 186@dataclass 187class WorkspaceFetchResponse(Response): 188 """Here's that workspace you asked for, boss.""" 189 190 state: Annotated[WorkspaceFetchState, IOAttrs('s')] 191 deletes: Annotated[list[str], IOAttrs('dlt', store_default=False)] = field( 192 default_factory=list 193 ) 194 downloads_inline: Annotated[ 195 dict[str, bytes], IOAttrs('dinl', store_default=False) 196 ] = field(default_factory=dict) 197 198 done: Annotated[bool, IOAttrs('d')] = False
Here's that workspace you asked for, boss.
201@ioprepped 202@dataclass 203class MerchAvailabilityMessage(Message): 204 """Can we show merch link?""" 205 206 @override 207 @classmethod 208 def get_response_types(cls) -> list[type[Response] | None]: 209 return [MerchAvailabilityResponse]
Can we show merch link?
212@ioprepped 213@dataclass 214class MerchAvailabilityResponse(Response): 215 """About that merch...""" 216 217 url: Annotated[str | None, IOAttrs('u')]
About that merch...
220@ioprepped 221@dataclass 222class SignInMessage(Message): 223 """Can I sign in please?""" 224 225 login_type: Annotated[LoginType, IOAttrs('l')] 226 sign_in_token: Annotated[str, IOAttrs('t')] 227 228 # For debugging. Can remove soft_default once build 20988+ is ubiquitous. 229 description: Annotated[str, IOAttrs('d', soft_default='-')] 230 apptime: Annotated[float, IOAttrs('at', soft_default=-1.0)] 231 232 @override 233 @classmethod 234 def get_response_types(cls) -> list[type[Response] | None]: 235 return [SignInResponse]
Can I sign in please?
238@ioprepped 239@dataclass 240class SignInResponse(Response): 241 """Here's that sign-in result you asked for, boss.""" 242 243 credentials: Annotated[str | None, IOAttrs('c')]
Here's that sign-in result you asked for, boss.
246@ioprepped 247@dataclass 248class ManageAccountMessage(Message): 249 """Message asking for a manage-account url.""" 250 251 weblocation: Annotated[WebLocation, IOAttrs('l')] = ( 252 WebLocation.ACCOUNT_EDITOR 253 ) 254 255 @override 256 @classmethod 257 def get_response_types(cls) -> list[type[Response] | None]: 258 return [ManageAccountResponse]
Message asking for a manage-account url.
261@ioprepped 262@dataclass 263class ManageAccountResponse(Response): 264 """Here's that sign-in result you asked for, boss.""" 265 266 url: Annotated[str | None, IOAttrs('u')]
Here's that sign-in result you asked for, boss.
269@ioprepped 270@dataclass 271class StoreQueryMessage(Message): 272 """Message asking about purchasable stuff and store related state.""" 273 274 @override 275 @classmethod 276 def get_response_types(cls) -> list[type[Response] | None]: 277 return [StoreQueryResponse]
Message asking about purchasable stuff and store related state.
280@ioprepped 281@dataclass 282class StoreQueryResponse(Response): 283 """Here's that store info you asked for, boss.""" 284 285 class Result(Enum): 286 """Our overall result.""" 287 288 SUCCESS = 's' 289 ERROR = 'e' 290 291 @dataclass 292 class Purchase: 293 """Info about a purchasable thing.""" 294 295 purchaseid: Annotated[str, IOAttrs('id')] 296 297 # Overall result; all data is undefined if not SUCCESS. 298 result: Annotated[Result, IOAttrs('r')] 299 300 tokens: Annotated[int, IOAttrs('t')] 301 gold_pass: Annotated[bool, IOAttrs('g')] 302 303 available_purchases: Annotated[list[Purchase], IOAttrs('p')] 304 token_info_url: Annotated[str, IOAttrs('tiu')]
Here's that store info you asked for, boss.
Our overall result.
291 @dataclass 292 class Purchase: 293 """Info about a purchasable thing.""" 294 295 purchaseid: Annotated[str, IOAttrs('id')]
Info about a purchasable thing.
307@ioprepped 308@dataclass 309class SecureDataCheckMessage(Message): 310 """Was this data signed by the master-server?.""" 311 312 data: Annotated[bytes, IOAttrs('d')] 313 signature: Annotated[bytes, IOAttrs('s')] 314 315 @override 316 @classmethod 317 def get_response_types(cls) -> list[type[Response] | None]: 318 return [SecureDataCheckResponse]
Was this data signed by the master-server?.
321@ioprepped 322@dataclass 323class SecureDataCheckResponse(Response): 324 """Here's the result of that data check, boss.""" 325 326 # Whether the data signature was valid. 327 result: Annotated[bool, IOAttrs('v')]
Here's the result of that data check, boss.
330@ioprepped 331@dataclass 332class SecureDataCheckerRequest(Message): 333 """Can I get a checker over here?.""" 334 335 @override 336 @classmethod 337 def get_response_types(cls) -> list[type[Response] | None]: 338 return [SecureDataCheckerResponse]
Can I get a checker over here?.
341@ioprepped 342@dataclass 343class SecureDataCheckerResponse(Response): 344 """Here's that checker ya asked for, boss.""" 345 346 checker: Annotated[SecureDataChecker, IOAttrs('c')]
Here's that checker ya asked for, boss.