import json import os import random import threading from threading import Timer import time from flask import Flask, request from telegram import Update from telegram.ext import Updater, CommandHandler, CallbackContext, MessageHandler, Filters app = Flask(__name__) # Folder untuk menyimpan data permainan folder_path = 'game_databases' dictionary_bank = [] # Memuat kata dari file JSON def load_words(): global dictionary_bank try: with open('kata.json', 'r', encoding='utf-8') as file: data = json.load(file) dictionary_bank = data.get('words', []) except FileNotFoundError: print("File kata.json tidak ditemukan.") except json.JSONDecodeError: print("Kesalahan saat mendekode file JSON.") load_words() # Panggil fungsi untuk memuat kata saat bot dimulai # Struktur global untuk menyimpan data ruangan rooms = {} # Menyimpan timer untuk setiap ruangan timers = {} # Fungsi untuk membuat file dan mendata ruangan def create_files(id_group, admin, group_name, admin_name): room = { 'id': id_group, 'admin': admin, 'group_name': group_name, 'match_over': False, 'is_started': False, 'participants': [{'id': admin, 'name': admin_name, 'score': 0, 'attempts': 0}], 'leader': [], 'turn_to_play': {'current': 0}, 'questions': [], 'answers': [], 'lastkata': '' } rooms[id_group] = room os.makedirs(os.path.join(folder_path, id_group), exist_ok=True) with open(os.path.join(folder_path, id_group, 'participants.json'), 'w') as f: json.dump(room['participants'], f) # Mengundang pemain ke ruangan def invitation(id, group_name, admin): return f""" 🎮 Pendaftaran Game! 🎮 Selamat datang di grup '{group_name}'! Gunakan perintah: - /join: untuk bergabung - /mulai: untuk memulai permainan Waktu bergabung: 60 detik! ========================== /help: untuk melihat bantuan lebih lanjut """ # Fungsi untuk memberikan instruksi permainan def help_command(): return ( "Selamat datang di permainan sambung kata! \n\n" "Permainan ini adalah permainan seru di mana setiap peserta akan diberi giliran untuk menyambung kata sebelumnya.\n\n" "*Contoh:* \n" "1. Di awal permainan, bot akan memilih kata pertama yang nanti wajib disambung oleh peserta pertama. Misalnya, kata yang dipilih adalah '*MAKAN*'.\n" " - Bot: *MAK(AN)*\n" " - Bot memilih awalan 'AN' untuk nantinya disambung oleh peserta pertama.\n" "2. Peserta 1 menyambung dengan kata *ANgka*.\n" " - Bot memilih awalan 'KA' untuk nantinya disambung oleh peserta kedua.\n" " - Peserta kedua wajib menyambung kata dengan awalan yang akan ditentukan oleh bot.\n\n" "*📜 Aturan Permainan:*\n" "1. Peserta diberi giliran menjawab dengan kesempatan 3 kali. Jika menjawab salah, peserta memiliki waktu 45 detik untuk memberikan jawaban yang benar.\n" "2. Jika lebih dari 45 detik peserta tidak menjawab, mereka akan dianggap gugur, dan giliran akan dilempar ke peserta berikutnya yang masih aktif.\n" "3. Peserta akan ditandai oleh bot saat tiba gilirannya. 🔔\n" "4. Untuk menjawab, silakan balas pesan dari bot. 💬\n\n" "*🛠️ Perintah yang Tersedia:*\n" "1. /players@SambungKataRobot: *Menampilkan informasi peserta.*\n" "2. /create@SambungKataRobot: *Membuat ruangan permainan baru.*\n" "3. /join@SambungKataRobot: *Bergabung dalam permainan.*\n" "4. /start@SambungKataRobot: *Memulai permainan.*\n" "5. /leave@SambungKataRobot: *Meninggalkan permainan.*\n" "6. /kill@SambungKataRobot: *Menghentikan permainan (hanya untuk admin).* ⚠️\n" ) # Menghasilkan pertanyaan untuk pemain def question(id, target, previous_answer, next_question, time_limit=45): return f""" 🎮 *Room ID:* {id} ---------------------------- *Giliran:* {target} *Balas* untuk menjawab ---------------------------- *Kata Terakhir:* {previous_answer.upper()} *Pertanyaan Selanjutnya:* {next_question.upper()}... 🕓 *Waktu untuk menjawab:* {time_limit} detik """ def help_bot(update: Update, context: CallbackContext): context.bot.send_message(update.effective_chat.id, help_command(), parse_mode='Markdown') # Memeriksa jawaban dari pemain def is_correct_answer(word, room): normalized_word = word.lower().strip() # Normalisasi input player ke huruf kecil dan hapus spasi if not room['questions']: return {'status': False, 'message': 'Tidak ada pertanyaan yang tersedia.'} last_question = room['lastkata'] # Ambil kata terakhir yang digunakan last_letter = last_question # Ambil huruf terakhir dari kata tersebut # Cek apakah jawaban dimulai dengan huruf yang benar if normalized_word.startswith(last_letter): # Cek apakah kata itu ada dalam dictionary_bank if normalized_word in (w.lower() for w in dictionary_bank): # Cek di bank kata if have_answered(normalized_word, room['answers']): return {'status': False, 'message': 'Cari jawaban lain'} else: room['answers'].append(normalized_word) return {'status': True, 'message': 'Jawaban benar'} else: return {'status': False, 'message': 'Jawaban Salah'} else: return {'status': False, 'message': f'Jawaban harus dimulai dengan: {last_letter}'} # Mengatur timer untuk setiap giliran def handle_next_turn(update: Update, context: CallbackContext, room): # Cek jika tidak ada pemain tersisa if len(room['participants']) == 0: update.message.reply_text("🏆 Game Over 🏆\nTidak ada pemenang.") room['match_over'] = True return elif len(room['participants']) == 1: # Cek jika hanya satu pemain tersisa delete_timer(room['id']) check_winner(update, context, room) return # Ganti giliran jika belum mencapai batas room['turn_to_play']['current'] += 1 if room['turn_to_play']['current'] >= len(room['participants']): room['turn_to_play']['current'] = 0 # Reset ke pemain pertama # Cek indeks sebelum mengakses if room['turn_to_play']['current'] < len(room['participants']): set_timer(update, context, room['id']) ask_question(update, context, room) def delete_timer(id_group): """Menghapus timer yang ada (utama dan notifikasi) untuk grup tertentu.""" # Menghapus timer utama if id_group in timers: timers[id_group].cancel() # Menghentikan timer yang sedang berjalan del timers[id_group] # Menghapus timer dari dictionary print(f"Timer untuk room {id_group} telah dihapus.") # Menghapus timer notifikasi notification_timer_key = f"{id_group}_notification" if notification_timer_key in timers: timers[notification_timer_key].cancel() # Menghentikan timer notifikasi del timers[notification_timer_key] # Menghapus timer notifikasi dari dictionary print(f"Timer notifikasi untuk room {id_group} telah dihapus.") def set_timer(update: Update, context: CallbackContext, id_group): delete_timer(id_group) # Hapus timer yang ada (utama dan notifikasi) def handle_timer_expiration(): if id_group in rooms and rooms[id_group]['is_started']: room = rooms[id_group] current_index = room['turn_to_play']['current'] if current_index < len(room['participants']): current_player = room['participants'][current_index] if len(room['participants']) > 2: context.bot.send_message(update.effective_chat.id, f"Waktu habis, @{current_player['name']} tidak menjawab. Giliran beralih!") # Jika peserta tinggal satu atau dua, tidak mengirim pesan room['leader'].append(room['participants'][current_index]) room['participants'].pop(current_index) handle_next_turn(update, context, room) else: context.bot.send_message(update.effective_chat.id, "Error: Pemain tidak ditemukan.") # Timer untuk waktu habis timer_thread = Timer(45, handle_timer_expiration) timer_thread.start() timers[id_group] = timer_thread # Simpan timer utama # Notifikasi sisa waktu def notify_final_jawab_time(): if id_group in rooms and rooms[id_group]['is_started']: context.bot.send_message(update.effective_chat.id, "⏳ 15 detik tersisa untuk menjawab!") # Simpan referensi timer notifikasi notification_timer_thread = Timer(30, notify_final_jawab_time) notification_timer_thread.start() timers[f"{id_group}_notification"] = notification_timer_thread # Simpan timer notifikasi # Menghasilkan petunjuk acak untuk pertanyaan berikutnya def generate_random_clue(room): previous_answers = room['answers'] next_word = '' while True: next_word = take_random_choice(dictionary_bank) if not have_answered(next_word, previous_answers): break clue = get_clue(next_word) return next_word, clue # Memeriksa apakah jawaban sudah pernah diberikan def have_answered(current_answer, previous_answers): return current_answer in previous_answers # Mengambil huruf awal dari kata def get_clue(word): arr = word[-1] i = len(word) - 2 while i >= 0: if not is_all_consonant(arr): break arr = word[i] + arr i -= 1 return arr # Memeriksa apakah semua karakter adalah konsonan def is_all_consonant(word): return all(char not in "aeiou" for char in word) # Mengambil pilihan secara acak dari bank kata def take_random_choice(bank): return random.choice(bank) def get_name(user): if user.first_name: # Cek jika username tidak kosong return user.first_name elif user.username: # Jika username kosong, gunakan first_name return user.username else: return "Ngak Punya Nama" # Jika keduanya kosong, kembalikan "None" # Membuat ruangan permainan def create(update: Update, context: CallbackContext): user = update.message.from_user id_group = str(update.effective_chat.id) group_name = update.effective_chat.title if id_group not in rooms: create_files(id_group, user.id, group_name, get_name(user)) update.message.reply_text(invitation(id_group, group_name, get_name(user))) set_join_timer(update, context, id_group) # Panggil fungsi untuk mengatur timer pendaftaran else: update.message.reply_text('Room sudah ada!') # Mengatur timer untuk pendaftaran def set_join_timer(update: Update, context: CallbackContext, id_group): def handle_join_timer_expiration(): if id_group in rooms and not rooms[id_group]['is_started']: room = rooms[id_group] # Cek jika jumlah peserta cukup untuk memulai permainan if len(room['participants']) > 1: context.bot.send_message(update.effective_chat.id, "⏳ Waktu pendaftaran telah habis. Permainan akan segera dimulai!") start_game(update, context) # Memulai permainan else: context.bot.send_message(update.effective_chat.id, "Tidak cukup pemain, Room akan dihapus.") delete_room(id_group) # Menghapus room karena tidak cukup peserta timer_thread = Timer(60, handle_join_timer_expiration) timer_thread.start() timers[id_group] = timer_thread # Simpan timer # Notifikasi waktu tersisa def notify_remaining_time(): try: if id_group in rooms and not rooms[id_group]['is_started']: context.bot.send_message(update.effective_chat.id, "⏳ 30 detik tersisa untuk bergabung!") except Exception as e: context.bot.send_message(update.effective_chat.id, f"Terjadi kesalahan saat mengirim notifikasi: {str(e)}") def notify_final_time(): try: if id_group in rooms and not rooms[id_group]['is_started']: context.bot.send_message(update.effective_chat.id, "⏳ 10 detik tersisa untuk bergabung!") except Exception as e: context.bot.send_message(update.effective_chat.id, f"Terjadi kesalahan saat mengirim notifikasi: {str(e)}") # Timer untuk notifikasi Timer(30, notify_remaining_time).start() Timer(50, notify_final_time).start() def delete_room(id_group): """Menghapus room berdasarkan id_group.""" if id_group in rooms: del rooms[id_group] # Menghapus room dari dictionary print(f"Room {id_group} telah dihapus karena tidak cukup peserta.") # Menghapus timer pendaftaran def delete_join_timer(id_group): if id_group in timers: timers[id_group].cancel() # Menghentikan timer yang sedang berjalan del timers[id_group] print(f"Timer pendaftaran untuk room {id_group} telah dihapus.") # Bergabung ke ruangan permainan def join(update: Update, context: CallbackContext): user = update.message.from_user id_group = str(update.effective_chat.id) if id_group in rooms: room = rooms[id_group] if room['is_started']: update.message.reply_text('Permainan sudah dimulai. Anda tidak dapat bergabung sekarang.') return if any(participant['id'] == user.id for participant in room['participants']): update.message.reply_text(f'Anda sudah berada di dalam permainan, {user.first_name}!') else: if len(room['participants']) < 12: room['participants'].append({'id': user.id, 'name': get_name(user), 'score': 0, 'attempts': 0}) # Tambah percobaan untuk pemain baru update.message.reply_text(f'{user.first_name} bergabung dalam permainan!') # Cek jika peserta mencapai 12 if len(room['participants']) == 12: context.bot.send_message(update.effective_chat.id, '🎉 Batas peserta tercapai! Permainan akan segera dimulai!') room['is_started'] = True start_game(update, context) # Mulai permainan otomatis else: update.message.reply_text('Pendaftaran sudah penuh. Tidak bisa bergabung lagi.') else: update.message.reply_text('Tidak ada ruangan aktif. Silakan buat ruangan terlebih dahulu.') # Meninggalkan ruangan permainan def leave(update: Update, context: CallbackContext): user = update.message.from_user id_group = str(update.effective_chat.id) if id_group in rooms: room = rooms[id_group] if user.id == room['admin']: delete_timer(id_group) # Hapus timer jika admin keluar del rooms[id_group] update.message.reply_text(f'Ruangan ditutup oleh admin {user.first_name}.') else: index = next((i for i, p in enumerate(room['participants']) if p['id'] == user.id), None) if index is not None: room['participants'].pop(index) update.message.reply_text(f'{user.first_name} meninggalkan permainan.') else: update.message.reply_text('Tidak ada ruangan aktif untuk ditinggalkan.') # Memulai permainan def start_game(update: Update, context: CallbackContext): user = update.message.from_user id_group = str(update.effective_chat.id) if id_group in rooms: room = rooms[id_group] print(f"Jumlah peserta: {len(room['participants'])}") # Debugging: cek jumlah peserta print(f"Status permainan: {'dimulai' if room['is_started'] else 'belum dimulai'}") # Debugging: cek status permainan # Cek apakah permainan sudah dimulai if room['is_started']: context.bot.send_message( update.effective_chat.id, 'Permainan sudah dimulai.' ) return # Cek jumlah peserta if len(room['participants']) < 2: context.bot.send_message( update.effective_chat.id, 'Tidak cukup pemain untuk memulai game. Minimal 2 pemain diperlukan. Pendaftaran dibatalkan' ) delete_room(id_group) return # Jika semua kondisi terpenuhi, mulai permainan room['is_started'] = True delete_join_timer(id_group) # Hapus timer pendaftaran set_timer(update, context, id_group) # Atur timer untuk permainan start_question(update, context, room) # Mulai pertanyaan else: update.message.reply_text('Tidak ada ruangan aktif. Silakan buat ruangan terlebih dahulu.') # Menanyakan pertanyaan kepada pemain def ask_question(update: Update, context: CallbackContext, room): if not room['is_started']: return target = room['participants'][room['turn_to_play']['current']]['name'] # Format pertanyaan sesuai permintaan previous_answer = room['answers'][-1] if room['answers'] else '' clue = room['lastkata'] context.bot.send_message(update.effective_chat.id, question(room['id'], target, previous_answer, clue), parse_mode='Markdown') # pertanyaan pertama def start_question(update: Update, context: CallbackContext, room): if not room['is_started']: return # Menghasilkan kata pertanyaan dan petunjuk question_word, clue = generate_random_clue(room) room['questions'].append(question_word) # Simpan kata terakhir yang digunakan current_index = room['turn_to_play']['current'] target = room['participants'][current_index]['name'] # Format pertanyaan sesuai permintaan previous_answer = room['questions'][-1] if room['questions'] else '' room['lastkata'] = clue context.bot.send_message(update.effective_chat.id, question(room['id'], target, previous_answer, clue), parse_mode='Markdown') # Menangani jawaban dari pemain def answer(update: Update, context: CallbackContext): user = update.message.from_user id_group = str(update.effective_chat.id) # Cek apakah pesan adalah balasan pada pesan dari bot dengan ID tertentu if not update.message.reply_to_message or update.message.reply_to_message.from_user.id != 6921935430: return if id_group in rooms: room = rooms[id_group] if room['is_started']: current_player = room['participants'][room['turn_to_play']['current']] if user.id != current_player['id']: update.message.reply_text('Bukan giliran kamu untuk menjawab!') return user_answer = update.message.text.strip() # Ambil jawaban dari pengguna correct_answer_check = is_correct_answer(user_answer, room) if correct_answer_check['status']: delete_timer(id_group) # Hapus timer jika jawaban benar # Tambahkan jawaban dan petunjuk room['lastkata'] = get_clue(user_answer) room['participants'][room['turn_to_play']['current']]['score'] += 1 # Pindahkan ke pemain berikutnya handle_next_turn(update, context, room) else: delete_timer(id_group) if correct_answer_check['message'] == 'Cari jawaban lain': update.message.reply_text(f"{correct_answer_check['message']}") set_timer(update, context, id_group) return # Hentikan eksekusi room['participants'][room['turn_to_play']['current']]['attempts'] += 1 remaining_attempts = 3 - room['participants'][room['turn_to_play']['current']]['attempts'] if remaining_attempts <= 0: # Jika sudah tidak ada kesempatan idx = room['turn_to_play']['current'] room['leader'].append(room['participants'][idx]) room['participants'].pop(idx) # Hapus pemain dari daftar handle_next_turn(update, context, room) # Panggil fungsi handle_next_turn untuk mengganti giliran else: set_timer(update, context, id_group) update.message.reply_text(f"{correct_answer_check['message']} Kamu punya {remaining_attempts} kesempatan lagi.") else: update.message.reply_text('Permainan belum dimulai.') else: update.message.reply_text('Tidak ada ruang aktif.') # Memeriksa pemenang def check_winner(update: Update, context: CallbackContext, room): if len(room['participants']) == 1: winner = room['participants'][0] id_group = str(update.effective_chat.id) winner['score'] += 20 # Tambahkan poin pemenang # Mendapatkan informasi kontak pemenang gold = '🥇' silver = '🥈' str_message = '🎉 **Permainan Selesai!** 🎉\n\n' str_message += f'Selamat untuk **{winner["name"]}**! Kamu berhasil mendapat **20 poin** tambahan!\n\n' str_message += '**Peringkat Akhir:**\n------------------------------------------------------\n' room['leader'].append(winner) # Mengurutkan peserta berdasarkan skor room['leader'] = quick_sort(room['leader']) for i, leader in enumerate(room['leader']): if i == 0: str_message += f"{i + 1}. **{leader['name']}** ({leader['score']}) {gold}\n" elif i == 1: str_message += f"{i + 1}. **{leader['name']}** ({leader['score']}) {silver}\n" else: str_message += f"{i + 1}. **{leader['name']}** ({leader['score']})\n" str_message += '------------------------------------------------------\n\n' str_message += 'Terima kasih sudah bermain! Jangan lupa siapkan kata-kata seru untuk permainan selanjutnya. Sampai jumpa di sambung kata berikutnya ya! ' context.bot.send_message(update.effective_chat.id, str_message, parse_mode='Markdown') room['match_over'] = True # Tandai bahwa permainan telah berakhir if id_group in rooms: room = rooms[id_group] delete_timer(id_group) del rooms[id_group] def quick_sort(leader): if len(leader) <= 1: return leader else: pivot = leader[len(leader) // 2] # Pilih elemen tengah sebagai pivot left = [x for x in leader if x.get('score', 0) > pivot.get('score', 0)] # Lebih besar dari pivot middle = [x for x in leader if x.get('score', 0) == pivot.get('score', 0)] # Sama dengan pivot right = [x for x in leader if x.get('score', 0) < pivot.get('score', 0)] # Lebih kecil dari pivot return quick_sort(left) + middle + quick_sort(right) # Gabungkan hasil # Menampilkan klasemen pemain def players(update: Update, context: CallbackContext): user = update.message.from_user id_group = str(update.effective_chat.id) if id_group in rooms: room = rooms[id_group] participant_list = "Daftar Pemain:\n" for i, participant in enumerate(room['participants'], start=1): participant_list += f"{i}. **{participant['name']}** (Score: {participant['score']})\n" update.message.reply_text(participant_list, parse_mode='Markdown') else: update.message.reply_text('Tidak ada ruang aktif.') # Menghentikan permainan def kill_game(update: Update, context: CallbackContext): user = update.message.from_user id_group = str(update.effective_chat.id) if id_group in rooms: room = rooms[id_group] if user.id == room['admin']: delete_timer(id_group) # Hapus timer sebelum menghentikan permainan del rooms[id_group] # Hapus data ruangan update.message.reply_text(f'Permainan dihentikan oleh admin {user.first_name}. Semua peserta telah dikeluarkan.') else: update.message.reply_text('Hanya admin yang dapat menghentikan permainan.') else: update.message.reply_text('Tidak ada permainan aktif untuk dihentikan.') @app.route('/webhook', methods=['POST']) def webhook(): update = request.get_json() if update: updater.dispatcher.process_update(Update.de_json(update, updater.bot)) return 'OK', 200 @app.route('/') def index(): return "Bot is running", 200 def main(): global updater updater = Updater("6921935430:AAGmSrcmn7Jc5_egkDjqeLXVhHjkPUoXu-4", use_context=True) # Ganti dengan token bot Anda dp = updater.dispatcher dp.add_handler(CommandHandler("create", create)) dp.add_handler(CommandHandler("join", join)) dp.add_handler(CommandHandler("leave", leave)) dp.add_handler(CommandHandler("mulai", start_game)) dp.add_handler(CommandHandler("kill", kill_game)) dp.add_handler(CommandHandler("players", players)) dp.add_handler(CommandHandler("help", help_bot)) dp.add_handler(MessageHandler(Filters.text & ~Filters.command, answer)) # Jalankan bot di thread terpisah threading.Thread(target=updater.start_polling).start() # Jalankan Flask app.run(host='0.0.0.0', port=8000) if __name__ == '__main__': main()