Untitled
unknown
plain_text
9 months ago
26 kB
6
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