ct 0.10.8.115__py3-none-any.whl → 0.10.8.116__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cantools/__init__.py +1 -1
- cantools/config.py +3 -0
- cantools/util/admin.py +1 -27
- cantools/util/ai/__init__.py +8 -0
- cantools/util/ai/tox/__init__.py +15 -0
- cantools/util/ai/tox/duck.py +80 -0
- cantools/util/ai/tox/fzn.py +22 -0
- cantools/util/ai/tox/g4free.py +73 -0
- cantools/util/ai/vox.py +84 -0
- cantools/util/apper/__init__.py +1 -0
- cantools/util/apper/ander.py +101 -0
- cantools/util/apper/data.py +167 -0
- cantools/util/system.py +2 -23
- {ct-0.10.8.115.dist-info → ct-0.10.8.116.dist-info}/METADATA +2 -2
- {ct-0.10.8.115.dist-info → ct-0.10.8.116.dist-info}/RECORD +19 -10
- {ct-0.10.8.115.dist-info → ct-0.10.8.116.dist-info}/LICENSE +0 -0
- {ct-0.10.8.115.dist-info → ct-0.10.8.116.dist-info}/WHEEL +0 -0
- {ct-0.10.8.115.dist-info → ct-0.10.8.116.dist-info}/entry_points.txt +0 -0
- {ct-0.10.8.115.dist-info → ct-0.10.8.116.dist-info}/top_level.txt +0 -0
cantools/__init__.py
CHANGED
cantools/config.py
CHANGED
|
@@ -40,6 +40,9 @@ gcpath = os.path.join(os.path.expanduser("~"), "ct.cfg")
|
|
|
40
40
|
if os.path.isfile(gcpath):
|
|
41
41
|
print("loading global configuration at: %s"%(gcpath,))
|
|
42
42
|
lines = read(gcpath, True) + lines
|
|
43
|
+
if os.path.isfile("extra.cfg"):
|
|
44
|
+
print("loading extra configuration at: extra.cfg")
|
|
45
|
+
lines = lines + read("extra.cfg", True)
|
|
43
46
|
for line in lines:
|
|
44
47
|
if line.startswith("#"):
|
|
45
48
|
continue
|
cantools/util/admin.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os, sys, rel, time, datetime
|
|
2
|
+
from fyg.util import pcount, pcheck, pkill
|
|
2
3
|
from cantools.util import cmd, output, error, log, set_log, close_log, read, write, confirm, rm
|
|
3
4
|
|
|
4
5
|
coremods = ["screen", "ctstart", "ctpubsub", "ctutil", "ctinit", "dez_reverse_proxy", "dez_websocket_proxy"]
|
|
@@ -65,33 +66,6 @@ def certs(dpath="/root", sname=None):
|
|
|
65
66
|
log("goodbye")
|
|
66
67
|
close_log()
|
|
67
68
|
|
|
68
|
-
def pcount(pname):
|
|
69
|
-
log("checking count: %s"%(pname,), important=True)
|
|
70
|
-
num = int(output("ps -ef | grep %s | egrep -v 'screener|pcount|grep' | wc -l"%(pname,)))
|
|
71
|
-
log("%s count: %s"%(pname, num), 1)
|
|
72
|
-
return num
|
|
73
|
-
|
|
74
|
-
def pcheck(pname, target, starter):
|
|
75
|
-
if target and pcount(pname) != target:
|
|
76
|
-
log("not enough %s processes - restarting screen!"%(pname,), 1)
|
|
77
|
-
log(output("screen -Q windows"), important=True)
|
|
78
|
-
cmd("killall screen; %s"%(starter,))
|
|
79
|
-
return True
|
|
80
|
-
|
|
81
|
-
def pkill(pname, force=False):
|
|
82
|
-
pblock = output("ps -ef | grep %s | egrep -v 'screener|pkill|grep'"%(pname,))
|
|
83
|
-
if not pblock:
|
|
84
|
-
log("no '%s' processes!"%(pname,))
|
|
85
|
-
else:
|
|
86
|
-
plines = pblock.split("\n")
|
|
87
|
-
log("found %s '%s' processes"%(len(plines), pname), important=True)
|
|
88
|
-
if plines:
|
|
89
|
-
procs = [[w for w in line.split(" ") if w][1] for line in plines]
|
|
90
|
-
if force or confirm("kill %s '%s' processes"%(len(procs), pname)):
|
|
91
|
-
for proc in procs:
|
|
92
|
-
cmd("kill -9 %s"%(proc,))
|
|
93
|
-
log("goodbye")
|
|
94
|
-
|
|
95
69
|
def binpath(bpath="/usr/bin/"):
|
|
96
70
|
log("checking %s core modules"%(len(coremods),), important=True)
|
|
97
71
|
missings = []
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from .tox import tox, g4chat, g4image, fzn, duck, pb
|
|
2
|
+
from .vox import vox, kvoices
|
|
3
|
+
|
|
4
|
+
def tellme(prompt, voice="random", identity="gpt-4o-mini", filename="tts", unsaid=False, silent=False):
|
|
5
|
+
print("tellme(%s, %s, %s)"%(voice, identity, filename), prompt)
|
|
6
|
+
resp = tox(prompt, identity)
|
|
7
|
+
unsaid or vox(resp, voice, filename=filename, say=not silent)
|
|
8
|
+
return resp
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .duck import duck # not currently working :'(
|
|
2
|
+
from .g4free import g4chat, g4image, gchatters
|
|
3
|
+
from .fzn import fzn
|
|
4
|
+
|
|
5
|
+
# pb
|
|
6
|
+
def pb(question, botname, appid, userkey):
|
|
7
|
+
from pb_py import main as PB
|
|
8
|
+
resp = PB.talk(userkey, appid, "aiaas.pandorabots.com", botname, question)["response"]
|
|
9
|
+
return resp.split("[URL]")[0]
|
|
10
|
+
|
|
11
|
+
# wrapper
|
|
12
|
+
def tox(statement, identity="Anonymous", name=None, mood=None, asker=None, options=None):
|
|
13
|
+
if identity in gchatters:
|
|
14
|
+
return g4chat(statement, identity)
|
|
15
|
+
return fzn(statement, identity, name, mood, asker, options)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import random
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
def nosay():
|
|
5
|
+
return random.choice([
|
|
6
|
+
"i'm confused",
|
|
7
|
+
"you broke me",
|
|
8
|
+
"you're killing me bro",
|
|
9
|
+
"that question ruined me",
|
|
10
|
+
"your words somehow fried my circuits",
|
|
11
|
+
"i literally don't know how to answer that"
|
|
12
|
+
])
|
|
13
|
+
|
|
14
|
+
def notyet():
|
|
15
|
+
return random.choice([
|
|
16
|
+
"slow down",
|
|
17
|
+
"not so fast",
|
|
18
|
+
"wait a minute",
|
|
19
|
+
"take your time",
|
|
20
|
+
"slow your roll",
|
|
21
|
+
"i need a moment",
|
|
22
|
+
"hold your horses"
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
# ddg
|
|
26
|
+
DDGAI = {
|
|
27
|
+
"bot": None,
|
|
28
|
+
"last": None
|
|
29
|
+
}
|
|
30
|
+
DUX = ["gpt-4o-mini", "llama-3.3-70b", "claude-3-haiku", "o3-mini", "mistral-small-3"]
|
|
31
|
+
OLDUX = ["o3-mini", "gpt-4o-mini", "claude-3-haiku", "llama-3.1-70b", "mixtral-8x7b"]
|
|
32
|
+
delimiases = {
|
|
33
|
+
"False": False,
|
|
34
|
+
True: "\n",
|
|
35
|
+
"True": "\n",
|
|
36
|
+
"LINE": "\n",
|
|
37
|
+
"PARAGRAPH": "\n",
|
|
38
|
+
"SENTENCE": ".",
|
|
39
|
+
"PHRASE": [".", "!", "?"]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
def ddgai():
|
|
43
|
+
if not DDGAI["bot"]:
|
|
44
|
+
from duckai import DuckAI
|
|
45
|
+
DDGAI["bot"] = DuckAI()
|
|
46
|
+
return DDGAI["bot"]
|
|
47
|
+
|
|
48
|
+
def toosoon():
|
|
49
|
+
now = datetime.now()
|
|
50
|
+
if DDGAI["last"] and (now - DDGAI["last"]).seconds < 15:
|
|
51
|
+
return True
|
|
52
|
+
DDGAI["last"] = now
|
|
53
|
+
|
|
54
|
+
def duck(prompt, model="gpt-4o-mini", shorten=False, strip=False, timeout=30):
|
|
55
|
+
if toosoon():
|
|
56
|
+
return notyet()
|
|
57
|
+
try:
|
|
58
|
+
resp = ddgai().chat(prompt, model, int(timeout))
|
|
59
|
+
except:
|
|
60
|
+
resp = nosay()
|
|
61
|
+
print(resp)
|
|
62
|
+
if shorten in delimiases:
|
|
63
|
+
shorten = delimiases[shorten]
|
|
64
|
+
if shorten:
|
|
65
|
+
if type(shorten) is not list:
|
|
66
|
+
shorten = [shorten]
|
|
67
|
+
for sho in shorten:
|
|
68
|
+
resp = resp.split(sho).pop(0)
|
|
69
|
+
print("shortened to:")
|
|
70
|
+
print(resp)
|
|
71
|
+
if strip:
|
|
72
|
+
resp = resp.replace("\n", " ").replace(" & ", " and ")
|
|
73
|
+
while " <" in resp and "> " in resp:
|
|
74
|
+
resp = resp[:resp.index(" <") + 1] + resp[resp.index("> ") + 1:]
|
|
75
|
+
while "```" in resp:
|
|
76
|
+
start = resp.index("```") + 3
|
|
77
|
+
resp = resp[:start] + resp[resp.index("```", start) + 3:]
|
|
78
|
+
print("stripped to:")
|
|
79
|
+
print(resp)
|
|
80
|
+
return resp
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# aiio
|
|
2
|
+
afcfg = {
|
|
3
|
+
"host": "ai.fzn.party",
|
|
4
|
+
"path": "_respond",
|
|
5
|
+
"proto": "https"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
def fzn(statement, identity="Anonymous", name=None, mood=None, asker=None, options=None):
|
|
9
|
+
from cantools.web import post
|
|
10
|
+
params = {
|
|
11
|
+
"identity": identity,
|
|
12
|
+
"statement": statement,
|
|
13
|
+
"options": options,
|
|
14
|
+
"mood": mood,
|
|
15
|
+
"name": name,
|
|
16
|
+
"asker": asker
|
|
17
|
+
}
|
|
18
|
+
fu = "%s://%s/%s"%(afcfg["proto"], afcfg["host"], afcfg["path"])
|
|
19
|
+
print("fzn", fu, params)
|
|
20
|
+
resp = post(fu, data=params, ctjson=True)
|
|
21
|
+
print(resp)
|
|
22
|
+
return resp
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
|
|
2
|
+
class G4F(object):
|
|
3
|
+
def __init__(self, log=print):
|
|
4
|
+
self.logger = log
|
|
5
|
+
self.client = None
|
|
6
|
+
|
|
7
|
+
def log(self, *msg):
|
|
8
|
+
self.logger("G4F", *msg)
|
|
9
|
+
|
|
10
|
+
def getClient(self):
|
|
11
|
+
if not self.client:
|
|
12
|
+
self.log("setting up client")
|
|
13
|
+
from g4f.client import Client
|
|
14
|
+
self.client = Client()
|
|
15
|
+
return self.client
|
|
16
|
+
|
|
17
|
+
def chat(self, prompt, model="gpt-4o-mini", short=True):
|
|
18
|
+
self.log("chat(%s)"%(model,), prompt)
|
|
19
|
+
messages = [{ "role": "user", "content": prompt }]
|
|
20
|
+
short and messages.insert(0, {
|
|
21
|
+
"role": "user",
|
|
22
|
+
"content": "one sentence responses please"
|
|
23
|
+
})
|
|
24
|
+
return self.getClient().chat.completions.create(
|
|
25
|
+
model=model,
|
|
26
|
+
messages=messages
|
|
27
|
+
).choices[0].message.content
|
|
28
|
+
|
|
29
|
+
def image(self, prompt, model="flux"):
|
|
30
|
+
self.log("image(%s)"%(model,), prompt)
|
|
31
|
+
return self.getClient().images.generate(
|
|
32
|
+
model=model,
|
|
33
|
+
prompt=prompt,
|
|
34
|
+
response_format="url"
|
|
35
|
+
).data[0].url
|
|
36
|
+
|
|
37
|
+
def __call__(self, action, prompt, model):
|
|
38
|
+
self.log("calling", action, "with", model, ":", prompt)
|
|
39
|
+
resp = getattr(self, action)(prompt, model)
|
|
40
|
+
self.log("got", resp)
|
|
41
|
+
return resp
|
|
42
|
+
|
|
43
|
+
gchatters = ["gpt-4o-mini", "gpt-4o", "gpt-4.1", "deepseek-v3"]
|
|
44
|
+
gimagers = ["flux", "dalle-3", "gpt-image"]
|
|
45
|
+
VAGENT = None
|
|
46
|
+
g4fer = G4F()
|
|
47
|
+
|
|
48
|
+
def vagent():
|
|
49
|
+
global VAGENT
|
|
50
|
+
if not VAGENT:
|
|
51
|
+
from venvr import getagent
|
|
52
|
+
VAGENT = getagent("g4f", [{
|
|
53
|
+
"requirements": "requirements-slim.txt",
|
|
54
|
+
"git": "xtekky/gpt4free",
|
|
55
|
+
"sym": "g4f"
|
|
56
|
+
}])
|
|
57
|
+
VAGENT.register(G4F)
|
|
58
|
+
return VAGENT
|
|
59
|
+
|
|
60
|
+
def g4do(action, prompt, model):
|
|
61
|
+
try:
|
|
62
|
+
import g4f
|
|
63
|
+
print("found g4f")
|
|
64
|
+
return g4fer(action, prompt, model)
|
|
65
|
+
except:
|
|
66
|
+
print("no g4f - using venvr")
|
|
67
|
+
return vagent().run("G4F", action, prompt, model)
|
|
68
|
+
|
|
69
|
+
def g4image(prompt, model="flux"):
|
|
70
|
+
return g4do("image", prompt, model)
|
|
71
|
+
|
|
72
|
+
def g4chat(prompt, model="gpt-4o-mini"):
|
|
73
|
+
return g4do("chat", prompt, model)
|
cantools/util/ai/vox.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import random
|
|
2
|
+
from ..system import cmd
|
|
3
|
+
|
|
4
|
+
class Kvox(object):
|
|
5
|
+
def __init__(self, log=print):
|
|
6
|
+
self.logger = log
|
|
7
|
+
self.kpipes = {}
|
|
8
|
+
|
|
9
|
+
def log(self, *msg):
|
|
10
|
+
self.logger("Kvox", *msg)
|
|
11
|
+
|
|
12
|
+
def getpipe(self, lang_code):
|
|
13
|
+
from kokoro import KPipeline
|
|
14
|
+
if lang_code not in self.kpipes:
|
|
15
|
+
self.kpipes[lang_code] = KPipeline(lang_code=lang_code)
|
|
16
|
+
return self.kpipes[lang_code]
|
|
17
|
+
|
|
18
|
+
def vi(self, t, v):
|
|
19
|
+
t = round(t)
|
|
20
|
+
self.log("viseme with time", t, "and value", v)
|
|
21
|
+
return { "time": t, "value": v }
|
|
22
|
+
|
|
23
|
+
def token2visemes(self, token, visemes):
|
|
24
|
+
self.log("token2visemes", token.text, token.phonemes, token.start_ts, token.end_ts)
|
|
25
|
+
phos = token.text # phonemes are full of unicode....
|
|
26
|
+
step = 1000 * (token.end_ts - token.start_ts) / len(phos)
|
|
27
|
+
t = 1000 * token.start_ts
|
|
28
|
+
for pho in phos:
|
|
29
|
+
visemes.append(self.vi(t, pho))
|
|
30
|
+
t += step
|
|
31
|
+
return t
|
|
32
|
+
|
|
33
|
+
def tokens2visemes(self, tokens):
|
|
34
|
+
self.log("tokens2visemes")
|
|
35
|
+
t = 0
|
|
36
|
+
visemes = []
|
|
37
|
+
for token in tokens:
|
|
38
|
+
t = self.token2visemes(token, visemes)
|
|
39
|
+
visemes.append(self.vi(t, "sil"))
|
|
40
|
+
return visemes
|
|
41
|
+
|
|
42
|
+
def __call__(self, text, voice="af_heart", speed=1, lang_code='a', filename="tts"):
|
|
43
|
+
import json, soundfile
|
|
44
|
+
pipeline = self.getpipe(lang_code)
|
|
45
|
+
nothing, tokens = pipeline.g2p(text)
|
|
46
|
+
for result in pipeline.generate_from_tokens(tokens=tokens, voice=voice, speed=float(speed)):
|
|
47
|
+
self.log(result.phonemes)
|
|
48
|
+
vz = self.tokens2visemes(result.tokens)
|
|
49
|
+
vname = "%s.json"%(filename,)
|
|
50
|
+
aname = "%s.mp3"%(filename,)
|
|
51
|
+
self.log("writing", vname)
|
|
52
|
+
with open(vname, "w") as f:
|
|
53
|
+
f.write("%s\n"%("\n".join([json.dumps(v) for v in vz]),))
|
|
54
|
+
self.log("writing", aname)
|
|
55
|
+
soundfile.write(aname, result.audio, 24000)
|
|
56
|
+
|
|
57
|
+
VAGENT = None
|
|
58
|
+
kvox = Kvox()
|
|
59
|
+
kvoices = [
|
|
60
|
+
"af_heart", "af_bella", "af_nicole", "af_aoede", "af_kore", "af_sarah", "af_nova",
|
|
61
|
+
"af_sky", "af_alloy", "af_jessica", "af_river", "am_michael", "am_fenrir", "am_puck",
|
|
62
|
+
"am_echo", "am_eric", "am_liam", "am_onyx", "am_santa", "am_adam", "bf_emma",
|
|
63
|
+
"bf_isabella", "bf_alice", "bf_lily", "bm_george", "bm_fable", "bm_lewis", "bm_daniel"
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
def vagent():
|
|
67
|
+
global VAGENT
|
|
68
|
+
if not VAGENT:
|
|
69
|
+
from venvr import getagent
|
|
70
|
+
VAGENT = getagent("kovo", ["kokoro", "soundfile"])
|
|
71
|
+
VAGENT.register(Kvox)
|
|
72
|
+
return VAGENT
|
|
73
|
+
|
|
74
|
+
def vox(text, voice="af_heart", speed=1, lang_code='a', filename="tts", say=False):
|
|
75
|
+
if voice == "random":
|
|
76
|
+
voice = random.choice(kvoices)
|
|
77
|
+
try:
|
|
78
|
+
import kokoro
|
|
79
|
+
print("found kokoro")
|
|
80
|
+
kvox(text, voice, speed, lang_code, filename)
|
|
81
|
+
except:
|
|
82
|
+
print("no kokoro - using venvr")
|
|
83
|
+
vagent().run("Kvox", text, voice, speed, lang_code, filename)
|
|
84
|
+
say and cmd("mplayer %s.mp3"%(filename,))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .ander import android
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from fyg.util import log, ask, confirm, selnum
|
|
3
|
+
from cantools.util import cmd, output, mkdir, cp
|
|
4
|
+
from cantools.util.system import envget, envset
|
|
5
|
+
from cantools.util.admin import _which, javaver
|
|
6
|
+
from .data import TEMPLATES
|
|
7
|
+
|
|
8
|
+
class Android(object):
|
|
9
|
+
def __init__(self, url, name=None, package=None, auto=False):
|
|
10
|
+
self.setVars(url, name, package, auto)
|
|
11
|
+
self.setMode()
|
|
12
|
+
self.dirs()
|
|
13
|
+
self.files()
|
|
14
|
+
self.icons()
|
|
15
|
+
os.chdir(self.name)
|
|
16
|
+
self.apk()
|
|
17
|
+
self.install()
|
|
18
|
+
log("goodbye")
|
|
19
|
+
|
|
20
|
+
def setVars(self, url, name=None, package=None, auto=False):
|
|
21
|
+
if "://" not in url:
|
|
22
|
+
url = "https://%s"%(url,)
|
|
23
|
+
parts = url.split("://").pop().split("/").pop(0).split(".")
|
|
24
|
+
self.url = url
|
|
25
|
+
self.auto = auto
|
|
26
|
+
self.name = name or self.ask("name", parts[0])
|
|
27
|
+
parts.reverse()
|
|
28
|
+
self.package = package or self.ask("package name", ".".join(parts))
|
|
29
|
+
self.dir = self.package.replace(".", "/")
|
|
30
|
+
self.tmps = TEMPLATES["android"]
|
|
31
|
+
|
|
32
|
+
def ask(self, question, default=None):
|
|
33
|
+
if default and self.auto:
|
|
34
|
+
log("question '%s' -> choosing '%s'"%(question, default), important=True)
|
|
35
|
+
return default
|
|
36
|
+
return ask(question, default)
|
|
37
|
+
|
|
38
|
+
def conf(self, condition, default=False):
|
|
39
|
+
if self.auto:
|
|
40
|
+
log("confirmation '%s' -> deciding '%s'"%(condition, default), important=True)
|
|
41
|
+
return default
|
|
42
|
+
return confirm(condition, default)
|
|
43
|
+
|
|
44
|
+
def setMode(self, mode="debug"):
|
|
45
|
+
self.mode = mode
|
|
46
|
+
self.debug = mode == "debug"
|
|
47
|
+
|
|
48
|
+
def dirs(self):
|
|
49
|
+
if not self.conf("assemble directory hierarchy", True): return
|
|
50
|
+
mkdir("%s/src/main/java/%s"%(self.name, self.dir), True)
|
|
51
|
+
mkdir("%s/src/main/res"%(self.name,))
|
|
52
|
+
|
|
53
|
+
def files(self):
|
|
54
|
+
if not self.conf("create project files", True): return
|
|
55
|
+
self.jv = javaver()
|
|
56
|
+
cp(self.tmps["manifest"]%(self.name, self.package, self.name),
|
|
57
|
+
"%s/src/main/AndroidManifest.xml"%(self.name))
|
|
58
|
+
cp(self.tmps["activity"]%(self.package, self.url),
|
|
59
|
+
"%s/src/main/java/%s/MainActivity.java"%(self.name, self.dir))
|
|
60
|
+
cp(self.tmps["gradle"]%(self.package, self.jv, self.jv),
|
|
61
|
+
"%s/build.gradle"%(self.name,))
|
|
62
|
+
cp(self.tmps["properties"], "%s/gradle.properties"%(self.name,))
|
|
63
|
+
|
|
64
|
+
def icons(self):
|
|
65
|
+
if not self.conf("generate icons", True): return
|
|
66
|
+
ipath = self.ask("image path", "icon.png")
|
|
67
|
+
for k, v in self.tmps["isizes"].items():
|
|
68
|
+
wpath = "%s/src/main/res/drawable-%s"%(self.name, k)
|
|
69
|
+
mkdir(wpath)
|
|
70
|
+
cmd(self.tmps["icons"]%(ipath, v, wpath))
|
|
71
|
+
|
|
72
|
+
def chenv(self):
|
|
73
|
+
for name, cfg in self.tmps["env"].items():
|
|
74
|
+
if not envget(name):
|
|
75
|
+
log("%s not set!"%(name,))
|
|
76
|
+
envset(name, self.ask("%s location"%(name,),
|
|
77
|
+
output("locate %s"%(cfg["locater"].replace("_JV_",
|
|
78
|
+
self.jv),)).split(cfg["splitter"]).pop(0)))
|
|
79
|
+
|
|
80
|
+
def apk(self):
|
|
81
|
+
if not self.conf("build apk", True): return
|
|
82
|
+
self.chenv()
|
|
83
|
+
self.setMode(selnum(["debug", "release"]))
|
|
84
|
+
gcmd = self.debug and "assembleDebug" or "assemble"
|
|
85
|
+
if self.conf("add stacktrace flag"):
|
|
86
|
+
gcmd = "%s --stacktrace"%(gcmd,)
|
|
87
|
+
if self.conf("specify trustStore cacerts", True):
|
|
88
|
+
tsca = selnum(output("locate cacerts | grep java").split("\n"))
|
|
89
|
+
tspw = self.ask("trustStore password", "changeit")
|
|
90
|
+
gcmd = self.tmps["tstore"]%(gcmd, tsca, tspw)
|
|
91
|
+
cmd("gradle %s"%(gcmd,))
|
|
92
|
+
|
|
93
|
+
def install(self):
|
|
94
|
+
if not self.conf("install on device", True): return
|
|
95
|
+
aname = self.debug and "%s-debug"%(self.name,) or self.name
|
|
96
|
+
cmd("adb install build/outputs/apk/%s/%s.apk"%(self.mode, aname))
|
|
97
|
+
|
|
98
|
+
def android(url=None, auto=False):
|
|
99
|
+
if not _which("java", "gradle", "adb", "convert"):
|
|
100
|
+
return log("build aborted!")
|
|
101
|
+
Android(url or ask("url", "http://ct.mkult.co"), auto=auto)
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# reference:
|
|
2
|
+
# - https://github.com/raelmax/android-webview
|
|
3
|
+
# - https://czak.pl/posts/minimal-android-project
|
|
4
|
+
|
|
5
|
+
AMAN = """<?xml version="1.0" encoding="utf-8"?>
|
|
6
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
7
|
+
android:versionCode="1"
|
|
8
|
+
android:versionName="1.0" >
|
|
9
|
+
|
|
10
|
+
<uses-sdk />
|
|
11
|
+
|
|
12
|
+
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
|
13
|
+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
|
14
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
15
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
16
|
+
|
|
17
|
+
<application
|
|
18
|
+
android:allowBackup="true"
|
|
19
|
+
android:icon="@drawable/ic_launcher"
|
|
20
|
+
android:label="%s"
|
|
21
|
+
android:theme="@android:style/Theme.NoTitleBar" >
|
|
22
|
+
<activity
|
|
23
|
+
android:name="%s.MainActivity"
|
|
24
|
+
android:configChanges="orientation|screenSize"
|
|
25
|
+
android:label="%s"
|
|
26
|
+
android:exported="true" >
|
|
27
|
+
<intent-filter>
|
|
28
|
+
<action android:name="android.intent.action.MAIN" />
|
|
29
|
+
<category android:name="android.intent.category.LAUNCHER" />
|
|
30
|
+
</intent-filter>
|
|
31
|
+
</activity>
|
|
32
|
+
</application>
|
|
33
|
+
</manifest>
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
AMAC = """package %s;
|
|
37
|
+
import android.Manifest;
|
|
38
|
+
import android.app.Activity;
|
|
39
|
+
import android.os.Bundle;
|
|
40
|
+
import android.view.KeyEvent;
|
|
41
|
+
import android.view.Window;
|
|
42
|
+
import android.webkit.WebView;
|
|
43
|
+
import android.webkit.WebViewClient;
|
|
44
|
+
import android.webkit.WebChromeClient;
|
|
45
|
+
import android.webkit.PermissionRequest;
|
|
46
|
+
import android.content.pm.PackageManager;
|
|
47
|
+
import androidx.core.content.ContextCompat;
|
|
48
|
+
|
|
49
|
+
public class MainActivity extends Activity {
|
|
50
|
+
private WebView mWebView;
|
|
51
|
+
|
|
52
|
+
@Override
|
|
53
|
+
public void onCreate(Bundle savedInstanceState) {
|
|
54
|
+
super.onCreate(savedInstanceState);
|
|
55
|
+
MainActivity thaz = this;
|
|
56
|
+
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
|
57
|
+
mWebView = new WebView(this);
|
|
58
|
+
mWebView.getSettings().setMediaPlaybackRequiresUserGesture(false);
|
|
59
|
+
mWebView.getSettings().setJavaScriptEnabled(true);
|
|
60
|
+
mWebView.getSettings().setDomStorageEnabled(true);
|
|
61
|
+
mWebView.getSettings().setUseWideViewPort(true);
|
|
62
|
+
mWebView.loadUrl("%s");
|
|
63
|
+
mWebView.setWebViewClient(new WebViewClient() {
|
|
64
|
+
@Override
|
|
65
|
+
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
|
66
|
+
view.loadUrl(url);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
mWebView.setWebChromeClient(new WebChromeClient(){
|
|
71
|
+
// Need to accept permissions to use the camera
|
|
72
|
+
@Override
|
|
73
|
+
public void onPermissionRequest(final PermissionRequest request) {
|
|
74
|
+
// grants permission for app. video not showing
|
|
75
|
+
if (ContextCompat.checkSelfPermission(thaz, Manifest.permission.CAMERA)
|
|
76
|
+
!= PackageManager.PERMISSION_GRANTED
|
|
77
|
+
) {
|
|
78
|
+
requestPermissions(new String[]{Manifest.permission.CAMERA}, 1010);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (ContextCompat.checkSelfPermission(thaz, Manifest.permission.RECORD_AUDIO)
|
|
82
|
+
!= PackageManager.PERMISSION_GRANTED
|
|
83
|
+
) {
|
|
84
|
+
requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 1010);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
request.grant(request.getResources()); // is this right?
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
this.setContentView(mWebView);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@Override
|
|
94
|
+
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
|
|
95
|
+
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
|
|
96
|
+
mWebView.goBack();
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
return super.onKeyDown(keyCode, event);
|
|
100
|
+
}
|
|
101
|
+
}"""
|
|
102
|
+
|
|
103
|
+
ABG = """buildscript {
|
|
104
|
+
repositories {
|
|
105
|
+
google()
|
|
106
|
+
mavenCentral()
|
|
107
|
+
}
|
|
108
|
+
dependencies {
|
|
109
|
+
classpath 'com.android.tools.build:gradle:8.5.2'
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
repositories {
|
|
114
|
+
google()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
apply plugin: 'com.android.application'
|
|
118
|
+
|
|
119
|
+
dependencies {
|
|
120
|
+
implementation 'androidx.appcompat:appcompat:1.1.0'
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
android {
|
|
124
|
+
namespace = "%s"
|
|
125
|
+
|
|
126
|
+
compileSdkVersion 34
|
|
127
|
+
|
|
128
|
+
defaultConfig {
|
|
129
|
+
targetSdk = 34
|
|
130
|
+
minSdkVersion = 14
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
compileOptions {
|
|
134
|
+
sourceCompatibility JavaVersion.VERSION_%s
|
|
135
|
+
targetCompatibility JavaVersion.VERSION_%s
|
|
136
|
+
}
|
|
137
|
+
}"""
|
|
138
|
+
|
|
139
|
+
AGP = """android.useAndroidX=true
|
|
140
|
+
android.enableJetifier=true"""
|
|
141
|
+
|
|
142
|
+
TEMPLATES = {
|
|
143
|
+
"android": {
|
|
144
|
+
"manifest": AMAN,
|
|
145
|
+
"activity": AMAC,
|
|
146
|
+
"gradle": ABG,
|
|
147
|
+
"properties": AGP,
|
|
148
|
+
"icons": "convert -background none %s -resize %s %s/ic_launcher.png",
|
|
149
|
+
"tstore": "%s -Djavax.net.ssl.trustStore=%s -Djavax.net.ssl.trustStorePassword=%s",
|
|
150
|
+
"isizes": {
|
|
151
|
+
'ldpi': '36x36',
|
|
152
|
+
'mdpi': '48x48',
|
|
153
|
+
'hdpi': '72x72',
|
|
154
|
+
'xhdpi': '96x96'
|
|
155
|
+
},
|
|
156
|
+
"env": {
|
|
157
|
+
"JAVA_HOME": {
|
|
158
|
+
"locater": "cacerts|grep jvm/java-_JV_-",
|
|
159
|
+
"splitter": "/lib/security"
|
|
160
|
+
},
|
|
161
|
+
"ANDROID_HOME": {
|
|
162
|
+
"locater": "/lib/android-sdk",
|
|
163
|
+
"splitter": "\n"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
cantools/util/system.py
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import os, sys
|
|
2
|
-
|
|
3
|
-
from commands import getoutput # py2
|
|
4
|
-
except:
|
|
5
|
-
from subprocess import getoutput # py3
|
|
6
|
-
from fyg.util import log, read, write, rm
|
|
1
|
+
import os, sys
|
|
2
|
+
from fyg.util import log, read, write, rm, sudoed, cmd, output
|
|
7
3
|
|
|
8
4
|
def cp(content, fname): # more write than copy, buuuut...
|
|
9
5
|
log("writing %s"%(fname,), 2)
|
|
@@ -40,23 +36,6 @@ def mkdir(pname, recursive=False):
|
|
|
40
36
|
def sed(fname, flag, replacement, target=None):
|
|
41
37
|
write(read(fname).replace(flag, replacement), target or fname)
|
|
42
38
|
|
|
43
|
-
def sudoed(cline, sudo=False):
|
|
44
|
-
if sudo and platform.system() != "Windows" and os.geteuid(): # !root
|
|
45
|
-
cline = "sudo %s"%(cline,)
|
|
46
|
-
return cline
|
|
47
|
-
|
|
48
|
-
def cmd(cline, sudo=False, silent=False):
|
|
49
|
-
cline = sudoed(cline, sudo)
|
|
50
|
-
silent or log('issuing command: "%s"'%(cline,), 2)
|
|
51
|
-
subprocess.call(cline, shell=True)
|
|
52
|
-
|
|
53
|
-
def output(cline, sudo=False, silent=False, loud=False):
|
|
54
|
-
cline = sudoed(cline, sudo)
|
|
55
|
-
silent or log('getting output for: "%s"'%(cline,), 2)
|
|
56
|
-
output = getoutput(cline)
|
|
57
|
-
loud and log(output)
|
|
58
|
-
return output
|
|
59
|
-
|
|
60
39
|
def envget(name):
|
|
61
40
|
return output("echo $%s"%(name,))
|
|
62
41
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ct
|
|
3
|
-
Version: 0.10.8.
|
|
3
|
+
Version: 0.10.8.116
|
|
4
4
|
Summary: Modern minimal web framework
|
|
5
5
|
Author: Mario Balibrera
|
|
6
6
|
Author-email: mario.balibrera@gmail.com
|
|
@@ -17,7 +17,7 @@ Requires-Dist: babyweb >=0.1.2
|
|
|
17
17
|
Requires-Dist: catmail >=0.1.9.1
|
|
18
18
|
Requires-Dist: databae >=0.1.5.1
|
|
19
19
|
Requires-Dist: dez >=0.10.10.45
|
|
20
|
-
Requires-Dist: fyg >=0.1.7.
|
|
20
|
+
Requires-Dist: fyg >=0.1.7.9
|
|
21
21
|
Requires-Dist: rel >=0.4.9.23
|
|
22
22
|
Requires-Dist: venvr >=0.1.5.8
|
|
23
23
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
cantools/__init__.py,sha256=
|
|
1
|
+
cantools/__init__.py,sha256=pYszyy7Gwi8eK0nDPsJq5aIok5xQnYLERkM74roIzFE,824
|
|
2
2
|
cantools/_db.py,sha256=g-kXshJpjfLiYPg8GtTrae1bD0uZ8eWbYi8ORKaXGNs,5075
|
|
3
3
|
cantools/_memcache.py,sha256=xv_rumi1mrr6Br4ofZXTA6deNOEyZg6nwFW6QEcUVZ4,2154
|
|
4
4
|
cantools/_pay.py,sha256=hvu8SiFVUySVIu5W1SZuD_6DHaUWnmy0XOITPlt1C3M,1292
|
|
5
5
|
cantools/admin.py,sha256=MuRKvFQ_LhGqRro6cY6e6PGOBjLXGwKKcTJ1GuAa3K0,1182
|
|
6
6
|
cantools/cfg.py,sha256=N1hBT_MwuHIYAtZ0vczjnE9B_cvwq_IcgTWRvoBUGZI,8920
|
|
7
|
-
cantools/config.py,sha256=
|
|
7
|
+
cantools/config.py,sha256=FA9VmBhYhSIshizUjA7zrGNmgjYgoItrRGac-mKhp0o,5182
|
|
8
8
|
cantools/geo.py,sha256=WQMWp142niNzRf4WXRurzGPiFaZ3orbDpBwQC2gZXn0,5422
|
|
9
9
|
cantools/hooks.py,sha256=kEnYBt8Fl6EFJz_u-jPQjGQTXa9TxACZxPMbgUW3cHc,219
|
|
10
10
|
cantools/db/__init__.py,sha256=slJyO03jrCTEqDWzXUkrsmolGoJTO42FPJSLCE87RSM,614
|
|
@@ -30,18 +30,27 @@ cantools/scripts/pubsub/channel.py,sha256=racPeZThUoKJK2TYFfNtwKkxjP5jLUwTWozoZz
|
|
|
30
30
|
cantools/scripts/pubsub/ps.py,sha256=enpSroRCKdJ4GS7M7mpv5mh0z6_rtv_JLpri2NvyJQ0,5216
|
|
31
31
|
cantools/scripts/pubsub/user.py,sha256=B1LdCmmwxgtTNgWGmtxDVzPruYjgw5J-vIjmcB0kbck,1554
|
|
32
32
|
cantools/util/__init__.py,sha256=zZ_0RSbOyChUvp4PBfuzKNy4KRQHJXv8abv-LO2T06I,3444
|
|
33
|
-
cantools/util/admin.py,sha256=
|
|
33
|
+
cantools/util/admin.py,sha256=b5lb4X1N7VfeL0MutMT_cWIpTMz3ehL900YkCN9DLPE,18469
|
|
34
34
|
cantools/util/data.py,sha256=RrvVmgQkelSCXHj0oqRkg_aifX6qI7IsrBAJr0wvq8s,2911
|
|
35
35
|
cantools/util/media.py,sha256=-znYq87blesL_rMc2OfYmum69z1uiyMe0XmGL--bS2g,8585
|
|
36
36
|
cantools/util/package.py,sha256=cKJwC7OoLRmQ2dzYtjZjpQoW09ojZ_nTTqgx0fCimHI,3998
|
|
37
|
-
cantools/util/system.py,sha256=
|
|
37
|
+
cantools/util/system.py,sha256=DqwxuQWX1f60N7-fdfazc5vfhB_k1LgzqED3xpjgXyw,1301
|
|
38
|
+
cantools/util/ai/__init__.py,sha256=MXVRzXC8IGGKMxyhBYma-UzRi0EY4tdDe2to77zhp14,358
|
|
39
|
+
cantools/util/ai/vox.py,sha256=aoEhO3nwo1ITi9ONSfuNviSWFqWmUCvtJhD0AlNcN1g,2601
|
|
40
|
+
cantools/util/ai/tox/__init__.py,sha256=Gd8XKP_201ZjiovuO7t0dIueGYQxRTKPQ3vx6ALdgx0,542
|
|
41
|
+
cantools/util/ai/tox/duck.py,sha256=rH8trJg8JygAmHzdTOeyaR27wgS3bjEtRdSBYTwYcvg,1818
|
|
42
|
+
cantools/util/ai/tox/fzn.py,sha256=u0LWvJb8T_xkhQidRNuAbVVQybMt2zI9_D-O2lgrFwE,501
|
|
43
|
+
cantools/util/ai/tox/g4free.py,sha256=9mxrNGmB_5C5Yv9g452EQpkt5a-Wua5iTgIE3iBYl4E,1792
|
|
44
|
+
cantools/util/apper/__init__.py,sha256=SZdGKLZfPtwxhLjLmdihWERD09cGNJBwnvu8cTPStis,26
|
|
45
|
+
cantools/util/apper/ander.py,sha256=gnvwMqtSLgQDjYIbHCbI9PAOU-ToV_A2rYpc-JI_OrY,3504
|
|
46
|
+
cantools/util/apper/data.py,sha256=HSk_LRBnuCAzorK8Waw5yDFoyBZoTvAd46pPQYlE51E,5146
|
|
38
47
|
cantools/web/__init__.py,sha256=nJOA_sNuT31LgFauWGUjznjhWoubjb1JqGVg2XWXjhM,407
|
|
39
48
|
cantools/web/bw.py,sha256=N-cimUHYyDuHv8v2Ut0twsi8yBDMb3alw1m9EpCYDEs,2704
|
|
40
49
|
cantools/web/gae_server.py,sha256=dPLkEsvzOb-wEFCSMleCvIElNnJoLRMdAGw5DYytwGU,2007
|
|
41
50
|
cantools/web/util.py,sha256=ovacYpR6VtJVw9MvL9jJuXdkMKKv8A5GxjWE-KlYe70,4367
|
|
42
|
-
ct-0.10.8.
|
|
43
|
-
ct-0.10.8.
|
|
44
|
-
ct-0.10.8.
|
|
45
|
-
ct-0.10.8.
|
|
46
|
-
ct-0.10.8.
|
|
47
|
-
ct-0.10.8.
|
|
51
|
+
ct-0.10.8.116.dist-info/LICENSE,sha256=lUkxqJkYxLYhvL52PQdG8L3aqmG241XXmThs30isJTU,1078
|
|
52
|
+
ct-0.10.8.116.dist-info/METADATA,sha256=0yjaCxOawMmRBy0WNIgVMrSCF7im9xnsQ3Ls5fkJyPE,1175
|
|
53
|
+
ct-0.10.8.116.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
54
|
+
ct-0.10.8.116.dist-info/entry_points.txt,sha256=Gd3wUZp5AMeazvq2oIOh4qn57T2joZ8NeHenLhXG4Hw,261
|
|
55
|
+
ct-0.10.8.116.dist-info/top_level.txt,sha256=Tl_rJpBXgVrhgmBrKozbrcQrKnOIX89JqFF7aF_4EbA,9
|
|
56
|
+
ct-0.10.8.116.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|