From f269dba019cf195034c8b13d8b0480973f83b648 Mon Sep 17 00:00:00 2001 From: Madleyym Date: Thu, 13 Feb 2025 19:37:24 +0700 Subject: [PATCH 01/13] Enhance reply generation with rate limit handling and retry logic --- discord.py | 57 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/discord.py b/discord.py index ba92195..82a3a89 100644 --- a/discord.py +++ b/discord.py @@ -22,51 +22,62 @@ def log_message(message): print(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {message}") def generate_reply(prompt, use_google_ai=True, use_file_reply=False, language="id"): - """Membuat balasan, menghindari duplikasi jika menggunakan Google Gemini AI""" - - global last_ai_response # Gunakan variabel global agar dapat diakses di seluruh sesi - + """Membuat balasan dengan penanganan rate limit""" + + global last_ai_response + if use_file_reply: log_message("💬 Menggunakan pesan dari file sebagai balasan.") return {"candidates": [{"content": {"parts": [{"text": get_random_message()}]}}]} - + if use_google_ai: # Pilihan bahasa if language == "en": ai_prompt = f"{prompt}\n\nRespond with only one sentence in casual urban English, like a natural conversation, and do not use symbols." else: ai_prompt = f"{prompt}\n\nBerikan 1 kalimat saja dalam bahasa gaul daerah Jakarta seperti obrolan dan jangan gunakan simbol apapun." - + url = f'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key={google_api_key}' headers = {'Content-Type': 'application/json'} data = {'contents': [{'parts': [{'text': ai_prompt}]}]} - - for attempt in range(3): # Coba sampai 3 kali jika AI mengulang pesan yang sama + + max_retries = 5 + base_delay = 1 # Start with 1 second delay + + for attempt in range(max_retries): try: response = requests.post(url, headers=headers, json=data) + + if response.status_code == 429: # Rate limit exceeded + retry_delay = base_delay * (2 ** attempt) # Exponential backoff + log_message(f"⚠️ Rate limit exceeded, waiting {retry_delay} seconds before retry...") + time.sleep(retry_delay) + continue + response.raise_for_status() ai_response = response.json() - - # Ambil teks dari respons AI + + # Check for duplicate response response_text = ai_response['candidates'][0]['content']['parts'][0]['text'] - - # Cek apakah respons AI sama dengan yang terakhir if response_text == last_ai_response: - log_message("⚠️ AI memberikan balasan yang sama, mencoba ulang...") - continue # Coba lagi dengan permintaan baru + if attempt < max_retries - 1: + log_message("⚠️ Duplicate response, retrying...") + time.sleep(base_delay) + continue - last_ai_response = response_text # Simpan respons terbaru + last_ai_response = response_text return ai_response - + except requests.exceptions.RequestException as e: log_message(f"⚠️ Request failed: {e}") - return None - - log_message("⚠️ AI terus memberikan balasan yang sama, menggunakan respons terakhir yang tersedia.") - return {"candidates": [{"content": {"parts": [{"text": last_ai_response or 'Maaf, tidak dapat membalas pesan.'}]}}]} - - else: - return {"candidates": [{"content": {"parts": [{"text": get_random_message()}]}}]} + if attempt < max_retries - 1: + retry_delay = base_delay * (2 ** attempt) + log_message(f"Retrying in {retry_delay} seconds...") + time.sleep(retry_delay) + else: + return {"candidates": [{"content": {"parts": [{"text": "Maaf, sedang ada masalah dengan sistem AI. Coba lagi nanti ya!"}]}}]} + + return {"candidates": [{"content": {"parts": [{"text": get_random_message()}]}}]} def get_random_message(): """Mengambil pesan acak dari file pesan.txt""" From 547fce7b5e1cf68b9c93e3913bb9a213b7b6fe46 Mon Sep 17 00:00:00 2001 From: Madleyym Date: Thu, 13 Feb 2025 19:46:47 +0700 Subject: [PATCH 02/13] Update README.md with improved instructions for obtaining Discord token and setting up the project --- README.md | 91 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 69 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index bf8a0c4..10cdb6a 100644 --- a/README.md +++ b/README.md @@ -2,30 +2,25 @@ * Tutorial with VIDEO : https://youtu.be/B6IkggY6ct8 -# Get your discord token, different ways: - -First method: - -Open your browser and activate developer mode - -Login your discord account - -Go to developer mode and click on XHR tab - -Find login request and click - -Go to Responses tab and find token value - -Copy that token +## Prerequisites +Pastikan kamu sudah menginstal: +- [Python](https://www.python.org/downloads/) (Centang **"Add Python to PATH"** saat instalasi!) +- Git Bash (jika belum punya, unduh dari [Git for Windows](https://git-scm.com/downloads)) -Second method: - -Make sure that you already login into your discord account - -Go to Developers tool in your browser +# Get your discord token, different ways: -Find javascript console, and paste code below: +### First method: +1. Open your browser and activate developer mode +2. Login your discord account +3. Go to developer mode and click on XHR tab +4. Find login request and click +5. Go to Responses tab and find token value +6. Copy that token +### Second method: +1. Make sure that you already login into your discord account +2. Go to Developers tool in your browser +3. Find javascript console, and paste code below: ``` ( @@ -46,7 +41,6 @@ Find javascript console, and paste code below: ).exports.default.getToken() ``` - # HOW TO GET GEMINI API : go to : https://aistudio.google.com/apikey @@ -57,6 +51,55 @@ go to : https://aistudio.google.com/apikey # PASTE YOUR DISCORD TOKEN & GEMINI API IN FILE .ENV +## Steps to Setup + +### 1. Buka Git Bash dan Masuk ke Direktori Proyek +Jika belum ada, clone repository proyek terlebih dahulu: +```bash + git clone +``` +Lalu masuk ke folder proyek: +```bash + cd discord-gemini +``` + +### 2. Buat Virtual Environment +Jalankan perintah berikut untuk membuat virtual environment: +```bash + python -m venv discord +``` +Jika `python` tidak dikenali, coba: +```bash + python3 -m venv discord +``` + +### 3. Aktifkan Virtual Environment +Karena di **Windows**, jalankan perintah berikut: +```bash + source discord/Scripts/activate +``` +Jika berhasil, prompt akan berubah menjadi `(discord)`, menandakan virtual environment aktif. + +### 4. Install Dependencies +Jalankan: +```bash + pip install -r requirements.txt +``` + +### 5. Menjalankan Proyek +Setelah instalasi selesai, kamu bisa menjalankan proyek sesuai dengan instruksi yang ada di repository ini. + +### 6. (Opsional) Menonaktifkan Virtual Environment +Jika ingin keluar dari virtual environment, jalankan: +```bash + deactivate +``` + +## Troubleshooting +Jika ada error seperti **"python not found"**, pastikan: +- Python sudah ditambahkan ke PATH +- Coba jalankan dengan `python3` daripada `python` + # Youtube Channel : * https://www.youtube.com/@SHAREITHUB_COM @@ -65,3 +108,7 @@ go to : https://aistudio.google.com/apikey # Group Telegram : * https://t.me/DISS_SHAREITHUB + +--- +Semoga berhasil! 🚀 Jika ada kendala, silakan cek dokumentasi atau tanyakan di forum komunitas. 😃 + From 3d00f147e4c878ee5caec13800902cb1bf6bed78 Mon Sep 17 00:00:00 2001 From: Madleyym Date: Thu, 13 Feb 2025 20:18:49 +0700 Subject: [PATCH 03/13] Refactor code structure for improved readability and maintainability --- discord.py | 332 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 196 insertions(+), 136 deletions(-) diff --git a/discord.py b/discord.py index 82a3a89..dfe700a 100644 --- a/discord.py +++ b/discord.py @@ -1,14 +1,11 @@ import json import time -import shareithub import os import random import requests -from dotenv import load_dotenv from datetime import datetime -from shareithub import shareithub +from dotenv import load_dotenv -shareithub() load_dotenv() discord_token = os.getenv('DISCORD_TOKEN') @@ -16,22 +13,38 @@ last_message_id = None bot_user_id = None -last_ai_response = None # Menyimpan respons AI terakhir + +# Rate limiting parameters +GEMINI_RETRY_DELAY = 60 # Delay in seconds before retrying Gemini API +DISCORD_RETRY_DELAY = 5 # Delay in seconds before retrying Discord API +MAX_RETRIES = 3 def log_message(message): print(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {message}") -def generate_reply(prompt, use_google_ai=True, use_file_reply=False, language="id"): - """Membuat balasan dengan penanganan rate limit""" - - global last_ai_response - - if use_file_reply: - log_message("💬 Menggunakan pesan dari file sebagai balasan.") - return {"candidates": [{"content": {"parts": [{"text": get_random_message()}]}}]} - - if use_google_ai: - # Pilihan bahasa +def get_random_message(): + try: + with open('pesan.txt', 'r', encoding='utf-8') as file: + lines = [line.strip() for line in file.readlines() if line.strip()] + if lines: + return random.choice(lines) + else: + log_message("⚠️ pesan.txt is empty") + return "No messages available" + except FileNotFoundError: + log_message("⚠️ pesan.txt not found") + return "pesan.txt not found" + except Exception as e: + log_message(f"⚠️ Error reading pesan.txt: {str(e)}") + return "Error reading messages" + +def generate_gemini_reply(prompt, language="id", retry_count=0): + """Generate reply using Google Gemini AI with retry logic""" + if retry_count >= MAX_RETRIES: + log_message("❌ Max retries reached for Gemini API") + return None + + try: if language == "en": ai_prompt = f"{prompt}\n\nRespond with only one sentence in casual urban English, like a natural conversation, and do not use symbols." else: @@ -41,153 +54,200 @@ def generate_reply(prompt, use_google_ai=True, use_file_reply=False, language="i headers = {'Content-Type': 'application/json'} data = {'contents': [{'parts': [{'text': ai_prompt}]}]} - max_retries = 5 - base_delay = 1 # Start with 1 second delay + response = requests.post(url, headers=headers, json=data) - for attempt in range(max_retries): - try: - response = requests.post(url, headers=headers, json=data) - - if response.status_code == 429: # Rate limit exceeded - retry_delay = base_delay * (2 ** attempt) # Exponential backoff - log_message(f"⚠️ Rate limit exceeded, waiting {retry_delay} seconds before retry...") - time.sleep(retry_delay) - continue - - response.raise_for_status() - ai_response = response.json() - - # Check for duplicate response - response_text = ai_response['candidates'][0]['content']['parts'][0]['text'] - if response_text == last_ai_response: - if attempt < max_retries - 1: - log_message("⚠️ Duplicate response, retrying...") - time.sleep(base_delay) - continue - - last_ai_response = response_text - return ai_response - - except requests.exceptions.RequestException as e: - log_message(f"⚠️ Request failed: {e}") - if attempt < max_retries - 1: - retry_delay = base_delay * (2 ** attempt) - log_message(f"Retrying in {retry_delay} seconds...") - time.sleep(retry_delay) - else: - return {"candidates": [{"content": {"parts": [{"text": "Maaf, sedang ada masalah dengan sistem AI. Coba lagi nanti ya!"}]}}]} - - return {"candidates": [{"content": {"parts": [{"text": get_random_message()}]}}]} - -def get_random_message(): - """Mengambil pesan acak dari file pesan.txt""" - try: - with open('pesan.txt', 'r') as file: - lines = file.readlines() - if lines: - return random.choice(lines).strip() - else: - log_message("File pesan.txt kosong.") - return "Tidak ada pesan yang tersedia." - except FileNotFoundError: - log_message("File pesan.txt tidak ditemukan.") - return "File pesan.txt tidak ditemukan." + if response.status_code == 429: + log_message(f"⏳ Rate limited by Gemini API, waiting {GEMINI_RETRY_DELAY} seconds...") + time.sleep(GEMINI_RETRY_DELAY) + return generate_gemini_reply(prompt, language, retry_count + 1) + + response.raise_for_status() + result = response.json() + + if 'candidates' in result and len(result['candidates']) > 0: + text = result['candidates'][0]['content']['parts'][0]['text'] + log_message(f"✅ Gemini response: {text}") + return text + + return None + + except Exception as e: + log_message(f"⚠️ Gemini API error: {str(e)}") + if retry_count < MAX_RETRIES: + log_message(f"⏳ Retrying in {GEMINI_RETRY_DELAY} seconds...") + time.sleep(GEMINI_RETRY_DELAY) + return generate_gemini_reply(prompt, language, retry_count + 1) + return None + +def send_message(channel_id, message_text, reply_to=None, reply_mode=True, retry_count=0): + """Send message to Discord with retry logic""" + if retry_count >= MAX_RETRIES: + log_message("❌ Max retries reached for Discord API") + return False -def send_message(channel_id, message_text, reply_to=None, reply_mode=True): - """Mengirim pesan ke Discord, bisa dengan atau tanpa reply""" headers = { 'Authorization': f'{discord_token}', 'Content-Type': 'application/json' } payload = {'content': message_text} - - # Hanya tambahkan reply jika reply_mode diaktifkan if reply_mode and reply_to: payload['message_reference'] = {'message_id': reply_to} try: - response = requests.post(f"https://discord.com/api/v9/channels/{channel_id}/messages", json=payload, headers=headers) + response = requests.post( + f"https://discord.com/api/v9/channels/{channel_id}/messages", + json=payload, + headers=headers + ) + + if response.status_code == 429: + retry_after = response.json().get('retry_after', DISCORD_RETRY_DELAY) + log_message(f"⏳ Rate limited by Discord, waiting {retry_after} seconds...") + time.sleep(retry_after) + return send_message(channel_id, message_text, reply_to, reply_mode, retry_count + 1) + response.raise_for_status() - - if response.status_code == 201: - log_message(f"✅ Sent message: {message_text}") - else: - log_message(f"⚠️ Failed to send message: {response.status_code}") - except requests.exceptions.RequestException as e: - log_message(f"⚠️ Request error: {e}") - -def auto_reply(channel_id, read_delay, reply_delay, use_google_ai, use_file_reply, language, reply_mode): - """Fungsi untuk auto-reply di Discord dengan menghindari duplikasi AI""" + log_message(f"✅ Sent: {message_text}") + return True + + except Exception as e: + log_message(f"⚠️ Discord API error: {str(e)}") + if retry_count < MAX_RETRIES: + log_message(f"⏳ Retrying in {DISCORD_RETRY_DELAY} seconds...") + time.sleep(DISCORD_RETRY_DELAY) + return send_message(channel_id, message_text, reply_to, reply_mode, retry_count + 1) + return False + +def auto_reply(channel_id, read_delay, reply_delay, use_gemini, language, reply_mode): global last_message_id, bot_user_id headers = {'Authorization': f'{discord_token}'} try: - bot_info_response = requests.get('https://discord.com/api/v9/users/@me', headers=headers) - bot_info_response.raise_for_status() - bot_user_id = bot_info_response.json().get('id') - except requests.exceptions.RequestException as e: - log_message(f"⚠️ Failed to retrieve bot information: {e}") + bot_info = requests.get('https://discord.com/api/v9/users/@me', headers=headers) + bot_info.raise_for_status() + bot_user_id = bot_info.json().get('id') + log_message(f"✅ Bot started with ID: {bot_user_id}") + except Exception as e: + log_message(f"⚠️ Failed to get bot info: {str(e)}") return + consecutive_errors = 0 while True: try: - response = requests.get(f'https://discord.com/api/v9/channels/{channel_id}/messages', headers=headers) - response.raise_for_status() - - if response.status_code == 200: - messages = response.json() - if len(messages) > 0: - most_recent_message = messages[0] - message_id = most_recent_message.get('id') - author_id = most_recent_message.get('author', {}).get('id') - message_type = most_recent_message.get('type', '') - - if (last_message_id is None or int(message_id) > int(last_message_id)) and author_id != bot_user_id and message_type != 8: - user_message = most_recent_message.get('content', '') - log_message(f"💬 Received message: {user_message}") + actual_read_delay = read_delay + random.uniform(1, 5) + actual_reply_delay = reply_delay + random.uniform(1, 5) - result = generate_reply(user_message, use_google_ai, use_file_reply, language) - response_text = result['candidates'][0]['content']['parts'][0]['text'] if result else "Maaf, tidak dapat membalas pesan." - - log_message(f"⏳ Waiting {reply_delay} seconds before replying...") - time.sleep(reply_delay) - send_message(channel_id, response_text, reply_to=message_id if reply_mode else None, reply_mode=reply_mode) + response = requests.get( + f'https://discord.com/api/v9/channels/{channel_id}/messages', + headers=headers + ) + + if response.status_code == 429: + retry_after = response.json().get('retry_after', read_delay) + log_message(f"⏳ Rate limited, waiting {retry_after} seconds...") + time.sleep(retry_after) + continue + + response.raise_for_status() + messages = response.json() + consecutive_errors = 0 # Reset error counter on success + + if messages and isinstance(messages, list): + latest_message = messages[0] + message_id = latest_message.get('id') + author_id = latest_message.get('author', {}).get('id') + + if (last_message_id is None or int(message_id) > int(last_message_id)) and author_id != bot_user_id: + user_message = latest_message.get('content', '') + log_message(f"📥 Received: {user_message}") + + response_text = None + if use_gemini: + response_text = generate_gemini_reply(user_message, language) + + if response_text is None: + if use_gemini: + log_message("⚠️ Falling back to pesan.txt") + response_text = get_random_message() + + time.sleep(actual_reply_delay) + + if send_message(channel_id, response_text, message_id if reply_mode else None, reply_mode): last_message_id = message_id - log_message(f"⏳ Waiting {read_delay} seconds before checking for new messages...") - time.sleep(read_delay) - except requests.exceptions.RequestException as e: - log_message(f"⚠️ Request error: {e}") - time.sleep(read_delay) - -if __name__ == "__main__": - use_reply = input("Ingin menggunakan fitur auto-reply? (y/n): ").lower() == 'y' - channel_id = input("Masukkan ID channel: ") - - if use_reply: - use_google_ai = input("Gunakan Google Gemini AI untuk balasan? (y/n): ").lower() == 'y' - use_file_reply = input("Gunakan pesan dari file pesan.txt? (y/n): ").lower() == 'y' - reply_mode = input("Ingin membalas pesan (reply) atau hanya mengirim pesan? (reply/send): ").lower() == 'reply' - language_choice = input("Pilih bahasa untuk balasan (id/en): ").lower() + time.sleep(actual_read_delay) + + except Exception as e: + log_message(f"⚠️ Error in main loop: {str(e)}") + consecutive_errors += 1 + + if consecutive_errors >= 5: + log_message("❌ Too many consecutive errors, waiting 5 minutes...") + time.sleep(300) + consecutive_errors = 0 + else: + time.sleep(actual_read_delay) - if language_choice not in ["id", "en"]: - log_message("⚠️ Bahasa tidak valid, default ke bahasa Indonesia.") - language_choice = "id" +def random_message_mode(channel_id, interval): + log_message("✅ Random message mode started") + consecutive_errors = 0 + + while True: + try: + message_text = get_random_message() + if send_message(channel_id, message_text, reply_mode=False): + consecutive_errors = 0 + actual_interval = interval + random.uniform(1, 5) + time.sleep(actual_interval) + else: + consecutive_errors += 1 + if consecutive_errors >= 5: + log_message("❌ Too many consecutive errors, waiting 5 minutes...") + time.sleep(300) + consecutive_errors = 0 + + except Exception as e: + log_message(f"⚠️ Error in random message mode: {str(e)}") + time.sleep(interval) - read_delay = int(input("Set Delay Membaca Pesan Terbaru (dalam detik): ")) - reply_delay = int(input("Set Delay Balas Pesan (dalam detik): ")) +if __name__ == "__main__": + try: + print("\n=== Discord Auto Reply Bot ===\n") + + mode = input("Pilih mode (1: Auto Reply, 2: Random Message): ").strip() + channel_id = input("Masukkan ID channel: ").strip() - log_message(f"✅ Mode reply {'aktif' if reply_mode else 'non-reply'} dalam bahasa {'Indonesia' if language_choice == 'id' else 'Inggris'}...") - auto_reply(channel_id, read_delay, reply_delay, use_google_ai, use_file_reply, language_choice, reply_mode) + if mode == "1": + use_gemini = input("Gunakan Google Gemini AI? (y/n): ").lower() == 'y' + reply_mode = input("Mode balas pesan? (y/n): ").lower() == 'y' + language = input("Pilih bahasa (id/en): ").lower() + + if language not in ["id", "en"]: + language = "id" + log_message("⚠️ Invalid language, using Indonesian") + + read_delay = int(input("Delay membaca pesan (detik): ")) + reply_delay = int(input("Delay balas pesan (detik): ")) - else: - send_interval = int(input("Set Interval Pengiriman Pesan (dalam detik): ")) - log_message("✅ Mode kirim pesan acak aktif...") + print("\nKonfigurasi Bot:") + print(f"- Menggunakan Gemini AI: {'Ya' if use_gemini else 'Tidak (menggunakan pesan.txt)'}") + print(f"- Mode Reply: {'Ya' if reply_mode else 'Tidak'}") + print(f"- Bahasa: {'Indonesia' if language == 'id' else 'Inggris'}") + print(f"- Delay Membaca: {read_delay}s") + print(f"- Delay Membalas: {reply_delay}s\n") - while True: - message_text = get_random_message() - send_message(channel_id, message_text, reply_mode=False) - log_message(f"⏳ Waiting {send_interval} seconds before sending the next message...") - time.sleep(send_interval) + auto_reply(channel_id, read_delay, reply_delay, use_gemini, language, reply_mode) + + elif mode == "2": + interval = int(input("Interval kirim pesan (detik): ")) + random_message_mode(channel_id, interval) + + else: + print("⚠️ Mode tidak valid") + + except KeyboardInterrupt: + print("\n\n✅ Bot dihentikan oleh user") + except Exception as e: + print(f"\n⚠️ Error fatal: {str(e)}") \ No newline at end of file From f2670d80cef39af9a597bad01731a872fd37ae0e Mon Sep 17 00:00:00 2001 From: Madleyym Date: Fri, 14 Feb 2025 03:27:22 +0700 Subject: [PATCH 04/13] Update README.md with specific repository URL for cloning --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 10cdb6a..5d0d417 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ go to : https://aistudio.google.com/apikey ### 1. Buka Git Bash dan Masuk ke Direktori Proyek Jika belum ada, clone repository proyek terlebih dahulu: ```bash - git clone + git clone ``` Lalu masuk ke folder proyek: ```bash From 2d0dc0d245cedf118bb62137e72a9d128673d4ea Mon Sep 17 00:00:00 2001 From: Madleyym Date: Fri, 14 Feb 2025 03:30:25 +0700 Subject: [PATCH 05/13] Fix formatting of clone command in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5d0d417..e3571e3 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ go to : https://aistudio.google.com/apikey ### 1. Buka Git Bash dan Masuk ke Direktori Proyek Jika belum ada, clone repository proyek terlebih dahulu: ```bash - git clone + git clone https://github.com/Madleyym/discord-gemini/tree/main ``` Lalu masuk ke folder proyek: ```bash From ba2f6e82e689ef4fd7544ccfe4e745c818206bee Mon Sep 17 00:00:00 2001 From: Madleyym Date: Fri, 14 Feb 2025 03:31:42 +0700 Subject: [PATCH 06/13] Update README.md to clarify instructions for pasting Discord token and Gemini API in .ENV file --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index e3571e3..1e22863 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,7 @@ go to : https://aistudio.google.com/apikey * Create API Key * Copy API Key -# PASTE YOUR DISCORD TOKEN & GEMINI API IN FILE .ENV - -## Steps to Setup +## PASTE YOUR DISCORD TOKEN & GEMINI API IN FILE .ENV ### 1. Buka Git Bash dan Masuk ke Direktori Proyek Jika belum ada, clone repository proyek terlebih dahulu: From 90fd4c8d40f8b6bc6316904a70d38000a361205e Mon Sep 17 00:00:00 2001 From: Madleyym Date: Fri, 14 Feb 2025 03:37:32 +0700 Subject: [PATCH 07/13] Enhance send_message function with improved retry logic and exponential backoff for Discord API --- discord.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/discord.py b/discord.py index dfe700a..0bc56bc 100644 --- a/discord.py +++ b/discord.py @@ -80,7 +80,7 @@ def generate_gemini_reply(prompt, language="id", retry_count=0): return None def send_message(channel_id, message_text, reply_to=None, reply_mode=True, retry_count=0): - """Send message to Discord with retry logic""" + """Send message to Discord with improved retry logic and exponential backoff""" if retry_count >= MAX_RETRIES: log_message("❌ Max retries reached for Discord API") return False @@ -102,9 +102,11 @@ def send_message(channel_id, message_text, reply_to=None, reply_mode=True, retry ) if response.status_code == 429: - retry_after = response.json().get('retry_after', DISCORD_RETRY_DELAY) - log_message(f"⏳ Rate limited by Discord, waiting {retry_after} seconds...") - time.sleep(retry_after) + retry_after = float(response.json().get('retry_after', DISCORD_RETRY_DELAY)) + # Add exponential backoff + wait_time = retry_after * (2 ** retry_count) + log_message(f"⏳ Rate limited by Discord, waiting {wait_time} seconds...") + time.sleep(wait_time) return send_message(channel_id, message_text, reply_to, reply_mode, retry_count + 1) response.raise_for_status() @@ -114,8 +116,10 @@ def send_message(channel_id, message_text, reply_to=None, reply_mode=True, retry except Exception as e: log_message(f"⚠️ Discord API error: {str(e)}") if retry_count < MAX_RETRIES: - log_message(f"⏳ Retrying in {DISCORD_RETRY_DELAY} seconds...") - time.sleep(DISCORD_RETRY_DELAY) + # Add exponential backoff for other errors too + wait_time = DISCORD_RETRY_DELAY * (2 ** retry_count) + log_message(f"⏳ Retrying in {wait_time} seconds...") + time.sleep(wait_time) return send_message(channel_id, message_text, reply_to, reply_mode, retry_count + 1) return False From 6469b384adf5a4b146ca988226903274df072d21 Mon Sep 17 00:00:00 2001 From: Madleyym Date: Fri, 14 Feb 2025 03:43:26 +0700 Subject: [PATCH 08/13] Add chinesse.txt file --- chinesse.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 chinesse.txt diff --git a/chinesse.txt b/chinesse.txt new file mode 100644 index 0000000..e69de29 From ed8cf441bba4dd568946e8f59e3e644a1d46fe59 Mon Sep 17 00:00:00 2001 From: Madleyym Date: Fri, 14 Feb 2025 03:44:11 +0700 Subject: [PATCH 09/13] Add english.txt file --- english.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 english.txt diff --git a/english.txt b/english.txt new file mode 100644 index 0000000..e69de29 From e7e7b0a67e42e612def4265205e751d6382f062e Mon Sep 17 00:00:00 2001 From: Madleyym Date: Fri, 14 Feb 2025 03:44:16 +0700 Subject: [PATCH 10/13] Add indonesian.txt file --- indonesian.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 indonesian.txt diff --git a/indonesian.txt b/indonesian.txt new file mode 100644 index 0000000..e69de29 From 1ea4a32434d813d360c8bf224b0f922e3afc2058 Mon Sep 17 00:00:00 2001 From: Madleyym Date: Fri, 14 Feb 2025 03:44:37 +0700 Subject: [PATCH 11/13] Add russian.txt file --- russian.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 russian.txt diff --git a/russian.txt b/russian.txt new file mode 100644 index 0000000..e69de29 From 57b0a86dd8dab9573995002e500552c8858007bb Mon Sep 17 00:00:00 2001 From: Madleyym Date: Fri, 14 Feb 2025 03:44:51 +0700 Subject: [PATCH 12/13] Update pesan.txt with new gaming-related phrases --- pesan.txt | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/pesan.txt b/pesan.txt index 7026912..33c003a 100644 --- a/pesan.txt +++ b/pesan.txt @@ -1,4 +1,20 @@ -pesan 1 -pesan 2 -pesan 3 -seterusnya +lol yeah that's so true +just take it easy, no need to rush +gaming all night long, let's go +just send it, don't overthink it +that's hilarious fr fr +you're absolutely right +wow your skills are insane +still learning, got a long way to go +anyone up for some games? +come on guys, join the fun +noted, I'll give it a try later +chill gaming is the best gaming +you're too funny lmao +haven't played in ages, missing it +been too busy, rarely online +aight bet, let's grind some rank +gaming till dawn, let's go +you're cracked at this fr +pro player alert! +hop in vc let's go \ No newline at end of file From 796173ce726fbc14b2f216198f6f3b9f7a14f9a7 Mon Sep 17 00:00:00 2001 From: Madleyym Date: Fri, 14 Feb 2025 04:10:47 +0700 Subject: [PATCH 13/13] Refactor send_message function for improved rate limit handling and retry logic --- discord.py | 153 +++++++++++++++++++++++++---------------------------- 1 file changed, 73 insertions(+), 80 deletions(-) diff --git a/discord.py b/discord.py index 0bc56bc..cde136a 100644 --- a/discord.py +++ b/discord.py @@ -79,12 +79,8 @@ def generate_gemini_reply(prompt, language="id", retry_count=0): return generate_gemini_reply(prompt, language, retry_count + 1) return None -def send_message(channel_id, message_text, reply_to=None, reply_mode=True, retry_count=0): - """Send message to Discord with improved retry logic and exponential backoff""" - if retry_count >= MAX_RETRIES: - log_message("❌ Max retries reached for Discord API") - return False - +def send_message(channel_id, message_text, reply_to=None, reply_mode=True): + """Send message to Discord with improved rate limit handling""" headers = { 'Authorization': f'{discord_token}', 'Content-Type': 'application/json' @@ -94,40 +90,33 @@ def send_message(channel_id, message_text, reply_to=None, reply_mode=True, retry if reply_mode and reply_to: payload['message_reference'] = {'message_id': reply_to} - try: - response = requests.post( - f"https://discord.com/api/v9/channels/{channel_id}/messages", - json=payload, - headers=headers - ) - - if response.status_code == 429: - retry_after = float(response.json().get('retry_after', DISCORD_RETRY_DELAY)) - # Add exponential backoff - wait_time = retry_after * (2 ** retry_count) - log_message(f"⏳ Rate limited by Discord, waiting {wait_time} seconds...") - time.sleep(wait_time) - return send_message(channel_id, message_text, reply_to, reply_mode, retry_count + 1) + while True: + try: + response = requests.post( + f"https://discord.com/api/v9/channels/{channel_id}/messages", + json=payload, + headers=headers + ) - response.raise_for_status() - log_message(f"✅ Sent: {message_text}") - return True - - except Exception as e: - log_message(f"⚠️ Discord API error: {str(e)}") - if retry_count < MAX_RETRIES: - # Add exponential backoff for other errors too - wait_time = DISCORD_RETRY_DELAY * (2 ** retry_count) - log_message(f"⏳ Retrying in {wait_time} seconds...") - time.sleep(wait_time) - return send_message(channel_id, message_text, reply_to, reply_mode, retry_count + 1) - return False + if response.status_code == 429: + retry_after = float(response.json().get('retry_after', DISCORD_RETRY_DELAY)) + log_message(f"⏳ Rate limited by Discord, waiting {retry_after} seconds...") + time.sleep(retry_after) + continue # Retry immediately after timeout + + response.raise_for_status() + log_message(f"✅ Sent: {message_text}") + return True + + except Exception as e: + log_message(f"⚠️ Discord API error: {str(e)}") + return False def auto_reply(channel_id, read_delay, reply_delay, use_gemini, language, reply_mode): global last_message_id, bot_user_id headers = {'Authorization': f'{discord_token}'} - + try: bot_info = requests.get('https://discord.com/api/v9/users/@me', headers=headers) bot_info.raise_for_status() @@ -137,80 +126,84 @@ def auto_reply(channel_id, read_delay, reply_delay, use_gemini, language, reply_ log_message(f"⚠️ Failed to get bot info: {str(e)}") return - consecutive_errors = 0 + # Set initial last_message_id + try: + response = requests.get( + f'https://discord.com/api/v9/channels/{channel_id}/messages?limit=1', + headers=headers + ) + response.raise_for_status() + messages = response.json() + if messages and isinstance(messages, list) and len(messages) > 0: + last_message_id = messages[0].get('id') + log_message(f"✅ Initial message ID set to: {last_message_id}") + except Exception as e: + log_message(f"⚠️ Failed to get initial message ID: {str(e)}") + while True: try: - actual_read_delay = read_delay + random.uniform(1, 5) - actual_reply_delay = reply_delay + random.uniform(1, 5) - + # Get latest messages, limit to 5 to catch any missed messages response = requests.get( - f'https://discord.com/api/v9/channels/{channel_id}/messages', + f'https://discord.com/api/v9/channels/{channel_id}/messages?limit=5', headers=headers ) if response.status_code == 429: - retry_after = response.json().get('retry_after', read_delay) + retry_after = float(response.json().get('retry_after', read_delay)) log_message(f"⏳ Rate limited, waiting {retry_after} seconds...") time.sleep(retry_after) continue response.raise_for_status() messages = response.json() - consecutive_errors = 0 # Reset error counter on success if messages and isinstance(messages, list): - latest_message = messages[0] - message_id = latest_message.get('id') - author_id = latest_message.get('author', {}).get('id') + # Sort messages by ID to process them in order + messages.sort(key=lambda x: int(x.get('id', 0))) - if (last_message_id is None or int(message_id) > int(last_message_id)) and author_id != bot_user_id: - user_message = latest_message.get('content', '') - log_message(f"📥 Received: {user_message}") - - response_text = None - if use_gemini: - response_text = generate_gemini_reply(user_message, language) - - if response_text is None: - if use_gemini: - log_message("⚠️ Falling back to pesan.txt") - response_text = get_random_message() - - time.sleep(actual_reply_delay) + for message in messages: + message_id = message.get('id') + author_id = message.get('author', {}).get('id') - if send_message(channel_id, response_text, message_id if reply_mode else None, reply_mode): - last_message_id = message_id + # Check if this is a new message from someone else + if (last_message_id is None or int(message_id) > int(last_message_id)) and author_id != bot_user_id: + user_message = message.get('content', '') + log_message(f"📥 Received new message: {user_message}") - time.sleep(actual_read_delay) + response_text = None + if use_gemini: + response_text = generate_gemini_reply(user_message, language) + + if response_text is None: + if use_gemini: + log_message("⚠️ Falling back to pesan.txt") + response_text = get_random_message() + + # Wait before replying + if reply_delay > 0: + time.sleep(reply_delay) + + # Send the reply + if send_message(channel_id, response_text, message_id if reply_mode else None, reply_mode): + last_message_id = message_id + log_message(f"✅ Updated last message ID to: {last_message_id}") + + # Wait before checking for new messages + if read_delay > 0: + time.sleep(read_delay) except Exception as e: log_message(f"⚠️ Error in main loop: {str(e)}") - consecutive_errors += 1 - - if consecutive_errors >= 5: - log_message("❌ Too many consecutive errors, waiting 5 minutes...") - time.sleep(300) - consecutive_errors = 0 - else: - time.sleep(actual_read_delay) + time.sleep(read_delay) def random_message_mode(channel_id, interval): log_message("✅ Random message mode started") - consecutive_errors = 0 while True: try: message_text = get_random_message() - if send_message(channel_id, message_text, reply_mode=False): - consecutive_errors = 0 - actual_interval = interval + random.uniform(1, 5) - time.sleep(actual_interval) - else: - consecutive_errors += 1 - if consecutive_errors >= 5: - log_message("❌ Too many consecutive errors, waiting 5 minutes...") - time.sleep(300) - consecutive_errors = 0 + send_message(channel_id, message_text, reply_mode=False) + time.sleep(interval) except Exception as e: log_message(f"⚠️ Error in random message mode: {str(e)}")