Skip to main content

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™️

Metadata

Release files for jklm 1.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jklm 1.0.3
File Size Uploaded
jklm-1.0.3.tar.gz 49.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jklm 1.0.3
File Interpreter ABI Platform
jklm-1.0.3-py3-none-any.whl Python 3 none any Details

Total release size: 83.8 kB

Release files / jklm-1.0.3.tar.gz

Download URL jklm-1.0.3.tar.gz
Size 49.4 kB
Tags Source
SHA-256 checksum
How to use checksums
9b2165af7918d57b087e096f1a71be35fb80a51a68466642b0971135357b1e3c
BLAKE2b-256 checksum
How to use checksums
884a3ab35b8bed545bcfacae70daaa83094ce6191a52d2b7bb454f4f4272e1a5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.9.21

Release files / jklm-1.0.3-py3-none-any.whl

Download URL jklm-1.0.3-py3-none-any.whl
Size 34.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
58ab4a6d8c4bc479b1f408ec0156e9b060cba88c208cf987b563ae72b55f6553
BLAKE2b-256 checksum
How to use checksums
a7955cad967584ee8ae09eb3e561a8e20b79b076e3181be033c654712b43d340
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.9.21

Release history Release notifications | RSS feed

This release

1.0.3 This release

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page