v1 server and lifecycle manager working. execution contract added

This commit is contained in:
Lyncoln
2026-10-08 09:31:29 -04:00
commit f4742f00e0
108 changed files with 750 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
.git
.gitignore
__pycache__/
*.pyc
*.pyo
*.pyd
.venv/
venv/
+9
View File
@@ -0,0 +1,9 @@
FROM python:3.13-slim
WORKDIR /rmmanager
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
from plugins import rmass
from core.operation.operation import buildOperation
outputs = buildOperation(rmass.checkavailableoperation)
+5
View File
@@ -0,0 +1,5 @@
from .apirequest import executeRequest
from .runtime import buildSave, loadSave, buildLog, manageFailure, buildOutput
#from .execution import buildOperation, buildAction
from .execution.action import buildAction
from .execution.operation import buildOperation
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
from .execute import executeRequest
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+26
View File
@@ -0,0 +1,26 @@
import requests
from .request import buildRequest
session = requests.Session()
def executeRequest(definition, outputs):
request = buildRequest(definition, outputs)
response = session.request(
method=request["method"],
url=request["path"],
json=request.get("object"),
headers=request.get("headers")
)
# if definition hasattr(cookies):
# sessioncookies = {}
# for cookie_name, cookie_key in cookies:
# session.cookies.set(
# sessioncookies[cookie_name] = response[cookie_key],
return response
+9
View File
@@ -0,0 +1,9 @@
def buildKey(key, outputs):
value = key["keyvalue"]
if isinstance(value, dict) and "output" in value:
value = outputs[value["output"]]
return {
key["keytitle"]: value,
}
+11
View File
@@ -0,0 +1,11 @@
from .key import buildKey
def buildObject(keys, outputs):
object = {}
for key in keys:
object.update(
buildKey(key, outputs),
)
return object
+28
View File
@@ -0,0 +1,28 @@
from .object import buildObject
def buildRequest(definition, outputs):
request = {
"method": definition.method,
"path": definition.path,
}
if hasattr(definition, "keys"):
request["object"] = buildObject(definition.keys, outputs)
if hasattr(definition, "headers"):
headers = {}
for header_name, header_value in definition.headers.items():
value = header_value
if isinstance(value, dict) and "output" in header_value:
value = outputs[value["output"]]
if isinstance(header_value, dict) and "prefix" in header_value:
value = header_value["prefix"] + value
headers[header_name] = value
request["headers"] = headers
return request
+2
View File
@@ -0,0 +1,2 @@
#from . import action
#from . import operation
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
from .action import buildAction
#from .runtime import buildSave, loadSave, buildLog, manageFailure, buildOutput
#rom .orchestrator import buildOperation
+25
View File
@@ -0,0 +1,25 @@
from core.apirequest import executeRequest
from core.runtime.savedata import loadSave, buildSave
from core.runtime.outputs import buildOutput
from core.runtime.logs import buildLog
def buildAction (action, outputs):
if hasattr(action, "loaddata"):
outputs = loadSave(outputs, action)
response = executeRequest(action, outputs)
if hasattr(action, "outputs"):
outputs = buildOutput(response, action, outputs)
if hasattr(action, "savedata"):
save_file = buildSave(outputs, action)
if hasattr(action, "prints"):
logs = buildLog(action, outputs)
if action.showstatus is True and response.status_code == 200:
print(f"{action.name} successful")
return outputs, response
+9
View File
@@ -0,0 +1,9 @@
from core.execution.action import buildAction
from core.execution.operation import buildOperation
def executeContract(module)
if module.kind == "operation":
outputs = buildOperation(module)
elif module.kind == "action":
outputs = buildAction(module)
+1
View File
@@ -0,0 +1 @@
from .operation import buildOperation
+41
View File
@@ -0,0 +1,41 @@
import logging
#from core import buildAction
from core.runtime.savedata import buildSave, loadSave
from core.runtime.logs import buildLog
from core.runtime.outputs import buildOutput
from core.runtime.managefailure import manageFailure
from core.execution.action.action import buildAction
from .skipstep import skipStep
def buildOperation(definition):
actions = definition.actions
# responses = []
outputs = {}
for action in actions:
try:
if hasattr(definition, "skipstep"):
if skipStep(definition, action, outputs):
continue
outputs, response = buildAction(action, outputs)
# responses.append(response)
except Exception as error:
outputs = manageFailure(error, action, outputs)
# if hasattr(action, "cookies"):
# for cookiename, cookie, in cookies:
# session.cookies.set(
# cookiename,
# outputs[cookie],
# domain=domainname
# )
# print(
# {outputs[cookie]},
# domain,
# cookiename,
# )
+14
View File
@@ -0,0 +1,14 @@
def skipStep(definition, action, outputs):
try:
if action in definition.skipstep:
condition = definition.skipstep[action]
current_value = outputs[condition["output"]]
if condition["operator"] == ">=":
if current_value >= condition["value"]:
print(f"Skipping {action.name}")
return True
except TypeError, KeyError:
return False
+1
View File
@@ -0,0 +1 @@
from . import manager
Binary file not shown.
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
from datetime import datetime
from core.operation.operation import buildOperation
def buildManager(definition):
operations = definition.operations
current_time = datetime.now().time()
for operation in operations:
if hasattr(operation, "role") and operation.role == "activate":
if definition.uptime <= current_time <= definition.downtime:
while True:
current_time = datetime.now().time()
outputs = buildOperation(operation)
if hasattr(operation, "role") and operation.role == "deactivate":
if definition.downtime <= current_time <= definition.uptime:
while True:
current_time = datetime.now().time()
outputs = buildOperation(operation)
# if hasattr(operation, "role") and role == offline:
# outputs = buildOperation(operation)
+9
View File
@@ -0,0 +1,9 @@
#from .managefailure import manageFailure
#from . import outputs
#from . import managefailure
#from . import savedata
from .outputs import buildOutput
from .savedata import buildSave, loadSave
from .logs import buildLog
#from . import managefailure
from .managefailure import manageFailure
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+11
View File
@@ -0,0 +1,11 @@
def buildLog (action, outputs):
# try:
for print_name, print_key in action.prints.items():
print(
f"{print_name}:",
f"{outputs[print_key]}",
)
# except KeyError:
#outputs[print_key] = "N/A"
# return
+19
View File
@@ -0,0 +1,19 @@
from core.runtime.outputs import buildOutput
from core.execution.action.action import buildAction
#from core.runtime.outputs import buildOutput
def manageFailure(error, action, outputs):
if hasattr(action, "recoverPolicy"):
errors = action.recoverPolicy
error_type = type(error)
print(f"{type(error).__name__} detected during {action.name}. Applying recovery policy.")
if error_type in errors:
repairs = errors[error_type]
for repair in repairs:
outputs, response = buildAction(repair, outputs)
return outputs
print(f"{error}")
+21
View File
@@ -0,0 +1,21 @@
def buildOutput(response, action, outputs):
response_data = response.json()
response_objects = [response_data]
while response_objects:
response_object = response_objects.pop()
for key, value, in response_object.items():
if isinstance(value, dict):
response_objects.append(value)
for output_name, response_key in action.outputs.items():
if key == response_key:
outputs[output_name] = value
return outputs
+24
View File
@@ -0,0 +1,24 @@
import json
def buildSave(outputs, action):
save_data = {}
for save_name in action.savedata:
save_data[save_name] = outputs[save_name]
with open(f"{action.savepath}", "w") as save_file:
json.dump(save_data, save_file)
print(f"{save_name} saved by {action.name}")
return save_file
def loadSave(outputs, action):
with open(f"{action.savepath}", "r") as save_file:
load_data = json.load(save_file)
for load_name in action.loaddata:
outputs[load_name] = load_data[load_name]
print(f"{load_name} loaded by {action.name}")
return outputs
+55
View File
@@ -0,0 +1,55 @@
from core.execution.action import buildAction
from core.execution.operation import buildOperation
import plugins
import importlib
import threading
from lifecyclemanager import manageLifecycle
lifecycles = {}
def buildDispatch(request):
dispatch = []
for key in request["module"]:
dispatch.append(request["module"][key])
resolveddispatch = "plugins."+ ".".join(dispatch)
module = importlib.import_module(resolveddispatch)
mode = request.get("mode", "oneshot")
if mode == "continuous":
state = {
"mode": "continuous"
}
lifecycles[resolveddispatch] = state
print(f"Dispatching: {module.name}")
thread = threading.Thread(
target=manageLifecycle,
args=(module, state)
)
thread.start()
print(f"Started lifecycle: {module.name}")
elif mode == "stop":
if resolveddispatch in lifecycles:
state = lifecycles[resolveddispatch]
state["mode"] = "stop"
print(f"Stopping lifecycle: {module.name}")
else:
print(f"No registered lifecycle: {module.name}")
elif mode == "oneshot":
outputs = manageLifecycle(module, {"mode": "oneshot"})
else:
raise ValueError(f"Unsupported lifecycle mode {mode}")
return
#outputs = manageLifecycle(module, state)
+23
View File
@@ -0,0 +1,23 @@
from core.execution.operation import buildOperation
from core.execution.action import buildAction
import time
def executeContract(module):
if module.kind == "operation":
outputs = buildOperation(module)
elif module.kind == "action":
outputs = buildAction(module)
def manageLifecycle(module, state):
if state["mode"] == "continuous":
while state["mode"] == "continuous":
outputs = executeContract(module)
time.sleep(module.sleeptime)
elif state["mode"] == "oneshot":
outputs = executeContract(module)
return
+5
View File
@@ -0,0 +1,5 @@
from plugins import rmass
from core.execution.operation import buildOperation
outputs = buildOperation(rmass.operations.login)
+4
View File
@@ -0,0 +1,4 @@
from plugins import rmass
from operation import buildOperation
outputs = buildOperation(rmass.checkavailableoperation)
+4
View File
@@ -0,0 +1,4 @@
from core.manager.manager import buildManager
from plugins import rmass
outputs = buildManager(rmass.manager)
+1
View File
@@ -0,0 +1 @@
from . import rmass
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
from . import actions, operations
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
from . import login, getcsrf, refresh_tokens, checkavailable, setavailable, setunavailable, setnotset
+30
View File
@@ -0,0 +1,30 @@
name = "Availability check"
kind = "action"
showstatus = False
method = "GET"
path = "https://rentmasseur.com/api/v1/account/keeponline"
outputs = {
"message": "message",
"validTo": "validTo",
}
headers = {
"Authorization": {
"output": "access_token",
"prefix": "Bearer ",
},
}
prints = {
"Status": "message",
"Time Remaining": "validTo",
}
recoverPolicy = {
KeyError: []
}
+13
View File
@@ -0,0 +1,13 @@
name = "Get CSRF token"
kind = "action"
showstatus = True
method = "GET"
path = "https://rentmasseur.com/api/v1/login"
outputs = {
"csrf_token": "csrf"
}
+47
View File
@@ -0,0 +1,47 @@
import os
name = "Login"
kind = "action"
showstatus = True
username = os.environ["RM_USER"]
password = os.environ["RM_PASS"]
keys = [
{
"keytitle": "email",
"keyvalue": username,
},
{
"keytitle": "password",
"keyvalue": password,
},
{
"keytitle": "csrf",
"keyvalue": {
"outputs": "csrf_token",
},
},
{
"keytitle": "remember",
"keyvalue": 1,
},
]
method = "POST"
path = "https://rentmasseur.com/api/v1/login"
outputs = {
"access_token": "accessToken",
"refresh_token": "refreshToken",
}
savedata = [
"access_token",
"refresh_token",
]
savepath = "/home/david/tokens.json"
+51
View File
@@ -0,0 +1,51 @@
from . import getcsrf, login
name = "Token refresh"
showstatus = True
method = "GET"
path = "https://rentmasseur.com/api/v1/refresh/tokens"
headers = {
"Authorization": {
"output": "refresh_token",
"prefix": "Bearer ",
},
}
domainname="rentmasseur.com"
cookies = {
"accessToken": {
"output": "access_token",
},
"refeshToken": {
"output": "refresh_token",
},
}
loaddata = [
"access_token",
"refresh_token",
]
savedata = [
"access_token",
"refresh_token",
]
savepath = "/home/david/tokens.json"
outputs = {
"access_token": "accessToken",
"refresh_token": "refreshToken",
}
recoverPolicy = {
FileNotFoundError: [
getcsrf,
login,
],
}
+47
View File
@@ -0,0 +1,47 @@
import os
name = "Set availability"
kind = "action"
id = "setavailable"
showstatus = True
skipstep = {
"checkavailable": "validTo >= 3600"
}
availabletime = int(os.environ.get("AVAILABLE_DURATION",3))
duration = availabletime - 1
keys = [
{
"keytitle": "option",
"keyvalue": 1,
},
{
"keytitle": "duration",
"keyvalue": duration,
},
]
method = "PUT"
path = "https://rentmasseur.com/api/v1/account/dashboard/availability"
headers = {
"Authorization": {
"output": "access_token",
"prefix": "Bearer ",
},
}
outputs = {
"available_until": "validTo"
}
prints = {
"Available until": "available_until"
}
+24
View File
@@ -0,0 +1,24 @@
import os
name = "Unset Availability"
kind = "action"
availabletime = int(os.environ.get("AVAILABLE_DURATION",3))
duration = availabletime - 5
keys = [
{
"keytitle": "online",
"keyvalue": 0,
},
]
method = "PUT"
path = "https://rentmasseur.com/api/v1/account/dashboard/availability"
#headers = "Authorization:" f"Bearer {access_token}"
+26
View File
@@ -0,0 +1,26 @@
import os
unavailabletime = int(os.environ.get("AVAILABLE_DURATION",3))
duration = unavailabletime - 1
name = "Set Unavailable"
kind = "action"
keys = [
{
"keytitle": "online",
"keyvalue": 2,
},
{
"keytitle": "duration",
"keytitle": duration,
},
]
method = "PUT"
path = "https://rentmasseur.com/api/v1/account/dashboard/availability"
#headers = "Authorization:" f"Bearer {access_token}"
+24
View File
@@ -0,0 +1,24 @@
import os
from datetime import datetime
from . import setavailableoperation
operations = [
setavailableoperation,
#setunavailableoperation
#setnotsetoperation
]
uptime = datetime.strptime(
os.environ["TIME_UPTIME"],
"%H:%M"
).time
downtime = datetime.strptime(
os.environ["TIME_UPTIME"],
"%H:%M"
).time
offline = datetime.strptime(
os.environ["TIME_OFFLINE"],
"%H:%M"
).time
+2
View File
@@ -0,0 +1,2 @@
from . import login, checkavailable, setavailable

Some files were not shown because too many files have changed in this diff Show More