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()