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
*.ui
settings.json
*.kdev4

View File

@@ -8,7 +8,7 @@
# Установка
```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
```
# Использование

298
hlna.py
View File

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

View File

@@ -3,15 +3,45 @@ import os
import hlna
import time
import unittest
from unittest.mock import patch
import zipfile
from click.testing import CliRunner
from pathlib import Path
home_dir = Path.home()
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):
def setUp(self):
self.test_dir = 'test_dir'
@@ -94,6 +124,13 @@ class TestBackup(unittest.TestCase):
self.assertEqual(zip_file.testzip(), None)
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__':
unittest.main()