Untitled
unknown
plain_text
15 days ago
26 kB
3
Indexable
import sys import time import threading import logging from datetime import datetime from pathlib import Path import json from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QLineEdit, QTextEdit, QSpinBox, QMessageBox, QTabWidget, QGroupBox, QFormLayout, QFileDialog, QCheckBox, QComboBox, QProgressBar, QStatusBar) from PyQt5.QtCore import Qt, QThread, pyqtSignal, pyqtSlot from PyQt5.QtGui import QIcon, QFont, QTextCursor # Import kelas RoyalDreamBot dari script asli from royal_dream_botkencang_update10mart import RoyalDreamBot # Kelas pekerja yang berjalan di thread terpisah untuk menjalankan bot class BotWorker(QThread): signal_log = pyqtSignal(str) signal_progress = pyqtSignal(int) signal_status = pyqtSignal(str) def __init__(self, bot, check_interval=30): QThread.__init__(self) self.bot = bot self.check_interval = check_interval self.running = False self.processed_orders = 0 def run(self): self.running = True self.signal_status.emit("Memulai...") # Hubungkan ke database if not self.bot.connect_database(): self.signal_log.emit("โ Gagal menghubungkan ke database. Bot berhenti.") self.signal_status.emit("Disconnected") self.running = False return self.signal_log.emit("โ Berhasil terhubung ke database!") self.signal_status.emit("Connected") # Loop utama bot while self.running: try: # Refresh koneksi if not self.bot.conn or not self.bot.conn.is_connected(): self.signal_log.emit("๐ Menyambungkan kembali ke database...") self.bot.connect_database() # Dapatkan pesanan pending orders = self.bot.get_pending_orders() if orders: self.signal_log.emit(f"๐ฆ Menemukan {len(orders)} pesanan pending") # Proses pesanan for idx, order in enumerate(orders): if not self.running: break order_id = order['id'] user_id = order['data'] service_name = order['service_name'] self.signal_log.emit(f"โณ Memproses pesanan #{order_id} untuk user ID {user_id} ({service_name})") self.signal_status.emit(f"Memproses #{order_id}") # Update progress bar progress_percent = int((idx + 1) / len(orders) * 100) self.signal_progress.emit(progress_percent) success = self.bot.process_order(order) if success: self.processed_orders += 1 self.signal_log.emit(f"โ Pesanan #{order_id} berhasil diproses (total: {self.processed_orders})") else: self.signal_log.emit(f"โ Pesanan #{order_id} gagal diproses") # Jeda antar pesanan time.sleep(1) self.signal_log.emit(f"โ Selesai memproses batch") self.signal_status.emit("Idle") self.signal_progress.emit(0) # Reset progress bar else: self.signal_log.emit(f"๐ค Tidak ada pesanan pending, menunggu {self.check_interval} detik...") self.signal_status.emit("Menunggu") # Tunggu interval yang ditentukan for i in range(self.check_interval): if not self.running: break # Update status setiap 5 detik if i % 5 == 0: self.signal_status.emit(f"Menunggu... ({self.check_interval - i})") time.sleep(1) except Exception as e: self.signal_log.emit(f"โ Error dalam loop bot: {str(e)}") self.signal_status.emit("Error") time.sleep(5) self.signal_log.emit("๐ Bot dihentikan") self.signal_status.emit("Stopped") def stop(self): self.running = False self.signal_log.emit("๐ Menghentikan bot, mohon tunggu...") self.wait(5000) # Tunggu maksimal 5 detik # Kelas untuk menangani output log class LogHandler(logging.Handler): def __init__(self, signal_function): super().__init__() self.signal_function = signal_function def emit(self, record): log_message = self.format(record) self.signal_function.emit(log_message) # Jendela utama aplikasi class RoyalDreamBotUI(QMainWindow): def __init__(self): super().__init__() self.bot = None self.worker = None self.initUI() # Setup logging handler untuk UI self.log_handler = None def initUI(self): # Setup window utama self.setWindowTitle('Royal Dream Bot Manager') self.setGeometry(100, 100, 900, 700) # Widget utama dan layout central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) # Tab widget tab_widget = QTabWidget() main_layout.addWidget(tab_widget) # ---- TAB 1: Kontrol Bot ---- tab_control = QWidget() tab_widget.addTab(tab_control, "Kontrol Bot") control_layout = QVBoxLayout(tab_control) # Grup Database db_group = QGroupBox("Konfigurasi Database") db_layout = QFormLayout() self.db_host = QLineEdit("103.186.1.63") self.db_port = QLineEdit("3306") self.db_user = QLineEdit("sql_kiosratu_id") self.db_pass = QLineEdit("f85a1d35ca69d") self.db_name = QLineEdit("sql_kiosratu_id") self.db_pass.setEchoMode(QLineEdit.Password) db_layout.addRow("Host:", self.db_host) db_layout.addRow("Port:", self.db_port) db_layout.addRow("Username:", self.db_user) db_layout.addRow("Password:", self.db_pass) db_layout.addRow("Database:", self.db_name) self.btn_test_conn = QPushButton("Tes Koneksi") self.btn_test_conn.clicked.connect(self.test_database_connection) db_layout.addRow("", self.btn_test_conn) db_group.setLayout(db_layout) control_layout.addWidget(db_group) # Grup Pengaturan Bot bot_group = QGroupBox("Pengaturan Bot") bot_layout = QFormLayout() self.check_interval = QSpinBox() self.check_interval.setRange(5, 300) self.check_interval.setValue(30) self.check_interval.setSuffix(" detik") coord_layout = QHBoxLayout() self.coord_path = QLineEdit() self.coord_path.setReadOnly(True) self.btn_browse_coord = QPushButton("Pilih...") self.btn_browse_coord.clicked.connect(self.browse_coord_file) coord_layout.addWidget(self.coord_path) coord_layout.addWidget(self.btn_browse_coord) bot_layout.addRow("Interval Cek:", self.check_interval) bot_layout.addRow("File Koordinat:", coord_layout) bot_group.setLayout(bot_layout) control_layout.addWidget(bot_group) # Grup Kontrol control_group = QGroupBox("Kontrol Bot") control_box = QHBoxLayout() self.btn_start = QPushButton("Mulai Bot") self.btn_start.setIcon(QIcon.fromTheme("media-playback-start")) self.btn_start.clicked.connect(self.start_bot) self.btn_stop = QPushButton("Hentikan Bot") self.btn_stop.setIcon(QIcon.fromTheme("media-playback-stop")) self.btn_stop.setEnabled(False) self.btn_stop.clicked.connect(self.stop_bot) self.btn_template = QPushButton("Buat Template") self.btn_template.setIcon(QIcon.fromTheme("document-new")) self.btn_template.clicked.connect(self.create_template) control_box.addWidget(self.btn_start) control_box.addWidget(self.btn_stop) control_box.addWidget(self.btn_template) control_group.setLayout(control_box) control_layout.addWidget(control_group) # Status area status_group = QGroupBox("Status") status_layout = QVBoxLayout() status_row = QHBoxLayout() status_row.addWidget(QLabel("Proses Batch:")) self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 100) self.progress_bar.setValue(0) status_row.addWidget(self.progress_bar) status_layout.addLayout(status_row) stat_info_layout = QHBoxLayout() stat_info_layout.addWidget(QLabel("Pesanan Diproses:")) self.order_count_label = QLabel("0") self.order_count_label.setStyleSheet("font-weight: bold;") stat_info_layout.addWidget(self.order_count_label) stat_info_layout.addStretch() status_layout.addLayout(stat_info_layout) status_group.setLayout(status_layout) control_layout.addWidget(status_group) # ---- TAB 2: Log ---- tab_log = QWidget() tab_widget.addTab(tab_log, "Log") log_layout = QVBoxLayout(tab_log) self.log_text = QTextEdit() self.log_text.setReadOnly(True) self.log_text.setFont(QFont("Courier", 9)) log_button_layout = QHBoxLayout() self.btn_clear_log = QPushButton("Bersihkan Log") self.btn_clear_log.clicked.connect(self.clear_log) self.btn_save_log = QPushButton("Simpan Log") self.btn_save_log.clicked.connect(self.save_log) log_button_layout.addWidget(self.btn_clear_log) log_button_layout.addWidget(self.btn_save_log) log_layout.addWidget(self.log_text) log_layout.addLayout(log_button_layout) # ---- TAB 3: Bantuan ---- tab_help = QWidget() tab_widget.addTab(tab_help, "Bantuan") help_layout = QVBoxLayout(tab_help) help_text = QTextEdit() help_text.setReadOnly(True) help_text.setHtml(""" <h2>Bantuan Royal Dream Bot Manager</h2> <h3>Langkah-langkah Menjalankan Bot:</h3> <ol> <li><b>Persiapan:</b> Pastikan BlueStacks sudah berjalan dengan Royal Dream terbuka dan login</li> <li><b>Buat Template:</b> Klik tombol "Buat Template" dan ikuti instruksi untuk mengkalibrasi koordinat UI</li> <li><b>Konfigurasi:</b> Periksa pengaturan database dan interval pengecekan</li> <li><b>Jalankan Bot:</b> Klik tombol "Mulai Bot" untuk memulai operasi</li> <li><b>Monitoring:</b> Pantau status dan log aktivitas bot pada tab "Log"</li> </ol> <h3>Tips Penting:</h3> <ul> <li>Jangan menggerakkan mouse atau menggunakan BlueStacks saat bot sedang berjalan</li> <li>Jika bot tidak menemukan elemen UI, kalibrasi ulang dengan "Buat Template"</li> <li>Interval yang direkomendasikan adalah 30 detik</li> <li>Gunakan tombol "Hentikan Bot" untuk menghentikan operasi dengan aman</li> </ul> <h3>Pemecahan Masalah:</h3> <ul> <li><b>Bot gagal menemukan elemen:</b> Kalibrasi ulang koordinat UI</li> <li><b>Koneksi database terputus:</b> Periksa pengaturan database dan jaringan</li> <li><b>Screenshot gagal:</b> Pastikan folder screenshot tersedia dan dapat diakses</li> <li><b>Bot crash:</b> Periksa log untuk detail masalah dan restart aplikasi</li> </ul> """) help_layout.addWidget(help_text) # Status Bar self.statusBar = QStatusBar() self.setStatusBar(self.statusBar) self.status_label = QLabel("Siap") self.statusBar.addWidget(self.status_label) # Cari file koordinat di lokasi default default_coord_path = Path("C:/BOTRD/templates/coordinates.json") if default_coord_path.exists(): self.coord_path.setText(str(default_coord_path)) self.log("โ File koordinat ditemukan di lokasi default") # Tampilkan pesan selamat datang self.log("๐ Royal Dream Bot Manager v1.0") self.log("โ ๏ธ Pastikan BlueStack sudah berjalan dengan Royal Dream terbuka") self.log("๐ก Klik 'Buat Template' jika ini pertama kali menjalankan bot") self.log("๐ Klik 'Mulai Bot' setelah konfigurasi siap") def log(self, message): timestamp = datetime.now().strftime("%H:%M:%S") log_entry = f"[{timestamp}] {message}" self.log_text.append(log_entry) # Scroll ke bawah self.log_text.moveCursor(QTextCursor.End) def clear_log(self): self.log_text.clear() self.log("๐งน Log dibersihkan") def save_log(self): filename, _ = QFileDialog.getSaveFileName( self, "Simpan File Log", "", "Log Files (*.log);;Text Files (*.txt);;All Files (*)" ) if filename: try: with open(filename, 'w', encoding='utf-8') as file: file.write(self.log_text.toPlainText()) self.log(f"โ Log berhasil disimpan ke {filename}") except Exception as e: self.log(f"โ Gagal menyimpan log: {e}") QMessageBox.critical(self, "Error", f"Gagal menyimpan log: {str(e)}") def test_database_connection(self): import mysql.connector self.log("๐ Menguji koneksi database...") db_config = { 'host': self.db_host.text(), 'port': int(self.db_port.text()), 'user': self.db_user.text(), 'password': self.db_pass.text(), 'database': self.db_name.text() } try: conn = mysql.connector.connect(**db_config) if conn.is_connected(): server_info = conn.get_server_info() self.log(f"โ Berhasil terhubung ke database MySQL (versi {server_info})") cursor = conn.cursor() cursor.execute("SELECT DATABASE();") db_name = cursor.fetchone()[0] self.log(f"โ Database aktif: {db_name}") cursor.close() conn.close() QMessageBox.information(self, "Koneksi Berhasil", f"Berhasil terhubung ke database MySQL (versi {server_info})\nDatabase aktif: {db_name}") else: self.log("โ Gagal terhubung ke database") QMessageBox.critical(self, "Koneksi Gagal", "Gagal terhubung ke database") except Exception as e: self.log(f"โ Error koneksi database: {str(e)}") QMessageBox.critical(self, "Error Koneksi", f"Error: {str(e)}") def browse_coord_file(self): filename, _ = QFileDialog.getOpenFileName( self, "Pilih File Koordinat", "C:/BOTRD/templates", "JSON Files (*.json);;All Files (*)" ) if filename: self.coord_path.setText(filename) self.log(f"๐ File koordinat dipilih: {filename}") def create_template(self): # Konfirmasi terlebih dahulu reply = QMessageBox.question(self, "Konfirmasi", "Pastikan BlueStack sudah berjalan dengan Royal Dream terbuka dan siap untuk kalibrasi.\n\nLanjutkan?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return # Inisialisasi bot untuk template self.log("๐ Memulai pembuatan template...") db_config = { 'host': self.db_host.text(), 'port': int(self.db_port.text()), 'user': self.db_user.text(), 'password': self.db_pass.text(), 'database': self.db_name.text() } try: self.bot = RoyalDreamBot(db_config) # Mengatur custom log handler untuk menampilkan log di UI self.setup_log_handler() # Jalankan pembuatan template self.log("โ ๏ธ Ikuti instruksi di jendela console untuk membuat template") self.log("โ ๏ธ Jangan tutup aplikasi ini selama proses berlangsung") QMessageBox.information(self, "Perhatian", "Proses pembuatan template akan dimulai di jendela console.\n\nLihat jendela console untuk mengikuti instruksi, lalu kembali ke aplikasi ini setelah selesai.") # Jalankan pembuatan template success = self.bot.create_template_images() if success: self.log("โ Template berhasil dibuat!") # Update path koordinat coord_path = Path("C:/BOTRD/templates/coordinates.json") if coord_path.exists(): self.coord_path.setText(str(coord_path)) QMessageBox.information(self, "Sukses", "Template berhasil dibuat!\n\nKoordinat UI telah disimpan dan siap digunakan.") else: self.log("โ Gagal membuat template") QMessageBox.critical(self, "Gagal", "Gagal membuat template. Lihat log untuk detail.") except Exception as e: self.log(f"โ Error saat membuat template: {str(e)}") QMessageBox.critical(self, "Error", f"Error saat membuat template: {str(e)}") def setup_log_handler(self): if self.bot: # Hapus handler yang sudah ada jika ada logger = logging.getLogger("RoyalDreamBot") if self.log_handler in logger.handlers: logger.removeHandler(self.log_handler) # Buat handler baru self.log_handler = LogHandler(self.log) self.log_handler.setFormatter(logging.Formatter('%(message)s')) logger.addHandler(self.log_handler) def start_bot(self): # Validasi konfigurasi if not Path(self.coord_path.text()).exists(): QMessageBox.critical(self, "Error", "File koordinat tidak ditemukan. Silakan buat template terlebih dahulu.") return # Cek apakah bot sudah berjalan if self.worker and self.worker.isRunning(): QMessageBox.warning(self, "Peringatan", "Bot sudah berjalan!") return # Konfirmasi reply = QMessageBox.question(self, "Konfirmasi", "Pastikan BlueStack sudah berjalan dengan Royal Dream terbuka.\n\nMulai bot sekarang?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return # Setup konfigurasi database db_config = { 'host': self.db_host.text(), 'port': int(self.db_port.text()), 'user': self.db_user.text(), 'password': self.db_pass.text(), 'database': self.db_name.text() } try: # Buat instance bot baru self.bot = RoyalDreamBot(db_config) # Muat koordinat dari file try: with open(self.coord_path.text(), 'r') as f: self.bot.coordinates = json.load(f) self.log("โ Koordinat berhasil dimuat dari file") except Exception as e: self.log(f"โ Error memuat koordinat: {str(e)}") QMessageBox.critical(self, "Error", f"Gagal memuat file koordinat: {str(e)}") return # Mengatur log handler self.setup_log_handler() # Buat worker thread self.worker = BotWorker(self.bot, self.check_interval.value()) # Hubungkan sinyal self.worker.signal_log.connect(self.log) self.worker.signal_progress.connect(self.progress_bar.setValue) self.worker.signal_status.connect(self.update_status) # Tambahkan koneksi untuk menangani jumlah pesanan yang diproses def update_processed_count(): if self.bot: self.order_count_label.setText(str(self.bot.processed_count)) # Timer untuk update jumlah pesanan self.order_count_timer = threading.Timer(1.0, update_processed_count) self.order_count_timer.daemon = True self.order_count_timer.start() # Mulai thread self.worker.start() # Update UI self.btn_start.setEnabled(False) self.btn_stop.setEnabled(True) self.btn_template.setEnabled(False) self.log("๐ Bot dimulai dengan interval " + str(self.check_interval.value()) + " detik") except Exception as e: self.log(f"โ Error memulai bot: {str(e)}") QMessageBox.critical(self, "Error", f"Gagal memulai bot: {str(e)}") def stop_bot(self): if self.worker and self.worker.isRunning(): self.log("๐ Menghentikan bot... (mohon tunggu)") self.worker.stop() # Nonaktifkan tombol stop untuk mencegah klik ganda self.btn_stop.setEnabled(False) # Tunggu hingga thread selesai (maksimal 5 detik) if not self.worker.wait(5000): self.log("โ ๏ธ Terpaksa mengakhiri thread bot") self.worker.terminate() # Update UI self.btn_start.setEnabled(True) self.btn_stop.setEnabled(False) self.btn_template.setEnabled(True) self.log("๐ Bot dihentikan") self.update_status("Dihentikan") self.progress_bar.setValue(0) # Hentikan timer if hasattr(self, 'order_count_timer') and self.order_count_timer: self.order_count_timer.cancel() def update_status(self, status): self.status_label.setText(status) def closeEvent(self, event): # Hentikan bot saat aplikasi ditutup if self.worker and self.worker.isRunning(): reply = QMessageBox.question(self, "Konfirmasi", "Bot masih berjalan. Hentikan dan keluar?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: self.log("๐ Menghentikan bot karena aplikasi ditutup...") self.worker.stop() # Tunggu hingga thread selesai (maksimal 2 detik) if not self.worker.wait(2000): self.worker.terminate() event.accept() else: event.ignore() else: event.accept() # Entri utama aplikasi if __name__ == "__main__": try: # Buat direktori log jika belum ada log_dir = Path("C:/BOTRD") log_dir.mkdir(exist_ok=True) app = QApplication(sys.argv) window = RoyalDreamBotUI() window.show() sys.exit(app.exec_()) except Exception as e: # Tangkap error untuk mencegah crash tanpa pemberitahuan error_path = Path("C:/BOTRD/error_log.txt") with open(error_path, "w") as f: f.write(f"Error saat menjalankan aplikasi: {str(e)}\n") import traceback f.write(traceback.format_exc()) # Tampilkan pesan error dalam dialog if QApplication.instance(): QMessageBox.critical(None, "Error Fatal", f"Terjadi error fatal: {str(e)}\n\nDetail error tersimpan di: {error_path}")
Editor is loading...
Leave a Comment