Загрузка...

Script SSH Server Multitool Panel

Thread in Python created by rasez Jul 28, 2025 at 9:13 PM. 107 views

  1. rasez
    rasez Topic starter Jul 28, 2025 at 9:13 PM Стим аккаунты тут - lolz.live/threads/7680775 :da: 1500 Apr 29, 2025
    upd: Изменил весь дизайн simple ssh тула своего, добавил испорт и экспорт серверов.
    Улучшил оверал интерфейс и его скорость, добавил безделушку типо сертификата отчистку консоли и пауза вывода если спамит. А также сохранение всего из консоли в txt файл
    SSH Server tool программа для упрощения работы с серверами/хостингами
    Работает через powershell и putty
    Помогает и упрощает общую работу с ними, вместо вечного копирования один раз внесите ваши данные и жмите 1 кнопку. Удобное переключение :finger_up:
    Просто если у вас много разных серверов можно легче ими управлять введя свои данные один раз и забыть про все
    Простой выбор серверов подключение через powershell или же putty
    Дизайн мусор но все же.
    Пишите идеи для дополнения в коментЫ
    [IMG]
    Python
    import sys
    import re
    import json
    import os
    import subprocess
    from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QPushButton, QLineEdit, QLabel, QTextEdit, QComboBox,
    QMessageBox, QScrollArea, QAction, QDialog, QFileDialog, QFontComboBox, QFontDialog)
    from PyQt5.QtCore import QProcess, Qt, QTimer, QByteArray
    from PyQt5.QtGui import QTextCursor, QKeyEvent, QTextCharFormat, QColor
    from PyQt5.QtGui import QFont

    class AboutDialog(QDialog):
    def __init__(self, parent=None):
    super().__init__(parent)
    self.setWindowTitle("About program and creator")
    self.setFixedSize(500, 400)

    layout = QVBoxLayout()


    title = QLabel("SSH Server Tool")
    title.setStyleSheet("""
    font-size: 24px;
    color: #ffcc00;
    font-weight: bold;
    padding: 10px;
    text-align: center;
    """)
    layout.addWidget(title)


    version = QLabel("Версия: 1.1.0 (Stable)")
    version.setStyleSheet("""
    font-size: 16px;
    color: #e6e6e6;
    text-align: center;
    padding-bottom: 20px;
    """)
    layout.addWidget(version)


    desc = QLabel("""
    <p>Профессиональный инструмент для управления SSH соединениями.<br>
    Поддержка множества серверов с безопасным хранением учетных данных.</p>

    <p><b>Системные требования:</b><br>
    • Windows 10/11 или Linux<br>
    • Python 3.8+<br>
    • SSH клиент (OpenSSH или PuTTY)</p>
    """)
    desc.setStyleSheet("font-size: 14px;color: #BFBCBB")
    desc.setWordWrap(True)
    layout.addWidget(desc)


    cert = QLabel("""
    <div style='
    border: 1px solid #ffcc00;
    padding: 10px;
    margin: 10px 0;
    background-color: #3a3a3a;
    font-family: Consolas;
    font-size: 12px;
    color: #CFC884;
    '>
    <b>Digital Certificate:</b><br>
    Issued to: Rasez Softworks<br>
    Issued by: Rasez Certification Authority<br>
    Valid from: 01/01/2025 to 31/12/2030<br>
    SHA-256 Fingerprint: 12:34:56:78:90:AB:CD:EF...
    </div>
    """)
    cert.setWordWrap(True)
    layout.addWidget(cert)


    copyright = QLabel(" 2025 Rasez Softworks. Все права защищены.")
    copyright.setStyleSheet("""
    font-size: 12px;
    color: #a0a0a0;
    text-align: center;
    padding-top: 20px;
    """)
    layout.addWidget(copyright)

    self.setLayout(layout)
    self.setStyleSheet("background-color: #2d2d2d;")


    class ConsoleOutput(QTextEdit):
    def __init__(self, parent=None):
    super().__init__(parent)

    self.setReadOnly(True)
    self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
    self.setLineWrapMode(QTextEdit.NoWrap)
    self.document().setMaximumBlockCount(1000)
    self.setStyleSheet("font-family: 'Monospace'; font-size: 12pt;")
    self.default_format = QTextCharFormat()
    self.default_format.setForeground(QColor("#e6e6e6"))

    self.error_format = QTextCharFormat()
    self.error_format.setForeground(QColor("#ff4444"))

    self.success_format = QTextCharFormat()
    self.success_format.setForeground(QColor("#44ff44"))

    self.command_format = QTextCharFormat()
    self.command_format.setForeground(QColor("#ffcc00"))
    self.command_format.setFontWeight(75)

    self.buffer = QByteArray()
    self.partial_line = ""

    def append_text(self, text, fmt=None):
    cursor = self.textCursor()
    cursor.movePosition(QTextCursor.End)

    if fmt:
    cursor.setCharFormat(fmt)
    else:
    cursor.setCharFormat(self.default_format)

    cursor.insertText(text)
    self.setTextCursor(cursor)
    self.ensureCursorVisible()

    def process_data(self, data):

    text = data.data().decode('utf-8', errors='replace')

    text = self.partial_line + text

    lines = text.split('\n')
    if text.endswith('\n'):
    self.partial_line = ""
    else:
    self.partial_line = lines.pop()


    for line in lines:
    self.append_line(line + '\n')

    def append_line(self, line):
    clean_line = self.clean_ansi(line)

    if clean_line.startswith("ERROR") or "fail" in clean_line.lower():
    self.append_text(clean_line, self.error_format)
    elif clean_line.startswith("$ "):
    self.append_text(clean_line, self.command_format)
    elif "success" in clean_line.lower() or "connected" in clean_line.lower():
    self.append_text(clean_line, self.success_format)
    else:
    self.append_text(clean_line)

    @staticmethod
    def clean_ansi(text):
    return text


    class SSHServerTool(QMainWindow):
    def __init__(self):
    super().__init__()
    self.setWindowTitle("SSH Server Tool")
    self.setGeometry(100, 100, 1000, 800)

    self.central_widget = QWidget()
    self.setCentralWidget(self.central_widget)
    self.init_menu()
    self.main_layout = QVBoxLayout()
    self.central_widget.setLayout(self.main_layout)

    self.init_ui()

    self.ssh_servers = []
    self.current_process = None
    self.connected = False
    self.current_server = None
    self.command_history = []
    self.history_index = -1
    self.pause_output = False
    self.output_buffer = ""

    self.load_servers()
    self.update_combo_box()
    self.connect_buttons()
    self.setup_styles()

    self.output_timer = QTimer()
    self.output_timer.timeout.connect(self.flush_buffer)
    self.output_timer.start(100)

    def init_ui(self):
    self.label = QLabel("SSH SERVER TOOL")
    self.label.setAlignment(Qt.AlignCenter)
    self.main_layout.addWidget(self.label)

    self.server_management_layout = QVBoxLayout()

    self.row1_layout = QHBoxLayout()
    self.pushButton = QPushButton("ADD SSH")
    self.row1_layout.addWidget(self.pushButton)
    self.lineEdit = QLineEdit()
    self.lineEdit.setPlaceholderText("Username@Ip")
    self.row1_layout.addWidget(self.lineEdit)
    self.server_management_layout.addLayout(self.row1_layout)

    self.row2_layout = QHBoxLayout()
    self.lineEdit_3 = QLineEdit()
    self.lineEdit_3.setPlaceholderText("SSH NAME")
    self.row2_layout.addWidget(self.lineEdit_3)
    self.lineEdit_2 = QLineEdit()
    self.lineEdit_2.setPlaceholderText("Password")
    self.lineEdit_2.setEchoMode(QLineEdit.Password)
    self.row2_layout.addWidget(self.lineEdit_2)
    self.server_management_layout.addLayout(self.row2_layout)

    self.row3_layout = QHBoxLayout()
    self.pushButton_2 = QPushButton("CONNECT")
    self.row3_layout.addWidget(self.pushButton_2)
    self.pushButton_3 = QPushButton("SERVER LIST")
    self.row3_layout.addWidget(self.pushButton_3)
    self.pushButton_4 = QPushButton("POWERSHELL")
    self.row3_layout.addWidget(self.pushButton_4)
    self.pushButton_5 = QPushButton("DELETE")
    self.row3_layout.addWidget(self.pushButton_5)
    self.comboBox = QComboBox()
    self.row3_layout.addWidget(self.comboBox)
    self.server_management_layout.addLayout(self.row3_layout)

    self.main_layout.addLayout(self.server_management_layout)

    self.console_tools_layout = QHBoxLayout()

    self.clear_btn = QPushButton("Clear Console")
    self.clear_btn.clicked.connect(self.clear_console)
    self.console_tools_layout.addWidget(self.clear_btn)

    self.pause_btn = QPushButton("Pause Output")
    self.pause_btn.setCheckable(True)
    self.pause_btn.toggled.connect(self.toggle_pause)
    self.console_tools_layout.addWidget(self.pause_btn)

    self.save_btn = QPushButton("Save Output")
    self.save_btn.clicked.connect(self.save_output)
    self.console_tools_layout.addWidget(self.save_btn)

    self.main_layout.addLayout(self.console_tools_layout)

    self.console_widget = QWidget()
    self.console_layout = QVBoxLayout()
    self.console_widget.setLayout(self.console_layout)

    self.textEdit = ConsoleOutput()

    self.command_input = QLineEdit()
    self.command_input.setPlaceholderText("Введите команду и нажмите Enter...")
    self.command_input.returnPressed.connect(self.send_command)

    self.console_layout.addWidget(self.textEdit)
    self.console_layout.addWidget(self.command_input)

    scroll = QScrollArea()
    scroll.setWidget(self.console_widget)
    scroll.setWidgetResizable(True)
    self.main_layout.addWidget(scroll)

    def init_menu(self):
    menubar = self.menuBar()
    menubar.setStyleSheet("""
    QMenuBar {
    background-color: #3a3a3a;
    font: 14pt 'Segoe UI';
    }
    QMenuBar::item {
    background-color: #3a3a3a;
    padding: 5px 10px;
    color: white;
    }
    QMenuBar::item:selected {
    background-color: #ffcc00;
    color: black;
    }
    QMenu {
    background-color: #3a3a3a;
    color: #e6e6e6;
    font: 12pt 'Segoe UI';
    border: 1px solid #4d4d4d;
    }
    QMenu::item:selected {
    background-color: #ffcc00;
    color: black;
    }
    """)

    file_menu = menubar.addMenu("File")
    export_action = QAction("Export Servers", self)
    export_action.triggered.connect(self.export_servers)
    file_menu.addAction(export_action)

    import_action = QAction("Import Servers", self)
    import_action.triggered.connect(self.import_servers)
    file_menu.addAction(import_action)

    help_menu = menubar.addMenu("Help")
    about_action = QAction("About", self)
    about_action.triggered.connect(self.show_about_dialog)
    help_menu.addAction(about_action)

    def setup_styles(self):
    self.setStyleSheet("""
    background-color: #2d2d2d;
    """)

    self.label.setStyleSheet("""
    font: bold 32pt 'Segoe UI';
    background: none;
    color: #ffcc00;
    padding: 10px;
    """)

    button_style = """
    QPushButton {
    font: 14pt 'Segoe UI';
    background-color: #3a3a3a;
    color: #e6e6e6;
    font-weight: 600;
    border: 1px solid #4d4d4d;
    border-radius: 5px;
    padding: 8px;
    min-width: 100px;
    }
    QPushButton:hover {
    background-color: #4a4a4a;
    border: 1px solid #5d5d5d;
    }
    QPushButton:pressed {
    background-color: #2a2a2a;
    }
    QPushButton:checked {
    background-color: #ffcc00;
    color: black;
    }
    """

    for btn in [self.pushButton, self.pushButton_2, self.pushButton_3,
    self.pushButton_4, self.pushButton_5, self.clear_btn,
    self.pause_btn, self.save_btn]:
    btn.setStyleSheet(button_style)

    line_edit_style = """
    QLineEdit {
    border: 1.5px solid #ffcc00;
    font: 14pt 'Consolas';
    color: #e6e6e6;
    background-color: #3a3a3a;
    font-weight: 700;
    border-radius: 5px;
    padding: 8px;
    selection-background-color: #ffcc00;
    selection-color: #000000;
    }
    """

    for le in [self.lineEdit, self.lineEdit_2, self.lineEdit_3, self.command_input]:
    le.setStyleSheet(line_edit_style)

    self.comboBox.setStyleSheet("""
    QComboBox {
    font: 12pt 'Segoe UI';
    color: #e6e6e6;
    background-color: #3a3a3a;
    border: 1px solid #4d4d4d;
    border-radius: 5px;
    padding: 8px;
    }
    QComboBox::drop-down {
    border: none;
    }
    QComboBox QAbstractItemView {
    background-color: #3a3a3a;
    color: #e6e6e6;
    selection-background-color: #ffcc00;
    selection-color: #000000;
    }
    """)

    def connect_buttons(self):
    self.pushButton.clicked.connect(self.add_ssh_server)
    self.pushButton_2.clicked.connect(self.connect_to_server)
    self.pushButton_3.clicked.connect(self.show_server_list)
    self.pushButton_4.clicked.connect(self.open_powershell)
    self.pushButton_5.clicked.connect(self.delete_server)

    def load_servers(self):
    try:
    if os.path.exists("ssh_servers.json"):
    with open("ssh_servers.json", "r") as f:
    self.ssh_servers = json.load(f)
    except Exception as e:
    self.show_error(f"Ошибка загрузки серверов: {str(e)}")

    def save_servers(self):
    try:
    with open("ssh_servers.json", "w") as f:
    json.dump(self.ssh_servers, f, indent=4)
    except Exception as e:
    self.show_error(f"Ошибка сохранения серверов: {str(e)}")

    def export_servers(self):
    try:
    file_path, _ = QFileDialog.getSaveFileName(
    self, "Export Servers", "", "JSON Files (*.json)")
    if file_path:
    with open(file_path, "w") as f:
    json.dump(self.ssh_servers, f, indent=4)
    self.textEdit.append_text(f"Серверы успешно экспортированы в {file_path}",
    self.textEdit.success_format)
    except Exception as e:
    self.show_error(f"Ошибка экспорта: {str(e)}")

    def import_servers(self):
    try:
    file_path, _ = QFileDialog.getOpenFileName(
    self, "Import Servers", "", "JSON Files (*.json)")
    if file_path:
    with open(file_path, "r") as f:
    imported_servers = json.load(f)

    new_servers = [s for s in imported_servers
    if not any(es['name'] == s['name'] for es in self.ssh_servers)]

    if len(new_servers) < len(imported_servers):
    dup_count = len(imported_servers) - len(new_servers)
    self.textEdit.append_text(f"Пропущено {dup_count} дубликатов",
    self.textEdit.error_format)

    self.ssh_servers.extend(new_servers)
    self.save_servers()
    self.update_combo_box()
    self.textEdit.append_text(f"Успешно импортировано {len(new_servers)} серверов",
    self.textEdit.success_format)
    except Exception as e:
    self.show_error(f"Ошибка импорта: {str(e)}")

    def update_combo_box(self):
    self.comboBox.clear()
    for server in self.ssh_servers:
    self.comboBox.addItem(server["name"])

    def add_ssh_server(self):
    name = self.lineEdit_3.text().strip()
    connection = self.lineEdit.text().strip()
    password = self.lineEdit_2.text().strip()

    if not name or not connection or not password:
    self.show_error("Все поля должны быть заполнены!")
    return

    if "@" not in connection:
    self.show_error("Формат подключения: username@ip")
    return

    if any(s["name"] == name for s in self.ssh_servers):
    self.show_error("Сервер с таким именем уже существует!")
    return

    self.ssh_servers.append({
    "name": name,
    "connection": connection,
    "password": password
    })

    self.save_servers()
    self.update_combo_box()
    self.textEdit.append_text(f"Сервер {name} успешно добавлен!",
    self.textEdit.success_format)

    self.lineEdit.clear()
    self.lineEdit_2.clear()
    self.lineEdit_3.clear()

    def connect_to_server(self):
    if self.connected and self.current_process:
    self.disconnect_from_server()
    return

    index = self.comboBox.currentIndex()
    if index == -1:
    self.show_error("Нет доступных серверов!")
    return

    server = self.ssh_servers[index]
    self.current_server = server
    self.textEdit.clear()
    self.textEdit.append_text(f"Подключаемся к {server['name']} ({server['connection']})...")

    username, ip = server['connection'].split('@')
    password = server['password']

    self.current_process = QProcess()
    self.current_process.setProcessChannelMode(QProcess.MergedChannels)
    self.current_process.readyReadStandardOutput.connect(self.handle_stdout)
    self.current_process.finished.connect(self.process_finished)

    if os.name == 'nt':
    command = f'plink -ssh {username}@{ip} -pw {password}'
    self.current_process.start('cmd.exe', ['/c', command])
    else:
    command = f"sshpass -p '{password}' ssh {username}@{ip}"
    self.current_process.start('bash', ['-c', command])

    self.connected = True
    self.pushButton_2.setText("DISCONNECT")
    QTimer.singleShot(1000, self.focus_command_input)

    def disconnect_from_server(self):
    if self.current_process:
    self.current_process.terminate()
    self.current_process = None
    self.connected = False
    self.current_server = None
    self.pushButton_2.setText("CONNECT")
    self.textEdit.append_text("\nОтключено от сервера",
    self.textEdit.success_format)

    def process_finished(self, exit_code, exit_status):
    self.disconnect_from_server()
    self.textEdit.append_text(f"\nСоединение прервано с кодом выхода: {exit_code}",
    self.textEdit.error_format if exit_code != 0 else self.textEdit.success_format)

    def focus_command_input(self):
    self.command_input.setFocus()

    def handle_stdout(self):
    if not self.current_process or self.pause_output:
    return


    data = self.current_process.readAllStandardOutput()
    if data:
    text = data.data().decode('utf-8', errors='replace')
    self.output_buffer += text
    #4istka buferaaa
    def flush_buffer(self):
    if not self.pause_output and self.output_buffer != "":
    self.textEdit.process_data(QByteArray(self.output_buffer.encode()))
    self.output_buffer = ""

    def send_command(self):
    if not self.connected or not self.current_process:
    self.textEdit.append_text("Сначала подключитесь к серверу!",
    self.textEdit.error_format)
    return

    command = self.command_input.text().strip()
    if not command:
    return

    self.command_history.append(command)
    self.history_index = len(self.command_history)

    self.textEdit.append_text(f"$ {command}\n",
    self.textEdit.command_format)

    if os.name == 'nt':
    command += "\r\n"
    else:
    command += "\n"

    self.current_process.write(command.encode())
    self.command_input.clear()

    def keyPressEvent(self, event: QKeyEvent):
    if event.key() == Qt.Key_Up and self.command_history:
    if self.history_index > 0:
    self.history_index -= 1
    self.command_input.setText(self.command_history[self.history_index])
    elif event.key() == Qt.Key_Down and self.command_history:
    if self.history_index < len(self.command_history) - 1:
    self.history_index += 1
    self.command_input.setText(self.command_history[self.history_index])
    else:
    self.history_index = len(self.command_history)
    self.command_input.clear()
    else:
    super().keyPressEvent(event)

    def show_server_list(self):
    if not self.ssh_servers:
    self.textEdit.append_text("Нет сохраненных серверов.",
    self.textEdit.error_format)
    return

    self.textEdit.clear()
    self.textEdit.append_text("Список сохраненных серверов:",
    self.textEdit.success_format)
    self.textEdit.append_text("=" * 50 + "\n",
    self.textEdit.default_format)

    for i, server in enumerate(self.ssh_servers, 1):
    self.textEdit.append_text(f"{i}. {server['name']} ({server['connection']})\n",
    self.textEdit.default_format)
    #смерть
    def open_powershell(self):
    index = self.comboBox.currentIndex()
    if index == -1:
    self.show_error("Нет доступных серверов!")
    return

    server = self.ssh_servers[index]
    username, ip = server['connection'].split('@')
    password = server['password']

    try:
    if os.name == 'nt':
    command = f'plink -ssh {username}@{ip} -pw {password}'
    subprocess.Popen(['start', 'cmd', '/k', command], shell=True)
    self.textEdit.append_text(f"Открыт внешний терминал PuTTY для {server['name']}",
    self.textEdit.success_format)
    else:
    command = f"sshpass -p '{password}' ssh {username}@{ip}"
    subprocess.Popen(['x-terminal-emulator', '-e', command])
    self.textEdit.append_text(f"Открыт внешний терминал для {server['name']}",
    self.textEdit.success_format)
    except Exception as e:
    error_msg = f"Ошибка: {str(e)}\n"
    error_msg += "Установите PuTTY (Windows) или sshpass (Linux)" if os.name == 'nt' else "Установите sshpass"
    self.show_error(error_msg)

    def delete_server(self):
    index = self.comboBox.currentIndex()
    if index == -1:
    self.show_error("Нет доступных серверов!")
    return

    server = self.ssh_servers[index]
    reply = QMessageBox.question(
    self, 'Подтверждение',
    f"Удалить сервер {server['name']}?",
    QMessageBox.Yes | QMessageBox.No
    )

    if reply == QMessageBox.Yes:
    del self.ssh_servers[index]
    self.save_servers()
    self.update_combo_box()
    self.textEdit.append_text(f"Сервер {server['name']} удален",
    self.textEdit.success_format)

    def clear_console(self):
    self.textEdit.clear()

    def toggle_pause(self, paused):
    self.pause_output = paused
    self.pause_btn.setText("Resume" if paused else "Pause")
    if not paused and self.output_buffer:
    self.flush_buffer()

    def save_output(self):
    try:
    file_path, _ = QFileDialog.getSaveFileName(
    self, "Save Console Output", "", "Text Files (*.txt);;All Files (*)")
    if file_path:
    with open(file_path, "w", encoding="utf-8") as f:
    f.write(self.textEdit.toPlainText())
    self.textEdit.append_text(f"Вывод сохранен в {file_path}",
    self.textEdit.success_format)
    except Exception as e:
    self.show_error(f"Ошибка сохранения: {str(e)}")

    def show_about_dialog(self):
    dialog = AboutDialog(self)
    dialog.exec_()

    def show_error(self, message):
    QMessageBox.critical(self, "Ошибка", message)

    def closeEvent(self, event):
    if self.current_process and self.current_process.state() == QProcess.Running:
    self.current_process.terminate()
    self.current_process.waitForFinished(1000)
    event.accept()


    if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = SSHServerTool()
    window.show()
    sys.exit(app.exec_())
    github обновлю щяс

    Вроде нормальни дизайн теперь да MALWARE ?? Хз мне не нрав такие стандартные аккуратные програмки мне по кайфу чтоб ГЛАЗА АЖ БОЛЕЛИ))))) :fap: :fap: :muscle: :muscle:
     
    1. Хаус
      rasez, жду радужную тему
    2. rasez Topic starter
  2. 9henight
    9henight Jul 28, 2025 at 9:14 PM Изменить статус 193 Jun 20, 2021
    Вроде нормально
     
  3. RBT
    Ну норм вроде бы
    Порт на линукс буде?
     
    1. rasez Topic starter
      RBT, а чо там на линуксе порты нужны еще
    2. rasez Topic starter
      RBT, вроде и так все чики пуки фурычит с любыми серверами
  4. nichind
    nichind Jul 28, 2025 at 11:20 PM запрет, закон, поправка, критерий, уголовка, привлечение, донос 7648 Sep 15, 2020
    ssh keys??
     
Loading...
Top