update: flow transaction service and job
This commit is contained in:
5
.env
Normal file
5
.env
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
OPENROUTER_API_KEY=your_key_here
|
||||||
|
BOT_API_SECRET=microjob_bot_secret_2024
|
||||||
|
BACKEND_URL=http://localhost:3001/api
|
||||||
|
TRANSACTION_TYPES=SERVICE,JOB
|
||||||
|
COUNT_PER_TYPE=1
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
OPENROUTER_API_KEY=your_openrouter_api_key_here
|
OPENROUTER_API_KEY=your_openrouter_api_key_here
|
||||||
BOT_API_SECRET=your_super_secret_bot_key
|
BOT_API_SECRET=your_super_secret_bot_key
|
||||||
BACKEND_URL=http://localhost:3001/api
|
BACKEND_URL=http://localhost:3001/api
|
||||||
|
TRANSACTION_TYPES=SERVICE,JOB
|
||||||
|
COUNT_PER_TYPE=1
|
||||||
29
README.md
29
README.md
@@ -4,10 +4,10 @@ Bot ini dirancang untuk mensimulasikan aktivitas nyata di platform Microjob (mem
|
|||||||
|
|
||||||
## 🚀 Fitur Utama
|
## 🚀 Fitur Utama
|
||||||
|
|
||||||
- **AI-Driven Data**: Menggunakan OpenRouter (Llama-3) untuk menghasilkan nama orang Indonesia, nama bisnis profesional, deskripsi, hingga ulasan yang realistis.
|
- **Dual Flow Simulation**: Mendukung simulasi transaksi **Layanan** (pembelian langsung) dan **Lowongan** (post job -> lamar -> terima).
|
||||||
- **Dynamic Location**: Menghasilkan koordinat (Lat/Lng) dan alamat acak di berbagai kota besar di Indonesia.
|
- **AI-Driven Data**: Menggunakan OpenRouter (Gemini/Llama) untuk menghasilkan persona Indonesia yang realistis.
|
||||||
- **Smart Simulation**: Meniru perilaku manusia dengan jeda waktu (*delay*) antar tindakan.
|
- **Natural Behavior**: Meniru perilaku manusia dengan jeda waktu acak dan pengacakan urutan transaksi.
|
||||||
- **Automated Transactions**: Melewati gerbang pembayaran (*skip payment*) untuk memudahkan simulasi internal.
|
- **Automated Transactions**: Melewati gerbang pembayaran untuk memudahkan pengisian data internal.
|
||||||
|
|
||||||
## 🛠️ Instalasi
|
## 🛠️ Instalasi
|
||||||
|
|
||||||
@@ -29,22 +29,29 @@ Buat file `.env` (salin dari `.env.example`) dan isi variabel berikut:
|
|||||||
OPENROUTER_API_KEY=your_key_here
|
OPENROUTER_API_KEY=your_key_here
|
||||||
BOT_API_SECRET=your_backend_bot_secret
|
BOT_API_SECRET=your_backend_bot_secret
|
||||||
BACKEND_URL=http://localhost:3001/api
|
BACKEND_URL=http://localhost:3001/api
|
||||||
|
TRANSACTION_TYPES=SERVICE,JOB
|
||||||
|
COUNT_PER_TYPE=1
|
||||||
```
|
```
|
||||||
|
|
||||||
- `OPENROUTER_API_KEY`: Kunci API Anda dari [OpenRouter](https://openrouter.ai/).
|
- `OPENROUTER_API_KEY`: Kunci API dari [OpenRouter](https://openrouter.ai/).
|
||||||
- `BOT_API_SECRET`: Kunci rahasia yang harus sama dengan yang ada di `.env` backend Anda (untuk otentikasi API Bot).
|
- `BOT_API_SECRET`: Kunci rahasia yang **harus sama** dengan yang ada di `.env` backend Anda.
|
||||||
- `BACKEND_URL`: URL API backend Microjob Anda.
|
- `TRANSACTION_TYPES`: Tipe transaksi yang ingin dijalankan (`SERVICE`, `JOB`, atau keduanya dipisah koma).
|
||||||
|
- `COUNT_PER_TYPE`: Berapa kali setiap tipe transaksi dijalankan dalam satu kali running.
|
||||||
|
|
||||||
## 🏁 Cara Menjalankan
|
## 🏁 Cara Menjalankan
|
||||||
|
|
||||||
### Jalankan Simulasi Sekali (On-Demand)
|
### Jalankan Sekali (On-Demand / Cron Job)
|
||||||
Untuk menjalankan satu siklus simulasi (Buat Akun -> Buat Jasa -> Order -> Selesai) secara instan:
|
Untuk menjalankan simulasi sesuai jumlah di `.env` lalu berhenti:
|
||||||
```bash
|
```bash
|
||||||
python main.py
|
python main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
### Jalankan Sebagai Scheduler (Harian)
|
### Jalankan Sebagai Background Service (VPS)
|
||||||
Bot ini mendukung penjadwalan otomatis. Anda bisa mengaktifkan bagian `schedule` di dalam `main.py` agar bot berjalan otomatis setiap hari pada jam tertentu.
|
Anda bisa menggunakan **PM2** agar bot ini berjalan terus dan otomatis restart jika terjadi error:
|
||||||
|
```bash
|
||||||
|
pm2 start main.py --name "microjob-bot" --interpreter python3
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## 📂 Struktur Folder
|
## 📂 Struktur Folder
|
||||||
|
|
||||||
|
|||||||
141
main.py
141
main.py
@@ -11,6 +11,12 @@ load_dotenv()
|
|||||||
BACKEND_URL = os.getenv("BACKEND_URL", "http://localhost:3001/api")
|
BACKEND_URL = os.getenv("BACKEND_URL", "http://localhost:3001/api")
|
||||||
BOT_SECRET = os.getenv("BOT_API_SECRET", "")
|
BOT_SECRET = os.getenv("BOT_API_SECRET", "")
|
||||||
|
|
||||||
|
# Ambil list tipe transaksi dari ENV (misal: "SERVICE,JOB")
|
||||||
|
ALLOWED_TYPES = os.getenv("TRANSACTION_TYPES", "SERVICE,JOB").split(",")
|
||||||
|
# Ambil jumlah transaksi per tipe
|
||||||
|
COUNT_PER_TYPE = int(os.getenv("COUNT_PER_TYPE", "1"))
|
||||||
|
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"x-bot-api-key": BOT_SECRET
|
"x-bot-api-key": BOT_SECRET
|
||||||
@@ -19,75 +25,154 @@ headers = {
|
|||||||
def log(msg):
|
def log(msg):
|
||||||
print(f"[BOT] {time.strftime('%Y-%m-%d %H:%M:%S')} - {msg}")
|
print(f"[BOT] {time.strftime('%Y-%m-%d %H:%M:%S')} - {msg}")
|
||||||
|
|
||||||
def run_daily_simulation():
|
def run_service_simulation():
|
||||||
log("=== Memulai Simulasi Transaksi Harian ===")
|
"""Menjalankan 1 siklus transaksi Layanan/Service"""
|
||||||
|
log("--- Memulai 1 Siklus Transaksi LAYANAN ---")
|
||||||
|
|
||||||
# 1. Generate Data AI
|
# 1. Generate Data AI
|
||||||
log("Generating data using AI...")
|
log("Generating data using AI...")
|
||||||
|
try:
|
||||||
ai_data = generate_bot_data()
|
ai_data = generate_bot_data()
|
||||||
|
except Exception as e:
|
||||||
|
log(f"AI Generation failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
# 2. Setup Accounts (Akun 1 & Akun 2)
|
# 2. Setup Accounts
|
||||||
log("Step 1: Setup Accounts")
|
log("Step 1: Setup Accounts")
|
||||||
# TODO: Panggil API Backend
|
|
||||||
req1 = requests.post(f"{BACKEND_URL}/bot/setup-account", json={"data": ai_data}, headers=headers)
|
req1 = requests.post(f"{BACKEND_URL}/bot/setup-account", json={"data": ai_data}, headers=headers)
|
||||||
if req1.status_code != 200:
|
if req1.status_code != 200:
|
||||||
log(f"Failed to setup accounts: {req1.text}")
|
log(f"Failed to setup accounts: {req1.text}")
|
||||||
return
|
return False
|
||||||
|
|
||||||
res_data1 = req1.json()
|
res_data1 = req1.json()
|
||||||
provider_id = res_data1['data']['user1']['providerProfile']['id']
|
provider_id = res_data1['data']['user1']['providerProfile']['id']
|
||||||
buyer_id = res_data1['data']['user2']['id']
|
buyer_id = res_data1['data']['user2']['id']
|
||||||
log(f"Accounts created! ProviderID: {provider_id}, BuyerID: {buyer_id}")
|
log(f"Accounts created! ProviderID: {provider_id}, BuyerID: {buyer_id}")
|
||||||
|
|
||||||
# Delay acak agar terlihat natural (misal: 10 - 30 menit)
|
time.sleep(random.randint(60, 180))
|
||||||
delay_minutes = random.randint(10, 30)
|
|
||||||
log(f"Menunggu {delay_minutes} menit sebelum membuat layanan...")
|
|
||||||
# time.sleep(delay_minutes * 60) # Uncomment in production
|
|
||||||
|
|
||||||
# 3. Create Service
|
# 3. Create Service
|
||||||
log("Step 2: Create Service")
|
log("Step 2: Create Service")
|
||||||
# TODO: Panggil API Backend
|
|
||||||
req2 = requests.post(f"{BACKEND_URL}/bot/create-service", json={
|
req2 = requests.post(f"{BACKEND_URL}/bot/create-service", json={
|
||||||
"providerId": provider_id,
|
"providerId": provider_id,
|
||||||
"data": ai_data
|
"data": ai_data
|
||||||
}, headers=headers)
|
}, headers=headers)
|
||||||
if req2.status_code != 200:
|
if req2.status_code != 200:
|
||||||
log(f"Failed to create service: {req2.text}")
|
log(f"Failed to create service: {req2.text}")
|
||||||
return
|
return False
|
||||||
|
|
||||||
res_data2 = req2.json()
|
res_data2 = req2.json()
|
||||||
service_id = res_data2['data']['id']
|
service_id = res_data2['data']['id']
|
||||||
log(f"Service created! ServiceID: {service_id}")
|
log(f"Service created! ServiceID: {service_id}")
|
||||||
|
|
||||||
# Delay tunggu pesanan (misal: 1 - 3 jam)
|
time.sleep(random.randint(120, 300))
|
||||||
delay_hours = random.randint(1, 3)
|
|
||||||
log(f"Menunggu {delay_hours} jam sebelum simulasi order...")
|
|
||||||
# time.sleep(delay_hours * 3600) # Uncomment in production
|
|
||||||
|
|
||||||
# 4. Simulate Transaction & Payment
|
# 4. Simulate Transaction
|
||||||
log("Step 3: Place Order & Skip Payment")
|
log("Step 3: Place Order (Service)")
|
||||||
# TODO: Panggil API Backend
|
|
||||||
req3 = requests.post(f"{BACKEND_URL}/bot/simulate-transaction", json={
|
req3 = requests.post(f"{BACKEND_URL}/bot/simulate-transaction", json={
|
||||||
"buyerId": buyer_id,
|
"buyerId": buyer_id,
|
||||||
"providerId": provider_id,
|
"providerId": provider_id,
|
||||||
"serviceId": service_id,
|
"serviceId": service_id,
|
||||||
"data": ai_data
|
"data": ai_data
|
||||||
}, headers=headers)
|
}, headers=headers)
|
||||||
|
|
||||||
if req3.status_code != 200:
|
if req3.status_code != 200:
|
||||||
log(f"Failed to simulate transaction: {req3.text}")
|
log(f"Failed to simulate transaction: {req3.text}")
|
||||||
return
|
return False
|
||||||
res_data3 = req3.json()
|
|
||||||
log(f"Transaction Simulated Successfully! OrderID: {res_data3['data']['orderId']}")
|
|
||||||
|
|
||||||
log("=== Simulasi Selesai ===")
|
log(f"Service Transaction Completed! OrderID: {req3.json()['data']['orderId']}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def run_job_simulation():
|
||||||
|
"""Menjalankan 1 siklus transaksi Lowongan/Job"""
|
||||||
|
log("--- Memulai 1 Siklus Transaksi JOB ---")
|
||||||
|
|
||||||
|
# 1. Generate Data AI
|
||||||
|
log("Generating data using AI...")
|
||||||
|
try:
|
||||||
|
ai_data = generate_bot_data()
|
||||||
|
except Exception as e:
|
||||||
|
log(f"AI Generation failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 2. Setup Accounts
|
||||||
|
log("Step 1: Setup Accounts")
|
||||||
|
req1 = requests.post(f"{BACKEND_URL}/bot/setup-account", json={"data": ai_data}, headers=headers)
|
||||||
|
if req1.status_code != 200:
|
||||||
|
log(f"Failed to setup accounts: {req1.text}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
res_data1 = req1.json()
|
||||||
|
provider_id = res_data1['data']['user1']['providerProfile']['id']
|
||||||
|
buyer_id = res_data1['data']['user2']['id']
|
||||||
|
log(f"Accounts created! ProviderID: {provider_id}, BuyerID: {buyer_id}")
|
||||||
|
|
||||||
|
time.sleep(random.randint(120, 300))
|
||||||
|
|
||||||
|
# 3. Simulate Job Transaction (Post Job -> Apply -> Accept -> Transact)
|
||||||
|
log("Step 2: Simulate Job Lifecycle")
|
||||||
|
req2 = requests.post(f"{BACKEND_URL}/bot/simulate-job", json={
|
||||||
|
"buyerId": buyer_id,
|
||||||
|
"providerId": provider_id,
|
||||||
|
"data": ai_data
|
||||||
|
}, headers=headers)
|
||||||
|
|
||||||
|
if req2.status_code != 200:
|
||||||
|
log(f"Failed to simulate job: {req2.text}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
res_data2 = req2.json()
|
||||||
|
log(f"Job Transaction Completed! JobID: {res_data2['data']['job']['id']}, OrderID: {res_data2['data']['transaction']['orderId']}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def run_job():
|
||||||
|
# Bersihkan whitespace dari list tipe
|
||||||
|
types = [t.strip().upper() for t in ALLOWED_TYPES if t.strip()]
|
||||||
|
|
||||||
|
log(f"=== Memulai Batch Harian: Tipe={types}, Jumlah/Tipe={COUNT_PER_TYPE} ===")
|
||||||
|
|
||||||
|
# Gabungkan semua tugas dan acak urutannya agar natural
|
||||||
|
tasks = []
|
||||||
|
for t in types:
|
||||||
|
tasks.extend([t] * COUNT_PER_TYPE)
|
||||||
|
|
||||||
|
random.shuffle(tasks)
|
||||||
|
|
||||||
|
success_count = 0
|
||||||
|
for i, task_type in enumerate(tasks):
|
||||||
|
log(f"Eksekusi ke-{i+1} dari {len(tasks)}: {task_type}")
|
||||||
|
|
||||||
|
success = False
|
||||||
|
if task_type == 'SERVICE':
|
||||||
|
success = run_service_simulation()
|
||||||
|
elif task_type == 'JOB':
|
||||||
|
success = run_job_simulation()
|
||||||
|
else:
|
||||||
|
log(f"Tipe transaksi '{task_type}' tidak dikenali. Melewati...")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if success:
|
||||||
|
success_count += 1
|
||||||
|
|
||||||
|
# Jeda antar simulasi dalam batch (15-45 menit)
|
||||||
|
if i < len(tasks) - 1:
|
||||||
|
batch_delay = random.randint(900, 2700)
|
||||||
|
log(f"Jeda antar simulasi batch: {batch_delay} detik...")
|
||||||
|
time.sleep(batch_delay)
|
||||||
|
|
||||||
|
log(f"=== Batch Selesai: {success_count}/{len(tasks)} Berhasil ===")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
log("Bot Juaskill Started!")
|
types = [t.strip().upper() for t in ALLOWED_TYPES if t.strip()]
|
||||||
|
total_trx = len(types) * COUNT_PER_TYPE
|
||||||
|
log(f"Bot Juaskill Started! (Target: {total_trx} trx/hari dari tipe: {types})")
|
||||||
|
|
||||||
# Jadwalkan bot untuk jalan 1 kali setiap hari di jam random antara 08:00 - 10:00 pagi
|
# Mode Langsung
|
||||||
# Untuk testing, kita jalankan langsung 1x
|
run_job()
|
||||||
run_daily_simulation()
|
|
||||||
|
|
||||||
# Uncomment untuk menjalankan scheduler
|
|
||||||
# schedule.every().day.at("09:00").do(run_daily_simulation)
|
# Mode Scheduler (Opsional untuk VPS)
|
||||||
|
# schedule.every().day.at("09:00").do(run_job)
|
||||||
# while True:
|
# while True:
|
||||||
# schedule.run_pending()
|
# schedule.run_pending()
|
||||||
# time.sleep(60)
|
# time.sleep(60)
|
||||||
|
|||||||
Reference in New Issue
Block a user