Actually send the puppet reads
Browse filesDeploy to hugging face
Add history
Add commands
- .github/workflows/cd.yml +21 -0
- .github/workflows/release_ci.yml +0 -8
- .gitignore +2 -0
- README.md +23 -1
- backend/backend.py +336 -94
- backend/crappy_test.py +26 -12
- backend/requirements.txt +4 -1
- puppet/.gitignore +1 -7
- puppet/.idea/.gitignore +0 -3
- puppet/.idea/androidTestResultsUserPreferences.xml +0 -35
- puppet/.idea/compiler.xml +0 -6
- puppet/.idea/gradle.xml +0 -19
- puppet/.idea/kotlinc.xml +0 -6
- puppet/.idea/misc.xml +0 -9
- puppet/.idea/vcs.xml +0 -6
- puppet/app/build.gradle +3 -8
- puppet/app/src/main/AndroidManifest.xml +7 -3
- puppet/app/src/main/java/com/ttt246/puppet/ChatterAct.kt +33 -36
- puppet/app/src/main/java/com/ttt246/puppet/PuppetAS.kt +162 -9
- puppet/app/src/main/java/com/ttt246/puppet/SettingsActivity.kt +72 -0
- puppet/app/src/main/res/layout/activity_main.xml +20 -0
- puppet/app/src/main/res/layout/activity_settings.xml +28 -0
- puppet/app/src/main/res/xml/network_security_config.xml +9 -0
- puppet/build.gradle +0 -1
- requirements.txt +1 -0
.github/workflows/cd.yml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Sync to Hugging Face hub
|
| 2 |
+
on:
|
| 3 |
+
push:
|
| 4 |
+
branches: [main]
|
| 5 |
+
|
| 6 |
+
# to run this workflow manually from the Actions tab
|
| 7 |
+
workflow_dispatch:
|
| 8 |
+
|
| 9 |
+
jobs:
|
| 10 |
+
sync-to-hub:
|
| 11 |
+
runs-on: ubuntu-latest
|
| 12 |
+
steps:
|
| 13 |
+
- uses: actions/checkout@v3
|
| 14 |
+
with:
|
| 15 |
+
fetch-depth: 0
|
| 16 |
+
lfs: true
|
| 17 |
+
- name: Push to hub
|
| 18 |
+
env:
|
| 19 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
| 20 |
+
run:
|
| 21 |
+
git push https://posix4e:$HF_TOKEN@huggingface.co/spaces/posix4e/puppet main
|
.github/workflows/release_ci.yml
CHANGED
|
@@ -32,14 +32,6 @@ jobs:
|
|
| 32 |
distribution: 'temurin'
|
| 33 |
cache: gradle
|
| 34 |
|
| 35 |
-
- name: Create file
|
| 36 |
-
run: cat /home/runner/work/puppet/puppet/puppet/app/google-services.json | base64
|
| 37 |
-
|
| 38 |
-
- name: Putting data
|
| 39 |
-
env:
|
| 40 |
-
DATA: ${{ secrets.GOOGLE_SERVICES_JSON }}
|
| 41 |
-
run: echo $DATA > /home/runner/work/puppet/puppet/puppet/app/google-services.json
|
| 42 |
-
|
| 43 |
- name: Grant execute permission for gradlew
|
| 44 |
run: chmod +x gradlew
|
| 45 |
|
|
|
|
| 32 |
distribution: 'temurin'
|
| 33 |
cache: gradle
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
- name: Grant execute permission for gradlew
|
| 36 |
run: chmod +x gradlew
|
| 37 |
|
.gitignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
**/*.swp
|
| 2 |
+
.vscode
|
README.md
CHANGED
|
@@ -1 +1,23 @@
|
|
| 1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: puppet
|
| 3 |
+
colorTo: indigo
|
| 4 |
+
app_file: backend/backend.py
|
| 5 |
+
sdk: gradio
|
| 6 |
+
sdk_version: 3.36.1
|
| 7 |
+
python_version: 3.11.2
|
| 8 |
+
pinned: false
|
| 9 |
+
license: mit
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
Fun!
|
| 13 |
+
|
| 14 |
+
## MVP
|
| 15 |
+
- [ ] switch to only rpcing every minute
|
| 16 |
+
- [ ] Run commands on android
|
| 17 |
+
- [x] Fix assist on android
|
| 18 |
+
- [ ] Testing
|
| 19 |
+
- [ ] Docs
|
| 20 |
+
|
| 21 |
+
## Todo Soon
|
| 22 |
+
|
| 23 |
+
- [ ] Other clients (browser extension/watch)
|
backend/backend.py
CHANGED
|
@@ -1,144 +1,386 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
from
|
| 4 |
-
|
| 5 |
-
import asyncio
|
| 6 |
import gradio as gr
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
import uvicorn
|
| 10 |
from dotenv import load_dotenv
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
load_dotenv()
|
| 13 |
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
-
#
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
|
| 21 |
class RegisterItem(BaseModel):
|
| 22 |
-
|
| 23 |
-
authDomain: str
|
| 24 |
-
databaseURL: str
|
| 25 |
-
storageBucket: str
|
| 26 |
|
| 27 |
|
| 28 |
@app.post("/register")
|
| 29 |
async def register(item: RegisterItem):
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
"storageBucket": item.storageBucket,
|
| 37 |
-
}
|
| 38 |
-
)
|
| 39 |
-
firebase_app = initialize_app(cred, name=str(len(user_data)))
|
| 40 |
-
# Add the Firebase app and auth details to the user_data dictionary
|
| 41 |
-
user_data[str(len(user_data))] = {
|
| 42 |
-
"firebase_app": firebase_app,
|
| 43 |
-
"authDomain": item.authDomain,
|
| 44 |
-
}
|
| 45 |
-
return {"uid": str(len(user_data) - 1)} # Return the user ID
|
| 46 |
|
| 47 |
|
| 48 |
-
class
|
| 49 |
uid: str
|
| 50 |
prompt: str
|
|
|
|
| 51 |
|
| 52 |
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
raise HTTPException(status_code=400, detail="Invalid uid")
|
| 61 |
|
| 62 |
# Call OpenAI
|
| 63 |
-
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
)
|
| 66 |
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
)
|
|
|
|
| 75 |
|
| 76 |
-
# Send the message asynchronously
|
| 77 |
-
asyncio.run(send_notification(message))
|
| 78 |
|
| 79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
|
| 88 |
-
def
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
"http://localhost:8000/register",
|
| 92 |
-
json={
|
| 93 |
-
"apiKey": apiKey,
|
| 94 |
-
"authDomain": authDomain,
|
| 95 |
-
"databaseURL": databaseURL,
|
| 96 |
-
"storageBucket": storageBucket,
|
| 97 |
-
},
|
| 98 |
-
)
|
| 99 |
-
return response.json()
|
| 100 |
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
-
|
| 108 |
-
fn=
|
| 109 |
inputs=[
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
gr.inputs.Textbox(label="databaseURL"),
|
| 114 |
-
gr.inputs.Textbox(label="storageBucket"),
|
| 115 |
-
],
|
| 116 |
-
[gr.inputs.Textbox(label="uid"), gr.inputs.Textbox(label="prompt")],
|
| 117 |
],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
outputs="json",
|
| 119 |
-
title="
|
| 120 |
-
description="
|
| 121 |
)
|
| 122 |
-
return demo
|
| 123 |
|
| 124 |
|
| 125 |
-
def
|
| 126 |
-
|
| 127 |
-
response =
|
| 128 |
-
return response
|
|
|
|
| 129 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
|
| 131 |
-
|
|
|
|
| 132 |
return gr.Interface(
|
| 133 |
-
fn=
|
| 134 |
inputs=[
|
| 135 |
gr.inputs.Textbox(label="UID", type="text"),
|
| 136 |
-
gr.inputs.Textbox(label="
|
| 137 |
],
|
| 138 |
-
outputs="
|
| 139 |
-
title="
|
| 140 |
-
description="
|
| 141 |
)
|
| 142 |
|
| 143 |
|
| 144 |
-
app = gr.mount_gradio_app(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import uuid
|
| 3 |
+
from datetime import datetime
|
| 4 |
+
|
|
|
|
| 5 |
import gradio as gr
|
| 6 |
+
import mistune
|
| 7 |
+
import openai
|
|
|
|
| 8 |
from dotenv import load_dotenv
|
| 9 |
+
from fastapi import FastAPI, HTTPException
|
| 10 |
+
from fastapi.testclient import TestClient
|
| 11 |
+
from pydantic import BaseModel
|
| 12 |
+
from pygments import highlight
|
| 13 |
+
from pygments.formatters import html
|
| 14 |
+
from pygments.lexers import get_lexer_by_name
|
| 15 |
+
from sqlalchemy import JSON, Column, Integer, String, create_engine
|
| 16 |
+
from sqlalchemy.ext.declarative import declarative_base
|
| 17 |
+
from sqlalchemy.orm import Session, sessionmaker
|
| 18 |
+
from sqlalchemy.sql import insert, select, text
|
| 19 |
+
from uvicorn import Config, Server
|
| 20 |
+
from fastapi.middleware.gzip import GZipMiddleware
|
| 21 |
+
|
| 22 |
+
LANGS = [
|
| 23 |
+
"text-davinci-002:100",
|
| 24 |
+
"text-davinci-003:1500",
|
| 25 |
+
"gpt-3.5-turbo:4000",
|
| 26 |
+
"gpt-4:6000",
|
| 27 |
+
]
|
| 28 |
+
|
| 29 |
+
Base = declarative_base()
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class User(Base):
|
| 33 |
+
__tablename__ = "user_data"
|
| 34 |
+
|
| 35 |
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
| 36 |
+
uid = Column(String, nullable=False)
|
| 37 |
+
openai_key = Column(String)
|
| 38 |
+
|
| 39 |
+
def __repr__(self):
|
| 40 |
+
return f"User(id={self.id}, uid={self.uid}"
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class History(Base):
|
| 44 |
+
__tablename__ = "history"
|
| 45 |
+
|
| 46 |
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
| 47 |
+
uid = Column(String, nullable=False)
|
| 48 |
+
question = Column(String, nullable=False)
|
| 49 |
+
answer = Column(JSON, nullable=False)
|
| 50 |
+
|
| 51 |
+
def __repr__(self):
|
| 52 |
+
return f"History(id={self.id}, uid={self.uid}, question={self.question}, answer={self.answer}"
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
# Add a new table to store the commands
|
| 56 |
+
class Command(Base):
|
| 57 |
+
__tablename__ = "commands"
|
| 58 |
+
|
| 59 |
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
| 60 |
+
uid = Column(String, nullable=False)
|
| 61 |
+
command = Column(String, nullable=False)
|
| 62 |
+
status = Column(String, nullable=False, default="queued")
|
| 63 |
+
|
| 64 |
+
def __repr__(self):
|
| 65 |
+
return f"self.command"
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
engine = create_engine("sqlite:///puppet.db")
|
| 69 |
+
Base.metadata.create_all(bind=engine)
|
| 70 |
+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
| 71 |
|
| 72 |
load_dotenv()
|
| 73 |
|
| 74 |
+
app = FastAPI(debug=True)
|
| 75 |
+
app.add_middleware(GZipMiddleware, minimum_size=1000)
|
| 76 |
+
|
| 77 |
|
| 78 |
+
# Add a new API endpoint to add commands to the queue
|
| 79 |
+
class CommandItem(BaseModel):
|
| 80 |
+
uid: str
|
| 81 |
+
command: str
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
@app.post("/add_command")
|
| 85 |
+
async def add_command(item: CommandItem):
|
| 86 |
+
db: Session = SessionLocal()
|
| 87 |
+
new_command = Command(uid=item.uid, command=item.command)
|
| 88 |
+
db.add(new_command)
|
| 89 |
+
db.commit()
|
| 90 |
+
db.refresh(new_command)
|
| 91 |
+
return {"message": "Command added"}
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
class EventItem(BaseModel):
|
| 95 |
+
uid: str
|
| 96 |
+
event: str
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
@app.post("/send_event")
|
| 100 |
+
async def send_event(item: EventItem):
|
| 101 |
+
print(f"Received event from {item.uid}:\n{item.event}")
|
| 102 |
+
|
| 103 |
+
with open(f"{item.uid}_events.txt", "a") as f:
|
| 104 |
+
f.write(f"{datetime.now()} - {item.event}\n")
|
| 105 |
+
|
| 106 |
+
db: Session = SessionLocal()
|
| 107 |
+
user = db.query(User).filter(User.uid == item.uid).first()
|
| 108 |
+
if not user:
|
| 109 |
+
raise HTTPException(status_code=400, detail="Invalid uid")
|
| 110 |
+
|
| 111 |
+
# Update the last time send_event was called and increment the number of events
|
| 112 |
+
user.last_event = datetime.now()
|
| 113 |
+
db.commit()
|
| 114 |
|
| 115 |
+
# Get all the queued commands for this user
|
| 116 |
+
commands = (
|
| 117 |
+
db.query(Command)
|
| 118 |
+
.filter(Command.uid == item.uid, Command.status == "queued")
|
| 119 |
+
.all()
|
| 120 |
+
)
|
| 121 |
+
for command in commands:
|
| 122 |
+
command.status = "running"
|
| 123 |
+
db.commit()
|
| 124 |
+
|
| 125 |
+
return {
|
| 126 |
+
"message": "Event received",
|
| 127 |
+
"commands": [command.command for command in commands],
|
| 128 |
+
}
|
| 129 |
|
| 130 |
|
| 131 |
class RegisterItem(BaseModel):
|
| 132 |
+
openai_key: str
|
|
|
|
|
|
|
|
|
|
| 133 |
|
| 134 |
|
| 135 |
@app.post("/register")
|
| 136 |
async def register(item: RegisterItem):
|
| 137 |
+
db: Session = SessionLocal()
|
| 138 |
+
new_user = User(uid=str(uuid.uuid4()), openai_key=item.openai_key)
|
| 139 |
+
db.add(new_user)
|
| 140 |
+
db.commit()
|
| 141 |
+
db.refresh(new_user)
|
| 142 |
+
return {"uid": new_user.uid}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
|
| 145 |
+
class AssistItem(BaseModel):
|
| 146 |
uid: str
|
| 147 |
prompt: str
|
| 148 |
+
version: str
|
| 149 |
|
| 150 |
|
| 151 |
+
def generate_quick_completion(prompt, model):
|
| 152 |
+
dropdrown = model.split(":")
|
| 153 |
+
engine = dropdrown[0]
|
| 154 |
+
max_tokens = int(dropdrown[1])
|
| 155 |
+
if "gpt" in model:
|
| 156 |
+
message = [{"role": "user", "content": prompt}]
|
| 157 |
+
response = openai.ChatCompletion.create(
|
| 158 |
+
model=engine,
|
| 159 |
+
messages=message,
|
| 160 |
+
temperature=0.2,
|
| 161 |
+
max_tokens=max_tokens,
|
| 162 |
+
frequency_penalty=0.0,
|
| 163 |
+
)
|
| 164 |
+
elif "davinci" in model:
|
| 165 |
+
response = openai.Completion.create(
|
| 166 |
+
engine=engine, prompt=prompt, max_tokens=max_tokens
|
| 167 |
+
)
|
| 168 |
+
else:
|
| 169 |
+
raise Exception("Unknown model")
|
| 170 |
+
return response
|
| 171 |
|
| 172 |
+
|
| 173 |
+
@app.post("/assist")
|
| 174 |
+
async def assist(item: AssistItem):
|
| 175 |
+
db: Session = SessionLocal()
|
| 176 |
+
user = db.query(User).filter(User.uid == item.uid).first()
|
| 177 |
+
if not user:
|
| 178 |
raise HTTPException(status_code=400, detail="Invalid uid")
|
| 179 |
|
| 180 |
# Call OpenAI
|
| 181 |
+
openai.api_key = user.openai_key
|
| 182 |
+
response = generate_quick_completion(item.prompt, item.version)
|
| 183 |
+
|
| 184 |
+
# Update the last time assist was called
|
| 185 |
+
user.last_assist = datetime.now()
|
| 186 |
+
|
| 187 |
+
# Store the history
|
| 188 |
+
new_history = History(
|
| 189 |
+
uid=item.uid, question=item.prompt, answer=json.loads(str(response))
|
| 190 |
)
|
| 191 |
|
| 192 |
+
db.add(new_history)
|
| 193 |
+
db.commit()
|
| 194 |
+
|
| 195 |
+
return response
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
@app.get("/get_history/{uid}")
|
| 199 |
+
async def get_history(uid: str):
|
| 200 |
+
db: Session = SessionLocal()
|
| 201 |
+
history = db.query(History).filter(History.uid == uid).all()
|
| 202 |
+
commands = db.query(Command).filter(Command.uid == uid).all()
|
| 203 |
+
if not history:
|
| 204 |
+
raise HTTPException(status_code=400, detail="No history found for this uid")
|
| 205 |
+
ret = {"history": [h.__dict__ for h in history]}
|
| 206 |
+
|
| 207 |
+
if commands and len(commands) > 0:
|
| 208 |
+
try:
|
| 209 |
+
with open(f"{uid}_events.txt", "r") as f:
|
| 210 |
+
events = f.read().split()
|
| 211 |
+
except FileNotFoundError:
|
| 212 |
+
events = None
|
| 213 |
+
|
| 214 |
+
return {
|
| 215 |
+
"events": events,
|
| 216 |
+
"history": [h.__dict__ for h in history],
|
| 217 |
+
"commands": [c.__dict__ for c in commands],
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
else:
|
| 221 |
+
return {"history": [h.__dict__ for h in history]}
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
def assist_interface(uid, prompt, gpt_version):
|
| 225 |
+
client = TestClient(app)
|
| 226 |
+
|
| 227 |
+
response = client.post(
|
| 228 |
+
"/assist",
|
| 229 |
+
json={"uid": uid, "prompt": prompt, "version": gpt_version},
|
| 230 |
)
|
| 231 |
+
return display_json(response.text)
|
| 232 |
|
|
|
|
|
|
|
| 233 |
|
| 234 |
+
def get_user_interface(uid):
|
| 235 |
+
db: Session = SessionLocal()
|
| 236 |
+
user = db.query(User).filter(User.uid == uid).first()
|
| 237 |
+
if not user:
|
| 238 |
+
return {"message": "No user with this uid found"}
|
| 239 |
+
return str(user)
|
| 240 |
|
| 241 |
|
| 242 |
+
class HighlightRenderer(mistune.HTMLRenderer):
|
| 243 |
+
def block_code(self, code, info=None):
|
| 244 |
+
if info:
|
| 245 |
+
lexer = get_lexer_by_name(info, stripall=True)
|
| 246 |
+
formatter = html.HtmlFormatter()
|
| 247 |
+
return highlight(code, lexer, formatter)
|
| 248 |
+
return "<pre><code>" + mistune.escape(code) + "</code></pre>"
|
| 249 |
|
| 250 |
|
| 251 |
+
def display_json(data):
|
| 252 |
+
html_output = "<html>"
|
| 253 |
+
json_data = json.loads(data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
|
| 255 |
+
id = json_data["id"]
|
| 256 |
+
object = json_data["object"]
|
| 257 |
+
created = json_data["created"]
|
| 258 |
+
model = json_data["model"]
|
| 259 |
+
choices = json_data["choices"]
|
| 260 |
+
|
| 261 |
+
html_output += f"<h2>ID: {id}</h2>"
|
| 262 |
+
html_output += f"<p>Object: {object}</p>"
|
| 263 |
+
html_output += f"<p>Created: {created}</p>"
|
| 264 |
+
html_output += f"<p>Model: {model}</p>"
|
| 265 |
+
|
| 266 |
+
html_output += "<h3>Choices:</h3>"
|
| 267 |
+
if "davinci" in model:
|
| 268 |
+
for choice in choices:
|
| 269 |
+
text = choice["text"]
|
| 270 |
+
html_output += f"<p>Text: {text}</p>"
|
| 271 |
+
elif "gpt" in model:
|
| 272 |
+
for choice in choices:
|
| 273 |
+
try:
|
| 274 |
+
markdown = mistune.create_markdown(renderer=HighlightRenderer())
|
| 275 |
+
text = markdown(choice["message"]["content"])
|
| 276 |
+
html_output += f"<p>Text: {text}</p>"
|
| 277 |
+
except:
|
| 278 |
+
markdown = mistune.create_markdown()
|
| 279 |
+
text = markdown(choice["message"]["content"])
|
| 280 |
+
html_output += f"<p>Text: {text}</p>"
|
| 281 |
+
|
| 282 |
+
html_output += "</html>"
|
| 283 |
+
return html_output
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
def get_assist_interface():
|
| 287 |
+
gpt_version_dropdown = gr.inputs.Dropdown(label="GPT Version", choices=LANGS)
|
| 288 |
|
| 289 |
+
return gr.Interface(
|
| 290 |
+
fn=assist_interface,
|
| 291 |
inputs=[
|
| 292 |
+
gr.inputs.Textbox(label="UID", type="text"),
|
| 293 |
+
gr.inputs.Textbox(label="Prompt", type="text"),
|
| 294 |
+
gpt_version_dropdown,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
],
|
| 296 |
+
outputs="html",
|
| 297 |
+
title="OpenAI Text Generation",
|
| 298 |
+
description="Generate text using OpenAI's GPT-4 model.",
|
| 299 |
+
)
|
| 300 |
+
|
| 301 |
+
|
| 302 |
+
def get_db_interface():
|
| 303 |
+
return gr.Interface(
|
| 304 |
+
fn=get_user_interface,
|
| 305 |
+
inputs="text",
|
| 306 |
+
outputs="text",
|
| 307 |
+
title="Get User Details",
|
| 308 |
+
description="Get user details from the database",
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
def register_interface(openai_key):
|
| 313 |
+
client = TestClient(app)
|
| 314 |
+
response = client.post(
|
| 315 |
+
"/register",
|
| 316 |
+
json={"openai_key": openai_key},
|
| 317 |
+
)
|
| 318 |
+
return response.json()
|
| 319 |
+
|
| 320 |
+
|
| 321 |
+
def get_register_interface():
|
| 322 |
+
return gr.Interface(
|
| 323 |
+
fn=register_interface,
|
| 324 |
+
inputs=[gr.inputs.Textbox(label="OpenAI Key", type="text")],
|
| 325 |
outputs="json",
|
| 326 |
+
title="Register New User",
|
| 327 |
+
description="Register a new user by entering an OpenAI key.",
|
| 328 |
)
|
|
|
|
| 329 |
|
| 330 |
|
| 331 |
+
def get_history_interface(uid):
|
| 332 |
+
client = TestClient(app)
|
| 333 |
+
response = client.get(f"/get_history/{uid}")
|
| 334 |
+
return response.json()
|
| 335 |
+
|
| 336 |
|
| 337 |
+
def get_history_gradio_interface():
|
| 338 |
+
return gr.Interface(
|
| 339 |
+
fn=get_history_interface,
|
| 340 |
+
inputs=[gr.inputs.Textbox(label="UID", type="text")],
|
| 341 |
+
outputs="json",
|
| 342 |
+
title="Get User History",
|
| 343 |
+
description="Get the history of questions and answers for a given user.",
|
| 344 |
+
)
|
| 345 |
+
|
| 346 |
+
|
| 347 |
+
def add_command_interface(uid, command):
|
| 348 |
+
client = TestClient(app)
|
| 349 |
+
response = client.post(
|
| 350 |
+
"/add_command",
|
| 351 |
+
json={"uid": uid, "command": command},
|
| 352 |
+
)
|
| 353 |
+
return response.json()
|
| 354 |
|
| 355 |
+
|
| 356 |
+
def get_add_command_interface():
|
| 357 |
return gr.Interface(
|
| 358 |
+
fn=add_command_interface,
|
| 359 |
inputs=[
|
| 360 |
gr.inputs.Textbox(label="UID", type="text"),
|
| 361 |
+
gr.inputs.Textbox(label="Command", type="text"),
|
| 362 |
],
|
| 363 |
+
outputs="json",
|
| 364 |
+
title="Add Command",
|
| 365 |
+
description="Add a new command for a given user.",
|
| 366 |
)
|
| 367 |
|
| 368 |
|
| 369 |
+
app = gr.mount_gradio_app(
|
| 370 |
+
app,
|
| 371 |
+
gr.TabbedInterface(
|
| 372 |
+
[
|
| 373 |
+
get_assist_interface(),
|
| 374 |
+
get_db_interface(),
|
| 375 |
+
get_register_interface(),
|
| 376 |
+
get_history_gradio_interface(),
|
| 377 |
+
get_add_command_interface(),
|
| 378 |
+
]
|
| 379 |
+
),
|
| 380 |
+
path="/",
|
| 381 |
+
)
|
| 382 |
+
|
| 383 |
+
if __name__ == "__main__":
|
| 384 |
+
config = Config("backend:app", host="0.0.0.0", port=7860, reload=True)
|
| 385 |
+
server = Server(config)
|
| 386 |
+
server.run()
|
backend/crappy_test.py
CHANGED
|
@@ -1,21 +1,35 @@
|
|
| 1 |
-
import
|
| 2 |
-
|
| 3 |
import pytest
|
| 4 |
-
import
|
|
|
|
| 5 |
import backend
|
| 6 |
-
|
|
|
|
| 7 |
|
| 8 |
|
| 9 |
@pytest.mark.asyncio
|
| 10 |
async def test_register():
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
data = {
|
| 14 |
-
"
|
| 15 |
-
"
|
| 16 |
-
"
|
| 17 |
-
"storageBucket": "test-storage-bucket",
|
| 18 |
-
"openai_key": "test-openai-key",
|
| 19 |
}
|
| 20 |
-
|
| 21 |
-
client.post("/register", json=data)
|
|
|
|
| 1 |
+
import uuid
|
| 2 |
+
|
| 3 |
import pytest
|
| 4 |
+
from fastapi.testclient import TestClient
|
| 5 |
+
|
| 6 |
import backend
|
| 7 |
+
|
| 8 |
+
client = TestClient(backend.app)
|
| 9 |
|
| 10 |
|
| 11 |
@pytest.mark.asyncio
|
| 12 |
async def test_register():
|
| 13 |
+
data = {
|
| 14 |
+
"openai_key": "garbage",
|
| 15 |
+
}
|
| 16 |
+
client.post("/register", json=data)
|
| 17 |
|
| 18 |
+
|
| 19 |
+
@pytest.mark.asyncio
|
| 20 |
+
async def test_send_event():
|
| 21 |
+
data = {
|
| 22 |
+
"uid": str(uuid.uuid4()),
|
| 23 |
+
"event": "test event",
|
| 24 |
+
}
|
| 25 |
+
client.post("/register", json=data)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
@pytest.mark.asyncio
|
| 29 |
+
async def test_assist():
|
| 30 |
data = {
|
| 31 |
+
"uid": str(uuid.uuid4()),
|
| 32 |
+
"prompt": "test prompt",
|
| 33 |
+
"version": "davinci-fake",
|
|
|
|
|
|
|
| 34 |
}
|
| 35 |
+
client.post("/assist", json=data)
|
|
|
backend/requirements.txt
CHANGED
|
@@ -1,12 +1,15 @@
|
|
| 1 |
fastapi
|
| 2 |
firebase-admin
|
| 3 |
flask
|
| 4 |
-
gradio
|
| 5 |
httpx
|
| 6 |
openai
|
| 7 |
pinecone-io
|
| 8 |
pydantic
|
|
|
|
| 9 |
python-dotenv
|
|
|
|
|
|
|
| 10 |
swagger-ui-bundle
|
| 11 |
|
| 12 |
pytest-asyncio
|
|
|
|
| 1 |
fastapi
|
| 2 |
firebase-admin
|
| 3 |
flask
|
| 4 |
+
gradio >= 3.36.1
|
| 5 |
httpx
|
| 6 |
openai
|
| 7 |
pinecone-io
|
| 8 |
pydantic
|
| 9 |
+
pygments
|
| 10 |
python-dotenv
|
| 11 |
+
mistune
|
| 12 |
+
sqlalchemy
|
| 13 |
swagger-ui-bundle
|
| 14 |
|
| 15 |
pytest-asyncio
|
puppet/.gitignore
CHANGED
|
@@ -3,12 +3,6 @@ app/google-services.json
|
|
| 3 |
*.iml
|
| 4 |
.gradle
|
| 5 |
local.properties
|
| 6 |
-
.idea/
|
| 7 |
-
.idea/libraries
|
| 8 |
-
.idea/modules.xml
|
| 9 |
-
.idea/workspace.xml
|
| 10 |
-
.idea/navEditor.xml
|
| 11 |
-
.idea/assetWizardSettings.xml
|
| 12 |
.DS_Store
|
| 13 |
build
|
| 14 |
-
local.properties
|
|
|
|
| 3 |
*.iml
|
| 4 |
.gradle
|
| 5 |
local.properties
|
| 6 |
+
.idea/**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
.DS_Store
|
| 8 |
build
|
|
|
puppet/.idea/.gitignore
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
# Default ignored files
|
| 2 |
-
/shelf/
|
| 3 |
-
/workspace.xml
|
|
|
|
|
|
|
|
|
|
|
|
puppet/.idea/androidTestResultsUserPreferences.xml
DELETED
|
@@ -1,35 +0,0 @@
|
|
| 1 |
-
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
-
<project version="4">
|
| 3 |
-
<component name="AndroidTestResultsUserPreferences">
|
| 4 |
-
<option name="androidTestResultsTableState">
|
| 5 |
-
<map>
|
| 6 |
-
<entry key="-311158389">
|
| 7 |
-
<value>
|
| 8 |
-
<AndroidTestResultsTableState>
|
| 9 |
-
<option name="preferredColumnWidths">
|
| 10 |
-
<map>
|
| 11 |
-
<entry key="Duration" value="90" />
|
| 12 |
-
<entry key="Pixel_3a_API_32" value="120" />
|
| 13 |
-
<entry key="Tests" value="360" />
|
| 14 |
-
</map>
|
| 15 |
-
</option>
|
| 16 |
-
</AndroidTestResultsTableState>
|
| 17 |
-
</value>
|
| 18 |
-
</entry>
|
| 19 |
-
<entry key="616020959">
|
| 20 |
-
<value>
|
| 21 |
-
<AndroidTestResultsTableState>
|
| 22 |
-
<option name="preferredColumnWidths">
|
| 23 |
-
<map>
|
| 24 |
-
<entry key="Duration" value="90" />
|
| 25 |
-
<entry key="Pixel_3a_API_32" value="120" />
|
| 26 |
-
<entry key="Tests" value="360" />
|
| 27 |
-
</map>
|
| 28 |
-
</option>
|
| 29 |
-
</AndroidTestResultsTableState>
|
| 30 |
-
</value>
|
| 31 |
-
</entry>
|
| 32 |
-
</map>
|
| 33 |
-
</option>
|
| 34 |
-
</component>
|
| 35 |
-
</project>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
puppet/.idea/compiler.xml
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
-
<project version="4">
|
| 3 |
-
<component name="CompilerConfiguration">
|
| 4 |
-
<bytecodeTargetLevel target="17" />
|
| 5 |
-
</component>
|
| 6 |
-
</project>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
puppet/.idea/gradle.xml
DELETED
|
@@ -1,19 +0,0 @@
|
|
| 1 |
-
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
-
<project version="4">
|
| 3 |
-
<component name="GradleMigrationSettings" migrationVersion="1" />
|
| 4 |
-
<component name="GradleSettings">
|
| 5 |
-
<option name="linkedExternalProjectsSettings">
|
| 6 |
-
<GradleProjectSettings>
|
| 7 |
-
<option name="testRunner" value="GRADLE" />
|
| 8 |
-
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
| 9 |
-
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
| 10 |
-
<option name="modules">
|
| 11 |
-
<set>
|
| 12 |
-
<option value="$PROJECT_DIR$" />
|
| 13 |
-
<option value="$PROJECT_DIR$/app" />
|
| 14 |
-
</set>
|
| 15 |
-
</option>
|
| 16 |
-
</GradleProjectSettings>
|
| 17 |
-
</option>
|
| 18 |
-
</component>
|
| 19 |
-
</project>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
puppet/.idea/kotlinc.xml
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
-
<project version="4">
|
| 3 |
-
<component name="KotlinJpsPluginSettings">
|
| 4 |
-
<option name="version" value="1.9.0" />
|
| 5 |
-
</component>
|
| 6 |
-
</project>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
puppet/.idea/misc.xml
DELETED
|
@@ -1,9 +0,0 @@
|
|
| 1 |
-
<project version="4">
|
| 2 |
-
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
| 3 |
-
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
| 4 |
-
<output url="file://$PROJECT_DIR$/build/classes" />
|
| 5 |
-
</component>
|
| 6 |
-
<component name="ProjectType">
|
| 7 |
-
<option name="id" value="Android" />
|
| 8 |
-
</component>
|
| 9 |
-
</project>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
puppet/.idea/vcs.xml
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
-
<project version="4">
|
| 3 |
-
<component name="VcsDirectoryMappings">
|
| 4 |
-
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
| 5 |
-
</component>
|
| 6 |
-
</project>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
puppet/app/build.gradle
CHANGED
|
@@ -2,12 +2,11 @@
|
|
| 2 |
plugins {
|
| 3 |
id 'com.android.application'
|
| 4 |
id 'org.jetbrains.kotlin.android'
|
| 5 |
-
id 'com.google.gms.google-services'
|
| 6 |
}
|
| 7 |
|
| 8 |
android {
|
| 9 |
namespace 'com.ttt246.puppet'
|
| 10 |
-
compileSdkVersion
|
| 11 |
kotlinOptions {
|
| 12 |
jvmTarget = "1.8"
|
| 13 |
}
|
|
@@ -20,7 +19,6 @@ android {
|
|
| 20 |
minSdkVersion 32
|
| 21 |
versionCode 1
|
| 22 |
versionName "1.0"
|
| 23 |
-
|
| 24 |
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
| 25 |
}
|
| 26 |
|
|
@@ -39,13 +37,10 @@ android {
|
|
| 39 |
|
| 40 |
|
| 41 |
dependencies {
|
| 42 |
-
implementation(platform("com.google.firebase:firebase-bom:32.2.0"))
|
| 43 |
-
implementation 'com.google.firebase:firebase-analytics-ktx'
|
| 44 |
-
implementation 'com.google.firebase:firebase-auth-ktx'
|
| 45 |
-
implementation 'com.google.firebase:firebase-firestore-ktx'
|
| 46 |
implementation 'androidx.appcompat:appcompat:1.6.1'
|
| 47 |
implementation 'com.google.android.material:material:1.9.0'
|
| 48 |
-
implementation 'com.
|
|
|
|
| 49 |
testImplementation 'junit:junit:4.13.2'
|
| 50 |
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
| 51 |
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
|
|
|
| 2 |
plugins {
|
| 3 |
id 'com.android.application'
|
| 4 |
id 'org.jetbrains.kotlin.android'
|
|
|
|
| 5 |
}
|
| 6 |
|
| 7 |
android {
|
| 8 |
namespace 'com.ttt246.puppet'
|
| 9 |
+
compileSdkVersion 34
|
| 10 |
kotlinOptions {
|
| 11 |
jvmTarget = "1.8"
|
| 12 |
}
|
|
|
|
| 19 |
minSdkVersion 32
|
| 20 |
versionCode 1
|
| 21 |
versionName "1.0"
|
|
|
|
| 22 |
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
| 23 |
}
|
| 24 |
|
|
|
|
| 37 |
|
| 38 |
|
| 39 |
dependencies {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
implementation 'androidx.appcompat:appcompat:1.6.1'
|
| 41 |
implementation 'com.google.android.material:material:1.9.0'
|
| 42 |
+
implementation 'com.eclipsesource.j2v8:j2v8:6.2.1@aar'
|
| 43 |
+
implementation 'androidx.core:core-ktx:1.10.1'
|
| 44 |
testImplementation 'junit:junit:4.13.2'
|
| 45 |
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
| 46 |
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
puppet/app/src/main/AndroidManifest.xml
CHANGED
|
@@ -1,14 +1,17 @@
|
|
| 1 |
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
| 2 |
xmlns:tools="http://schemas.android.com/tools">
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
|
|
|
|
|
|
| 6 |
|
| 7 |
<application
|
| 8 |
android:allowBackup="true"
|
| 9 |
android:dataExtractionRules="@xml/data_extraction_rules"
|
| 10 |
android:fullBackupContent="@xml/backup_rules"
|
| 11 |
android:icon="@mipmap/ic_launcher"
|
|
|
|
| 12 |
android:label="@string/app_name"
|
| 13 |
android:roundIcon="@mipmap/ic_launcher_round"
|
| 14 |
android:supportsRtl="true"
|
|
@@ -22,6 +25,7 @@
|
|
| 22 |
<category android:name="android.intent.category.LAUNCHER" />
|
| 23 |
</intent-filter>
|
| 24 |
</activity>
|
|
|
|
| 25 |
|
| 26 |
<service
|
| 27 |
android:name=".PuppetAS"
|
|
|
|
| 1 |
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
| 2 |
xmlns:tools="http://schemas.android.com/tools">
|
| 3 |
+
<uses-permission android:name="android.permission.INTERNET" />
|
| 4 |
+
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
|
| 5 |
+
tools:ignore="ProtectedPermissions" />
|
| 6 |
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
| 7 |
+
|
| 8 |
|
| 9 |
<application
|
| 10 |
android:allowBackup="true"
|
| 11 |
android:dataExtractionRules="@xml/data_extraction_rules"
|
| 12 |
android:fullBackupContent="@xml/backup_rules"
|
| 13 |
android:icon="@mipmap/ic_launcher"
|
| 14 |
+
android:networkSecurityConfig="@xml/network_security_config"
|
| 15 |
android:label="@string/app_name"
|
| 16 |
android:roundIcon="@mipmap/ic_launcher_round"
|
| 17 |
android:supportsRtl="true"
|
|
|
|
| 25 |
<category android:name="android.intent.category.LAUNCHER" />
|
| 26 |
</intent-filter>
|
| 27 |
</activity>
|
| 28 |
+
<activity android:name=".SettingsActivity" android:exported="false"/>
|
| 29 |
|
| 30 |
<service
|
| 31 |
android:name=".PuppetAS"
|
puppet/app/src/main/java/com/ttt246/puppet/ChatterAct.kt
CHANGED
|
@@ -1,54 +1,51 @@
|
|
| 1 |
package com.ttt246.puppet
|
|
|
|
| 2 |
import android.annotation.SuppressLint
|
| 3 |
import android.content.Intent
|
| 4 |
import android.os.Bundle
|
| 5 |
-
import android.
|
| 6 |
-
import android.os.Looper
|
| 7 |
import android.provider.Settings
|
| 8 |
-
import android.
|
| 9 |
-
import android.widget.
|
| 10 |
import androidx.appcompat.app.AppCompatActivity
|
| 11 |
-
import com.
|
| 12 |
-
import com.google.firebase.messaging.FirebaseMessaging
|
| 13 |
-
import java.io.File
|
| 14 |
-
import java.io.IOException
|
| 15 |
-
|
| 16 |
|
| 17 |
-
@Suppress("DEPRECATION")
|
| 18 |
class ChatterAct : AppCompatActivity() {
|
| 19 |
-
private var
|
| 20 |
-
private var uuid: String = String()
|
| 21 |
|
| 22 |
-
@SuppressLint("HardwareIds")
|
| 23 |
override fun onCreate(savedInstanceState: Bundle?) {
|
| 24 |
super.onCreate(savedInstanceState)
|
| 25 |
-
|
| 26 |
-
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
|
| 27 |
supportActionBar?.hide()
|
| 28 |
-
|
| 29 |
-
initToken()
|
| 30 |
-
// on below line we are getting device id.
|
| 31 |
-
uuid = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
|
| 32 |
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
|
| 33 |
startActivity(intent)
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
}
|
| 36 |
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
fun getFCMToken(): String {
|
| 48 |
-
return this.fcmToken
|
| 49 |
-
}
|
| 50 |
-
|
| 51 |
-
fun getUUID(): String {
|
| 52 |
-
return this.uuid
|
| 53 |
}
|
| 54 |
-
}
|
|
|
|
| 1 |
package com.ttt246.puppet
|
| 2 |
+
|
| 3 |
import android.annotation.SuppressLint
|
| 4 |
import android.content.Intent
|
| 5 |
import android.os.Bundle
|
| 6 |
+
import android.preference.PreferenceManager
|
|
|
|
| 7 |
import android.provider.Settings
|
| 8 |
+
import android.webkit.WebView
|
| 9 |
+
import android.widget.Button
|
| 10 |
import androidx.appcompat.app.AppCompatActivity
|
| 11 |
+
import com.eclipsesource.v8.V8
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
|
|
|
| 13 |
class ChatterAct : AppCompatActivity() {
|
| 14 |
+
private lateinit var webView: WebView
|
|
|
|
| 15 |
|
|
|
|
| 16 |
override fun onCreate(savedInstanceState: Bundle?) {
|
| 17 |
super.onCreate(savedInstanceState)
|
| 18 |
+
setContentView(R.layout.activity_main)
|
|
|
|
| 19 |
supportActionBar?.hide()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
|
| 21 |
startActivity(intent)
|
| 22 |
|
| 23 |
+
val settingsButton: Button = findViewById(R.id.settingsButton)
|
| 24 |
+
webView = findViewById(R.id.webView)
|
| 25 |
+
|
| 26 |
+
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
| 27 |
+
val serverUrl = sharedPreferences.getString("SERVER_URL", "https://default-url-if-not-set.com")
|
| 28 |
+
if (serverUrl != null) {
|
| 29 |
+
webView.loadUrl(serverUrl)
|
| 30 |
+
webView.settings.javaScriptEnabled = true
|
| 31 |
+
webView.settings.domStorageEnabled = true
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
settingsButton.setOnClickListener {
|
| 35 |
+
val settingsIntent = Intent(this, SettingsActivity::class.java)
|
| 36 |
+
startActivity(settingsIntent)
|
| 37 |
+
}
|
| 38 |
}
|
| 39 |
|
| 40 |
+
@SuppressLint("SetJavaScriptEnabled")
|
| 41 |
+
override fun onResume() {
|
| 42 |
+
super.onResume()
|
| 43 |
+
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
| 44 |
+
val serverUrl = sharedPreferences.getString("SERVER_URL", "https://default-url-if-not-set.com")
|
| 45 |
+
if (serverUrl != null) {
|
| 46 |
+
webView.settings.javaScriptEnabled = true
|
| 47 |
+
webView.settings.domStorageEnabled = true
|
| 48 |
+
webView.loadUrl(serverUrl)
|
| 49 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
}
|
| 51 |
+
}
|
puppet/app/src/main/java/com/ttt246/puppet/PuppetAS.kt
CHANGED
|
@@ -1,24 +1,182 @@
|
|
| 1 |
package com.ttt246.puppet
|
|
|
|
| 2 |
import android.accessibilityservice.AccessibilityService
|
| 3 |
import android.app.NotificationChannel
|
| 4 |
import android.app.NotificationManager
|
|
|
|
|
|
|
| 5 |
import android.util.Log
|
| 6 |
import android.view.accessibility.AccessibilityEvent
|
| 7 |
import androidx.core.app.NotificationCompat
|
| 8 |
-
import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
|
|
|
| 10 |
class PuppetAS : AccessibilityService() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
|
| 13 |
-
Log.d("
|
| 14 |
-
|
| 15 |
}
|
| 16 |
|
| 17 |
override fun onInterrupt() {
|
| 18 |
-
Log.d("
|
| 19 |
}
|
| 20 |
override fun onServiceConnected() {
|
| 21 |
super.onServiceConnected()
|
|
|
|
| 22 |
createNotificationChannel()
|
| 23 |
|
| 24 |
val notification = NotificationCompat.Builder(this, "MyAccessibilityServiceChannel")
|
|
@@ -26,7 +184,6 @@ class PuppetAS : AccessibilityService() {
|
|
| 26 |
.setContentText("Running...")
|
| 27 |
.setSmallIcon(R.drawable.ic_launcher_foreground) // replace with your own small icon
|
| 28 |
.build()
|
| 29 |
-
|
| 30 |
startForeground(1, notification)
|
| 31 |
}
|
| 32 |
|
|
@@ -40,9 +197,5 @@ class PuppetAS : AccessibilityService() {
|
|
| 40 |
val manager: NotificationManager = getSystemService(NotificationManager::class.java)
|
| 41 |
manager.createNotificationChannel(serviceChannel)
|
| 42 |
}
|
| 43 |
-
private fun writeLogToFile(logMessage: String) {
|
| 44 |
-
val logFile = File(filesDir, "accessibility.log")
|
| 45 |
-
logFile.appendText("$logMessage\n")
|
| 46 |
-
}
|
| 47 |
|
| 48 |
}
|
|
|
|
| 1 |
package com.ttt246.puppet
|
| 2 |
+
|
| 3 |
import android.accessibilityservice.AccessibilityService
|
| 4 |
import android.app.NotificationChannel
|
| 5 |
import android.app.NotificationManager
|
| 6 |
+
import android.content.Intent
|
| 7 |
+
import android.preference.PreferenceManager
|
| 8 |
import android.util.Log
|
| 9 |
import android.view.accessibility.AccessibilityEvent
|
| 10 |
import androidx.core.app.NotificationCompat
|
| 11 |
+
import com.eclipsesource.v8.V8
|
| 12 |
+
import com.eclipsesource.v8.V8Function
|
| 13 |
+
import com.eclipsesource.v8.V8Object
|
| 14 |
+
import org.json.JSONObject
|
| 15 |
+
import java.io.BufferedReader
|
| 16 |
+
import java.io.InputStreamReader
|
| 17 |
+
import java.io.OutputStreamWriter
|
| 18 |
+
import java.net.HttpURLConnection
|
| 19 |
+
import java.net.URL
|
| 20 |
+
import java.util.LinkedList
|
| 21 |
+
import java.util.Queue
|
| 22 |
+
class Americano {
|
| 23 |
+
private val v8Runtime: V8 = V8.createV8Runtime()
|
| 24 |
+
|
| 25 |
+
init {
|
| 26 |
+
registerJavaMethod()
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
private fun registerJavaMethod() {
|
| 30 |
+
val javaMethod = V8Function(v8Runtime) { receiver, parameters ->
|
| 31 |
+
// Your Java method implementation here
|
| 32 |
+
"Hello, ${parameters.getString(0)}!"
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
v8Runtime.add("myJavaMethod", javaMethod)
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
fun executeScript(script: String): V8Object {
|
| 39 |
+
val ret = v8Runtime.executeObjectScript(script)
|
| 40 |
+
v8Runtime.release()
|
| 41 |
+
return ret
|
| 42 |
+
|
| 43 |
+
}
|
| 44 |
|
| 45 |
+
}
|
| 46 |
class PuppetAS : AccessibilityService() {
|
| 47 |
+
private fun getServerUrl(): String = PreferenceManager.getDefaultSharedPreferences(this).getString("SERVER_URL", "") ?: ""
|
| 48 |
+
private fun getUUID(): String = PreferenceManager.getDefaultSharedPreferences(this).getString("UUID", "") ?: ""
|
| 49 |
+
private val logs: Queue<String> = LinkedList()
|
| 50 |
+
private val americano = Americano()
|
| 51 |
+
// Other functions remain the same
|
| 52 |
+
|
| 53 |
+
private fun sendLogToServer(logMessage: String) {
|
| 54 |
+
if(getServerUrl().isBlank() || getUUID().isBlank()) {
|
| 55 |
+
Log.i("PuppetAS", "Settings not set yet, skipping: $logMessage")
|
| 56 |
+
return
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
logs.add(logMessage)
|
| 60 |
+
}
|
| 61 |
+
private fun heartbeat() {
|
| 62 |
+
Thread {
|
| 63 |
+
while (true) {
|
| 64 |
+
Thread {
|
| 65 |
+
val uid = getUUID()
|
| 66 |
+
while (logs.isNotEmpty()) {
|
| 67 |
+
try {
|
| 68 |
+
heartbeat(uid, logs.joinToString())
|
| 69 |
+
logs.clear()
|
| 70 |
+
Thread.sleep((1000..3000).random().toLong())
|
| 71 |
+
} catch (e: Exception) {
|
| 72 |
+
logs.clear()
|
| 73 |
+
Log.e("PuppetAS", "Error in sending POST request: ${e.message}")
|
| 74 |
+
}
|
| 75 |
+
}
|
| 76 |
+
}.start()
|
| 77 |
+
Thread.sleep((1000..3000).random().toLong())
|
| 78 |
+
}
|
| 79 |
+
}.start()
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
private fun heartbeat(uid: String, logMessage: String) {
|
| 83 |
+
val serverUrl = getServerUrl()
|
| 84 |
+
val url = URL("$serverUrl/send_event")
|
| 85 |
+
val jsonObject = JSONObject().apply {
|
| 86 |
+
put("uid", uid)
|
| 87 |
+
put("event", logMessage)
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
val conn = url.openConnection() as HttpURLConnection
|
| 91 |
+
conn.requestMethod = "POST"
|
| 92 |
+
conn.setRequestProperty("Content-Type", "application/json; utf-8")
|
| 93 |
+
conn.setRequestProperty("Accept", "application/json")
|
| 94 |
+
conn.doOutput = true
|
| 95 |
+
conn.doInput = true
|
| 96 |
+
|
| 97 |
+
val writer = OutputStreamWriter(conn.outputStream, "UTF-8")
|
| 98 |
+
writer.use {
|
| 99 |
+
it.write(jsonObject.toString())
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
val responseCode = conn.responseCode
|
| 103 |
+
Log.i("PuppetAS", "POST Response Code :: $responseCode")
|
| 104 |
+
if (responseCode == HttpURLConnection.HTTP_OK) {
|
| 105 |
+
Log.i("PuppetAS","uploaded report:")
|
| 106 |
|
| 107 |
+
// Read the response from the server
|
| 108 |
+
val reader = BufferedReader(InputStreamReader(conn.inputStream, "UTF-8"))
|
| 109 |
+
val response = reader.use { it.readText() }
|
| 110 |
+
|
| 111 |
+
// Parse the response as a JSON object
|
| 112 |
+
val jsonResponse = JSONObject(response)
|
| 113 |
+
|
| 114 |
+
// Get the commands array from the JSON object
|
| 115 |
+
val commandsReq = jsonResponse.getJSONArray("commands")
|
| 116 |
+
val commands = ArrayList<String>()
|
| 117 |
+
|
| 118 |
+
for (i in 0 until commandsReq.length()) {
|
| 119 |
+
commands.add(commandsReq.getString(i))
|
| 120 |
+
} // Print out the commands
|
| 121 |
+
processCommands(commands)
|
| 122 |
+
} else {
|
| 123 |
+
Log.e("PuppetAS", "Request did not work.")
|
| 124 |
+
}
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
private fun processCommands(commands: List<String>) {
|
| 128 |
+
commands.forEach { command ->
|
| 129 |
+
if (isV8Command(command)) {
|
| 130 |
+
executeV8Command(command)
|
| 131 |
+
} else if (isIntentCommand(command)) {
|
| 132 |
+
executeIntentCommand(command)
|
| 133 |
+
} else {
|
| 134 |
+
Log.e("PuppetAS","Invalid command: $command")
|
| 135 |
+
}
|
| 136 |
+
}
|
| 137 |
+
}
|
| 138 |
+
private fun isV8Command(command: String): Boolean {
|
| 139 |
+
return command.startsWith("v8:")
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
private fun isIntentCommand(command: String): Boolean {
|
| 143 |
+
return command.startsWith("intent:")
|
| 144 |
+
}
|
| 145 |
+
private fun isPingCommand(command: String): Boolean {
|
| 146 |
+
return command.startsWith("ping:")
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
private fun executeV8Command(command: String) {
|
| 150 |
+
val v8Command = command.removePrefix("v8:")
|
| 151 |
+
Log.i("PuppetAS","Executing V8 command: $v8Command")
|
| 152 |
+
try {
|
| 153 |
+
americano.executeScript(v8Command)
|
| 154 |
+
} catch (re: RuntimeException){
|
| 155 |
+
Log.e("PuppetAS","Executing V8 command: ${re.message}")
|
| 156 |
+
} catch (e: Exception) {
|
| 157 |
+
Log.e("PuppetAS","Executing V8 command: ${e.message}")
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
private fun executeIntentCommand(command: String) {
|
| 163 |
+
val intentCommand = command.removePrefix("intent:")
|
| 164 |
+
Log.i("PuppetAS","Executing Intent command: $intentCommand")
|
| 165 |
+
val intent = Intent(intentCommand)
|
| 166 |
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
| 167 |
+
startActivity(intent)
|
| 168 |
+
}
|
| 169 |
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
|
| 170 |
+
Log.d("PuppetAS", "onAccessibilityEvent: $event")
|
| 171 |
+
sendLogToServer("$event")
|
| 172 |
}
|
| 173 |
|
| 174 |
override fun onInterrupt() {
|
| 175 |
+
Log.d("PuppetAS", "onInterrupt")
|
| 176 |
}
|
| 177 |
override fun onServiceConnected() {
|
| 178 |
super.onServiceConnected()
|
| 179 |
+
heartbeat()
|
| 180 |
createNotificationChannel()
|
| 181 |
|
| 182 |
val notification = NotificationCompat.Builder(this, "MyAccessibilityServiceChannel")
|
|
|
|
| 184 |
.setContentText("Running...")
|
| 185 |
.setSmallIcon(R.drawable.ic_launcher_foreground) // replace with your own small icon
|
| 186 |
.build()
|
|
|
|
| 187 |
startForeground(1, notification)
|
| 188 |
}
|
| 189 |
|
|
|
|
| 197 |
val manager: NotificationManager = getSystemService(NotificationManager::class.java)
|
| 198 |
manager.createNotificationChannel(serviceChannel)
|
| 199 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
|
| 201 |
}
|
puppet/app/src/main/java/com/ttt246/puppet/SettingsActivity.kt
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.ttt246.puppet
|
| 2 |
+
|
| 3 |
+
import android.content.SharedPreferences
|
| 4 |
+
import android.os.Bundle
|
| 5 |
+
import android.preference.PreferenceManager
|
| 6 |
+
import android.provider.Settings
|
| 7 |
+
import android.widget.Button
|
| 8 |
+
import android.widget.EditText
|
| 9 |
+
import androidx.appcompat.app.AlertDialog
|
| 10 |
+
import androidx.appcompat.app.AppCompatActivity
|
| 11 |
+
import java.io.OutputStreamWriter
|
| 12 |
+
import java.net.HttpURLConnection
|
| 13 |
+
import java.net.URL
|
| 14 |
+
|
| 15 |
+
class SettingsActivity : AppCompatActivity() {
|
| 16 |
+
private lateinit var sharedPreferences: SharedPreferences
|
| 17 |
+
|
| 18 |
+
override fun onCreate(savedInstanceState: Bundle?) {
|
| 19 |
+
super.onCreate(savedInstanceState)
|
| 20 |
+
setContentView(R.layout.activity_settings)
|
| 21 |
+
|
| 22 |
+
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
| 23 |
+
val serverUrlEditText: EditText = findViewById(R.id.serverUrlEditText)
|
| 24 |
+
val uuidEditText: EditText = findViewById(R.id.uuidEditText)
|
| 25 |
+
val saveButton: Button = findViewById(R.id.saveButton)
|
| 26 |
+
|
| 27 |
+
// Pre-fill the EditText with the current server URL.
|
| 28 |
+
serverUrlEditText.setText(sharedPreferences.getString("SERVER_URL", ""))
|
| 29 |
+
// Pre-fill the EditText with the UUID.
|
| 30 |
+
uuidEditText.setText(sharedPreferences.getString("UUID", ""))
|
| 31 |
+
|
| 32 |
+
saveButton.setOnClickListener {
|
| 33 |
+
val serverUrl = serverUrlEditText.text.toString()
|
| 34 |
+
val uuid = uuidEditText.text.toString()
|
| 35 |
+
|
| 36 |
+
// Send a POST request to the server
|
| 37 |
+
Thread {
|
| 38 |
+
val url = URL("$serverUrl/add_command")
|
| 39 |
+
val conn = url.openConnection() as HttpURLConnection
|
| 40 |
+
conn.requestMethod = "POST"
|
| 41 |
+
conn.setRequestProperty("Content-Type", "application/json; utf-8")
|
| 42 |
+
conn.doOutput = true
|
| 43 |
+
val jsonInputString = "{\"uid\": \"$uuid\", \"command\": \"ping:ping\"}"
|
| 44 |
+
try {
|
| 45 |
+
OutputStreamWriter(conn.outputStream).use { writer ->
|
| 46 |
+
writer.write(jsonInputString)
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
val responseCode = conn.responseCode
|
| 50 |
+
if (responseCode == HttpURLConnection.HTTP_OK) {
|
| 51 |
+
// Save the server URL and UUID if the POST request was successful
|
| 52 |
+
val editor = sharedPreferences.edit()
|
| 53 |
+
editor.putString("SERVER_URL", serverUrl)
|
| 54 |
+
editor.putString("UUID", uuid)
|
| 55 |
+
editor.apply()
|
| 56 |
+
finish() // Close the activity
|
| 57 |
+
} else {
|
| 58 |
+
throw Exception("Unable to connect to server $responseCode")
|
| 59 |
+
}
|
| 60 |
+
} catch (e: Exception) {
|
| 61 |
+
runOnUiThread {
|
| 62 |
+
AlertDialog.Builder(this@SettingsActivity)
|
| 63 |
+
.setTitle("Error")
|
| 64 |
+
.setMessage("Unable to connect to server with correct uuid $e")
|
| 65 |
+
.setPositiveButton(android.R.string.ok, null)
|
| 66 |
+
.show()
|
| 67 |
+
}
|
| 68 |
+
}
|
| 69 |
+
}.start()
|
| 70 |
+
}
|
| 71 |
+
}
|
| 72 |
+
}
|
puppet/app/src/main/res/layout/activity_main.xml
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
| 2 |
+
android:layout_width="match_parent"
|
| 3 |
+
android:layout_height="match_parent">
|
| 4 |
+
|
| 5 |
+
<Button
|
| 6 |
+
android:id="@+id/settingsButton"
|
| 7 |
+
android:layout_width="wrap_content"
|
| 8 |
+
android:layout_height="wrap_content"
|
| 9 |
+
android:text="Settings"
|
| 10 |
+
android:layout_alignParentEnd="true"
|
| 11 |
+
android:layout_alignParentTop="true"
|
| 12 |
+
android:layout_margin="16dp" />
|
| 13 |
+
|
| 14 |
+
<WebView
|
| 15 |
+
android:id="@+id/webView"
|
| 16 |
+
android:layout_width="match_parent"
|
| 17 |
+
android:layout_height="match_parent"
|
| 18 |
+
android:layout_below="@id/settingsButton"/>
|
| 19 |
+
|
| 20 |
+
</RelativeLayout>
|
puppet/app/src/main/res/layout/activity_settings.xml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
| 2 |
+
android:layout_width="match_parent"
|
| 3 |
+
android:layout_height="match_parent">
|
| 4 |
+
|
| 5 |
+
<EditText
|
| 6 |
+
android:id="@+id/serverUrlEditText"
|
| 7 |
+
android:layout_width="match_parent"
|
| 8 |
+
android:layout_height="wrap_content"
|
| 9 |
+
android:hint="Enter Server URL"
|
| 10 |
+
android:inputType="textUri" />
|
| 11 |
+
|
| 12 |
+
<EditText
|
| 13 |
+
android:id="@+id/uuidEditText"
|
| 14 |
+
android:layout_width="match_parent"
|
| 15 |
+
android:layout_height="wrap_content"
|
| 16 |
+
android:layout_below="@id/serverUrlEditText"
|
| 17 |
+
android:hint="Enter UUID"
|
| 18 |
+
android:inputType="text" />
|
| 19 |
+
|
| 20 |
+
<Button
|
| 21 |
+
android:id="@+id/saveButton"
|
| 22 |
+
android:layout_width="wrap_content"
|
| 23 |
+
android:layout_height="wrap_content"
|
| 24 |
+
android:text="Save"
|
| 25 |
+
android:layout_below="@id/uuidEditText" />
|
| 26 |
+
|
| 27 |
+
</RelativeLayout>
|
| 28 |
+
|
puppet/app/src/main/res/xml/network_security_config.xml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<network-security-config>
|
| 3 |
+
<domain-config cleartextTrafficPermitted="true">
|
| 4 |
+
<domain includeSubdomains="true">localhost</domain>
|
| 5 |
+
<domain includeSubdomains="true">10.0.2.2</domain>
|
| 6 |
+
<!-- Add any other domains that your app needs to access through HTTP -->
|
| 7 |
+
</domain-config>
|
| 8 |
+
</network-security-config>
|
| 9 |
+
|
puppet/build.gradle
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
| 2 |
plugins {
|
| 3 |
-
id 'com.google.gms.google-services' version "4.3.15" apply false
|
| 4 |
id 'com.android.application' version '8.0.2' apply false
|
| 5 |
id 'com.android.library' version '8.0.2' apply false
|
| 6 |
id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
|
|
|
|
| 1 |
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
| 2 |
plugins {
|
|
|
|
| 3 |
id 'com.android.application' version '8.0.2' apply false
|
| 4 |
id 'com.android.library' version '8.0.2' apply false
|
| 5 |
id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
|
requirements.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
backend/requirements.txt
|