Skip to main content

Python client for the party game JKLM.fun

Project description

image

jklm-py-client

Flexible JKLM.fun client built for Python 3

✨ Features

  • Versatile implementation allowing you to build anything
  • Integrated JKLM client functions (chat, join, etc)
  • Proxy support allows safe and anonymous connection
  • Simple to use api

Installation

Install via pip

pip install -U jklm

Documentation

  1. Make A Room
  2. Simple Usage
  3. Proxy Usage
  4. Set A Profile Picture
  5. Connections (Twitch, Discord)
  6. Chat Client
  7. PopSauce
  8. Bomb Party
    • Someone make a PR

PopSauce Answers

[!NOTE]
As of 18/02/25 only 9775/9806 (~99.7%) of PopSauce are indexed due to the nature of scraping them x log(x)

popsauce.zip

heavy zipped folder of *all the questions, answers and data (image, etc)

popsauce_pairs.txt

light weight txt file of all the challenge_hash:answer pairings

extract_pairs.py

simple script to extract pairs from the unzipped popsauce folder to popsauce_pairs.txt

Make A Room

Here is an example of how to create a room

from jklm import JKLM

jklm = JKLM("BOT")

res = jklm.start_room("selector", True, "test")
print(res) # {'url': 'https://falcon.jklm.fun', 'room_code': 'RNGG'}

Simple Usage

Here is a simple example of how to connect to a room and join a round (gamemode agnostic)

from jklm import JKLM

def main(room_id):
    username = "EXAMPLE_BOT"

    # Initialize JKLM session
    session = JKLM(username)

    try:
        # Attempt to connect to specified room
        session.connect(room_id)
        print("[.] Successfully joined", room_id)
    except Exception as e:
        # Handle connection failures gracefully
        print("[X] Failed to join room:", e)
        return

    # Join active game round after successful connection
    session.join_round()

if __name__ == '__main__':
    # Launch bot and join room "ABUM"
    main("ABUM")

Proxy Usage

Here is an example of how to connect to a room using a proxy

from jklm import JKLM

def main(room_id):
    username = "EXAMPLE_BOT"

    proxy = {
        "host": "...",
        "port": 8080,
        "type": "http", # http, https, socks4, socks5
        "auth": ("username", "password")
    }

    # Initialize JKLM session
    session = JKLM(username, proxy=proxy)

    try:
        # Attempt to connect to specified room
        session.connect(room_id)
        print("[.] Successfully joined", room_id)
    except Exception as e:
        # Handle connection failures gracefully
        print("[X] Failed to join room:", e)
        return

    # Join active game round after successful connection
    session.join_round()

if __name__ == '__main__':
    # Launch bot and join room "ABUM"
    main("ABUM")

Set A Profile Picture

Here is an example of how to set a profile picture

from jklm import JKLM
import time

import psutil
import os


def main(room_id):
    image = open("logo.png", "rb").read() # Try to keep image 128x128

    username = "BOT_" + str(time.time())[-5:]
    session = JKLM(username, pfp=image)

    try:
        session.connect(room_id)
        print("[.] Successfully joined", room_id)
    except Exception as e:
        print("[X] Failed to join room:", e)
        return

    input("")
    current_system_pid = os.getpid()

    ThisSystem = psutil.Process(current_system_pid)
    ThisSystem.terminate()


if __name__ == '__main__':
    main("KMPW")

Connections (Twitch, Discord)

Here is an example of how to connect to a room using a twitch or discord account

from jklm import JKLM
import time

import psutil
import os


def main(room_id):
    image = open("logo.png", "rb").read() # Try to keep image 128x128

    username = "BOT_" + str(time.time())[-5:]
    session = JKLM(username, pfp=image, connection={
        "service": "twitch", # twitch, discord
        "username": "jklm_bot", # Isn't validated
        "token": "YOUR_TOKEN", # Validated
        "expiration": 0 # Isn't validated
    })

    try:
        session.connect(room_id)
        print("[.] Successfully joined", room_id)
    except Exception as e:
        print("[X] Failed to join room:", e)
        return

    input("")
    current_system_pid = os.getpid()

    ThisSystem = psutil.Process(current_system_pid)
    ThisSystem.terminate()


if __name__ == '__main__':
    main("KMPW")

Chat Client

Here is an example of how to connect to a room and chat back and forth with other users

from jklm import JKLM
import time

import psutil
import os

def main(room_id):
    
    username = "BOT_" + str(time.time())[-5:]
    session = JKLM(username)

    def chat_handler(code, raw_data):
        event = raw_data[0]
        data = raw_data[1]

        # 0 CODE = Kicked from room (event = TYPE ["KICKED" | "BAN"], data = INFO)

        if (code == 0):
            print("[X] Kicked from room:", data)
            return

        match event:
            case "chat":
                message = raw_data[2]

                print(f"[CHAT] {data['nickname']}: {message}")
                pass
            case "chatterAdded":
                print(f"[+] {data} joined the room")
            case "chatterRemoved":
                print(f"[-] {data} left the room")
            case "setPlayerCount":
                print(f"[!] {data} players in the room")
            case _:
                print(f"[UNHANDLED CHAT EVENT] {event}:", data)

    try:
        session.connect(room_id, chat_handler=chat_handler)
        print("[.] Successfully joined", room_id)
    except Exception as e:
        print("[X] Failed to join room:", e)
        return

    try:
        while True:
            message = input("")

            if message == "":
                raise KeyboardInterrupt

            session.send_chat_message(message)

            time.sleep(1)

            if session.reconnect_attempts > 5:
                print("Reconnect attempts exceeded")
                current_system_pid = os.getpid()

                ThisSystem = psutil.Process(current_system_pid)
                ThisSystem.terminate()
                break
    except KeyboardInterrupt:
        current_system_pid = os.getpid()

        ThisSystem = psutil.Process(current_system_pid)
        ThisSystem.terminate()

if __name__ == '__main__':
    main("ABUM")

Pop Sauce

Challenge Hasher

Here is an example of how to hash a challenge

from jklm import JKLM
import time

import hashlib

import psutil
import os

expecting_image = False
challenge = {
    "end_time": 0,
    "image": None,
    "prompt": None,
    "text": None,
    "hash": None
}

def sha1(input):
    if isinstance(input, str):
        input = input.encode()
    return hashlib.sha1(input).hexdigest()

def main(room_id):
    global expecting_image, challenge
    
    username = "BOT_" + str(time.time())[-5:]
    session = JKLM(username)

    def game_handler(code, raw_data):
        global expecting_image, challenge
        event = raw_data[0]
        data = raw_data[1]

        # -1 CODE = Image data
        # 0 CODE = Kicked from room (event = TYPE ["KICKED" | "BAN"], data = INFO)

        if (code == 0):
            print("[X] Kicked from room:", data)
            return

        if (code == -1):
            asset_type = challenge["image"]["type"]
            extension = ""

            match asset_type:
                case "image/svg+xml":
                    extension = "svg"
                case "image/png":
                    extension = "png"
                case "image/jpeg":
                    extension = "jpeg"

            challenge["hash"] = sha1(challenge["prompt"].encode() + raw_data)
            challenge["image"]["extension"] = extension

            print("[?] Challenge Hash:", challenge["hash"])
            print("[?] Image has been saved to image." + extension)

            with open("image." + extension, "wb") as f:
                f.write(raw_data)

            return

        match event:
            case "startChallenge":
                print("\n[!] New Challenge Started")

                challenge["end_time"] = data["endTime"]
                challenge["image"] = data["image"]
                challenge["prompt"] = data["prompt"]
                challenge["text"] = data["text"]

                if challenge["image"]:
                    expecting_image = True
                    print("[?] Image Challenge", challenge["prompt"])
                else:
                    expecting_image = False
                    challenge["hash"] = sha1(challenge["prompt"] + challenge["text"])

                    print("[?] Text Challenge:", challenge["prompt"])
                    print("[?] Challenge Hash:", challenge["hash"])
                    print("[?]", challenge["text"])


            case "endChallenge":
                print("\n[!] Challenge Ended")
                print("[X] Correct Answer:", data["source"])
                
            case "setPlayerState":
                event, peer_id, data = raw_data
                    
                guess = data["guess"]
                found_answer = data["hasFoundSource"]
                points = data["points"]
                elapsed_time = data["elapsedTime"]

                player = list(filter(lambda x: x["profile"]["peerId"] == peer_id, session.game["players"]))[0]

                if found_answer:
                    print(f"[!] {player['profile']['nickname']} with {points} points guessed it in {elapsed_time} seconds")
                else:
                    print(f"[!] {player['profile']['nickname']} with {points} points guessed {guess}")

            case "updatePlayer":
                event, peer_id, data, online = raw_data

                player = list(filter(lambda x: x["profile"]["peerId"] == peer_id, session.game["players"]))[0]

                if online:
                    print(f"[+] {player['profile']['nickname']} reconnected to the game")
                else:
                    print(f"[-] {player['profile']['nickname']} disconnected from the game")

            case "addPlayer":
                print(f"[+] {data['profile']['nickname']} joined the game")

            case _:
                print(f"[UNHANDLED GAME EVENT] {event}:", raw_data)

    try:
        session.connect(room_id, game_handler=game_handler)
        print("[.] Successfully joined", room_id)
    except Exception as e:
        print("[X] Failed to join room:", e)
        return

    session.join_round()

    # Checks if a challenge is already started

    if ("challenge" in session.game["milestone"]):
        print("\n[!] Challenge already started")
        
        current_challenge = session.game["milestone"]["challenge"]
        
        # If the challenge has ended but the next one hasn't started yet endTime will be null
        challenge["end_time"] = current_challenge["endTime"] if "endTime" in current_challenge else 0
        challenge["image"] = current_challenge["image"]
        challenge["prompt"] = current_challenge["prompt"]
        challenge["text"] = current_challenge["text"]

        if challenge["image"]:
            expecting_image = True
            print("[?] Image Challenge", challenge["prompt"])
        else:
            expecting_image = False

            challenge["hash"] = sha1(challenge["prompt"] + challenge["text"])

            print("[?] Text Challenge:", challenge["prompt"])
            print("[?]", challenge["text"])
            print("[?] Challenge Hash:", challenge["hash"])

    input("")
    current_system_pid = os.getpid()

    ThisSystem = psutil.Process(current_system_pid)
    ThisSystem.terminate()

if __name__ == '__main__':
    main("ABUM")

Auto-Answer

Here is an example of how to auto-answer and send the answer to the chat

from jklm import JKLM
import requests
import time

import hashlib

import psutil
import os

expecting_image = False
challenge = {
    "end_time": 0,
    "image": None,
    "prompt": None,
    "text": None,
    "hash": None
}

res = requests.get("https://cdn.jsdelivr.net/gh/joseph-gerald/jklm-py-client@main/answers/popsauce_pairs.txt")
answers = {x.split(":", 1)[0]: x.split(":", 1)[1].strip() for x in res.text.split("\n") if x}

def sha1(input):
    if isinstance(input, str):
        input = input.encode()
    return hashlib.sha1(input).hexdigest()

def main(room_id):
    global expecting_image, challenge
    
    username = "BOT_" + str(time.time())[-5:]
    session = JKLM(username)

    def game_handler(code, raw_data):
        global expecting_image, challenge
        event = raw_data[0]
        data = raw_data[1]

        # -1 CODE = Image data
        # 0 CODE = Kicked from room (event = TYPE ["KICKED" | "BAN"], data = INFO)

        if (code == 0):
            print("[X] Kicked from room:", data)
            return

        if (code == -1):
            asset_type = challenge["image"]["type"]
            extension = ""

            match asset_type:
                case "image/svg+xml":
                    extension = "svg"
                case "image/png":
                    extension = "png"
                case "image/jpeg":
                    extension = "jpeg"

            challenge["hash"] = sha1(challenge["prompt"].encode() + raw_data)
            challenge["image"]["extension"] = extension

            print("[?] Challenge Hash:", challenge["hash"])
            
            answer = answers.get(challenge["hash"])

            if answer:
                print("[!] Answer is indexed:", answer)
                session.submit_guess(answer)
                session.send_chat_message(answer)
            else:
                print("[!] Answer was not indexed")

            return

        match event:
            case "startChallenge":
                print("\n[!] New Challenge Started")

                challenge["end_time"] = data["endTime"]
                challenge["image"] = data["image"]
                challenge["prompt"] = data["prompt"]
                challenge["text"] = data["text"]

                if challenge["image"]:
                    expecting_image = True
                    print("[?] Image Challenge", challenge["prompt"])
                else:
                    expecting_image = False
                    challenge["hash"] = sha1(challenge["prompt"] + challenge["text"])

                    print("[?] Text Challenge:", challenge["prompt"])
                    print("[?] Challenge Hash:", challenge["hash"])
                    print("[?]", challenge["text"])

                    answer = answers.get(challenge["hash"])
                    if answer:
                        print("[!] Answer is indexed:", answer)
                        session.submit_guess(answer)
                        session.send_chat_message(answer)
                    else:
                        print("[!] Answer was not indexed")


            case "endChallenge":
                print("\n[!] Challenge Ended")
                print("[X] Correct Answer:", data["source"])
                
            case "setPlayerState":
                event, peer_id, data = raw_data
                    
                guess = data["guess"]
                found_answer = data["hasFoundSource"]
                points = data["points"]
                elapsed_time = data["elapsedTime"]

                print(f"[!] {peer_id} {data}")
                
                if peer_id == session.peer_id:
                    return

                player = list(filter(lambda x: x["profile"]["peerId"] == peer_id, session.game["players"]))[0]

                if found_answer:
                    print(f"[!] {player['profile']['nickname']} with {points} points guessed it in {elapsed_time} seconds")
                else:
                    print(f"[!] {player['profile']['nickname']} with {points} points guessed {guess}")

            case "updatePlayer":
                event, peer_id, data, online = raw_data

                player = list(filter(lambda x: x["profile"]["peerId"] == peer_id, session.game["players"]))[0]

                if online:
                    print(f"[+] {player['profile']['nickname']} reconnected to the game")
                else:
                    print(f"[-] {player['profile']['nickname']} disconnected from the game")

            case "addPlayer":
                print(f"[+] {data['profile']['nickname']} joined the game")

            case _:
                print(f"[UNHANDLED GAME EVENT] {event}:", raw_data)

    try:
        session.connect(room_id, game_handler=game_handler)
        print("[.] Successfully joined", room_id)
    except Exception as e:
        print("[X] Failed to join room:", e)
        return

    session.join_round()

    # Checks if a challenge is already started

    if ("challenge" in session.game["milestone"]):
        print("\n[!] Challenge already started")
        
        current_challenge = session.game["milestone"]["challenge"]
        
        # If the challenge has ended but the next one hasn't started yet endTime will be null
        challenge["end_time"] = current_challenge["endTime"] if "endTime" in current_challenge else 0
        challenge["image"] = current_challenge["image"]
        challenge["prompt"] = current_challenge["prompt"]
        challenge["text"] = current_challenge["text"]

        if challenge["image"]:
            expecting_image = True
            print("[?] Image Challenge", challenge["prompt"])
        else:
            expecting_image = False

            challenge["hash"] = sha1(challenge["prompt"] + challenge["text"])

            print("[?] Text Challenge:", challenge["prompt"])
            print("[?]", challenge["text"])
            print("[?] Challenge Hash:", challenge["hash"])

    input("")
    current_system_pid = os.getpid()

    ThisSystem = psutil.Process(current_system_pid)
    ThisSystem.terminate()

if __name__ == '__main__':
    main("ABUM")

Bomb Party

soon™️

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jklm_client-1.0.3.tar.gz (49.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jklm_client-1.0.3-py3-none-any.whl (34.5 kB view details)

Uploaded Python 3

File details

Details for the file jklm_client-1.0.3.tar.gz.

File metadata

  • Download URL: jklm_client-1.0.3.tar.gz
  • Upload date:
  • Size: 49.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.9.21

File hashes

Hashes for jklm_client-1.0.3.tar.gz
Algorithm Hash digest
SHA256 4bb20a40549c6bf15c1cbe1e4446e0872bf6352485844bdbbc1480323b386e12
MD5 f4493eda0c2d4a4b434b63204b1554fb
BLAKE2b-256 1de9ba4b62b57d95970eefb0bc44808e2bb39f5364aba937c55d973ecf06bb72

See more details on using hashes here.

File details

Details for the file jklm_client-1.0.3-py3-none-any.whl.

File metadata

  • Download URL: jklm_client-1.0.3-py3-none-any.whl
  • Upload date:
  • Size: 34.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.9.21

File hashes

Hashes for jklm_client-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 213f68d3f672694b443c6208eca022090a5ad1b0100b6bca216245cd64540ab3
MD5 1f4f8fee58689b6c0f916d000f9a4316
BLAKE2b-256 67787ce95be878f87e8e165424287047f1ce55fb95118244c3154c079958f34e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page