Skip to content

Commit

Permalink
added api-client mode
Browse files Browse the repository at this point in the history
  • Loading branch information
eliranwong committed Nov 5, 2024
1 parent 24b784c commit 12f00f0
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 17 deletions.
8 changes: 7 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
# https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/
setup(
name=package,
version="0.1.57",
version="0.1.59",
python_requires=">=3.8, <3.13",
description=f"UniqueBible App is a cross-platform & offline bible application, integrated with high-quality resources and unique features. Developers: Eliran Wong and Oliver Tseng",
long_description=long_description,
Expand Down Expand Up @@ -564,6 +564,12 @@
entry_points={
"console_scripts": [
f"{package}={package}.uba:main",
f"ub={package}.uba:stream",
f"ubapi={package}.uba:api",
f"ubhttp={package}.uba:http",
f"ubssh={package}.uba:ssh",
f"ubtelnet={package}.uba:telnet",
f"ubterm={package}.uba:term",
],
},
keywords="bible scripture na28 bsha hebrew greek ai marvelbible biblebento uba uniquebible",
Expand Down
4 changes: 3 additions & 1 deletion uniquebible/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
os.environ[key] = value

# check runmode and initial command
config.noQt = True if config.runMode in ("stream", "terminal", "ssh-server", "telnet-server", "http-server", "api-server", "execute-macro") or os.path.isdir("/data/data/com.termux/files/home") else False
config.noQt = True if config.runMode in ("stream", "terminal", "ssh-server", "telnet-server", "http-server", "api-server", "api-client", "execute-macro") or os.path.isdir("/data/data/com.termux/files/home") else False
config.cli = True if config.runMode == "cli" else False
config.enableCli = True if config.runMode in ("cli", "gui", "docker") else False
config.enableApiServer = True if config.runMode == "api-server" else False
Expand Down Expand Up @@ -112,6 +112,8 @@
from uniquebible.startup.nonGui import *
if config.runMode == "stream":
run_stream_mode()
elif config.runMode == "api-client":
run_api_client_mode()
elif config.runMode == "terminal":
run_terminal_mode()
elif config.runMode == "ssh-server":
Expand Down
124 changes: 119 additions & 5 deletions uniquebible/startup/nonGui.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def checkCommand(command):
def run_terminal_mode():
from uniquebible.util.LocalCliHandler import LocalCliHandler
from uniquebible.util.prompt_shared_key_bindings import prompt_shared_key_bindings
from uniquebible.util.uba_command_prompt_key_bindings import uba_command_prompt_key_bindings
from uniquebible.util.uba_command_prompt_key_bindings import api_command_prompt_key_bindings
from prompt_toolkit.key_binding import merge_key_bindings
from prompt_toolkit.shortcuts import set_title, clear_title
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
Expand Down Expand Up @@ -224,19 +224,133 @@ def run_terminal_mode():
clear_title()
sys.exit(0)

# api-client mode

def run_api_client_mode():

def getApiOutput(command: str):
private = f"private={config.web_api_private}&" if config.web_api_private else ""
url = f"""{config.web_api_endpoint}?{private}cmd={command}"""
response = requests.get(url, timeout=config.web_api_timeout)
response.encoding = "utf-8"
print(response.text.strip())

def multiturn_api_output(apiCommandSuggestions=None):
from uniquebible.util.prompt_shared_key_bindings import prompt_shared_key_bindings
from uniquebible.util.uba_command_prompt_key_bindings import api_command_prompt_key_bindings
from prompt_toolkit.key_binding import merge_key_bindings
from prompt_toolkit.shortcuts import set_title, clear_title
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.styles import Style
from prompt_toolkit.filters import Condition
from prompt_toolkit.completion import WordCompleter, NestedCompleter, ThreadedCompleter, FuzzyCompleter
import webbrowser

# startup
set_title("Unique Bible App API-Client")
print("Running Unique Bible App api-client ...")
print("Enter an Unique Bible App command:")
print("For API documentation, visit https://github.com/eliranwong/UniqueBibleAPI")

# make key bindings available in config to allow futher customisation via plugins
config.key_bindings = merge_key_bindings([
prompt_shared_key_bindings,
api_command_prompt_key_bindings,
])

# initiate main prompt session
initiateMainPrompt()
command_completer = FuzzyCompleter(ThreadedCompleter(NestedCompleter.from_nested_dict(apiCommandSuggestions))) if apiCommandSuggestions is not None else None
auto_suggestion=AutoSuggestFromHistory()
toolbar = " [ctrl+q] .quit [escape+h] .help "
style = Style.from_dict({
# User input (default text).
"": config.terminalCommandEntryColor1,
# Prompt.
"indicator": config.terminalPromptIndicatorColor1,
})
promptIndicator = ">>> "
promptIndicator = [
("class:indicator", promptIndicator),
]

command = ""
while True:
# User command input
command = config.main_prompt_session.prompt(
promptIndicator,
style=style,
completer=command_completer,
complete_in_thread=None,
auto_suggest=auto_suggestion,
bottom_toolbar=toolbar,
#default=default,
key_bindings=config.key_bindings,
# enable system prompt without auto-completion
# use escape+!
enable_system_prompt=True,
swap_light_and_dark_colors=Condition(lambda: not config.terminalResourceLinkColor.startswith("ansibright")),
#rprompt="Enter an UBA command",
).strip()
if command:
if command.lower() == ".quit":
break
elif command.lower() == ".help":
webbrowser.open("https://github.com/eliranwong/UniqueBibleAPI")
continue
command = checkCommand(command)
# remove spaces before and after ":::"
command = re.sub("[ ]*?:::[ ]+?([^ ])", r":::\1", command)
# remove "_" before ":::"
command = re.sub("_:::", ":::", command)
# format chapter no. and verse no
command = re.sub("([0-9]+?)_([0-9]+?)_([0-9])", r"\1.\2.\3", command)
command = re.sub("_([0-9]+?)_([0-9]+?,)", r" \1:\2", command)
command = re.sub("_([0-9]+?)_([0-9]+?)$", r" \1:\2", command)
# change full width characters
command = re.sub(":::", r":::", command)

getApiOutput(command)

clear_title()

try:
stdin_text = sys.stdin.read() if not sys.stdin.isatty() else ""
command = " ".join(sys.argv[2:]).strip()
if stdin_text:
command = f"{command} {stdin_text}"

if command:
# stream output directly
getApiOutput(command)
else:
# interactive mode
private = f"private={config.web_api_private}&" if config.web_api_private else ""
r = requests.get(f"{config.web_api_endpoint}?{private}cmd=.suggestions", timeout=config.web_api_timeout)
r.encoding = "utf-8"
apiCommandSuggestions = r.json()

multiturn_api_output(apiCommandSuggestions=apiCommandSuggestions)

except:
#import traceback
#print(traceback.format_exc())
print(f"Failed to connect '{config.web_api_endpoint}' at the moment!")

# raw mode
def run_stream_mode():
from uniquebible.util.LocalCliHandler import LocalCliHandler

input_text = sys.stdin.read() if not sys.stdin.isatty() else ""
stdin_text = sys.stdin.read() if not sys.stdin.isatty() else ""

# Set initial command
command = config.initial_command if config.initial_command else " ".join(sys.argv[2:]).strip()
if input_text:
command = f"{command} {input_text}"
command = " ".join(sys.argv[2:]).strip()
if stdin_text:
command = f"{command} {stdin_text}"
if command.strip():
config.mainWindow = LocalCliHandler()
output_text = config.mainWindow.getContent(command, False)
output_text = re.sub("\n[-]+?$", "", output_text)
else:
output_text = "Command not given!"
print(output_text, file=sys.stdout)
Expand Down
27 changes: 26 additions & 1 deletion uniquebible/uba.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def main():

# check running mode and initial command
runMode = sys.argv[1] if len(sys.argv) > 1 else ""
enableCli = True if runMode.lower() in ("stream", "cli", "cli.py", "gui", "terminal", "docker", "telnet-server", "http-server", "execute-macro", "api-server") else False
enableCli = True if runMode.lower() in ("stream", "cli", "cli.py", "gui", "terminal", "docker", "telnet-server", "http-server", "execute-macro", "api-server", "api-client") else False
initialCommand = input("Enter command: ").strip() if runMode == "-i" else " ".join(sys.argv[1:]).strip()
initialCommand = initialCommand.strip()

Expand Down Expand Up @@ -197,5 +197,30 @@ def desktopFileContent():
else:
subprocess.Popen([python, mainFile, initialCommand] if initialCommand else [python, mainFile])

def stream():
sys.argv.insert(1, "stream")
main()

def api():
# web api-client, not api-server mode
sys.argv.insert(1, "api-client")
main()

def http():
sys.argv.insert(1, "http-server")
main()

def ssh():
sys.argv.insert(1, "ssh-server")
main()

def telnet():
sys.argv.insert(1, "telnet-server")
main()

def term():
sys.argv.insert(1, "terminal")
main()

if __name__ == '__main__':
main()
22 changes: 13 additions & 9 deletions uniquebible/util/ConfigUtil.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,8 @@ def setup(noQt=None, cli=None, enableCli=None, enableApiServer=None, enableHttpS
config.ubaUserDir = ubaUserDir if os.path.isdir(ubaUserDir) else os.path.dirname(os.path.dirname(os.path.realpath(__file__)))

# check running mode
config.runMode = sys.argv[1] if len(sys.argv) > 1 else ""
if " " in config.runMode:
import re
config.runMode, config.initial_command = config.runMode.split(" ", 1)
else:
config.initial_command = ""
config.runMode = config.runMode.lower()

if config.runMode and not config.runMode in ("stream", "setup-only", "cli", "gui", "terminal", "docker", "telnet-server", "http-server", "execute-macro", "api-server"):
config.runMode = sys.argv[1].lower() if len(sys.argv) > 1 else ""
if config.runMode and not config.runMode in ("stream", "setup-only", "cli", "gui", "terminal", "docker", "telnet-server", "http-server", "execute-macro", "api-server", "api-client"):
config.runMode = ""

# Temporary configurations
Expand Down Expand Up @@ -175,6 +168,17 @@ def getCurrentVenvDir():
# Use PySide6 as Qt Library, even config.qtLibrary is set to a value other than 'pyside6'.""",
False)

# Start of api-client mode setting
setConfig("web_api_endpoint", """
# UniqueBible App web API endpoint.""",
"https://bible.gospelchurch.uk/plain")
setConfig("web_api_timeout", """
# UniqueBible App web API timeout.""",
10)
setConfig("web_api_private", """
# UniqueBible App web API private key.""",
"")

# Start of terminal mode setting
setConfig("terminalWrapWords", """
# Wrap words in terminal mode.""",
Expand Down
1 change: 1 addition & 0 deletions uniquebible/util/RemoteHttpHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ def do_GET(self):
# convert to plain text for plain endpoint
if api in ("json", "plain"):
content = TextUtil.htmlToPlainText(content).strip()
content = re.sub("\n[-]+?$", "", content)
if api == "json":
output = {}
if not ":::" in self.command:
Expand Down
14 changes: 14 additions & 0 deletions uniquebible/util/uba_command_prompt_key_bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@
from prompt_toolkit.key_binding import KeyBindings
#from prompt_toolkit.application import run_in_terminal

api_command_prompt_key_bindings = KeyBindings()

# Escape+H launch help menu
@api_command_prompt_key_bindings.add("escape", "h")
def _(event):
event.app.current_buffer.text = ".help"
event.app.current_buffer.validate_and_handle()

# Ctrl+Q quit UBA
@api_command_prompt_key_bindings.add("c-q")
def _(event):
event.app.current_buffer.text = ".quit"
event.app.current_buffer.validate_and_handle()

uba_command_prompt_key_bindings = KeyBindings()

# add key bindings from Ctrl+B to Ctrl+Y, skipping Ctrl+A, C, D, E, H, J, M, N, O, P, T, V, X
Expand Down

0 comments on commit 12f00f0

Please sign in to comment.