Compare commits

...

19 Commits

Author SHA1 Message Date
8757203312 import shutil 2024-05-20 15:10:45 +03:00
480c13de13 print_line по длине контента 2024-05-15 18:05:35 +03:00
c1af88e361 Merge branch 'dev' 2024-01-17 15:06:13 +03:00
b0e1b72f71 Обновление установки 2024-01-17 14:58:49 +03:00
cc2bdab8bd git 2023-08-23 15:36:21 +03:00
21c1a68ec1 попытка в змейку) 2023-06-07 17:49:16 +03:00
4fed6b897c изменили работу status, изменили reconf, rename, printline 2023-06-06 23:08:00 +03:00
09edcffd1f меняли name_server(пока не победили) 2023-06-05 21:14:00 +03:00
sitisll
f6da023004 убрал state_config1 за ненадобностью 2023-06-05 15:36:00 +03:00
sitisll
5eb5eeb9ba проверка имени во время активации 2023-06-05 15:28:06 +03:00
0d8b8464a3 Добавлено обновление модов 2023-05-27 13:52:26 +03:00
81065847d1 Добавлено обновление модов 2023-05-27 11:44:44 +03:00
e5b10fc121 Добавлено обновление модов 2023-05-27 11:41:18 +03:00
02e4a16b59 Добавлено обновление модов 2023-05-23 23:18:01 +03:00
5918ee50b5 Основной функционал для ark 2023-05-21 23:11:18 +03:00
ad60da3dce merge dev 2023-04-08 16:36:48 +03:00
2158c56d5d Исправлено создание иерархии каталогов под конфиги 2023-04-08 15:07:35 +03:00
f3ced80056 readme 2023-03-17 10:12:14 +03:00
a363ef3c4a f"{home_dir}/.config/hlna/" 2023-03-10 11:10:52 +03:00
4 changed files with 211 additions and 132 deletions

2
.gitignore vendored
View File

@@ -77,3 +77,5 @@ CMakeLists.txt.user*
*.pyproject.user *.pyproject.user
*.ui *.ui
settings.json settings.json
*.kdev4

View File

@@ -8,7 +8,7 @@
# Установка # Установка
```shell ```shell
curl https://www.elara.ws/lure.sh | bash & lure ar -n xpamych-repo -u https://gitflic.ru/project/xpamych/xpamych-lure-repo.git & lure in hlna-git curl -fsSL lure.sh/install | bash && lure ar -n xpamych-repo -u https://gitflic.ru/project/xpamych/xpamych-lure-repo.git && lure in hlna-git
``` ```
# Использование # Использование

234
hlna.py
View File

@@ -1,15 +1,19 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import pprint
import re import re
import sys import sys
import zlib import zlib
import time import time
import shutil
import struct import struct
import curses import curses
import pprint
import tarfile import tarfile
import logging import logging
import datetime import datetime
import requests import requests
import threading
import subprocess import subprocess
import yaml import yaml
@@ -155,37 +159,46 @@ def print_line(*text, flag="", sep=" ", end="\n"):
color = colorama.Fore.CYAN color = colorama.Fore.CYAN
else: else:
color = colorama.Fore.WHITE color = colorama.Fore.WHITE
len_text = str(*text) len_text = str(*text)
len_text = len_text.split("\n") len_text = len_text.split("\n")
max_length = max(len(string) for string in len_text) + 2 max_string = max(len(str(string)) for string in len_text) + 2
print(color + "." * max_length) max_length = shutil.get_terminal_size()
max_length = max_length[0]
if max_string > max_length:
len_dots = max_length
else:
len_dots = max_string
print(color + "." * len_dots)
print(color, *text, sep=sep, end=end) print(color, *text, sep=sep, end=end)
print(color + "." * max_length + colorama.Style.RESET_ALL) print(color + "." * len_dots + colorama.Style.RESET_ALL)
def zmeyuka(stdscr, func): def zmeyuka(stdscr, func):
print_line(func)
curses.curs_set(0) curses.curs_set(0)
stdscr.nodelay(1) stdscr.nodelay(1)
x = 0
y = 0
stdscr.addstr(y, x, "")
stdscr.refresh()
def nachalo_stroki(): def nachalo_stroki():
nonlocal x, y while func.do_run:
while True: zmejka = '⠇⠋⠙⠸⠴⠦'
stdscr.addstr(0, 0, "", curses.A_REVERSE) for i in range(len(zmejka)):
stdscr.refresh() print('\r' + zmejka[i], end="", flush=True)
time.sleep(0.1) time.sleep(0.25)
stdscr.addstr(y, x, " ") th = threading.Thread(target=func)
x += 1 th.start()
if x > curses.COLS - 1: # Запуск потока со змеюкой
x = 0 zmeyuka_thread = threading.Thread(target=nachalo_stroki)
y +=1 zmeyuka_thread.start()
stdscr.addstr(y, x, "") # Ожидаем завершение функции
stdscr.refresh() th.join()
# Убираем змеюку
zmeyuka_thread.do_run = False
zmeyuka_thread.join()
curses.curs_set(1)
stdscr.nodelay(0)
def check_int(number=""): def check_int(number=""):
@@ -288,13 +301,14 @@ def ports(port, ports_arr, flag):
port = 27015 port = 27015
elif flag == 2: elif flag == 2:
port = 23331 port = 23331
print_line("Port=", port, flag="CYAN") print_line(f"Port={port}", flag="CYAN")
return port return port
else: else:
port = max(ports_arr) + 2 port = max(ports_arr) + 2
print_line("Port=", port, flag="CYAN") print_line(f"Port={port}", flag="CYAN")
if port in ports_arr: if port in ports_arr:
print_line("Порт уже занят", flag="RED") print_line("Порт уже занят", flag="RED")
port = check_int("Введите новый порт")
else: else:
return port return port
@@ -311,7 +325,7 @@ def config_cluster():
while True: while True:
cluster_id = input("Укажите id для кластера, любое сочетание символов: \n") cluster_id = input("Укажите id для кластера, любое сочетание символов: \n")
if cluster_id == '': if cluster_id == '':
print_line("Введите символы: ", glag="CYAN") print_line("Введите символы: ", flag="CYAN")
else: else:
create_dir(dir_server_ark + cluster_id) create_dir(dir_server_ark + cluster_id)
cluster_dir_override = (dir_server_ark + cluster_id) cluster_dir_override = (dir_server_ark + cluster_id)
@@ -395,28 +409,46 @@ def config_nameserver(map_s):
while new_name in list_config: while new_name in list_config:
new_name = f"{map_s}{str(count)}" new_name = f"{map_s}{str(count)}"
count += 1 count += 1
list_config.append(new_name) name_server = new_name
break break
else: else:
list_config.append(map_s) name_server = map_s
break break
else: else:
if name_server in list_allconfigs: if name_server in list_config:
choose_reconf = input("""Сервер существует. Перенастроить? (по умолчанию) нет choose_reconf = input("""Сервер существует. (по умолчанию - отмена)
1. Да 1. Переименовать
2. Нет 2. Изменить настройки
3. Отмена
:""") :""")
if choose_reconf == "": if choose_reconf == "" or choose_reconf == "3":
config_nameserver(map_s) exit()
elif choose_reconf == "1": elif choose_reconf == "1":
return name_server new_nameserver = input("Укажите название Сервера: \n")
try:
if new_nameserver != name_server:
os.system(f"systemctl --user disable {dir_unit}ark_{name_server.lower()}.service")
os.system(f"rm {dir_unit}ark_{name_server.lower()}.service")
os.system(f"mv {dir_maps_ark}{name_server} {dir_maps_ark}{new_nameserver}")
return new_nameserver, True
else: else:
print_line("Введите новое имя сервера", flag="YELLOW") print_line("Вы ввели тоже имя.")
config_nameserver(map_s) continue
else: except Exception:
list_config.append(name_server) # если enter, то ставим последним элементом карту print_line(Exception)
elif choose_reconf == "2":
config_ark(flag=False)
break break
return list_config else:
print_line("Выберите ВОЗМОЖНОЕ действие")
else:
break
return name_server
def reconf_yaml():
name_server = input("Укажите название Сервера: \n")
return name_server
def config_ports(port_s): def config_ports(port_s):
@@ -469,23 +501,27 @@ def config_listen():
return listen_server return listen_server
def config_ark(list_config=list_config): def config_ark(list_config=list_config, flag=False):
"""конфигурирование сервера арк""" """конфигурирование сервера арк"""
create_dir(dir_server_ark) create_dir(dir_server_ark)
create_dir(dir_maps_ark) create_dir(dir_maps_ark)
id_mods_ark = "" id_mods_ark = ""
cluster_server, cluster_id, cluster_dir_override = config_cluster() cluster_server, cluster_id, cluster_dir_override = config_cluster()
count_maps = config_nummap()
if list_config: if list_config:
print_line("Уже установленные карты: ", flag="CYAN") print_line("Существующие активные карты: ", flag="CYAN")
for i in list_config: for i in list_config:
data = read_yaml(g="ark", m=i) data = read_yaml(g="ark", m=i)
print_line(f"Карта - {i} : Имя сервера {data['SessionName']}", flag="CYAN") print_line(f"Карта - {data['map']} : Имя сервера {i}", flag="CYAN")
count_maps = config_nummap()
for i in range(count_maps): for i in range(count_maps):
map_s, port_s, query_p, rcon_p = config_maps(i) map_s, port_s, query_p, rcon_p = config_maps(i)
list_config = config_nameserver(map_s) if flag == True:
reconf_yaml()
break
else:
name_server, flag = config_nameserver(map_s)
if flag == False:
port_server = config_ports(port_s) port_server = config_ports(port_s)
query_port = config_query(query_p) query_port = config_query(query_p)
rcon_port = config_rcon(rcon_p) rcon_port = config_rcon(rcon_p)
@@ -495,9 +531,14 @@ def config_ark(list_config=list_config):
max_players = config_players() max_players = config_players()
listen_server = config_listen() listen_server = config_listen()
yaml_create("ark", "", cluster_server, map_s, list_config[-1], port_server, query_port, yaml_create("ark", "", cluster_server, map_s, name_server, port_server, query_port,
rcon_enabled, rcon_port, adminpassword_server, password_server, max_players, rcon_enabled, rcon_port, adminpassword_server, password_server, max_players,
id_mods_ark, cluster_id, cluster_dir_override, listen_server) id_mods_ark, cluster_id, cluster_dir_override, listen_server)
data = read_yaml(g="ark", m=map_s)
yaml_create("ark", data['ServerPath'], data['Cluster'], data['map'], name_server, data['Port'],
data['QueryPort'], data['RCONEnabled'], data['RCONPort'], data['ServerAdminPassword'],
data['ServerPassword'],data['MaxPlayers'], id_mods_ark, data['clusterid'], data['clusterdir'],
data['Listen'])
def config_7daystodie(): def config_7daystodie():
@@ -520,13 +561,11 @@ def config_7daystodie():
@click.argument('g', nargs=1) @click.argument('g', nargs=1)
@click.option('-m', default='all', help="Название карты для запуска или all для запуска всех карт") @click.option('-m', default='all', help="Название карты для запуска или all для запуска всех карт")
def config_backup(g, m): def config_backup(g, m):
if g == "ark": if g.lower() == "ark":
name_server = choose_map(g, m) name_server = choose_map(g, m)
if m == "all": if m == "all":
print_line(f"М: {m}, name_server: {name_server}")
all_empty = True # флаг all_empty = True # флаг
for i in name_server: for i in name_server:
print_line(f"{i},{g}")
data = read_yaml(g, m=i, flag=True) data = read_yaml(g, m=i, flag=True)
if 'ark_backup' not in data or data['ark_backup'] == "" or data['ark_backup'] == "False": if 'ark_backup' not in data or data['ark_backup'] == "" or data['ark_backup'] == "False":
all_empty = False # меняем флаг, если есть значение, которое не пустое all_empty = False # меняем флаг, если есть значение, которое не пустое
@@ -714,6 +753,7 @@ def modupdate(g, m, id_mod, dir_mod_ark):
g = g.lower() g = g.lower()
if g == "ark": if g == "ark":
print_line(f"Проверяем обновление мода {id_mod}", flag="CYAN") print_line(f"Проверяем обновление мода {id_mod}", flag="CYAN")
if os.path.isfile(f"{dir_mod_ark}/appworkshop_346110.acf"):
with open(os.path.join(dir_mod_ark, f"appworkshop_346110.acf"), "r") as f: with open(os.path.join(dir_mod_ark, f"appworkshop_346110.acf"), "r") as f:
content = f.readlines() content = f.readlines()
content = "".join(content) content = "".join(content)
@@ -726,7 +766,6 @@ def modupdate(g, m, id_mod, dir_mod_ark):
if '}' in line: if '}' in line:
break break
break break
data = { data = {
'itemcount': 1, 'itemcount': 1,
'publishedfileids[0]': id_mod 'publishedfileids[0]': id_mod
@@ -740,6 +779,9 @@ def modupdate(g, m, id_mod, dir_mod_ark):
moddownload(g, m, id_mod, dir_mod_ark) moddownload(g, m, id_mod, dir_mod_ark)
else: else:
print_line(f"Мод {id_mod} обновлен", flag="GREEN") print_line(f"Мод {id_mod} обновлен", flag="GREEN")
else:
print_line(f"Информация об обновлении не найдена. Переустанавливаем мод {id_mod}", flag="CYAN")
moddownload(g, m, id_mod, dir_mod_ark)
def modupdateall(g, m): def modupdateall(g, m):
@@ -783,7 +825,7 @@ def moddownload(g, m, id_mod, dir_mod_ark):
if os.path.isfile(uncompressed): if os.path.isfile(uncompressed):
os.remove(uncompressed) os.remove(uncompressed)
except Exception as e: except Exception as e:
print_line(e, flag="GREEN") print_line(e, flag="RED")
print_line(f"[x] Unpacking .z files failed, aborting mod install", flag="RED") print_line(f"[x] Unpacking .z files failed, aborting mod install", flag="RED")
return False return False
@@ -841,18 +883,14 @@ def switch(g, m, e): # добавить all
create_dir(dir_deactivated) create_dir(dir_deactivated)
if e: if e:
port_s, query_p, rcon_p = ports_array() port_s, query_p, rcon_p = ports_array()
state_config = delist_config if e else list_config
state_msg = "активных" if e else "не активных" state_msg = "активных" if e else "не активных"
for i in m: name_server = choose_map(g, m)
for i in name_server:
try: try:
state_config1 = list_config if e else delist_config
if i in state_config1:
print_line(f"Карта {i} уже есть в {state_msg}", flag="CYAN")
continue
data = read_yaml(g="ark", m=i, flag=not e) data = read_yaml(g="ark", m=i, flag=not e)
print_line(f"дата {data}", flag="RED")
if e: # добавить проверку занятости имени if e: # добавить проверку занятости имени
if i in list_config:
data['SessionName'] = config_nameserver(i, flag=False)[-1]
data['Port'] = ports(data['Port'], port_s, e) data['Port'] = ports(data['Port'], port_s, e)
data['QueryPort'] = ports(data['QueryPort'], port_s, e) data['QueryPort'] = ports(data['QueryPort'], port_s, e)
data['RCONPort'] = ports(data['RCONPort'], port_s, e) data['RCONPort'] = ports(data['RCONPort'], port_s, e)
@@ -882,42 +920,15 @@ def switch(g, m, e): # добавить all
@hlna.command(help='Выводит статус настроеных серверов') @hlna.command(help='Выводит статус настроеных серверов')
def status(list_config=list_config): @click.argument('g', nargs=1)
ext_ip = get_external_ip() @click.option("-m", default='all', help="Название cервера")
if list_config == [] and delist_config == []: def status(g, m="all", list_config=list_config):
print_line("Сервера не сконфигурированы", flag="RED") """print_status делает вывод, flag - отвечает за показ активных/неактивных, под 7days надо будет дописать"""
else: def print_status(g, status_map, flag=True):
for i in list_config: data = status_map
data = read_yaml(g="ark", m=i) print_line(data["status"], flag=("YELLOW", "RED")[flag] if data['status'] == "Не запущен" else "GREEN")
x = os.system(f"lsof -w -i :{data['Port']}")
if x == 0:
print_line("Сервер запущен", flag="GREEN")
else:
print_line("Сервер не запущен", flag="RED")
# этот принт надо отдельной функцией сделать, чтобы убрать дублирование текста
print_line(f""" print_line(f"""
Имя сервера: {i} Имя сервера: {data['SessionName']}
Карта: {data['map']}
Моды: {data['ModsId']}
Пароль: {data['ServerPassword']}S
Кластер: {data['Cluster']}
Кластер id: {data['clusterid']}
Query порт: {data['QueryPort']}
Порт сервера: {data['Port']}
Rcon включен: {data['RCONEnabled']}
Rcon порт : {data['RCONPort']}
Максимальное кол-во игроков: {data['MaxPlayers']}
steam://connect/{ext_ip}:{data['QueryPort']}""", flag="CYAN")
if delist_config != []:
x = input("Есть неактивные сервера, показать Y/n: ")
if x != "n":
for i in delist_config:
data = read_yaml(i, flag=False)
print_line(f"""
Имя сервера: {i}
Карта: {data['map']} Карта: {data['map']}
Моды: {data['ModsId']} Моды: {data['ModsId']}
Пароль: {data['ServerPassword']} Пароль: {data['ServerPassword']}
@@ -927,8 +938,35 @@ def status(list_config=list_config):
Порт сервера: {data['Port']} Порт сервера: {data['Port']}
Rcon включен: {data['RCONEnabled']} Rcon включен: {data['RCONEnabled']}
Rcon порт : {data['RCONPort']} Rcon порт : {data['RCONPort']}
Максимальное кол-во игроков: {data['MaxPlayers']}""", flag="YELLOW") Максимальное кол-во игроков: {data['MaxPlayers']}
steam://connect/{get_external_ip()}:{data['QueryPort']}""", flag=("YELLOW","CYAN")[flag])
def get_param(g, list_con, flag=True):
for i in list_con:
data = read_yaml(g=g, m=i, flag=flag)
status_map["ark"][flag][data["SessionName"]] = data
status_map["ark"][flag][data["SessionName"]]['status'] = "Не запущен" if os.system(f"lsof -w -i :{data['Port']}") else "Запущен"
if list_config == [] and delist_config == []:
print_line("Сервера не сконфигурированы", flag="RED")
exit()
"Это скорее всего можно как то покрасивее записать, но пока так)"
status_map = {}
status_map["ark"] = {}
status_map["ark"][True] = {}
status_map["ark"][False] = {}
get_param("ark", list_config)
get_param("ark", delist_config, False)
if g == "ark":
name_servers = choose_map(g, m)
for i in name_servers:
print_status(g, status_map[g][True][i])
if delist_config != []:
x = input("Есть неактивные сервера, показать Y/n: ")
if x != "n":
for i in delist_config:
print_status(g, status_map[g][False][i], False)
return status_map
@hlna.command(help='Запуск, сконфигурированного сервера или кластера <hlna start ark -m all>') @hlna.command(help='Запуск, сконфигурированного сервера или кластера <hlna start ark -m all>')
@click.argument('g', nargs=1) @click.argument('g', nargs=1)
@@ -988,7 +1026,6 @@ def start_stop(action, g, m):
x = os.system(f"systemctl --user {action} ark_{data['SessionName'].lower()}.service") x = os.system(f"systemctl --user {action} ark_{data['SessionName'].lower()}.service")
if x == 0: if x == 0:
print_line(f"Готово {action} для {g} {i}", flag="GREEN") print_line(f"Готово {action} для {g} {i}", flag="GREEN")
elif g == "7days": elif g == "7days":
x = os.system(f"systemctl --user {action} 7days.service") x = os.system(f"systemctl --user {action} 7days.service")
if x == 0: if x == 0:
@@ -1000,7 +1037,6 @@ def start_stop(action, g, m):
def read_yaml(g="", m="", flag=True): def read_yaml(g="", m="", flag=True):
"""Читает конфиги активных или неактивных карт в зависимости от флага и отдаёт данные туда где их запросили""" """Читает конфиги активных или неактивных карт в зависимости от флага и отдаёт данные туда где их запросили"""
g = g.lower() g = g.lower()
print_line(f"g: {g}, m: {m}")
if g == "ark": if g == "ark":
if m == "all": if m == "all":
print_line("Не правильный вызов yaml, должен вызываться из цикла") print_line("Не правильный вызов yaml, должен вызываться из цикла")
@@ -1038,11 +1074,15 @@ def choose_map(g, m, list_config=list_config):
print_line('Найдены сервера с этой картой', flag="CYAN") print_line('Найдены сервера с этой картой', flag="CYAN")
for i, map in enumerate(name_servers): for i, map in enumerate(name_servers):
print_line(f"{i + 1}) {map}", flag="CYAN") print_line(f"{i + 1}) {map}", flag="CYAN")
print_line(f"{i + 2} Все", flag="CYAN")
x = None
if i != 0: if i != 0:
while True: while True:
try: try:
x = input("Выберите сервер из списка, либо несколько через запятую: ").split(',') x = input("Выберите сервер из списка, либо несколько через запятую: ").split(',')
x = [int(i) for i in x] x = [int(j) for j in x]
if i + 2 in x:
return name_servers
break break
except: except:
print_line("Неправильный ввод", flag="RED") print_line("Неправильный ввод", flag="RED")

View File

@@ -3,15 +3,45 @@ import os
import hlna import hlna
import time import time
import unittest import unittest
from unittest.mock import patch
import zipfile import zipfile
from click.testing import CliRunner from click.testing import CliRunner
from pathlib import Path from pathlib import Path
home_dir = Path.home() home_dir = Path.home()
config_hlna = f"{home_dir}/.config/hlna/" config_hlna = f"{home_dir}/.config/hlna/"
from unittest.mock import patch, MagicMock
from hlna import config
class TestConfig(unittest.TestCase):
@patch('builtins.input', side_effect=['1'])
@patch('hlna.config_ark')
def test_config_ark_valid(self, config_ark_mock, input_mock):
config()
config_ark_mock.assert_called_once()
@patch('builtins.input', side_effect=['2'])
@patch('hlna.config_7daystodie')
def test_config_7daystodie_valid(self, config_7daystodie_mock, input_mock):
config()
config_7daystodie_mock.assert_called_once()
@patch('builtins.input', side_effect=['3'])
@patch('sys.stdout', new_callable=MagicMock)
def test_config_invalid_game(self, mocked_stdout, input_mock):
config()
self.assertEqual(mocked_stdout.getvalue().strip(), "Пока есть только ARK и 7Days xD")
@patch('builtins.input', side_effect=['a', '1'])
@patch('hlna.config_ark')
@patch('sys.stdout', new_callable=MagicMock)
def test_config_invalid_input(self, mocked_stdout, config_ark_mock, input_mock):
config()
config_ark_mock.assert_called_once()
self.assertEqual(mocked_stdout.getvalue().strip(),
"Выберите игру для конфигурирования\n1. ARK Survival Evolved\n2. 7 Days to Die\n: ")
class TestFindFile(unittest.TestCase): class TestFindFile(unittest.TestCase):
def setUp(self): def setUp(self):
self.test_dir = 'test_dir' self.test_dir = 'test_dir'
@@ -94,6 +124,13 @@ class TestBackup(unittest.TestCase):
self.assertEqual(zip_file.testzip(), None) self.assertEqual(zip_file.testzip(), None)
self.assertTrue(os.path.exists(os.path.join(target, f"{config_hlna}ARK/test_file.txt"))) self.assertTrue(os.path.exists(os.path.join(target, f"{config_hlna}ARK/test_file.txt")))
class TestGetExternalIP(unittest.TestCase):
def test_backup(self):
self.assertTrue(bool(hlna.get_external_ip()))
#class TestGetExternalIP(unittest.TestCase):
# def test_backup(self):
# self.assertTrue(bool(hlna.get_external_ip()))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()