From 3cbb708b3a43b6ac41e75c6cf1941d5729eea3a6 Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Tue, 17 Oct 2023 20:04:38 +0200 Subject: Initial commit --- .gitignore | 7 ++++ LICENSE | 22 +++++++++++++ MANIFEST.in | 3 ++ README.md | 3 ++ requirements.txt | 2 ++ setup.py | 38 +++++++++++++++++++++ south_browser/__init__.py | 2 ++ south_browser/__main__.py | 39 ++++++++++++++++++++++ south_browser/rpc.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++ tox.ini | 8 +++++ 10 files changed, 208 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 README.md create mode 100644 requirements.txt create mode 100755 setup.py create mode 100644 south_browser/__init__.py create mode 100644 south_browser/__main__.py create mode 100644 south_browser/rpc.py create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd4481d --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +build +dist +*.egg-info +__pycache__ +*.pyc +.tox +env diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..207309d --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) Jan Drögehoff + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..bb910eb --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include README.md +include LICENSE +include requirements.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..5d05605 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# South Browser + +Server Browser for Northstar using [SouthRPC](https://github.com/Jan200101/SouthRPC) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..28d6b26 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +requests +inquirer diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..46c6a5e --- /dev/null +++ b/setup.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +import setuptools +import re + +requirements = [] +with open('requirements.txt') as f: + requirements = f.read().splitlines() + +version = None +with open('south_browser/__init__.py') as f: + version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), re.MULTILINE).group(1) + +if not version: + raise RuntimeError('version is not set') + +long_description = "" +with open("README.md", "r") as fh: + long_description = fh.read() + + +setuptools.setup( + name = "south-browser", + version = version, + description = "python based", + long_description = long_description, + long_description_content_type = "text/markdown", + packages = [ + "south_browser", + ], + + python_requires = ">=3.6", + + entry_points = { + 'console_scripts': [ + 'south-browser=south_browser.__main__:main', + ], + }, +) diff --git a/south_browser/__init__.py b/south_browser/__init__.py new file mode 100644 index 0000000..9561ba9 --- /dev/null +++ b/south_browser/__init__.py @@ -0,0 +1,2 @@ + +__version__ = "0.0.0" diff --git a/south_browser/__main__.py b/south_browser/__main__.py new file mode 100644 index 0000000..f0040b3 --- /dev/null +++ b/south_browser/__main__.py @@ -0,0 +1,39 @@ +import inquirer + +from requests.exceptions import ConnectionError +from .rpc import * + +def main(): + try: + work() + except ConnectionError: + print("Failed to connect to RPC") + +def work(): + servers = NSGetGameServers() + + choices = [f"{s[2]} ({s[6]}/{s[7]})" for s in servers] + questions = [ + inquirer.List('server_index', + message="What server do you want to join?", + choices=choices, + ), + ] + + answers = inquirer.prompt(questions) + i = answers["server_index"] + si = choices.index(i) + + NSTryAuthWithServer(si) + + sleep(1) + + if not NSWasAuthSuccessful(): + print("Failed to auth") + return + + NSConnectToAuthedServer() + + +if __name__ == "__main__": + main() diff --git a/south_browser/rpc.py b/south_browser/rpc.py new file mode 100644 index 0000000..fe76829 --- /dev/null +++ b/south_browser/rpc.py @@ -0,0 +1,84 @@ +import json + +from requests import post +from time import time, sleep + +_PAYLOAD_ID = 0 +LOCAL_RPC = "http://localhost:26503/rpc" + +def cmd(method: str, params = None): + global _PAYLOAD_ID + _PAYLOAD_ID += 1 + + payload = { + "jsonrpc": "2.0", + "id": int(time()), + "method": method + } + + if params is not None: + payload["params"] = params + + r = post(LOCAL_RPC, data=json.dumps(payload)) + + if r.status_code != 200: + raise Exception(r.text) + + data = r.json() + return data + +def NSRequestServerList(): + return cmd("execute_squirrel", { + "code": "return NSRequestServerList()" + })["result"] + +def NSGetServerCount(repeat: bool = True): + data = cmd("execute_squirrel", { + "code": "return NSGetServerCount()" + }) + + c = data["result"] + + if not c and repeat: + NSRequestServerList() + sleep(1) # give it a second to popualte + return NSGetServerCount(False) + + return c + +def NSGetGameServers(): + # work around Squirrel closure bullshit + server_count = NSGetServerCount() + + for i in range(server_count): + + primret = [] + + for c in range(server_count): + primret.append(f"[s[{c}].index, s[{c}].id, s[{c}].name, s[{c}].description, s[{c}].map, s[{c}].playlist, s[{c}].playerCount, s[{c}].maxPlayerCount, s[{c}].requiresPassword, s[{c}].region]") + + ret = "[" + ",".join(primret) + "]" + + return cmd("execute_squirrel", { + "code": f"array s = NSGetGameServers()\nreturn {ret}" + })["result"] + + return [] + +def NSTryAuthWithServer(index: int, password: str = None): + if password is None: + password = "" + + return cmd("execute_squirrel", { + "code": f"return NSTryAuthWithServer({index}, {json.dumps(password)})" + })["result"] + +def NSWasAuthSuccessful(): + return cmd("execute_squirrel", { + "code": "return NSWasAuthSuccessful()" + })["result"] + +def NSConnectToAuthedServer(): + return cmd("execute_squirrel", { + "code": "return NSConnectToAuthedServer()" + })["result"] diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..f824619 --- /dev/null +++ b/tox.ini @@ -0,0 +1,8 @@ +[tox] +envlist = py38,mypy + +[testenv] +deps = pytest +commands = pytest +setenv = + TOX = 1 \ No newline at end of file -- cgit v1.2.3