├── tests ├── __init__.py └── entrypoint_test.py ├── launcher ├── app │ ├── __init__.py │ ├── app_model.py │ ├── web_app.py │ ├── app_controller.py │ └── launcher_app.py ├── templates │ ├── news_card.html │ ├── footer.html │ ├── index.html │ ├── launcher_card.html │ ├── bot_card.html │ └── layout.html ├── tools │ ├── __init__.py │ ├── executor.py │ ├── octobot_connector.py │ ├── version.py │ ├── github.py │ └── environment.py ├── static │ ├── favicon.ico │ ├── img │ │ ├── octobot.png │ │ └── svg │ │ │ ├── arrow_left.svg │ │ │ └── arrow_right.svg │ ├── css │ │ ├── layout.css │ │ └── style.css │ └── js │ │ └── commons.js ├── launcher_entry.py └── __init__.py ├── requirements.txt ├── dev_requirements.txt ├── MANIFEST.in ├── bin └── launcher.spec ├── appveyor.yml ├── CHANGELOG.md ├── .gitignore ├── README.md ├── setup.py ├── entrypoint.py ├── .travis.yml └── LICENSE /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /launcher/app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | future 2 | requests 3 | flask 4 | -------------------------------------------------------------------------------- /launcher/templates/news_card.html: -------------------------------------------------------------------------------- 1 | This service is currently unavailable. -------------------------------------------------------------------------------- /dev_requirements.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | git+git://github.com/Drakkar-Software/pyinstaller@develop 3 | twine 4 | -------------------------------------------------------------------------------- /launcher/tools/__init__.py: -------------------------------------------------------------------------------- 1 | BINARY_DOWNLOAD_PROGRESS_SIZE = 75 2 | TENTACLES_UPDATE_INSTALL_PROGRESS_SIZE = 15 3 | -------------------------------------------------------------------------------- /launcher/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drakkar-Software/OctoBot-Launcher/HEAD/launcher/static/favicon.ico -------------------------------------------------------------------------------- /launcher/static/img/octobot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drakkar-Software/OctoBot-Launcher/HEAD/launcher/static/img/octobot.png -------------------------------------------------------------------------------- /launcher/static/img/svg/arrow_left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /launcher/static/img/svg/arrow_right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include launcher *.html 2 | recursive-include launcher *.css 3 | recursive-include launcher *.js 4 | recursive-include launcher *.png 5 | recursive-include launcher *.svg 6 | recursive-include launcher *.ico 7 | 8 | include README.md 9 | include LICENSE 10 | include CHANGELOG.md 11 | include requirements.txt 12 | -------------------------------------------------------------------------------- /tests/entrypoint_test.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import shutil 4 | 5 | from entrypoint import LAUNCHER_PATH, main 6 | from launcher.launcher_entry import launcher 7 | 8 | 9 | def test_main(): 10 | launcher(["-nw"]) 11 | 12 | 13 | # def test_install_bot(): 14 | # launcher(["-nw", "-u"]) 15 | # assert glob.glob("OctoBot*") 16 | 17 | 18 | # def test_install_launcher(): 19 | # shutil.rmtree(LAUNCHER_PATH) 20 | # main() 21 | # assert os.path.exists(LAUNCHER_PATH) 22 | -------------------------------------------------------------------------------- /launcher/static/css/layout.css: -------------------------------------------------------------------------------- 1 | /* Nav bar disabling */ 2 | a.disabled { 3 | /* Make the disabled links grayish*/ 4 | color: gray; 5 | /* And disable the pointer events */ 6 | pointer-events: none; 7 | } 8 | 9 | .icon-green { 10 | color: #10FB03; 11 | } 12 | 13 | .icon-red { 14 | color: red; 15 | } 16 | 17 | .danger-color{ 18 | background-color: #ff4444; 19 | } 20 | .danger-color-dark{ 21 | background-color: #CC0000; 22 | } 23 | .warning-color{ 24 | background-color: #ffbb33; 25 | } 26 | .warning-color-dark{ 27 | background-color: #FF8800; 28 | } 29 | .success-color{ 30 | background-color: #00C851; 31 | } 32 | .success-color-dark{ 33 | background-color: #007E33; 34 | } 35 | .info-color{ 36 | background-color: #33b5e5; 37 | } 38 | .info-color-dark{ 39 | background-color: #0099CC; 40 | } 41 | 42 | .navbar-default{ 43 | z-index: 2000; 44 | } -------------------------------------------------------------------------------- /bin/launcher.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | 3 | block_cipher = None 4 | 5 | 6 | a = Analysis(['../entrypoint.py'], 7 | pathex=['../'], 8 | binaries=[], 9 | datas=[('../launcher', 'launcher')], 10 | hiddenimports=["glob", "subprocess", "json", "requests", "os", "logging", 11 | "distutils", "distutils.version", "config", "flask"], 12 | hookspath=[], 13 | runtime_hooks=[], 14 | excludes=[], 15 | win_no_prefer_redirects=False, 16 | win_private_assemblies=False, 17 | cipher=block_cipher) 18 | pyz = PYZ(a.pure, a.zipped_data, 19 | cipher=block_cipher) 20 | exe = EXE(pyz, 21 | a.scripts, 22 | a.binaries, 23 | a.zipfiles, 24 | a.datas, 25 | name='launcher_app', 26 | debug=False, 27 | strip=False, 28 | icon="favicon.ico", 29 | upx=True, 30 | runtime_tmpdir=None, 31 | console=True ) 32 | -------------------------------------------------------------------------------- /launcher/templates/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 16 | -------------------------------------------------------------------------------- /launcher/app/app_model.py: -------------------------------------------------------------------------------- 1 | # Drakkar-Software OctoBot 2 | # Copyright (c) Drakkar-Software, All rights reserved. 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 3.0 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with this library. 16 | 17 | from distutils.version import LooseVersion 18 | from launcher.tools.version import Version 19 | 20 | 21 | def is_up_to_date(current_version, server_version): 22 | if current_version == Version.NOT_INSTALLED_VERSION: 23 | return False 24 | return LooseVersion(current_version) >= LooseVersion(server_version) 25 | -------------------------------------------------------------------------------- /launcher/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block body %} 3 |
4 |
5 |
6 |
7 |

8 | OctoBot 9 | 10 |

11 |
12 |
13 |
14 |
15 |
16 |

17 | OctoBot-Launcher 18 | 19 |

20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |

28 | OctoBot news feed 29 | 30 |

31 |
32 |
33 |
34 |
35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | 3 | matrix: 4 | 5 | # For Python versions available on Appveyor, see 6 | # http://www.appveyor.com/docs/installed-software#python 7 | # The list here is complete (excluding Python 2.6, which 8 | # isn't covered by this document) at the time of writing. 9 | 10 | - PYTHON: "C:\\Python37-x64" 11 | 12 | install: 13 | - "%PYTHON%\\python.exe -m pip install -r requirements.txt -r dev_requirements.txt" 14 | 15 | build_script: 16 | - ps: Start-FileDownload 'https://raw.githubusercontent.com/Drakkar-Software/OctoBot/dev/interfaces/web/static/favicon.ico' 17 | - "echo f | xcopy /f /y favicon.ico launcher\\static\\favicon.ico" 18 | - "rd /s /q launcher\\app launcher\\tools" 19 | - "del /q launcher\\*.py launcher\\static\\* launcher\\templates\\*" 20 | - "%PYTHON%\\Scripts\\pyinstaller.exe bin/launcher.spec" 21 | - "echo f | xcopy /f /y dist\\launcher_app.exe dist\\launcher_app_windows.exe" 22 | - "powershell get-filehash -algorithm sha1 dist\\launcher_app_windows.exe" 23 | 24 | artifacts: 25 | - path: dist/launcher_app_windows.exe 26 | name: launcher_app_windows.exe 27 | 28 | deploy: 29 | - provider: Environment 30 | name: GITHUB_RELEASE_OCTOBOT_LAUNCHER 31 | draft: false 32 | prerelease: false 33 | artifact: launcher_app_windows.exe 34 | on: 35 | branch: master 36 | APPVEYOR_REPO_TAG: true 37 | -------------------------------------------------------------------------------- /launcher/tools/executor.py: -------------------------------------------------------------------------------- 1 | # Drakkar-Software OctoBot-Launcher 2 | # Copyright (c) Drakkar-Software, All rights reserved. 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 3.0 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with this library. 16 | 17 | 18 | import logging 19 | import subprocess 20 | from subprocess import PIPE 21 | 22 | 23 | def execute_command_on_current_binary(binary_path, commands): 24 | try: 25 | cmd = [f"{binary_path}"] + commands 26 | return subprocess.Popen(cmd, stdout=PIPE).stdout.read().rstrip().decode() 27 | except PermissionError as e: 28 | logging.error(f"Failed to run bot with command {commands} : {e}") 29 | except FileNotFoundError as e: 30 | logging.error(f"Can't find a valid binary") 31 | 32 | 33 | def execute_command_on_detached_binary(binary_path, commands=None): 34 | try: 35 | cmd = [f"{binary_path}"] + (commands if commands else []) 36 | return subprocess.Popen(cmd) 37 | except Exception as e: 38 | logging.error(f"Failed to run detached bot with command {commands} : {e}") 39 | return None 40 | -------------------------------------------------------------------------------- /launcher/templates/launcher_card.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |

6 | Current version : 7 | {% if is_up_to_date %} 8 | 9 | {% else %} 10 | 11 | {% endif %} 12 | {{ launcher_local_version }} 13 |

14 |

15 | Latest version : {{ launcher_server_version }} 16 |

17 |
18 |
19 | 20 |
21 |
22 |
23 |
24 | 25 |
26 |
27 | 28 |
29 |
30 |
31 | 34 |
35 |
36 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | *It is strongly advised to perform an update of your tentacles after updating OctoBot.* 2 | 3 | Changelog for 2.0.4 4 | ==================== 5 | *Released date : August 31 2019* 6 | ### Fixed 7 | - Now downloads strategy optimizer data 8 | 9 | Changelog for 2.0.3 10 | ==================== 11 | *Released date : June 12 2019* 12 | ### Fixed 13 | - Package data 14 | 15 | Changelog for 2.0.2 16 | ==================== 17 | *Released date : April 19 2019* 18 | 19 | - Fixed url typo 20 | 21 | Changelog for 2.0.1 22 | ==================== 23 | *Released date : April 19 2019* 24 | 25 | - Added config schema in environment build 26 | 27 | Changelog for 2.0.0 28 | ==================== 29 | *Released date : March 12 2019* 30 | 31 | - Updated to web launcher 32 | 33 | Changelog for 1.1.1 34 | ==================== 35 | *Released date : January 19 2019* 36 | 37 | - Fix tentacle installation issue 38 | 39 | Changelog for 1.1.0 40 | ==================== 41 | *Released date : January 19 2019* 42 | 43 | - Fix restart launcher issue 44 | - Checksum for releases 45 | - Support OctoBot package installation, update and start 46 | - Improve command line interface 47 | 48 | Changelog for 1.0.9-a 49 | ==================== 50 | *Released date : January 17 2019* 51 | 52 | - Small fixes 53 | 54 | Changelog for 1.0.9 55 | ==================== 56 | *Released date : January 17 2019* 57 | 58 | - Small fixes 59 | 60 | Changelog for 1.0.8 61 | ==================== 62 | *Released date : January 15 2019* 63 | 64 | # Features : 65 | - OctoBot package download and start 66 | 67 | Changelog for 1.0.7 68 | ==================== 69 | *Released date : January 11 2019* 70 | 71 | # Features : 72 | - Launcher latest version 73 | - MacOS support 74 | 75 | Changelog for 1.0.6 76 | ==================== 77 | *Released date : January 7 2019* 78 | 79 | # Bug fixes : 80 | - Fixed Windows startup issues 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | interfaces/ 107 | logs/ 108 | config/ 109 | .idea/ 110 | config.json 111 | tentacles/ 112 | OctoBot.exe 113 | vaderSentiment 114 | tests/static/*.data 115 | -------------------------------------------------------------------------------- /launcher/app/web_app.py: -------------------------------------------------------------------------------- 1 | # Drakkar-Software OctoBot-Launcher 2 | # Copyright (c) Drakkar-Software, All rights reserved. 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 3.0 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with this library. 16 | import logging 17 | import socket 18 | import threading 19 | 20 | from werkzeug.serving import make_server 21 | 22 | from launcher import DEFAULT_SERVER_IP, DEFAULT_SERVER_PORT, load_routes, server_instance 23 | 24 | 25 | class WebApp(threading.Thread): 26 | def __init__(self, web_ip=DEFAULT_SERVER_IP, web_port=DEFAULT_SERVER_PORT): 27 | super().__init__() 28 | self.server_ip = web_ip 29 | self.server_port = web_port 30 | 31 | self.srv = make_server(host=self.server_ip, 32 | port=self.server_port, 33 | threaded=True, 34 | app=server_instance) 35 | self.ctx = server_instance.app_context() 36 | self.ctx.push() 37 | 38 | logging.info(f"Interface successfully initialized and accessible at: {self.get_web_server_url()}") 39 | 40 | def get_web_server_url(self): 41 | return f"http://{socket.gethostbyname(socket.gethostname())}:{self.server_port}" 42 | 43 | def run(self): 44 | # load routes 45 | load_routes() 46 | 47 | try: 48 | self.srv.serve_forever() 49 | except (OSError, ValueError) as e: 50 | logging.error(f"Web server has stopped. ({e})") 51 | 52 | def prepare_stop(self): 53 | # self.srv.server_close() 54 | self.srv.socket.close() 55 | 56 | def stop(self): 57 | self.srv.shutdown() 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OctoBot-Launcher [2.0.4](https://github.com/Drakkar-Software/OctoBot-Launcher/tree/master/CHANGELOG.md) 2 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/b1b91943d078491d8c3c4ae39eb6d50f)](https://app.codacy.com/gh/Drakkar-Software/OctoBot-Launcher?utm_source=github.com&utm_medium=referral&utm_content=Drakkar-Software/OctoBot-Launcher&utm_campaign=Badge_Grade_Dashboard) 3 | [![PyPI](https://img.shields.io/pypi/v/OctoBot-Launcher.svg)](https://pypi.python.org/pypi/OctoBot-Launcher/) 4 | [![Downloads](https://pepy.tech/badge/octobot-launcher/month)](https://pepy.tech/project/octobot-launcher) 5 | [![Release](https://img.shields.io/github/downloads/Drakkar-Software/OctoBot-Launcher/total.svg)](https://github.com/Drakkar-Software/OctoBot-Launcher/releases) 6 | [![Build Status](https://api.travis-ci.org/Drakkar-Software/OctoBot-Launcher.svg?branch=master)](https://travis-ci.org/Drakkar-Software/OctoBot-Launcher) 7 | [![Build status](https://ci.appveyor.com/api/projects/status/bcb75xy9dch6c3nx?svg=true)](https://ci.appveyor.com/project/Herklos/octobot-Launcher) 8 | 9 | 10 | A launcher for your [OctoBot](https://github.com/Drakkar-Software/OctoBot) ! 11 | 12 | ![Demo](../assets/octobot_launcher.gif) 13 | 14 | ## Usage 15 | - Install OctoBot by pressing "Install/Update OctoBot" 16 | - To start OctoBot : "Start OctoBot" and wait for a new window and then click on "Open OctoBot" 17 | - Update launcher with "Update Launcher" 18 | 19 | ## Install 20 | 21 | ### [With binary](https://github.com/Drakkar-Software/OctoBot-Launcher/releases) 22 | **For 64 bits systems only** 23 | - Download [latest release](https://github.com/Drakkar-Software/OctoBot-Launcher/releases) 24 | - On Windows : double click on launcher_windows.exe 25 | - On Linux : type in a command line : 26 | ``` {.sourceCode .bash} 27 | $ chmox +x launcher_linux 28 | $ ./launcher_linux 29 | ``` 30 | - OSX version is released but not tested yet. It might work but we advise to use the python (Without binary) version for this OS. 31 | 32 | ### Without binary 33 | Install [Python 3.7](https://www.python.org/downloads/) and then type in a command line : 34 | ``` {.sourceCode .bash} 35 | $ python -m pip install OctoBot-Launcher 36 | $ octobot-launcher 37 | ``` 38 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Drakkar-Software OctoBot-Launcher 2 | # Copyright (c) Drakkar-Software, All rights reserved. 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 3.0 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with this library. 16 | 17 | from setuptools import setup, find_packages 18 | 19 | from launcher import PROJECT_NAME, VERSION 20 | 21 | PACKAGES = find_packages(exclude=["tests"]) 22 | 23 | # long description from README file 24 | with open('README.md', encoding='utf-8') as f: 25 | DESCRIPTION = f.read() 26 | 27 | REQUIRED = open('requirements.txt').read() 28 | REQUIRES_PYTHON = '>=3.7.2' 29 | 30 | setup( 31 | name=PROJECT_NAME, 32 | version=VERSION, 33 | url='https://github.com/Drakkar-Software/OctoBot-Launcher', 34 | license='LGPL-3.0', 35 | author='Drakkar-Software', 36 | author_email='drakkar-software@protonmail.com', 37 | description='OctoBot project launcher', 38 | py_modules=['entrypoint'], 39 | long_description=DESCRIPTION, 40 | install_requires=REQUIRED, 41 | tests_require=['pytest'], 42 | packages=PACKAGES, 43 | include_package_data=True, 44 | test_suite="tests", 45 | zip_safe=False, 46 | python_requires=REQUIRES_PYTHON, 47 | entry_points={ 48 | 'console_scripts': [ 49 | PROJECT_NAME + ' = entrypoint:main' 50 | ] 51 | }, 52 | classifiers=[ 53 | 'Development Status :: 5 - Production/Stable', 54 | 'Operating System :: OS Independent', 55 | 'Operating System :: MacOS :: MacOS X', 56 | 'Operating System :: Microsoft :: Windows', 57 | 'Operating System :: POSIX', 58 | 'Programming Language :: Python :: 3.7', 59 | ], 60 | ) 61 | -------------------------------------------------------------------------------- /launcher/tools/octobot_connector.py: -------------------------------------------------------------------------------- 1 | # Drakkar-Software OctoBot 2 | # Copyright (c) Drakkar-Software, All rights reserved. 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 3.0 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with this library. 16 | 17 | import os 18 | import json 19 | import socket 20 | import requests 21 | import time 22 | from launcher import OCTOBOT_DEFAULT_SERVER_PORT, CONFIG_CATEGORY_SERVICES, CONFIG_WEB, CONFIG_WEB_PORT, \ 23 | OCTOBOT_CONFIG_FILE, OCTOBOT_API_MAX_RETRIES 24 | 25 | 26 | class OctoBotConnector: 27 | 28 | def __init__(self): 29 | self.octobot_config = None 30 | self.port = OCTOBOT_DEFAULT_SERVER_PORT 31 | self.current_ip = socket.gethostbyname(socket.gethostname()) 32 | self._get_current_octobot_address() 33 | 34 | def get_current_version(self, allow_retry=False): 35 | max_retries = OCTOBOT_API_MAX_RETRIES if allow_retry else 1 36 | for i in range(max_retries): 37 | try: 38 | return json.loads(requests.get(f"{self.get_web_interface_url()}/api/version").text).split(" ")[1] 39 | except Exception as e: 40 | if i < max_retries-1: 41 | time.sleep(0.5) 42 | else: 43 | raise e 44 | 45 | def get_web_interface_url(self): 46 | return f"http://{self.current_ip}:{self.port}" 47 | 48 | def is_alive(self): 49 | try: 50 | self.get_current_version() 51 | return True 52 | except Exception: 53 | return False 54 | 55 | def _get_current_octobot_address(self): 56 | if os.path.isfile(OCTOBOT_CONFIG_FILE): 57 | try: 58 | with open(OCTOBOT_CONFIG_FILE) as json_data_file: 59 | self.octobot_config = json.load(json_data_file) 60 | if CONFIG_WEB in self.octobot_config[CONFIG_CATEGORY_SERVICES]: 61 | if CONFIG_WEB_PORT in self.octobot_config[CONFIG_CATEGORY_SERVICES][CONFIG_WEB_PORT]: 62 | self.port = self.octobot_config[CONFIG_CATEGORY_SERVICES][CONFIG_WEB_PORT] 63 | except Exception as e: 64 | print(f"Error when reading {OCTOBOT_CONFIG_FILE}: {e}") 65 | -------------------------------------------------------------------------------- /launcher/templates/bot_card.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |

6 | Current version : 7 | {% if bot_local_version == None %} 8 | NOT FOUND 9 | {% elif is_up_to_date %} 10 | {{ bot_local_version }} 11 | {% else %} 12 | {{ bot_local_version }} 13 | {% endif %} 14 |

15 |

16 | Latest version : {{ bot_server_version }} 17 |

18 |

19 | Bot status : 20 | {% if not bot_status %} 21 | stopped 22 | {% else %} 23 | running 24 | {% endif %} 25 |

26 |
27 |
28 | 29 | 30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 | 38 |
39 |
40 | Open OctoBot 41 |
42 |
43 |
44 | 47 |
48 |
49 | -------------------------------------------------------------------------------- /launcher/launcher_entry.py: -------------------------------------------------------------------------------- 1 | # Drakkar-Software OctoBot-Launcher 2 | # Copyright (c) Drakkar-Software, All rights reserved. 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 3.0 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with this library. 16 | 17 | import argparse 18 | import logging 19 | import sys 20 | import webbrowser 21 | 22 | from launcher import VERSION 23 | from launcher.tools import environment 24 | from launcher.tools.environment import ensure_minimum_environment 25 | 26 | 27 | def launcher(args=sys.argv[1:]): 28 | logging.basicConfig(level=logging.INFO) 29 | 30 | parser = argparse.ArgumentParser(description='OctoBot - Launcher') 31 | parser.add_argument('-e', '--export_logs', help="export Octobot's last logs", 32 | action='store_true') 33 | parser.add_argument('-v', '--version', help='show OctoBot Launcher current version', 34 | action='store_true') 35 | parser.add_argument('-u', '--update', help='update OctoBot with the latest version available', 36 | action='store_true') 37 | parser.add_argument('-s', '--start', help='Start OctoBot. OctoBot starting options can be added after ' 38 | '-s or --start. Examples: "-s no t" will start OctoBot ' 39 | 'with "ng" option and "t" that will use telegram interface, without gui.', 40 | nargs='*') 41 | parser.add_argument('-nw', '--no_web', help="Without web server", action='store_true') 42 | 43 | args = parser.parse_args(args) 44 | 45 | start_launcher(args) 46 | 47 | 48 | def start_launcher(args): 49 | if args.version: 50 | print(VERSION) 51 | else: 52 | ensure_minimum_environment() 53 | from launcher.app.launcher_app import LauncherApp 54 | if not args.no_web: 55 | try: 56 | app = LauncherApp() 57 | webbrowser.open(app.get_web_server_url()) 58 | except Exception as e: 59 | logging.error(f"Can't start gui, please try command line interface (use --help).\n{e}") 60 | 61 | if args.update: 62 | environment.install_bot(force_package=False) 63 | elif args.start: 64 | LauncherApp().start_bot_handler([f"-{arg}" for arg in args.start] if args.start else None) 65 | elif args.export_logs: 66 | pass 67 | else: 68 | pass 69 | -------------------------------------------------------------------------------- /launcher/static/js/commons.js: -------------------------------------------------------------------------------- 1 | function load_template(template_url){ 2 | const template_location = $("div[template='"+template_url+"']"); 3 | $.ajax({ 4 | url: template_url, 5 | beforeSend: function() { 6 | template_location.html("
Loading data
"); 7 | }, 8 | error: function() { 9 | template_location.html("
This service is temporary unavailable.
"); 10 | handle_route_button(); 11 | }, 12 | success: function(data) { 13 | template_location.html(data); 14 | handle_route_button(); 15 | }, 16 | complete: function() { 17 | const spinner = $(".fa-sync", template_location.parent()); 18 | if (spinner.hasClass("fa-spin")){ 19 | spinner.removeClass("fa-spin"); 20 | } 21 | } 22 | }); 23 | } 24 | 25 | function load_templates(){ 26 | $("div[template]").each(function () { 27 | load_template($(this).attr('template')); 28 | }); 29 | } 30 | 31 | 32 | function handle_route_button(){ 33 | $(".btn").click(function(){ 34 | const button = $(this); 35 | if (button[0].hasAttribute('route')){ 36 | const command = button.attr('route'); 37 | const origin_val = button.text(); 38 | let refreshIntervalId; 39 | $.ajax({ 40 | url: command, 41 | beforeSend: function() { 42 | button.html(""); 43 | refreshIntervalId = setInterval(function () { 44 | get_progress(button); 45 | }, 100); 46 | }, 47 | complete: function() { 48 | clearInterval(refreshIntervalId); 49 | button.html(origin_val); 50 | load_template(button.closest("div[template]").attr("template")); 51 | } 52 | }); 53 | } 54 | }); 55 | } 56 | 57 | function get_progress(element) { 58 | const body = element.closest(".card-body"); 59 | const progress = body.find(".progress"); 60 | const progress_bar = progress.find(".progress-bar"); 61 | 62 | // show progress if not 63 | if (progress.is(':hidden')){ 64 | progress.show(); 65 | } 66 | 67 | $.ajax({ 68 | url: "/progress", 69 | error: function() { 70 | console.log("An error occurred when updating progress."); 71 | }, 72 | success: function(data) { 73 | progress_bar.css("width", data + "%").attr("aria-valuenow", data); 74 | } 75 | }); 76 | } 77 | 78 | function animate_sync(){ 79 | $(".fa-sync").click(function(){ 80 | if (!$(this).hasClass("fa-spin")){ 81 | $(this).addClass("fa-spin"); 82 | } 83 | }); 84 | } 85 | 86 | $(document).ready(function () { 87 | load_templates(); 88 | animate_sync(); 89 | }); 90 | -------------------------------------------------------------------------------- /entrypoint.py: -------------------------------------------------------------------------------- 1 | # Drakkar-Software OctoBot-Launcher 2 | # Copyright (c) Drakkar-Software, All rights reserved. 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 3.0 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with this library. 16 | import importlib 17 | import logging 18 | import os 19 | import shutil 20 | import sys 21 | import zipfile 22 | from urllib.request import urlretrieve 23 | 24 | from past.translation import splitall 25 | 26 | LAUNCHER_PATH = "launcher" 27 | LAUNCHER_ENTRY_POINT = "entrypoint.py" 28 | RELEASE_PATH = "updates" 29 | LAUNCHER_SOURCE_EXT = "zip" 30 | sys.path.append(os.path.dirname(sys.executable)) 31 | 32 | 33 | def get_latest_source_url(): 34 | return "https://github.com/Drakkar-Software/OctoBot-Launcher/archive/master.zip" 35 | 36 | 37 | def get_latest_release_source_file(): 38 | return f"{LAUNCHER_PATH}.{LAUNCHER_SOURCE_EXT}" 39 | 40 | 41 | def download_latest_release_sources(): 42 | urlretrieve(get_latest_source_url(), get_latest_release_source_file()) 43 | 44 | 45 | def get_extraction_location(): 46 | source_file = zipfile.ZipFile(get_latest_release_source_file(), 'r') 47 | return os.path.commonprefix(source_file.namelist()) 48 | 49 | 50 | def extraction_filter(members): 51 | for info in members: 52 | file_path = splitall(info.filename) 53 | if len(file_path) > 1 and file_path[1] in [LAUNCHER_PATH, LAUNCHER_ENTRY_POINT]: 54 | yield info 55 | 56 | 57 | def extract_sources(): 58 | if os.path.isfile(get_latest_release_source_file()): 59 | with zipfile.ZipFile(get_latest_release_source_file()) as source_zip: 60 | source_zip.extractall(members=extraction_filter(source_zip.infolist()), path=RELEASE_PATH) 61 | 62 | 63 | def move_sources(): 64 | try: 65 | shutil.rmtree(LAUNCHER_PATH) 66 | except FileNotFoundError: 67 | pass 68 | 69 | try: 70 | shutil.move(os.path.join(RELEASE_PATH, get_extraction_location(), LAUNCHER_PATH), os.getcwd()) 71 | shutil.move(os.path.join(RELEASE_PATH, get_extraction_location(), LAUNCHER_ENTRY_POINT), os.getcwd()) 72 | except FileNotFoundError: 73 | logging.warning("Can't move updated sources.") 74 | 75 | 76 | def update_launcher(): 77 | download_latest_release_sources() 78 | extract_sources() 79 | move_sources() 80 | logging.info("Launcher updated") 81 | 82 | 83 | def main(): 84 | logging.getLogger().setLevel(logging.INFO) 85 | 86 | if not os.path.exists(LAUNCHER_PATH): 87 | update_launcher() 88 | logging.info("Launcher fresh install...") 89 | 90 | try: 91 | from launcher.launcher_entry import launcher 92 | except ImportError: 93 | importlib.import_module("launcher.app.launcher_app") 94 | 95 | try: 96 | launcher() 97 | except NameError as e: 98 | logging.error(f"Can't start launcher, please try to reinstall.\n{e}") 99 | 100 | 101 | if __name__ == '__main__': 102 | main() 103 | -------------------------------------------------------------------------------- /launcher/app/app_controller.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, render_template 2 | 3 | import launcher as launcher_module 4 | from entrypoint import update_launcher 5 | from launcher import server_instance, VERSION, launcher_instance, REDUCE_GITHUB_REQUESTS 6 | from launcher.tools.environment import update_tentacles 7 | from launcher.tools.github import GithubOctoBot, GithubLauncher 8 | from launcher.tools.version import OctoBotVersion 9 | from launcher.app.app_model import is_up_to_date 10 | from launcher.tools.octobot_connector import OctoBotConnector 11 | 12 | 13 | @server_instance.route("/") 14 | def home(): 15 | return render_template('index.html', 16 | launcher_version=VERSION) 17 | 18 | 19 | @server_instance.route("/launcher") 20 | def launcher(): 21 | server_version = None if REDUCE_GITHUB_REQUESTS else GithubLauncher().get_current_server_version() 22 | if not server_version: 23 | server_version = "0" 24 | 25 | return render_template('launcher_card.html', 26 | launcher_local_version=VERSION, 27 | launcher_server_version=server_version, 28 | is_up_to_date=is_up_to_date(VERSION, server_version)) 29 | 30 | 31 | @server_instance.route("/bot") 32 | def bot(): 33 | bot_connector = OctoBotConnector() 34 | is_bot_alive = launcher_instance.is_bot_alive() or bot_connector.is_alive() 35 | if is_bot_alive: 36 | local_version = bot_connector.get_current_version(allow_retry=True) 37 | bot_url = bot_connector.get_web_interface_url() 38 | else: 39 | local_version = OctoBotVersion().get_current_version() 40 | bot_url = None 41 | if not local_version: 42 | local_version = "0" 43 | 44 | server_version = None if REDUCE_GITHUB_REQUESTS else GithubOctoBot().get_current_server_version() 45 | if not server_version: 46 | server_version = "0" 47 | return render_template('bot_card.html', 48 | bot_local_version=local_version, 49 | bot_server_version=server_version, 50 | bot_status=is_bot_alive, 51 | bot_url=bot_url, 52 | is_up_to_date=is_up_to_date(local_version, server_version)) 53 | 54 | 55 | @server_instance.route("/news") 56 | def news(): 57 | return render_template('news_card.html') 58 | 59 | 60 | @server_instance.route("/update") 61 | def update(): 62 | update_launcher() 63 | return jsonify('ok') 64 | 65 | 66 | @server_instance.route("/install") 67 | def install(): 68 | launcher_instance.update_bot(launcher_module.bot_instance) 69 | return jsonify('ok') 70 | 71 | 72 | @server_instance.route("/tentacles") 73 | def tentacles(): 74 | update_tentacles(OctoBotVersion().get_local_binary()) 75 | return jsonify('ok') 76 | 77 | 78 | @server_instance.route("/stop") 79 | def stop(): 80 | launcher_instance.stop_bot() 81 | return jsonify('ok') 82 | 83 | 84 | @server_instance.route("/start") 85 | def start(): 86 | launcher_instance.start_bot_handler() 87 | return jsonify('ok') 88 | 89 | 90 | @server_instance.route("/restart") 91 | def restart(): 92 | launcher_instance.restart_launcher() 93 | return jsonify('ok') 94 | 95 | 96 | @server_instance.route("/stop_launcher") 97 | def stop_launcher(): 98 | launcher_instance.stop_launcher() 99 | return jsonify('ok') 100 | 101 | 102 | @server_instance.route("/progress") 103 | def progess(): 104 | return jsonify(launcher_module.processing) 105 | -------------------------------------------------------------------------------- /launcher/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OctoBot-Launcher 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {% block additional_meta %} 14 | {% endblock additional_meta %} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | {% block additional_style %} 33 | {% endblock additional_style %} 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 62 | 63 |
64 | {% block body %}{% endblock %} 65 |
66 | 67 | {% include "footer.html" %} 68 | 69 | {% block additional_scripts %} 70 | {% endblock additional_scripts %} 71 | 72 | 73 | -------------------------------------------------------------------------------- /launcher/__init__.py: -------------------------------------------------------------------------------- 1 | # Drakkar-Software OctoBot-Launcher 2 | # Copyright (c) Drakkar-Software, All rights reserved. 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 3.0 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with this library. 16 | from enum import Enum 17 | 18 | import flask 19 | 20 | PROJECT_NAME = "OctoBot-Launcher" 21 | OCTOBOT_BINARY = "OctoBot-Binary" 22 | OCTOBOT_NAME = "OctoBot" 23 | SHORT_VERSION = "2.0.4" 24 | PATCH_VERSION = "" # patch : pX 25 | VERSION_DEV_PHASE = "" # alpha : a / beta : b / release candidate : rc 26 | VERSION_PHASE = "" # XX 27 | VERSION = f"{SHORT_VERSION}{VERSION_DEV_PHASE}{VERSION_PHASE}" 28 | LONG_VERSION = f"{SHORT_VERSION}{PATCH_VERSION}{VERSION_DEV_PHASE}{VERSION_PHASE}" 29 | 30 | FORCE_BINARY = True 31 | REDUCE_GITHUB_REQUESTS = False 32 | 33 | OCTOBOT_REFERENCE_BRANCH = "master" 34 | OCTOBOT_LAUNCHER_VERSION_BRANCH = "master" 35 | OCTOBOT_DEV_PHASE = "beta" 36 | 37 | DEFAULT_CONFIG_FILE = "config/default_config.json" 38 | LOGGING_CONFIG_FILE = "config/logging_config.ini" 39 | STRATEGY_OPTIMIZER_DATA_FOLDER = "tests/static" 40 | 41 | GITHUB_RAW_CONTENT_URL = "https://raw.githubusercontent.com" 42 | GITHUB_API_CONTENT_URL = "https://api.github.com" 43 | GITHUB_BASE_URL = "https://github.com" 44 | GITHUB_ORGANISATION = "Drakkar-Software" 45 | OCTOBOT_GITHUB_REPOSITORY = f"{GITHUB_ORGANISATION}/{OCTOBOT_NAME}" 46 | OCTOBOT_BINARY_GITHUB_REPOSITORY = f"{GITHUB_ORGANISATION}/{OCTOBOT_BINARY}" 47 | LAUNCHER_GITHUB_REPOSITORY = f"{GITHUB_ORGANISATION}/{PROJECT_NAME}" 48 | GITHUB_URL = f"{GITHUB_BASE_URL}/{OCTOBOT_GITHUB_REPOSITORY}" 49 | 50 | LAUNCHER_PATH = "launcher" 51 | 52 | CONFIG_FILE = "user/config.json" 53 | CONFIG_FILE_SCHEMA_NAME = "config_schema.json" 54 | 55 | REPOSITORY_BRANCH = "master" 56 | 57 | TENTACLES_PATH = "tentacles" 58 | CONFIG_FILE_SCHEMA_WITH_PATH = f"config/{CONFIG_FILE_SCHEMA_NAME}" 59 | CONFIG_DEFAULT_EVALUATOR_FILE = "config/default_evaluator_config.json" 60 | CONFIG_DEFAULT_TRADING_FILE = "config/default_trading_config.json" 61 | 62 | CONFIG_INTERFACES = "interfaces" 63 | CONFIG_INTERFACES_WEB = "web" 64 | 65 | OCTOBOT_CONFIG_FILE = "config.json" 66 | CONFIG_CATEGORY_SERVICES = "services" 67 | CONFIG_WEB = "web" 68 | CONFIG_WEB_PORT = "port" 69 | 70 | OCTOBOT_BACKGROUND_IMAGE = "static/img/octobot.png" 71 | OCTOBOT_ICON = "static/favicon.ico" 72 | 73 | DEFAULT_SERVER_IP = '0.0.0.0' 74 | DEFAULT_SERVER_PORT = 5010 # prevent conflicts with OctoBot web interface 75 | 76 | OCTOBOT_DEFAULT_SERVER_PORT = 5001 77 | OCTOBOT_API_MAX_RETRIES = 20 78 | 79 | server_instance = flask.Flask(__name__) 80 | launcher_instance = None 81 | bot_instance = None 82 | processing = 0 83 | 84 | WINDOWS_OS_NAME = "nt" 85 | MAC_OS_NAME = "mac" 86 | LINUX_OS_NAME = "posix" 87 | 88 | 89 | class DeliveryPlatformsName(Enum): 90 | WINDOWS = "windows" 91 | LINUX = "linux" 92 | MAC = "osx" 93 | 94 | 95 | def get_launcher_instance(): 96 | global launcher_instance 97 | return launcher_instance 98 | 99 | 100 | def inc_progress(inc_size, to_min=False, to_max=False): 101 | global processing 102 | if to_max: 103 | processing = 100 104 | elif to_min: 105 | processing = 0 106 | else: 107 | processing += inc_size 108 | 109 | 110 | def load_routes(): 111 | from launcher.app import app_controller 112 | -------------------------------------------------------------------------------- /launcher/app/launcher_app.py: -------------------------------------------------------------------------------- 1 | # Drakkar-Software OctoBot-Launcher 2 | # Copyright (c) Drakkar-Software, All rights reserved. 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 3.0 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with this library. 16 | 17 | import os 18 | import signal 19 | import subprocess 20 | import sys 21 | from threading import Thread 22 | 23 | import launcher 24 | from entrypoint import update_launcher 25 | from launcher.tools import environment, executor 26 | from launcher.app.web_app import WebApp 27 | from launcher.tools.version import OctoBotVersion 28 | 29 | 30 | class LauncherApp(WebApp): 31 | def __init__(self): 32 | launcher.launcher_instance = self 33 | 34 | super().__init__() 35 | self.start_app() 36 | 37 | def update_bot_handler(self): 38 | thread = Thread(target=self.update_bot, args=(self,)) 39 | thread.start() 40 | 41 | def update_package_handler(self): 42 | thread = Thread(target=self.update_bot, args=(self,)) 43 | thread.start() 44 | 45 | @staticmethod 46 | def launcher_start_args(): 47 | # prevent binary to add self as first argument 48 | return sys.argv[0] if sys.argv[0].endswith(".py") else "" 49 | 50 | def update_launcher_handler(self): 51 | launcher_process = subprocess.Popen( 52 | [sys.executable, self.launcher_start_args(), "--update_launcher"] 53 | if self.launcher_start_args() else [sys.executable, "--update_launcher"] 54 | ) 55 | 56 | if launcher_process: 57 | launcher_process.wait() 58 | 59 | self.restart_launcher() 60 | 61 | def restart_launcher(self): 62 | self.prepare_stop() 63 | new_launcher_process = subprocess.Popen([sys.executable, self.launcher_start_args()] 64 | if self.launcher_start_args() else [sys.executable]) 65 | 66 | if new_launcher_process: 67 | new_launcher_process.wait() 68 | self.stop() 69 | 70 | def stop_launcher(self): 71 | self.prepare_stop() 72 | self.stop() 73 | 74 | @staticmethod 75 | def start_bot_handler(args=None): 76 | launcher.bot_instance = executor.execute_command_on_detached_binary(OctoBotVersion().get_local_binary(), 77 | commands=args) 78 | # if launcher.bot_instance: 79 | # launcher.bot_instance.wait() 80 | 81 | @staticmethod 82 | def is_bot_alive(): 83 | return launcher.bot_instance is not None and launcher.bot_instance.poll() is None 84 | 85 | @staticmethod 86 | def stop_bot(): 87 | if launcher.bot_instance: 88 | os.kill(launcher.bot_instance.pid, signal.SIGINT) 89 | launcher.bot_instance.terminate() 90 | launcher.bot_instance = None 91 | 92 | @staticmethod 93 | def update_bot(force_package=False): 94 | environment.install_bot(force_package=force_package) 95 | 96 | @staticmethod 97 | def update_launcher(): 98 | update_launcher() 99 | 100 | def export_logs(self): 101 | # TODO 102 | pass 103 | 104 | @staticmethod 105 | def close(): 106 | os._exit(0) 107 | 108 | def start_app(self): 109 | self.start() 110 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | notifications: 2 | email: false 3 | sudo: enabled 4 | os: linux 5 | cache: pip 6 | python: 3.7-dev 7 | 8 | matrix: 9 | include: 10 | - os: osx 11 | osx_image: xcode8.3 12 | language: generic 13 | before_cache: 14 | - brew cleanup 15 | cache: 16 | directories: 17 | - $HOME/Library/Caches/Homebrew 18 | before_install: 19 | - brew update || travis_terminate 1; 20 | - brew upgrade python || brew install python || travis_terminate 1; 21 | - python3 -m venv venv || travis_terminate 1; 22 | - source venv/bin/activate; 23 | - os: linux 24 | language: python 25 | 26 | install: 27 | - python3 -m pip install -r requirements.txt -r dev_requirements.txt 28 | 29 | script: 30 | - pytest 31 | 32 | before_deploy: 33 | - cp -r launcher launcher_save 34 | - rm -rf launcher/static/* launcher/templates/* launcher/app launcher/tools launcher/*.py 35 | - pyinstaller bin/launcher.spec 36 | - mv dist/launcher_app ./launcher_app_$TRAVIS_OS_NAME && rm -rf dist/ 37 | - rm -rf launcher 38 | - mv launcher_save launcher 39 | - RELEASE_SHASUM=$(openssl sha1 ./launcher_app_$TRAVIS_OS_NAME) 40 | - if [[ $TRAVIS_OS_NAME == 'linux' ]]; then python3 setup.py sdist bdist_wheel && python3 -m twine check dist/*; fi 41 | 42 | deploy: 43 | - provider: releases 44 | api_key: 45 | secure: cQaezwO+v0d3NJu2oY0+7e3JngZkSju+dXTUimIyTfZGDUaS7yCeGW5Q0Zb+LolEXYuMWdcv0VaIKZjSL5Yb4ROaTk9qg5g1jnHk8ntzxlemKAk/ahmsIe+MSx14g1PAGYTJBGftWTAqzZ/ZWMBQqSgL2zhxI5gLprnaDfRA4z1mZXNCGo8MASTI9mf+jrIt9rQsJ2pjldTTP7G5ofJfqqg3JLafcWQl6vg5HwaisFRlrtfyWurZIhA8y95Qrin51Kyj2jlxABVqA0mgC6cf2SpvYlID2sG/JzH2SWWQ1JO4wt832aACQAr3trDr/+Ko5ivJJXYqetDCvgTwd4V+4CmO5YyCcPZAOmLJVnY1I3svhWXR/XQqqK4hfMN42KC2NlHVCXvD/gYpbbJKwcAeGQI28tDjV9yZe8ZEu095pFb8AUPJVd40vNTAcYgScfIWneJ7NR0I/LK/itUmjeIOU6k+f1X94yKgNlZeAjAtBc/45Am/He1uCMVI4RD8yqlLBUqh9ofOvdzkNBOmkJ4xieJVsyrR/+QjNnOlFgt+ZDAKLhOr3RLqzy4FS7C+RrJIb0HZfSjnruj5HOdLEOubDdf3s2sbhWyl/DVJ/m1D/kLSitgql9ME3RYmTHadNRSw0u3jF5nATuin6iNZdG7VroiIacsIUlzbkx2UO/kGY1g= 46 | file: ./launcher_app_$TRAVIS_OS_NAME 47 | skip_cleanup: true 48 | name: Release - $TRAVIS_TAG 49 | body: $RELEASE_SHASUM 50 | on: 51 | repo: Drakkar-Software/OctoBot-Launcher 52 | branch: master 53 | tags: true 54 | - provider: pypi 55 | user: 56 | secure: gCnxIfOj9GYRu3rAmoaE73VeELUzINK1ZseAYo4RY2lzJ+QHNiXcaQuHrj/CyyaTcrKOeHzvCtA5J5e3WQqU29ul3JaJXSIdWSamxyx9H3YdoxFs9PjXQhDx3pE/3DLDasvzDRZNCux6oZ31WzL7qdaZr1eYNrg75uIRusLYvLu4hwG41w0OZ5YjnehK98Lx5YkiXYq+Gw5FChLrliN3NYI9HoM13XWQcdghatgg+jzXW3Yp2svL7n4kw54WCZ+et50dC74+mDEG5VGK6/LcQzVipTIppdcK3qpHBhL65ZIxC0IUSJkD/Vrb4s53nF7RLm/XiLrpHdWGQ20KGMIB0b8VTOUnuCEcayW7Q6ieU405r7h+hvr+Qn37v+EI7+aUfWTnz73ZWYJ3o3F94hNYQfaNBEY98NEWscIx4esPvjYsMxXIb/LvxjMQAZP1SWwiQ+DFgxTZNUXgk7cpaJkTzgBim6t5mwXnoQLRz/kC+B7CYg3wPxDJ8kwB7hJQzbkgul1ca83qBZHOSnpkESfkyjpVim1NZQkQH1D3T5Izp7i88epScV+0cBOtqOPiL9bPDZ4wve3/D1v2IsqqMAyiMTZF/QFa8hFvXL09QKKxoufl8/vuP7wUj1ebu0o+7qPhXS6gI6us4KUYYQdNMqZg/xHzSIiRAtFIlG8T+WpDcu8= 57 | password: 58 | secure: LDxZV2mJ6BB5bAt2XSf9AjWgUEJValfNHeiGZuH4WNsxgCQqOIRDhP1om/q8zL47c6iYHvV9N5h9xC0Nz9mIKVJxL8HWReJ8i4jYaE6dn+FKGNWZED5KJa0fTTsQUVdRhekU92fV8JMSgFKf8JI4kEofb4NNpgD3oC8zS16oG0CC4TZ90KWgunU5HJrV2m4tSw86wLJDehpPjnucan217oTuBTZxG8S+cNF7qVc0oSbJFOTW1jtGwFc6T9bLMF5CELRoChcpY3BhRGhu21LjCLwFc/BY0wNfTzlrY1sNlM1KorRvikIYWL8F0wHNCewztBORUIGgcYgvXj2I/Endrti4CdfcySHTMqn2uffv99gFinIftMy+F1E37cM7vRUY08na2jqH/3k2rrE/vKeLep1Lzk4LkaBG72Co0t509sORAFzQoIrVdnJPf5ZidJzEaivwi3ZG1/GkmXC5ERH5CBwWihHF4itdi0deSsmWKbHthJCEw3emqEhrbDAwwfdIV2MuwGmxvJ5oxjZBBSzpblO7b0LdHhoEqdeqbBs5BHVRwjmsNia86dxvzZH6lj7ylllAl6Z7XENynOUNUqgLhbQeDioR3EWLUTvXnGf3+gPieSPmD4F+YYfLzwIjRMWmu0yLX9N57DrkM/fAlq3l8AVnjX9TZFk7iVyuNkUfmYQ= 59 | skip_existing: true 60 | skip_cleanup: true 61 | distributions: "sdist bdist_wheel" 62 | on: 63 | repo: Drakkar-Software/OctoBot-Launcher 64 | branch: master 65 | tags: true 66 | condition: $TRAVIS_OS_NAME = 'linux' 67 | -------------------------------------------------------------------------------- /launcher/static/css/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Drakkar-Software OctoBot 3 | * Copyright (c) Drakkar-Software, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. 17 | */ 18 | 19 | html, body{ 20 | height: 100%; 21 | min-height: 100%; 22 | } 23 | 24 | body:before { 25 | content: ""; 26 | position: fixed; 27 | left: 0; 28 | right: 0; 29 | z-index: -1; 30 | 31 | background-image: url('../img/svg/octobot.svg'); 32 | 33 | background-repeat: no-repeat; 34 | background-size: cover; 35 | width: 100%; 36 | height: 100%; 37 | opacity : 0.2; 38 | 39 | } 40 | 41 | /* Cards */ 42 | .card-deck { 43 | display: flex; 44 | justify-content: flex-start; 45 | flex-flow: row wrap; 46 | align-items: stretch; 47 | } 48 | .card { 49 | display: block; 50 | flex-basis: 33.3%; 51 | .rounded-bottom !important; 52 | } 53 | .medium-size { 54 | max-width: 18rem; 55 | min-width: 12rem; 56 | } 57 | .small-size { 58 | max-width: 18rem; 59 | min-width: 12rem; 60 | max-height: 18rem; 61 | min-height: 14rem; 62 | } 63 | .very-small-size{ 64 | max-width: 3rem; 65 | min-width: 2rem; 66 | max-height: 3rem; 67 | min-height: 2rem; 68 | } 69 | 70 | /* elegant-color white-text */ 71 | 72 | /* Theme */ 73 | /* Elegant */ 74 | .card, 75 | .card-body, 76 | .card-text, 77 | .select2-selection__choice 78 | { 79 | background-color: #3E4551 !important; 80 | font-size: 16px !important; 81 | } 82 | 83 | .card, 84 | .card-body, 85 | .card-text, 86 | .select2 li span, 87 | .card-text{ 88 | color: white !important; 89 | } 90 | 91 | .card .card, 92 | .card .card .card-body, 93 | .card .card .card-text, 94 | .card .card-deck .card, 95 | .card .card-deck .card .card-body, 96 | .card .card-deck .card .card-text{ 97 | background-color: #4B515D !important; 98 | } 99 | 100 | .card .deck-container-modified { 101 | background-color: #FF8800 !important; 102 | -webkit-transition: all 0.5s ease; 103 | -moz-transition: all 0.5s ease; 104 | -o-transition: all 0.5s ease; 105 | transition: all 0.5s ease; 106 | } 107 | 108 | .card .card-modified, 109 | .card .card-modified .card-body, 110 | .card .card-modified .card-text, 111 | .card .card-deck .card-modified, 112 | .card .card-deck .card-modified .card-body, 113 | .card .card-deck .card-modified .card-text{ 114 | background-color: #FF8800 !important; 115 | -webkit-transition: all 0.5s ease; 116 | -moz-transition: all 0.5s ease; 117 | -o-transition: all 0.5s ease; 118 | transition: all 0.5s ease; 119 | } 120 | 121 | .card .card-long, 122 | .card .card-long .card-body, 123 | .card .card-long .card-text, 124 | .card .card-deck .card-long, 125 | .card .card-deck .card-long .card-body, 126 | .card .card-deck .card-long .card-text{ 127 | background-color: seagreen !important; 128 | -webkit-transition: all 0.5s ease; 129 | -moz-transition: all 0.5s ease; 130 | -o-transition: all 0.5s ease; 131 | transition: all 0.5s ease; 132 | } 133 | 134 | .card .card-short, 135 | .card .card-short .card-body, 136 | .card .card-short .card-text, 137 | .card .card-deck .card-short, 138 | .card .card-deck .card-short .card-body, 139 | .card .card-deck .card-short .card-text{ 140 | background-color: darkred !important; 141 | -webkit-transition: all 0.5s ease; 142 | -moz-transition: all 0.5s ease; 143 | -o-transition: all 0.5s ease; 144 | transition: all 0.5s ease; 145 | } 146 | 147 | .card a:link { 148 | color: #33b5e5; 149 | } 150 | 151 | .card a:hover { 152 | color: #42a5f5 !important; 153 | } 154 | 155 | .modal-text { 156 | color: #4B515D; 157 | } 158 | -------------------------------------------------------------------------------- /launcher/tools/version.py: -------------------------------------------------------------------------------- 1 | # Drakkar-Software OctoBot-Launcher 2 | # Copyright (c) Drakkar-Software, All rights reserved. 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 3.0 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with this library. 16 | import glob 17 | import logging 18 | import os 19 | import subprocess 20 | import sys 21 | from distutils.version import LooseVersion 22 | from subprocess import PIPE 23 | 24 | import pkg_resources 25 | 26 | from launcher import LINUX_OS_NAME, FORCE_BINARY, \ 27 | WINDOWS_OS_NAME, MAC_OS_NAME, PROJECT_NAME, OCTOBOT_NAME, launcher_instance, inc_progress 28 | from launcher.tools import executor, BINARY_DOWNLOAD_PROGRESS_SIZE 29 | 30 | 31 | class Version: 32 | PROJECT = "" 33 | NOT_INSTALLED_VERSION = "not installed" 34 | 35 | def is_package_installed(self): 36 | try: 37 | pkg_resources.get_distribution(self.PROJECT) 38 | return True 39 | except (pkg_resources.DistributionNotFound, pkg_resources.RequirementParseError): 40 | return False 41 | 42 | def download_package(self): 43 | try: 44 | cmd = [sys.executable, "-m", "pip", "install", "-U", self.PROJECT] 45 | return subprocess.Popen(cmd, stdout=PIPE).stdout.read().rstrip().decode() 46 | except PermissionError as e: 47 | logging.error(f"Failed to update package : {e}") 48 | except FileNotFoundError as e: 49 | logging.error(f"Can't find a valid python executable : {e}") 50 | 51 | def get_local_binary(self, force_binary=False): 52 | binary = None 53 | 54 | if not force_binary and self.is_package_installed(): 55 | binary = self.PROJECT 56 | else: 57 | try: 58 | # try to found in current folder binary 59 | if os.name == LINUX_OS_NAME: 60 | binary = f"./{next(iter(glob.glob(f'{self.PROJECT}*')))}" 61 | 62 | elif os.name == WINDOWS_OS_NAME: 63 | binary = next(iter(glob.glob(f'{self.PROJECT}*.exe'))) 64 | 65 | elif os.name == MAC_OS_NAME: 66 | binary = f"./{next(iter(glob.glob(f'{self.PROJECT}*')))}" 67 | except StopIteration: 68 | binary = None 69 | 70 | return binary 71 | 72 | @staticmethod 73 | def is_binary_available(binary_path): 74 | if binary_path is None or not os.path.isfile(binary_path): 75 | return False 76 | return True 77 | 78 | def get_current_version(self, binary_path=None, force_binary=FORCE_BINARY): 79 | if not binary_path: 80 | binary_path = self.get_local_binary(force_binary=force_binary) 81 | if not self.is_binary_available(binary_path): 82 | return self.NOT_INSTALLED_VERSION 83 | return executor.execute_command_on_current_binary(binary_path, ["--version"]).split("\r\n")[0] 84 | 85 | def get_local_version_or_download(self, github_instance, binary_path, 86 | latest_release_data=None, force_binary=FORCE_BINARY): 87 | last_release_version = github_instance.get_current_server_version(latest_release_data=latest_release_data) 88 | current_version = self.get_current_version(binary_path, force_binary=force_binary) 89 | 90 | try: 91 | if current_version == self.NOT_INSTALLED_VERSION: 92 | check_new_version = True 93 | else: 94 | check_new_version = LooseVersion(current_version) < LooseVersion(last_release_version) 95 | except AttributeError: 96 | check_new_version = False 97 | 98 | if check_new_version: 99 | logging.info(f"Upgrading {self.PROJECT} : from {current_version} to {last_release_version}...") 100 | return github_instance.download_binary(replace=True) 101 | else: 102 | logging.info(f"Nothing to do : {self.PROJECT} is up to date") 103 | if launcher_instance: 104 | inc_progress(BINARY_DOWNLOAD_PROGRESS_SIZE) 105 | return binary_path 106 | 107 | 108 | class OctoBotVersion(Version): 109 | PROJECT = OCTOBOT_NAME 110 | 111 | 112 | class LauncherVersion(Version): 113 | PROJECT = PROJECT_NAME 114 | -------------------------------------------------------------------------------- /launcher/tools/github.py: -------------------------------------------------------------------------------- 1 | # Drakkar-Software OctoBot-Launcher 2 | # Copyright (c) Drakkar-Software, All rights reserved. 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 3.0 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with this library. 16 | import json 17 | import logging 18 | import os 19 | 20 | import requests 21 | 22 | from launcher import GITHUB_API_CONTENT_URL, LAUNCHER_GITHUB_REPOSITORY, \ 23 | OCTOBOT_BINARY_GITHUB_REPOSITORY, WINDOWS_OS_NAME, DeliveryPlatformsName, LINUX_OS_NAME, OCTOBOT_NAME, PROJECT_NAME, \ 24 | inc_progress, OCTOBOT_DEV_PHASE 25 | from launcher.tools import BINARY_DOWNLOAD_PROGRESS_SIZE 26 | 27 | 28 | class Github: 29 | PROJECT_REPOSITORY = "" 30 | PROJECT = "" 31 | 32 | def get_latest_release_url(self): 33 | return f"{GITHUB_API_CONTENT_URL}/repos/{self.PROJECT_REPOSITORY}/releases/latest" 34 | 35 | def get_latest_release_data(self): 36 | return json.loads(requests.get(self.get_latest_release_url()).text) 37 | 38 | def get_asset_from_release_data(self): 39 | latest_release_data = self.get_latest_release_data() 40 | os_name = None 41 | 42 | # windows 43 | if os.name == WINDOWS_OS_NAME: 44 | os_name = DeliveryPlatformsName.WINDOWS 45 | 46 | # linux 47 | if os.name == LINUX_OS_NAME: 48 | os_name = DeliveryPlatformsName.LINUX 49 | 50 | # mac 51 | if os.name == 'mac': 52 | os_name = DeliveryPlatformsName.MAC 53 | 54 | # search for corresponding release 55 | try: 56 | for asset in latest_release_data["assets"]: 57 | asset_name, _ = os.path.splitext(asset["name"]) 58 | if f"{self.PROJECT}_{os_name.value}" in asset_name: 59 | return asset 60 | except KeyError as e: 61 | logging.error(f"Can't find any asset : {e}") 62 | return None 63 | 64 | def get_current_server_version(self, latest_release_data=None): 65 | if not latest_release_data: 66 | latest_release_data = self.get_latest_release_data() 67 | try: 68 | tag_name = latest_release_data["tag_name"] 69 | dev_phase_tag = f"-{OCTOBOT_DEV_PHASE}" 70 | if dev_phase_tag in tag_name: 71 | return tag_name.split(dev_phase_tag)[0] 72 | else: 73 | return tag_name 74 | except KeyError: 75 | return None 76 | 77 | def download_binary(self, replace=False): 78 | binary = self.get_asset_from_release_data() 79 | 80 | if binary: 81 | final_size = binary["size"] 82 | increment = (BINARY_DOWNLOAD_PROGRESS_SIZE / (final_size / 1024)) 83 | 84 | r = requests.get(binary["browser_download_url"], stream=True) 85 | 86 | binary_name, binary_ext = os.path.splitext(binary["name"]) 87 | path = f"{self.PROJECT}{binary_ext}" 88 | 89 | if r.status_code == 200: 90 | 91 | if replace and os.path.isfile(path): 92 | try: 93 | os.remove(path) 94 | except OSError as e: 95 | logging.error(f"Can't remove old version binary : {e}") 96 | 97 | with open(path, 'wb') as f: 98 | for chunk in r.iter_content(1024): 99 | f.write(chunk) 100 | inc_progress(increment) 101 | 102 | return path 103 | else: 104 | logging.error("Release not found on server") 105 | return None 106 | 107 | def update_binary(self, version_instance, force_package=False, force_binary=False): 108 | # parse latest release 109 | try: 110 | logging.info(f"{self.PROJECT} is checking for updates...") 111 | latest_release_data = self.get_latest_release_data() 112 | 113 | # try to find binary / package 114 | binary_path = version_instance.get_local_binary() 115 | 116 | if (version_instance.is_package_installed() and not force_binary) or force_package: 117 | version_instance.download_package() 118 | return binary_path 119 | else: 120 | # try to find in current folder binary 121 | # if current octobot binary found 122 | if binary_path: 123 | logging.info(f"{self.PROJECT} installation found, analyzing...") 124 | return version_instance.get_local_version_or_download(self, binary_path, latest_release_data) 125 | else: 126 | return self.download_binary(latest_release_data) 127 | except Exception as e: 128 | logging.exception(f"Failed to download latest release data : {e}") 129 | 130 | 131 | class GithubOctoBot(Github): 132 | PROJECT_REPOSITORY = OCTOBOT_BINARY_GITHUB_REPOSITORY 133 | PROJECT = OCTOBOT_NAME 134 | 135 | 136 | class GithubLauncher(Github): 137 | PROJECT_REPOSITORY = LAUNCHER_GITHUB_REPOSITORY 138 | PROJECT = PROJECT_NAME 139 | -------------------------------------------------------------------------------- /launcher/tools/environment.py: -------------------------------------------------------------------------------- 1 | # Drakkar-Software OctoBot-Launcher 2 | # Copyright (c) Drakkar-Software, All rights reserved. 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Lesser General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 3.0 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public 15 | # License along with this library. 16 | import logging 17 | import os 18 | import subprocess 19 | 20 | import urllib.request 21 | 22 | from launcher import CONFIG_FILE, OCTOBOT_GITHUB_REPOSITORY, \ 23 | GITHUB_RAW_CONTENT_URL, OCTOBOT_REFERENCE_BRANCH, DEFAULT_CONFIG_FILE, LOGGING_CONFIG_FILE, \ 24 | CONFIG_DEFAULT_EVALUATOR_FILE, CONFIG_DEFAULT_TRADING_FILE, OCTOBOT_NAME, LINUX_OS_NAME, MAC_OS_NAME, \ 25 | TENTACLES_PATH, inc_progress, FORCE_BINARY, CONFIG_FILE_SCHEMA_WITH_PATH, STRATEGY_OPTIMIZER_DATA_FOLDER 26 | from launcher.tools import executor 27 | from launcher.tools.github import GithubOctoBot 28 | from launcher.tools.version import OctoBotVersion 29 | 30 | FOLDERS_TO_CREATE = ["logs", "backtesting/collector/data", STRATEGY_OPTIMIZER_DATA_FOLDER] 31 | STRATEGY_OPTIMIZER_DATA = [ 32 | "binance_ADA_BTC_20180722_223335.data", 33 | "binance_BTC_USDT_20180428_121156.data", 34 | "binance_ETH_USDT_20180716_131148.data", 35 | "binance_ICX_BTC_20180716_131148.data", 36 | "binance_NEO_BTC_20180716_131148.data", 37 | "binance_ONT_BTC_20180722_230900.data", 38 | "binance_POWR_BTC_20180722_234855.data", 39 | "binance_VEN_BTC_20180716_131148.data", 40 | "binance_XLM_BTC_20180722_234305.data", 41 | "binance_XRB_BTC_20180716_131148.data", 42 | "bittrex_ADA_BTC_20180722_223357.data", 43 | "bittrex_ETC_BTC_20180726_210341.data", 44 | "bittrex_NEO_BTC_20180722_195942.data", 45 | "bittrex_WAX_BTC_20180726_205032.data", 46 | "bittrex_XRP_BTC_20180726_210927.data", 47 | "bittrex_XVG_BTC_20180726_211225.data" 48 | ] 49 | 50 | INSTALL_DOWNLOAD = [ 51 | ( 52 | f"{GITHUB_RAW_CONTENT_URL}/cjhutto/vaderSentiment/master/vaderSentiment/emoji_utf8_lexicon.txt", 53 | "vaderSentiment/emoji_utf8_lexicon.txt"), 54 | ( 55 | f"{GITHUB_RAW_CONTENT_URL}/cjhutto/vaderSentiment/master/vaderSentiment/vader_lexicon.txt", 56 | "vaderSentiment/vader_lexicon.txt"), 57 | ] 58 | 59 | OCTOBOT_REPOSITORY_FILES_ROOT = f"{GITHUB_RAW_CONTENT_URL}/{OCTOBOT_GITHUB_REPOSITORY}/{OCTOBOT_REFERENCE_BRANCH}" 60 | 61 | INSTALL_DOWNLOAD += [(f"{OCTOBOT_REPOSITORY_FILES_ROOT}/{STRATEGY_OPTIMIZER_DATA_FOLDER}/{data_file}", 62 | f"{STRATEGY_OPTIMIZER_DATA_FOLDER}/{data_file}") 63 | for data_file in STRATEGY_OPTIMIZER_DATA] 64 | 65 | FILES_TO_DOWNLOAD = [ 66 | ( 67 | f"{OCTOBOT_REPOSITORY_FILES_ROOT}/{DEFAULT_CONFIG_FILE}", CONFIG_FILE 68 | ), 69 | ( 70 | f"{OCTOBOT_REPOSITORY_FILES_ROOT}/{DEFAULT_CONFIG_FILE}", DEFAULT_CONFIG_FILE 71 | ), 72 | ( 73 | f"{OCTOBOT_REPOSITORY_FILES_ROOT}/{CONFIG_FILE_SCHEMA_WITH_PATH}", CONFIG_FILE_SCHEMA_WITH_PATH 74 | ), 75 | ( 76 | f"{OCTOBOT_REPOSITORY_FILES_ROOT}/{CONFIG_DEFAULT_EVALUATOR_FILE}", CONFIG_DEFAULT_EVALUATOR_FILE 77 | ), 78 | ( 79 | f"{OCTOBOT_REPOSITORY_FILES_ROOT}/{CONFIG_DEFAULT_TRADING_FILE}", CONFIG_DEFAULT_TRADING_FILE 80 | ), 81 | ( 82 | f"{OCTOBOT_REPOSITORY_FILES_ROOT}/{LOGGING_CONFIG_FILE}", LOGGING_CONFIG_FILE 83 | ) 84 | ] 85 | 86 | LIB_FILES_DOWNLOAD_PROGRESS_SIZE = 5 87 | CREATE_FOLDERS_PROGRESS_SIZE = 5 88 | 89 | 90 | def create_environment(): 91 | inc_progress(0, to_min=True) 92 | logging.info(f"{OCTOBOT_NAME} is checking your environment...") 93 | 94 | inc_progress(1) 95 | ensure_file_environment(INSTALL_DOWNLOAD) 96 | 97 | inc_progress(LIB_FILES_DOWNLOAD_PROGRESS_SIZE - 1) 98 | inc_progress(CREATE_FOLDERS_PROGRESS_SIZE) 99 | 100 | logging.info(f"Your {OCTOBOT_NAME} environment is ready !") 101 | 102 | 103 | def install_bot(force_package=False): 104 | create_environment() 105 | 106 | binary_path = GithubOctoBot().update_binary(OctoBotVersion(), force_package=force_package, 107 | force_binary=FORCE_BINARY) 108 | 109 | # give binary execution rights if necessary 110 | if binary_path: 111 | binary_execution_rights(binary_path) 112 | 113 | # if update tentacles 114 | if binary_path: 115 | executable_path = OctoBotVersion().get_local_binary(force_binary=FORCE_BINARY) 116 | update_tentacles(executable_path, force_install=True) 117 | else: 118 | logging.error(f"No {OCTOBOT_NAME} found to update tentacles.") 119 | 120 | 121 | def _ensure_directory(file_path): 122 | directory = os.path.dirname(file_path) 123 | 124 | if not os.path.exists(directory) and directory: 125 | os.makedirs(directory) 126 | 127 | 128 | def ensure_minimum_environment(): 129 | try: 130 | for folder in FOLDERS_TO_CREATE: 131 | if not os.path.exists(folder) and folder: 132 | os.makedirs(folder) 133 | 134 | ensure_file_environment(FILES_TO_DOWNLOAD) 135 | except Exception as e: 136 | print(f"Error when creating minimum launcher environment: {e} this should not prevent launcher " 137 | f"from working.") 138 | 139 | 140 | def ensure_file_environment(file_to_download): 141 | # download files 142 | for file_to_dl in file_to_download: 143 | 144 | _ensure_directory(file_to_dl[1]) 145 | 146 | file_name = file_to_dl[1] 147 | if not os.path.isfile(file_name) and file_name: 148 | urllib.request.urlretrieve(file_to_dl[0], file_name) 149 | 150 | 151 | def update_tentacles(binary_path, force_install=False): 152 | if binary_path: 153 | # update tentacles if installed 154 | if not force_install and os.path.exists(TENTACLES_PATH): 155 | executor.execute_command_on_current_binary(binary_path, ["-p", "update", "all"]) 156 | logging.info(f"Tentacles : all default tentacles have been updated.") 157 | else: 158 | executor.execute_command_on_current_binary(binary_path, ["-p", "install", "all", "force"]) 159 | logging.info(f"Tentacles : all default tentacles have been installed.") 160 | 161 | 162 | def binary_execution_rights(binary_path): 163 | if os.name in [LINUX_OS_NAME, MAC_OS_NAME]: 164 | try: 165 | rights_process = subprocess.Popen(["chmod", "+x", binary_path]) 166 | except Exception as e: 167 | logging.error(f"Failed to give execution rights to {binary_path} : {e}") 168 | rights_process = None 169 | 170 | if not rights_process: 171 | # show message if user has to type the command 172 | message = f"{OCTOBOT_NAME} binary need execution rights, " \ 173 | f"please type in a command line 'sudo chmod +x ./{OCTOBOT_NAME}'" 174 | logging.warning(message) 175 | # if self.launcher_app: 176 | # self.launcher_app.show_alert(f"{message} and then press OK", bitmap=WARNING) 177 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | --------------------------------------------------------------------------------