6 |
7 | # How to use
8 |
9 | * INSTALL TERMUX ON YOUR ANDROID PHONE
10 | * Install python: `pkg install python`
11 | * Install git: `pkg install git`
12 | * clone the repo: `git clone https://github.com/CPScript/Kitty-Tools`
13 | * Go to `LITE`'s dir: `cd Kitty-Tools/LITE`
14 | * Run script: `python lite.py`
15 |
16 | ---
17 |
18 | > This version of `Kitty-Tools` is ment to be ran on Android!
19 |
20 | ---
21 |
22 |
23 | © 2025 Kitty Tools LITE. All rights reserved.
24 | | BETA v1.10
25 |
26 |
--------------------------------------------------------------------------------
/Kitty/scripts/colors.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | try:
4 | from colorama import Style, Fore
5 |
6 | except ModuleNotFoundError:
7 | os.system("pip install colorama")
8 | import random
9 |
10 | all_col = [
11 | Style.BRIGHT + Fore.RED, Style.BRIGHT + Fore.CYAN,
12 | Style.BRIGHT + Fore.LIGHTCYAN_EX, Style.BRIGHT + Fore.LIGHTBLUE_EX,
13 | Style.BRIGHT + Fore.LIGHTCYAN_EX, Style.BRIGHT + Fore.LIGHTMAGENTA_EX,
14 | Style.BRIGHT + Fore.LIGHTYELLOW_EX
15 | ]
16 |
17 | ran = random.choice(all_col)
18 |
19 | lg = Style.BRIGHT + Fore.LIGHTGREEN_EX
20 | g = Style.BRIGHT + Fore.GREEN
21 | lc = Style.BRIGHT + Fore.LIGHTCYAN_EX
22 | c = Style.BRIGHT + Fore.CYAN
23 | ly = Style.BRIGHT + Fore.LIGHTYELLOW_EX
24 | y = Style.BRIGHT + Fore.YELLOW
25 | r = Style.BRIGHT + Fore.RED
26 | lr = Style.BRIGHT + Fore.LIGHTRED_EX
27 |
--------------------------------------------------------------------------------
/Kitty/Info/scripts/colors.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | try:
4 | from colorama import Style, Fore
5 |
6 | except ModuleNotFoundError:
7 | os.system("pip install colorama")
8 | import random
9 |
10 | all_col = [
11 | Style.BRIGHT + Fore.RED, Style.BRIGHT + Fore.CYAN,
12 | Style.BRIGHT + Fore.LIGHTCYAN_EX, Style.BRIGHT + Fore.LIGHTBLUE_EX,
13 | Style.BRIGHT + Fore.LIGHTCYAN_EX, Style.BRIGHT + Fore.LIGHTMAGENTA_EX,
14 | Style.BRIGHT + Fore.LIGHTYELLOW_EX
15 | ]
16 |
17 | ran = random.choice(all_col)
18 |
19 | lg = Style.BRIGHT + Fore.LIGHTGREEN_EX
20 | g = Style.BRIGHT + Fore.GREEN
21 | lc = Style.BRIGHT + Fore.LIGHTCYAN_EX
22 | c = Style.BRIGHT + Fore.CYAN
23 | ly = Style.BRIGHT + Fore.LIGHTYELLOW_EX
24 | y = Style.BRIGHT + Fore.YELLOW
25 | r = Style.BRIGHT + Fore.RED
26 | lr = Style.BRIGHT + Fore.LIGHTRED_EX
27 |
--------------------------------------------------------------------------------
/extra.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Website
4 | * {infomation here}
5 |
6 | ## LITE
7 | * This is a version of Kitty-Tools that is ment to run on slower systems as it doesn't have as many graphical effects so it should be faster, it also doesn't have as many non requires modules so it shouldn't run into any error!
8 |
9 | ## Definitions
10 | * apt: Advanced Package Tool (High level package installer)
11 | * pkg: Package
12 | * install git: Installs a github Repo via a terminal
13 | * cd: Change directory (Used to go to a file via terminal)
14 | * cmd: Command (You can type "CMD" in the windows start bar to open its command prompt!)
15 | * repo: repository (If your reading this than your looking at a repository's readme.md)
16 | * dir: directory (File location)
17 |
18 |
19 | ## Versions
20 | * You can past versions here:
21 |
22 | 
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.github/FAQ/readme.md:
--------------------------------------------------------------------------------
1 | ## Table of Contents
2 | - [Network responses](#network)
3 | - [Bot issues](#bot)
4 | - [Contributing & Issues](#contributing)
5 |
6 | ## Network
7 |
8 | There are some error codes you might get in return when attempting to use the answer hack. Here are a few of those listed to go ahead and help before you make an issue!
9 |
10 | * **HTTP Error : 403 - Forbidden** ~ The quiz you are attempting to acces using the quiz ID is most likely private. Try again and see if you get the same responce.
11 | * **HTTP Error :400 - Bad Request** ~ You will see this when you type the quiz ID incorrectly. Try re-typing it.
12 | * **HTTP Error :404 - Not found** ~ The quiz you are attempting to get answer from does not exist.
13 |
14 | ## Bot
15 |
16 | There are some isses with the current bot implementation.
17 |
18 | * **NPM: Not found** ~ You will need to install NPM properly. This project will automate the full process.
19 |
20 | * **The bot only supports 4 answer quizes.** It wont be able to do TOF, polls, typing questions, etc. ~ Im working on makiing a new bot that will support more quiz formats. the current bot was not made by me but edited.
21 |
22 |
23 | ## **Contributing**
24 | * Contributing to this project helps me out a lot. **All pull requests and issues are looked over by yours truely**
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: A bug was found within the Kitty-Tools repository
4 | title: ""
5 | labels: bug
6 | ---
7 |
8 | >[!WARNING]
9 | > **BEFORE YOU MAKE THIS ISSUE**;
10 | > * Make sure that there isn't an answer to your issue in [FAQ](https://github.com/CPScript/Kitty-Tools/blob/main/.github/FAQ/readme.md).
11 | > * Make sure that the issue hasnt be brought up in [past issues](https://github.com/CPScript/Kitty-Tools/issues?q=is%3Aissue%20state%3Aclosed).
12 | > * Fully fill out this template as much as you possibly can.
13 | > * **BLANK ISSUES WILL BE CLOSED!**
14 | >
15 | > **If you make an issue without this template or checking the FAQ then your issue will be ignored/closed.**
16 |
17 | ---
18 |
19 | ## **1.** Describe the bug
20 | * A clear and concise description of what the bug is.
21 |
22 |
23 | ## **2.** To Reproduce
24 | * Steps to reproduce the behavior:
25 | - Please tell us what you ran in the terminal to get such error
26 |
27 | ## **3.** Screenshots
28 | * If applicable, add screenshots to help explain your problem.
29 |
30 | ## **4.** Desktop (please complete the following information):
31 | - OS: [e.g. iOS]
32 | - Browser [e.g. chrome, safari]
33 | - Version [e.g. 22]
34 |
35 | ## **5.** Smartphone (please complete the following information):
36 | - Device: [e.g. iPhone6]
37 | - OS: [e.g. iOS8.1]
38 | - Browser [e.g. stock browser, safari]
39 | - Version [e.g. 22]
40 |
41 | ## **6.** Additional context
42 | Add any other context about the problem here.
43 |
--------------------------------------------------------------------------------
/Kitty/Info/main.py:
--------------------------------------------------------------------------------
1 | import os
2 | import platform
3 | import sys
4 | from scripts.sprint import sprint
5 | from scripts.colors import ran, y, r, g, c
6 | import time
7 | from subprocess import call
8 |
9 |
10 | def clear():
11 | system = platform.system().lower()
12 |
13 | if system == 'windows':
14 | _ = os.system('cls')
15 | elif system == 'linux' or system == 'darwin':
16 | _ = os.system('clear')
17 | elif system == 'android':
18 | _ = subprocess.run(['termux-exec', 'sh', '-c', 'clear'], check=False)
19 | else:
20 | print(f"Unsupported platform, please use Kitty-Tools LITE '{system}'")
21 | print(
22 | f"For more info go to https://github.com/CPScript/Kitty-Tools/more/moreinfo.md"
23 | )
24 |
25 |
26 | clear()
27 |
28 | print("[Extra-InfoChart v2.7")
29 | print("[====================")
30 | print("[1] GO BACK ")
31 | print("[2] More info ")
32 | print("[3] Contributors ")
33 | print("[4] Legal (COC & TOS)")
34 | print("[====================")
35 | choice = input("[U] >> ")
36 |
37 | if choice == "1":
38 | print(" ")
39 | print("Loading...")
40 | time.sleep(1)
41 | print("\n" * 64)
42 | call(["python", "main.py"])
43 |
44 | if choice == "2":
45 | print(" ")
46 | print("Loading...")
47 | time.sleep(1)
48 | print("\n" * 64)
49 | call(["python", "Kitty/Info/extra.py"])
50 |
51 | if choice == "3":
52 | print(" ")
53 | print("Loading...")
54 | time.sleep(1)
55 | print("\n" * 64)
56 | call(["python", "Kitty/Info/contributors.py"])
57 |
58 | if choice == "4":
59 | print(" ")
60 | print("Loading...")
61 | time.sleep(1)
62 | print("\n" * 64)
63 | call(["python", "Kitty/Info/legal.py"])
64 |
--------------------------------------------------------------------------------
/Kitty/htu.py:
--------------------------------------------------------------------------------
1 | import os
2 | import platform
3 | import sys
4 | from scripts.sprint import sprint
5 | from scripts.colors import ran,y,r,g,c
6 | print("\n" * 32)
7 | print("""
8 | $$\ $$\ $$\
9 | $$ | $$ | $$ |
10 | $$ | $$ | $$$$$$\ $$\ $$\ $$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$\
11 | $$$$$$$$ |$$ __$$\ $$ | $$ | $$ | \_$$ _| $$ __$$\ $$ | $$ |$$ _____|$$ __$$\
12 | $$ __$$ |$$ / $$ |$$ | $$ | $$ | $$ | $$ / $$ | $$ | $$ |\$$$$$$\ $$$$$$$$ |
13 | $$ | $$ |$$ | $$ |$$ | $$ | $$ | $$ |$$\ $$ | $$ | $$ | $$ | \____$$\ $$ ____|
14 | $$ | $$ |\$$$$$$ |\$$$$$\$$$$ | \$$$$ |\$$$$$$ | \$$$$$$ |$$$$$$$ |\$$$$$$$\
15 | \__| \__| \______/ \_____\____/ \____/ \______/ \______/ \_______/ \_______|
16 | ================================================================================================
17 | Selection '1' : You are looking at it!
18 | Selection '2' : This opens a catigory with more information such as; Contributers, the repo's licence, and more!
19 | Selection '3' : This runs a script that will flood the game lobbie with bots, its currently not working and I havn't fixed it yet~
20 | Selection '4' : This runs a script that gives you the answers to a kahoot game.
21 | """)
22 | sprint(f"{r} PS! if the quiz was assigned to you instead of hosted, it should be there so it should work :) ")
23 |
24 | import sys
25 | def progressbar(it, prefix="", size=60, out=sys.stdout): # Python3.3+
26 | count = len(it)
27 | def show(j):
28 | x = int(size*j/count)
29 | print("{}[{}{}] {}/{}".format(prefix, "#"*x, "."*(size-x), j, count),
30 | end='\r', file=out, flush=True)
31 | show(0)
32 | for i, item in enumerate(it):
33 | yield item
34 | show(i+1)
35 | print("\n", flush=True, file=out)
36 |
37 | import time
38 | for i in progressbar(range(500), "Loading: ", 35):
39 | time.sleep(0.1)
40 | print("\n" * 64)
41 | from subprocess import call
42 | call(["python", "Kahoot/kahoot.py"])
43 |
--------------------------------------------------------------------------------
/docs/style.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --dl-color-gray-700: #999999;
3 | --dl-color-gray-900: #c3c5c9ff;
4 | --dl-size-size-unit: 1rem;
5 | --dl-color-gray-black: #000000;
6 | --dl-color-gray-white: #FFFFFF;
7 | --dl-color-pimary-300: #0284c7ff;
8 | --dl-color-pimary-500: #4aa4e3ff;
9 | --dl-color-pimary-700: #c3e5faff;
10 | --dl-color-pimary-800: #e3e8efff;
11 | --dl-color-pimary-900: #f2f5f9ff;
12 | --dl-space-space-unit: 1rem;
13 | --dl-size-size-halfunit: 0.5rem;
14 | --dl-color-secondary-100: #111729ff;
15 | --dl-color-secondary-300: #1e293bff;
16 | --dl-color-secondary-400: #334155ff;
17 | --dl-color-secondary-500: #677b8eff;
18 | --dl-color-secondary-700: #94a3b8ff;
19 | --dl-radius-radius-round: 50%;
20 | --dl-size-size-doubleunit: 2rem;
21 | --dl-size-size-tripleunit: 3rem;
22 | --dl-space-space-halfunit: 0.5rem;
23 | --dl-radius-radius-radius4: 4px;
24 | --dl-space-space-fiveunits: 5rem;
25 | --dl-radius-radius-radius25: 0.25rem;
26 | --dl-radius-radius-radius50: 0.5rem;
27 | --dl-radius-radius-radius75: 0.75rem;
28 | --dl-space-space-doubleunit: 2rem;
29 | --dl-space-space-tripleunit: 3rem;
30 | --dl-space-space-twoandhalf: 2.5rem;
31 | --dl-space-space-unitandhalf: 1.5rem;
32 | --dl-space-space-triplequarter: 0.75rem;
33 | }
34 | .teleport-show {
35 | display: flex !important;
36 | transform: none !important;
37 | }
38 | .list {
39 | width: 100%;
40 | margin: 1em 0px 1em 0px;
41 | display: block;
42 | padding: 0px 0px 0px 1.5rem;
43 | list-style-type: none;
44 | list-style-position: outside;
45 | }
46 | .list-item {
47 | display: list-item;
48 | }
49 | .button {
50 | color: var(--dl-color-gray-900);
51 | display: inline-block;
52 | padding: 0.5rem 1rem;
53 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen","Ubuntu", "Cantarell", "Fira Sans","Droid Sans", "Helvetica Neue", sans-serif;
54 | border-color: var(--dl-color-gray-900);
55 | border-width: 1px;
56 | border-radius: 4px;
57 | background-color: var(--dl-color-gray-white);
58 | }
59 | .textarea {
60 | color: var(--dl-color-gray-900);
61 | cursor: auto;
62 | padding: 0.5rem;
63 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen","Ubuntu", "Cantarell", "Fira Sans","Droid Sans", "Helvetica Neue", sans-serif;
64 | border-color: var(--dl-color-gray-900);
65 | border-width: 1px;
66 | border-radius: 4px;
67 | background-color: var(--dl-color-gray-white);
68 | }
69 | .input {
70 | color: var(--dl-color-gray-900);
71 | cursor: auto;
72 | padding: 0.5rem 1rem;
73 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen","Ubuntu", "Cantarell", "Fira Sans","Droid Sans", "Helvetica Neue", sans-serif;
74 | border-color: var(--dl-color-gray-900);
75 | border-width: 1px;
76 | border-radius: 4px;
77 | background-color: var(--dl-color-gray-white);
78 | }
79 | .Healine {
80 | font-size: 1.875rem;
81 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen","Ubuntu", "Cantarell", "Fira Sans","Droid Sans", "Helvetica Neue", sans-serif;
82 | font-weight: 700;
83 | text-transform: none;
84 | text-decoration: none;
85 | }
86 | .TextXL {
87 | font-size: 1.25rem;
88 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen","Ubuntu", "Cantarell", "Fira Sans","Droid Sans", "Helvetica Neue", sans-serif;
89 | font-weight: 400;
90 | text-transform: none;
91 | text-decoration: none;
92 | }
93 | .TextSM {
94 | font-size: 0.87rem;
95 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen","Ubuntu", "Cantarell", "Fira Sans","Droid Sans", "Helvetica Neue", sans-serif;
96 | font-weight: 400;
97 | line-height: 1.25;
98 | text-decoration: none;
99 | }
100 | .TextXS {
101 | font-size: 0.75rem;
102 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen","Ubuntu", "Cantarell", "Fira Sans","Droid Sans", "Helvetica Neue", sans-serif;
103 | font-weight: 500;
104 | text-transform: none;
105 | text-decoration: none;
106 | }
107 | .Content {
108 | font-size: 1.12rem;
109 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen","Ubuntu", "Cantarell", "Fira Sans","Droid Sans", "Helvetica Neue", sans-serif;
110 | font-weight: 300;
111 | line-height: 1.75;
112 | text-transform: none;
113 | text-decoration: none;
114 | }
115 | .TextMD {
116 | font-size: 1rem;
117 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen","Ubuntu", "Cantarell", "Fira Sans","Droid Sans", "Helvetica Neue", sans-serif;
118 | font-weight: 400;
119 | line-height: 1.25;
120 | text-decoration: none;
121 | }
122 | .Text2XL {
123 | font-size: 2.25rem;
124 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen","Ubuntu", "Cantarell", "Fira Sans","Droid Sans", "Helvetica Neue", sans-serif;
125 | font-weight: 700;
126 | line-height: 1.25;
127 | text-transform: none;
128 | text-decoration: none;
129 | }
130 | .Text3XL {
131 | font-size: 3rem;
132 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen","Ubuntu", "Cantarell", "Fira Sans","Droid Sans", "Helvetica Neue", sans-serif;
133 | font-weight: 500;
134 | text-transform: none;
135 | text-decoration: none;
136 | }
137 | .TextLG {
138 | font-size: 1.125rem;
139 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen","Ubuntu", "Cantarell", "Fira Sans","Droid Sans", "Helvetica Neue", sans-serif;
140 | font-weight: 400;
141 | text-transform: none;
142 | text-decoration: none;
143 | }
144 |
--------------------------------------------------------------------------------
/LITE/lite.py:
--------------------------------------------------------------------------------
1 | # This version is purly just a one file script with no extra fetures, the simpleist it can be!
2 |
3 | import json
4 | import urllib.request
5 | import os
6 | from os import system
7 | import platform
8 |
9 | print(f"'{system}'") # OS Alert
10 |
11 | print("""
12 | - Kitty-Tools LITE v1.10 -
13 | ======= For Termux =========
14 |
15 | Please enter your quiz ID
16 | /
17 | >.<
18 | ----------------------------
19 | """)
20 | api = 'https://play.kahoot.it/rest/kahoots/'
21 | usrinput = input(f" Enter ID >> ")
22 | link = api + usrinput
23 | finished = False
24 |
25 | answers = {}
26 | images = {}
27 | questions = {}
28 |
29 | try:
30 | with urllib.request.urlopen(link) as url:
31 | print("")
32 | data = json.load(url)
33 | quizlength = len(data['questions'])
34 |
35 | def is_a_question(dat):
36 | try:
37 | eval(dat)
38 | return True
39 | except KeyError:
40 | return False
41 |
42 | question = 0
43 | for x in range(quizlength):
44 | if is_a_question("data['questions'][question]['choices']"):
45 | try:
46 | if data['questions'][question]['type'] == "quiz":
47 | if data['questions'][question]['choices'][0]['correct']:
48 | answers[f"Question {question + 1}"] = 'Red'
49 | elif data['questions'][question]['choices'][1]['correct']:
50 | answers[f"Question {question + 1}"] = 'Blue'
51 | elif data['questions'][question]['choices'][2]['correct']:
52 | answers[f"Question {question + 1}"] = 'Yellow'
53 | elif data['questions'][question]['choices'][3]['correct']:
54 | answers[f"Question {question + 1}"] = 'Green'
55 | elif data['questions'][question]['type'] == "jumble":
56 | length = len(data['questions'][question]['choices'])
57 | for y in range(length):
58 | if answers.get(f"Question {question + 1}") is None:
59 | answers[f"Question {question + 1}"] = ""
60 | answers[f"Question {question + 1}"] += str(data['questions'][question]['choices'][y]['answer']).upper()
61 |
62 | elif data['questions'][question]['type'] == "survey":
63 | answers[f"Question {question + 1}"] = "Could not find any answers"
64 |
65 | elif data['questions'][question]['type'] == "content":
66 | answers[f"Question {question + 1}"] = "Could not find any answers"
67 |
68 | elif data['questions'][question]['type'] == "multiple_select_quiz":
69 | multiselect = []
70 | for z in range(len(data['questions'][question]['choices'])):
71 | if data['questions'][question]['choices'][0]['correct']:
72 | multiselect.append("Blue")
73 |
74 | if data['questions'][question]['choices'][1]['correct']:
75 | multiselect.append("Red")
76 |
77 | if data['questions'][question]['choices'][2]['correct']:
78 | multiselect.append("Yellow")
79 |
80 | if data['questions'][question]['choices'][3]['correct']:
81 | multiselect.append("Green")
82 |
83 | answers[f"Question {question + 1}"] = list(dict.fromkeys(multiselect))
84 | else:
85 | answers[f"Question {question + 1}"] = 'Could not find any answers'
86 |
87 | questions[f"Question {question + 1}"] = data["questions"][question]["question"]
88 |
89 | if is_a_question('data["questions"][question]["image"]'):
90 | images[f"Question {question + 1}"] = data["questions"][question]["image"]
91 | else:
92 | images[f"Question {question + 1}"] = None
93 |
94 | question += 1
95 | if question + 1 == quizlength:
96 | finished = True
97 | except Exception as err:
98 | print(err)
99 | else:
100 | answers[f"Question {question + 1}"] = 'Could not find any answers'
101 | images[f"Question {question + 1}"] = 'Could not find any images'
102 | questions[f"Question {question + 1}"] = 'Could not find the question'
103 |
104 | question += 1
105 |
106 | except Exception as err:
107 | os.system('clear')
108 | print("Womp Womp! ")
109 | print("There was an error! Mabey you typed the 'Quiz ID' incorrectly!\n")
110 | print(err)
111 |
112 |
113 | def print_answers():
114 | for key, value in answers.items():
115 | if type(value) == list:
116 | print(key, ':', f"{', '.join(value)}\n")
117 | else:
118 | print(key, ':', f"{value}\n")
119 |
120 |
121 | print_answers()
122 |
123 | if finished:
124 | # try:
125 | dir_path = os.path.dirname(os.path.realpath(__file__))
126 | data_path = os.path.join(dir_path, "quizzes", f"{usrinput}.json")
127 |
128 | if not os.path.exists(os.path.join(dir_path, "quizzes")):
129 | os.mkdir(os.path.join(dir_path, "quizzes"), 0o666)
130 |
131 | if not os.path.exists(data_path):
132 | with open(data_path, "x") as f:
133 | startconfig = {"answers": {}, "questions": {}, "images": {}}
134 | json.dump(startconfig, f, indent=4)
135 |
136 | with open(data_path, "r") as f:
137 | config = json.load(f)
138 |
139 | for i, v in answers.items():
140 | if "Woops! Could not find any answers" in v:
141 | config["answers"][i] = None
142 | else:
143 | config["answers"][i] = v
144 |
145 | for i, v in questions.items():
146 | if "Woops! Could not find the question" in v:
147 | config["questions"][i] = None
148 | else:
149 | config["questions"][i] = str(v).replace("T or F: ", '').replace("", '').replace("
T or F: ", '').replace('', '')
150 |
151 | for i, v in images.items():
152 | if "Woops! Could not find any images" in v:
153 | config["images"][i] = None
154 | else:
155 | config["images"][i] = v
156 |
157 | with open(data_path, "w+") as f:
158 | json.dump(config, f, indent=4)
159 |
160 | # except Exception as err:
161 | # print(f"{err}\n")
162 |
163 | input("Enter to exit...")
164 |
--------------------------------------------------------------------------------
/Kitty/Info/legal.py:
--------------------------------------------------------------------------------
1 | import os
2 | from os import system
3 | import platform
4 |
5 |
6 | def clear():
7 | system = platform.system().lower()
8 |
9 | if system == 'windows':
10 | _ = os.system('cls')
11 | elif system == 'linux' or system == 'darwin':
12 | _ = os.system('clear')
13 | elif system == 'android':
14 | _ = subprocess.run(['termux-exec', 'sh', '-c', 'clear'], check=False)
15 | else:
16 | print(f"Unsupported platform, please use Kitty-Tools LITE '{system}'")
17 | print(
18 | f"For more info go to https://github.com/CPScript/Kitty-Tools/more/moreinfo.md"
19 | )
20 |
21 |
22 | # Call the clear function
23 | print("Loading")
24 | clear()
25 |
26 | print("""
27 | This Repo is not yours, most of the code here was made by me and i'dd like it if you didn't claim that you made it.
28 | -------------------------------------------------------
29 | > If you would like to bypass such you can do this:
30 | 1. Create large changes to the script making it differant than my script.
31 | -------------------------------------------------------
32 |
33 | I dont want to but I will DMCA your repo if you try to steal my work and say it is your own!
34 | """)
35 |
36 | print("CODE OF CONDUCT:")
37 | print("""
38 | > Contributor Covenant Code of Conduct
39 |
40 | > Our Pledge
41 |
42 | We as members, contributors, and leaders pledge to make participation in our
43 | community a harassment-free experience for everyone, regardless of age, body
44 | size, visible or invisible disability, ethnicity, sex characteristics, gender
45 | identity and expression, level of experience, education, socio-economic status,
46 | nationality, personal appearance, race, religion, or sexual identity
47 | and orientation.
48 |
49 | We pledge to act and interact in ways that contribute to an open, welcoming,
50 | diverse, inclusive, and healthy community.
51 |
52 | > Our Standards
53 |
54 | Examples of behavior that contributes to a positive environment for our
55 | community include:
56 |
57 | * Demonstrating empathy and kindness toward other people
58 | * Being respectful of differing opinions, viewpoints, and experiences
59 | * Giving and gracefully accepting constructive feedback
60 | * Accepting responsibility and apologizing to those affected by our mistakes,
61 | and learning from the experience
62 | * Focusing on what is best not just for us as individuals, but for the
63 | overall community
64 |
65 | Examples of unacceptable behavior include:
66 |
67 | * The use of sexualized language or imagery, and sexual attention or
68 | advances of any kind
69 | * Trolling, insulting or derogatory comments, and personal or political attacks
70 | * Public or private harassment
71 | * Publishing others' private information, such as a physical or email
72 | address, without their explicit permission
73 | * Other conduct which could reasonably be considered inappropriate in a
74 | professional setting
75 |
76 | > Enforcement Responsibilities
77 |
78 | Community leaders are responsible for clarifying and enforcing our standards of
79 | acceptable behavior and will take appropriate and fair corrective action in
80 | response to any behavior that they deem inappropriate, threatening, offensive,
81 | or harmful.
82 |
83 | Community leaders have the right and responsibility to remove, edit, or reject
84 | comments, commits, code, wiki edits, issues, and other contributions that are
85 | not aligned to this Code of Conduct, and will communicate reasons for moderation
86 | decisions when appropriate.
87 |
88 | > Scope
89 |
90 | This Code of Conduct applies within all community spaces, and also applies when
91 | an individual is officially representing the community in public spaces.
92 | Examples of representing our community include using an official e-mail address,
93 | posting via an official social media account, or acting as an appointed
94 | representative at an online or offline event.
95 |
96 | > Enforcement
97 |
98 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
99 | reported to the community leaders responsible for enforcement at
100 | .
101 | All complaints will be reviewed and investigated promptly and fairly.
102 |
103 | All community leaders are obligated to respect the privacy and security of the
104 | reporter of any incident.
105 |
106 | > Enforcement Guidelines
107 |
108 | Community leaders will follow these Community Impact Guidelines in determining
109 | the consequences for any action they deem in violation of this Code of Conduct:
110 |
111 | > 1. Correction
112 |
113 | *Community Impact*: Use of inappropriate language or other behavior deemed
114 | unprofessional or unwelcome in the community.
115 |
116 | *Consequence*: A private, written warning from community leaders, providing
117 | clarity around the nature of the violation and an explanation of why the
118 | behavior was inappropriate. A public apology may be requested.
119 |
120 | > 2. Warning
121 |
122 | *Community Impact*: A violation through a single incident or series
123 | of actions.
124 |
125 | *Consequence*: A warning with consequences for continued behavior. No
126 | interaction with the people involved, including unsolicited interaction with
127 | those enforcing the Code of Conduct, for a specified period of time. This
128 | includes avoiding interactions in community spaces as well as external channels
129 | like social media. Violating these terms may lead to a temporary or
130 | permanent ban.
131 |
132 | > 3. Temporary Ban
133 |
134 | *Community Impact*: A serious violation of community standards, including
135 | sustained inappropriate behavior.
136 |
137 | *Consequence*: A temporary ban from any sort of interaction or public
138 | communication with the community for a specified period of time. No public or
139 | private interaction with the people involved, including unsolicited interaction
140 | with those enforcing the Code of Conduct, is allowed during this period.
141 | Violating these terms may lead to a permanent ban.
142 |
143 | > 4. Permanent Ban
144 |
145 | *Community Impact*: Demonstrating a pattern of violation of community
146 | standards, including sustained inappropriate behavior, harassment of an
147 | individual, or aggression toward or disparagement of classes of individuals.
148 |
149 | *Consequence*: A permanent ban from any sort of public interaction within
150 | the community.
151 |
152 | > Attribution
153 |
154 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
155 | version 2.0, available at
156 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
157 |
158 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
159 | enforcement ladder](https://github.com/mozilla/diversity).
160 |
161 | [homepage]: https://www.contributor-covenant.org
162 |
163 | For answers to common questions about this code of conduct, see the FAQ at
164 | https://www.contributor-covenant.org/faq. Translations are available at
165 | https://www.contributor-covenant.org/translations.
166 |
167 | """)
168 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kitty-Tools - v36.2
2 | > Please go check out my other repositories. there might be something you like <3
3 |
4 |
5 |
6 | [](https://github.com/CPScript/Kitty-Tools)
7 | [](https://github.com/CPScript/Kitty-Tools/blob/main/LICENSE)
8 | [](https://github.com/CPScript/Kitty-Tools)
9 |
10 |
11 |
12 | ## Overview
13 |
14 | Kitty-Tools is a comprehensive suite of utilities designed for enhancing and analyzing Kahoot quiz interactions. The toolkit provides powerful features for educators, students, and quiz enthusiasts, enabling advanced quiz analysis, answer retrieval, and automated participation capabilities.
15 |
16 |
17 |
18 | 
19 |
20 |
21 |
22 | ## ✨ Key Features
23 |
24 | - **Answer Retrieval System** - Obtain answers for any Kahoot quiz using Quiz ID or Game PIN
25 | - **Multi-bot Participation** - Create multiple automated quiz participants with configurable behaviors **NOTE; YOU CAN NOT USE THIS OPTION IN GITHUB CODESPACE**
26 | - **Cross-Platform Support** - Works on Windows, macOS, Linux, and Android (via Termux)
27 | - **Modern GUI Interface** - Sleek, intuitive graphical user interface with dark theme
28 | - **Export Functionality** - Save quiz answers in text format for future reference
29 | - **Enhanced CLI Mode** - Full functionality available via command line for low-resource environments
30 |
31 |
32 |
33 |
34 |
35 | ## 🚀 Installation Guide
36 |
37 | ### Prerequisites
38 |
39 | - Python 3.6+
40 | - Git
41 | - Node.js (for Flooder functionality)
42 |
43 | ### Installation by Platform
44 |
45 |
46 | Windows
47 |
48 | 1. Install Python from [python.org/downloads](https://www.python.org/downloads/) (ensure "Add to PATH" is checked)
49 | 2. Install Git from [git-scm.com/download/win](https://git-scm.com/download/win)
50 | 3. Open Command Prompt and run:
51 | ```
52 | git clone https://github.com/CPScript/Kitty-Tools
53 | cd Kitty-Tools
54 | python main.py
55 | ```
56 |
57 |
58 |
59 | Linux/macOS
60 |
61 | 1. Install required packages:
62 | ```bash
63 | # Ubuntu/Debian
64 | sudo apt install python3 python3-pip git
65 |
66 | # Fedora
67 | sudo dnf install python3 python3-pip git
68 |
69 | # macOS (with Homebrew)
70 | brew install python git
71 | ```
72 |
73 | 2. Clone and run:
74 | ```bash
75 | git clone https://github.com/CPScript/Kitty-Tools
76 | cd Kitty-Tools
77 | python3 main.py
78 | ```
79 |
80 |
81 |
82 | Mobile
83 |
84 | 1. Install Termux by *F-Droid* **And** IOS using [iSH](https://ish.app/)
85 | 2. Install requirements:
86 | ```bash
87 | pkg install python git
88 | git clone https://github.com/CPScript/Kitty-Tools
89 | cd Kitty-Tools/LITE
90 | python lite.py
91 | ```
92 |
93 |
94 |
95 | GitHub Codespace / Replit
96 |
97 | #### Codespace
98 | 1. Create a new Codespace based on the repository
99 | 2. In the terminal, run:
100 | ```bash
101 | python main.py
102 | ```
103 |
104 | #### Replit
105 | 1. Use this replit link
106 | ```bash
107 | https://replit.com/@Kitty-Tools/Kitty-Tools
108 | ```
109 |
110 |
111 | ## 📊 Usage Guide
112 |
113 | ### 1. Answer Viewer
114 |
115 | The Answer Viewer module allows you to retrieve answers for Kahoot quizzes:
116 |
117 | 1. Start the application with `python main.py`
118 | 2. Select "Answer Hack" from the main menu
119 | 3. Enter the Kahoot Quiz ID (not the Game PIN)
120 |
121 |
122 | 5. View answers displayed in an organized format
123 | 6. Optionally export answers to a text file
124 |
125 | ### 2. Kahoot Flooder
126 |
127 | The Flooder creates multiple bot participants in a Kahoot game:
128 |
129 | 1. Start the application with `python main.py` (**Won't work in codespace**)
130 | 2. Select "Kahoot Flooder" from the main menu
131 | 3. Configure bot settings:
132 | - Game PIN
133 | - Number of bots
134 | - Name generation options
135 | - Bot behavior settings
136 | 4. Start the flooder and optionally control bot responses
137 |
138 | ### 3. Graphical Interface
139 |
140 | For a more user-friendly experience:
141 |
142 | 1. Start the application with `python main.py`
143 | 2. Select "GUI" from the main menu
144 | 3. Use the intuitive tabbed interface to access all features
145 |
146 | ## 🔧 Advanced Configuration
147 |
148 | Kitty-Tools supports various advanced configurations and custom settings:
149 |
150 | - **Name Generation** - Generate random names or use custom prefixes
151 | - **Anti-Detection Mode** - Implement techniques to avoid bot detection
152 | - **Export Format Control** - Customize how answers are exported
153 | - **Bot Behavior Patterns** - Configure response timing and answer selection
154 |
155 | ## ❓ Troubleshooting
156 |
157 | **Common Issues:**
158 |
159 | - **Module Not Found** - Run `pip install ` for any missing dependencies
160 | - **Node.js Not Found** - Install Node.js for Flooder functionality
161 | - **Game PIN Connection Fails** - Verify the PIN and ensure the Kahoot game is active
162 | - **Performance Issues** - Use LITE version on low-resource systems
163 |
164 | ## 📜 Legal Disclaimer
165 |
166 | Kitty-Tools is provided for **educational purposes only**. This software is designed to demonstrate educational platform vulnerabilities and to be used in controlled, ethical environments.
167 |
168 | The developers do not endorse or encourage any use of this software that violates terms of service of educational platforms or disrupts educational activities. Use at your own risk and responsibility.
169 |
170 | ## 🤝 Contributors
171 |
172 | A special thanks to all contributors who have helped make Kitty-Tools better:
173 |
174 | - **@CPScript** - Lead Developer & Project Maintainer
175 | - **@Ccode-lang** - Core Development & API Integration
176 | - **@xTobyPlayZ** - Flooder Module Development
177 | - **@cheepling** - Quality Assurance & Bug Reporting
178 | - **@Zacky2613** - Technical Support & Issue Resolution
179 | - **@KiraKenjiro** - Code Review & Optimization
180 |
181 | ## 📱 Mobile Support
182 |
183 | For mobile devices, we provide Kitty-Tools LITE - a streamlined version designed specifically for Android via Termux:
184 |
185 | ```bash
186 | cd Kitty-Tools/LITE
187 | python lite.py
188 | ```
189 |
190 | The LITE version offers core functionality with reduced resource requirements.
191 |
192 | ## 🌟 Star the Project
193 |
194 | If you find Kitty-Tools useful, please consider giving it a star on GitHub to help others discover it!
195 |
196 | ---
197 |
198 |
199 | © 2025 Kitty-Tools | All rights reserved
200 |
201 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
122 |
--------------------------------------------------------------------------------
/src/main.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import os
3 | import sys
4 | import platform
5 | import subprocess
6 | import time
7 |
8 | class Colors:
9 | RED = "\033[91m"
10 | GREEN = "\033[92m"
11 | YELLOW = "\033[93m"
12 | BLUE = "\033[94m"
13 | CYAN = "\033[96m"
14 | RESET = "\033[0m"
15 |
16 | @staticmethod
17 | def print(text, color):
18 | print(f"{color}{text}{Colors.RESET}")
19 |
20 | def clear_screen():
21 | if platform.system().lower() == "windows":
22 | os.system('cls')
23 | else:
24 | os.system('clear')
25 |
26 | def run_command_safe(command, cwd=None, timeout=30):
27 | is_windows = platform.system().lower() == "windows"
28 |
29 | try:
30 | result = subprocess.run(
31 | command,
32 | cwd=cwd,
33 | shell=is_windows,
34 | capture_output=True,
35 | text=True,
36 | timeout=timeout
37 | )
38 | return result.returncode == 0, result.stdout, result.stderr
39 | except subprocess.TimeoutExpired:
40 | return False, "", "Command timed out"
41 | except Exception as e:
42 | return False, "", str(e)
43 |
44 | def install_essential_packages():
45 | Colors.print("Installing essential packages for flood.js...", Colors.CYAN)
46 |
47 | script_dir = os.path.dirname(os.path.abspath(__file__))
48 | project_root = os.path.dirname(script_dir)
49 |
50 | essential_packages = [
51 | "readline-sync",
52 | "request",
53 | "random-name",
54 | "an-array-of-english-words",
55 | "console-title",
56 | "beepbeep"
57 | ]
58 |
59 | kahoot_packages = [
60 | "kahoot.js-latest",
61 | "kahoot.js-api",
62 | "node-kahoot"
63 | ]
64 |
65 | installed_count = 0
66 |
67 | for package in essential_packages:
68 | Colors.print(f"Installing {package}...", Colors.YELLOW)
69 | success, stdout, stderr = run_command_safe(
70 | ["npm", "install", package, "--no-fund", "--no-audit"],
71 | cwd=project_root
72 | )
73 |
74 | if success:
75 | Colors.print(f"Successfully installed {package}", Colors.GREEN)
76 | installed_count += 1
77 | else:
78 | Colors.print(f"Failed to install {package}", Colors.RED)
79 |
80 | kahoot_installed = False
81 | for package in kahoot_packages:
82 | if kahoot_installed:
83 | break
84 |
85 | Colors.print(f"Trying {package}...", Colors.YELLOW)
86 | success, stdout, stderr = run_command_safe(
87 | ["npm", "install", package, "--no-fund", "--no-audit"],
88 | cwd=project_root
89 | )
90 |
91 | if success:
92 | Colors.print(f"Successfully installed {package}", Colors.GREEN)
93 | installed_count += 1
94 | kahoot_installed = True
95 | else:
96 | Colors.print(f"Failed to install {package}", Colors.RED)
97 |
98 | return installed_count > 0
99 |
100 | def try_run_flood_js():
101 | script_dir = os.path.dirname(os.path.abspath(__file__))
102 | project_root = os.path.dirname(script_dir)
103 | flood_js_path = os.path.join(project_root, "Kitty", "Flood", "flood.js")
104 |
105 | if not os.path.exists(flood_js_path):
106 | Colors.print(f"ERROR: flood.js not found at {flood_js_path}", Colors.RED)
107 | return False
108 |
109 | Colors.print(f"Found flood.js at: {flood_js_path}", Colors.GREEN)
110 | Colors.print("Starting Kahoot Flooder...", Colors.CYAN)
111 | print("=" * 50)
112 |
113 | try:
114 | is_windows = platform.system().lower() == "windows"
115 |
116 | process = subprocess.Popen(
117 | ["node", flood_js_path],
118 | cwd=os.path.dirname(flood_js_path),
119 | stdout=subprocess.PIPE,
120 | stderr=subprocess.STDOUT,
121 | text=True,
122 | bufsize=1,
123 | universal_newlines=True,
124 | shell=is_windows
125 | )
126 |
127 | while True:
128 | output = process.stdout.readline()
129 | if output == '' and process.poll() is not None:
130 | break
131 | if output:
132 | print(output.strip())
133 |
134 | rc = process.poll()
135 | return rc == 0
136 |
137 | except FileNotFoundError:
138 | Colors.print("ERROR: Node.js not found!", Colors.RED)
139 | Colors.print("Please install Node.js from https://nodejs.org/", Colors.YELLOW)
140 | return False
141 | except KeyboardInterrupt:
142 | Colors.print("\\nFlooder stopped by user", Colors.YELLOW)
143 | return True
144 | except Exception as e:
145 | Colors.print(f"ERROR: {e}", Colors.RED)
146 | return False
147 |
148 | def main():
149 | clear_screen()
150 |
151 | Colors.print("Kitty-Tools Flooder Launcher", Colors.CYAN)
152 | print("=" * 40)
153 | Colors.print("This script will run the Kahoot flooder from Kitty/Flood/flood.js", Colors.BLUE)
154 | print("=" * 40)
155 |
156 | node_available, _, _ = run_command_safe(["node", "--version"])
157 | if not node_available:
158 | Colors.print("ERROR: Node.js is not installed or not in PATH", Colors.RED)
159 | Colors.print("Please install Node.js from https://nodejs.org/", Colors.YELLOW)
160 | input("Press Enter to exit...")
161 | return
162 |
163 | npm_available, _, _ = run_command_safe(["npm", "--version"])
164 | if not npm_available:
165 | Colors.print("ERROR: npm is not available", Colors.RED)
166 | input("Press Enter to exit...")
167 | return
168 |
169 | Colors.print("Node.js and npm are available", Colors.GREEN)
170 | print()
171 |
172 | Colors.print("Attempting to run flood.js...", Colors.CYAN)
173 |
174 | script_dir = os.path.dirname(os.path.abspath(__file__))
175 | project_root = os.path.dirname(script_dir)
176 |
177 | test_modules = [
178 | "readline-sync",
179 | "kahoot.js-latest",
180 | "an-array-of-english-words",
181 | "request",
182 | "random-name",
183 | "console-title",
184 | "beepbeep"
185 | ]
186 |
187 | missing_modules = []
188 | for module in test_modules:
189 | test_success, _, _ = run_command_safe(
190 | ["node", "-e", f"require('{module}'); console.log('{module} OK');"],
191 | cwd=project_root,
192 | timeout=5
193 | )
194 | if not test_success:
195 | missing_modules.append(module)
196 |
197 | if not missing_modules:
198 | Colors.print("All required modules are available!", Colors.GREEN)
199 | print()
200 | if try_run_flood_js():
201 | Colors.print("Flooder completed successfully!", Colors.GREEN)
202 | else:
203 | Colors.print("Flooder encountered an error", Colors.YELLOW)
204 | else:
205 | Colors.print(f"Missing modules: {', '.join(missing_modules)}", Colors.YELLOW)
206 | Colors.print("Installing required packages...", Colors.CYAN)
207 | print()
208 |
209 | if install_essential_packages():
210 | Colors.print("Package installation completed!", Colors.GREEN)
211 | print()
212 | Colors.print("Trying to run flood.js again...", Colors.CYAN)
213 | print()
214 |
215 | if try_run_flood_js():
216 | Colors.print("Flooder completed successfully!", Colors.GREEN)
217 | else:
218 | Colors.print("Flooder still failed to run", Colors.RED)
219 | print()
220 | Colors.print("Manual troubleshooting:", Colors.YELLOW)
221 | print("1. Try running: npm install kahoot.js-latest")
222 | print("2. Try running: npm install readline-sync")
223 | print("3. Try running: node Kitty/Flood/flood.js")
224 | else:
225 | Colors.print("Failed to install required packages", Colors.RED)
226 | Colors.print("Please try manually installing with:", Colors.YELLOW)
227 | print(" npm install kahoot.js-latest")
228 | print(" npm install readline-sync")
229 |
230 | print()
231 | input("Press Enter to exit...")
232 |
233 | if __name__ == "__main__":
234 | try:
235 | main()
236 | except KeyboardInterrupt:
237 | Colors.print("\\nExiting...", Colors.YELLOW)
238 | except Exception as e:
239 | Colors.print(f"Unexpected error: {e}", Colors.RED)
240 | input("Press Enter to exit...")
241 |
--------------------------------------------------------------------------------
/Kitty/Flood/main.py:
--------------------------------------------------------------------------------
1 | # THE CONTENT IN THIS FILE AND ITS SIMPLICITY IS UGLY AND MAKES MY MIND HURT
2 |
3 | import time
4 | import os
5 | import platform
6 | import subprocess
7 | import json
8 | from os import system
9 | from subprocess import call
10 |
11 | def clear():
12 | system = platform.system().lower()
13 |
14 | if system == 'windows':
15 | _ = os.system('cls')
16 | elif system == 'linux' or system == 'darwin':
17 | _ = os.system('clear')
18 | elif system == 'android':
19 | _ = subprocess.run(['termux-exec', 'sh', '-c', 'clear'], check=False)
20 | print("Please use LITE!")
21 | exit()
22 | else:
23 | print(f"Unsupported platform, please use Kitty-Tools LITE '{system}'")
24 | print(f"For more info go to https://github.com/CPScript/Kitty-Tools/extra.md")
25 |
26 | def check_node_installed():
27 | """Check if Node.js is installed"""
28 | try:
29 | result = subprocess.run(['node', '--version'], capture_output=True, text=True, check=True)
30 | return True, result.stdout.strip()
31 | except (subprocess.CalledProcessError, FileNotFoundError):
32 | return False, None
33 |
34 | def check_npm_installed():
35 | """Check if npm is installed"""
36 | try:
37 | result = subprocess.run(['npm', '--version'], capture_output=True, text=True, check=True)
38 | return True, result.stdout.strip()
39 | except (subprocess.CalledProcessError, FileNotFoundError):
40 | return False, None
41 |
42 | def install_nodejs_guide():
43 | """Show Node.js installation guide"""
44 | system_name = platform.system().lower()
45 |
46 | print("\nNode.js is required but not found on your system.")
47 | print("Please install Node.js using the following instructions:")
48 | print()
49 |
50 | if system_name == "windows":
51 | print("Windows Installation:")
52 | print("1. Go to https://nodejs.org/")
53 | print("2. Download the Windows Installer (.msi)")
54 | print("3. Run the installer and follow the installation wizard")
55 | print("4. Make sure to check 'Add to PATH' during installation")
56 | print("5. Restart your computer after installation")
57 | print()
58 | print("Alternative - Using Chocolatey:")
59 | print("choco install nodejs")
60 |
61 | elif system_name == "darwin": # macOS
62 | print("macOS Installation:")
63 | print("Option 1 - Using Homebrew (recommended):")
64 | print(" brew install node")
65 | print()
66 | print("Option 2 - Official installer:")
67 | print(" 1. Go to https://nodejs.org/")
68 | print(" 2. Download the macOS Installer (.pkg)")
69 | print(" 3. Run the installer")
70 |
71 | elif system_name == "linux":
72 | print("Linux Installation:")
73 | print("Ubuntu/Debian:")
74 | print(" sudo apt update && sudo apt install nodejs npm")
75 | print()
76 | print("Fedora:")
77 | print(" sudo dnf install nodejs npm")
78 | print()
79 | print("Arch Linux:")
80 | print(" sudo pacman -S nodejs npm")
81 | print()
82 | print("CentOS/RHEL:")
83 | print(" sudo yum install nodejs npm")
84 |
85 | else:
86 | print("For other platforms, please visit: https://nodejs.org/")
87 |
88 | print()
89 | print("After installation, restart your terminal and run this script again.")
90 |
91 | def create_package_json():
92 | """Create package.json file for dependencies"""
93 | script_dir = os.path.dirname(os.path.abspath(__file__))
94 | package_json_path = os.path.join(script_dir, 'package.json')
95 |
96 | package_data = {
97 | "name": "kitty-tools-kahoot-flooder",
98 | "version": "1.4.0",
99 | "description": "Kahoot game flooding utility",
100 | "main": "flood.js",
101 | "dependencies": {
102 | "readline-sync": "1.4.10",
103 | "kahoot.js-updated": "3.1.2",
104 | "an-array-of-english-words": "2.0.0",
105 | "request": "2.88.2",
106 | "random-name": "0.1.2",
107 | "console-title": "1.1.0",
108 | "beepbeep": "1.3.0"
109 | },
110 | "engines": {
111 | "node": ">=12.0.0"
112 | }
113 | }
114 |
115 | try:
116 | with open(package_json_path, 'w') as f:
117 | json.dump(package_data, f, indent=2)
118 | print("Created package.json file")
119 | return True
120 | except Exception as e:
121 | print(f"Failed to create package.json: {e}")
122 | return False
123 |
124 | def install_node_packages():
125 | """Install required Node.js packages"""
126 | script_dir = os.path.dirname(os.path.abspath(__file__))
127 |
128 | print("Installing Node.js dependencies...")
129 |
130 | # Create package.json first
131 | if not create_package_json():
132 | return False
133 |
134 | # Clean install to fix corrupted packages
135 | print("Cleaning previous installations...")
136 | node_modules_path = os.path.join(script_dir, 'node_modules')
137 | package_lock_path = os.path.join(script_dir, 'package-lock.json')
138 |
139 | # Remove node_modules and package-lock.json if they exist
140 | try:
141 | if os.path.exists(node_modules_path):
142 | import shutil
143 | shutil.rmtree(node_modules_path)
144 | print("Removed old node_modules directory")
145 |
146 | if os.path.exists(package_lock_path):
147 | os.remove(package_lock_path)
148 | print("Removed old package-lock.json")
149 | except Exception as e:
150 | print(f"Warning: Could not clean old files: {e}")
151 |
152 | try:
153 | # Clear npm cache
154 | print("Clearing npm cache...")
155 | subprocess.run(
156 | ['npm', 'cache', 'clean', '--force'],
157 | cwd=script_dir,
158 | capture_output=True,
159 | text=True,
160 | check=False
161 | )
162 |
163 | # Try installing from package.json
164 | print("Installing packages from package.json...")
165 | result = subprocess.run(
166 | ['npm', 'install', '--no-fund', '--no-audit', '--force'],
167 | cwd=script_dir,
168 | capture_output=True,
169 | text=True,
170 | check=True
171 | )
172 |
173 | if result.returncode == 0:
174 | print("Successfully installed all Node.js packages")
175 | return True
176 | else:
177 | print(f"npm install failed: {result.stderr}")
178 | return False
179 |
180 | except subprocess.CalledProcessError as e:
181 | print(f"Package installation failed, trying individual installation...")
182 |
183 | # Try installing packages individually with specific versions
184 | packages_with_versions = [
185 | "readline-sync@1.4.10",
186 | "kahoot.js-updated@3.1.2",
187 | "an-array-of-english-words@2.0.0",
188 | "request@2.88.2",
189 | "random-name@0.1.2",
190 | "console-title@1.1.0",
191 | "beepbeep@1.3.0"
192 | ]
193 |
194 | success_count = 0
195 | for package in packages_with_versions:
196 | try:
197 | print(f"Installing {package}...")
198 | subprocess.run(
199 | ['npm', 'install', package, '--no-fund', '--no-audit', '--force'],
200 | cwd=script_dir,
201 | capture_output=True,
202 | text=True,
203 | check=True
204 | )
205 | print(f"Successfully installed {package}")
206 | success_count += 1
207 | except subprocess.CalledProcessError:
208 | print(f"Failed to install {package}")
209 |
210 | # Special handling for kahoot.js-updated
211 | if "kahoot.js-updated" in package:
212 | print("Trying alternative Kahoot package...")
213 | try:
214 | subprocess.run(
215 | ['npm', 'install', 'kahoot.js@3.0.4', '--no-fund', '--no-audit', '--force'],
216 | cwd=script_dir,
217 | capture_output=True,
218 | text=True,
219 | check=True
220 | )
221 | print("Successfully installed alternative kahoot.js package")
222 | success_count += 1
223 | except subprocess.CalledProcessError:
224 | print("Failed to install alternative Kahoot package")
225 |
226 | if success_count >= 6: # Allow one failure
227 | print("Most packages installed successfully")
228 | return True
229 | else:
230 | print("Too many packages failed to install")
231 | return False
232 | except Exception as e:
233 | print(f"Unexpected error during package installation: {e}")
234 | return False
235 |
236 | def verify_installation():
237 | """Verify that Node.js modules are installed correctly"""
238 | script_dir = os.path.dirname(os.path.abspath(__file__))
239 |
240 | test_script = '''
241 | const modules = [
242 | 'readline-sync',
243 | 'an-array-of-english-words',
244 | 'request',
245 | 'random-name',
246 | 'console-title',
247 | 'beepbeep'
248 | ];
249 |
250 | // Special handling for Kahoot module with fallback
251 | const kahootModules = ['kahoot.js-updated', 'kahoot.js'];
252 |
253 | let success = 0;
254 | let failed = 0;
255 |
256 | // Test regular modules
257 | for (const moduleName of modules) {
258 | try {
259 | require(moduleName);
260 | console.log(`SUCCESS: ${moduleName}`);
261 | success++;
262 | } catch (error) {
263 | console.log(`FAILED: ${moduleName} - ${error.message}`);
264 | failed++;
265 | }
266 | }
267 |
268 | // Test Kahoot module with fallback
269 | let kahootSuccess = false;
270 | for (const kahootModule of kahootModules) {
271 | try {
272 | require(kahootModule);
273 | console.log(`SUCCESS: ${kahootModule}`);
274 | kahootSuccess = true;
275 | success++;
276 | break;
277 | } catch (error) {
278 | console.log(`FAILED: ${kahootModule} - ${error.message}`);
279 | }
280 | }
281 |
282 | if (!kahootSuccess) {
283 | console.log('FAILED: No working Kahoot module found');
284 | failed++;
285 | }
286 |
287 | console.log(`SUMMARY: ${success} success, ${failed} failed`);
288 | process.exit(failed > 0 ? 1 : 0);
289 | '''
290 |
291 | test_file = os.path.join(script_dir, 'test_modules.js')
292 |
293 | try:
294 | with open(test_file, 'w') as f:
295 | f.write(test_script)
296 |
297 | result = subprocess.run(['node', test_file], capture_output=True, text=True, cwd=script_dir)
298 |
299 | # Parse output
300 | for line in result.stdout.split('\n'):
301 | if line.startswith('SUCCESS:'):
302 | print(f"✓ Module verified: {line.split(': ')[1]}")
303 | elif line.startswith('FAILED:'):
304 | print(f"✗ Module failed: {line.split(': ')[1]}")
305 | elif line.startswith('SUMMARY:'):
306 | print(line)
307 |
308 | # Clean up test file
309 | os.remove(test_file)
310 |
311 | return result.returncode == 0
312 |
313 | except Exception as e:
314 | print(f"Verification failed: {e}")
315 | return False
316 |
317 | def run_flood_script():
318 | """Run the flood.js script"""
319 | script_dir = os.path.dirname(os.path.abspath(__file__))
320 | flood_js_path = os.path.join(script_dir, 'flood.js')
321 |
322 | if not os.path.exists(flood_js_path):
323 | print(f"Error: flood.js not found at {flood_js_path}")
324 | return False
325 |
326 | try:
327 | print("Starting Kahoot flooder...")
328 | subprocess.run(['node', flood_js_path], cwd=script_dir, check=False)
329 | return True
330 | except KeyboardInterrupt:
331 | print("\nFlooder stopped by user")
332 | return True
333 | except Exception as e:
334 | print(f"Error running flooder: {e}")
335 | return False
336 |
337 | clear() # call check
338 |
339 | print("Start???")
340 | print("yes | no")
341 | choice = input("").lower()
342 |
343 | if choice == "yes":
344 | time.sleep(1)
345 | clear()
346 |
347 | # Check if Node.js is installed
348 | node_installed, node_version = check_node_installed()
349 | npm_installed, npm_version = check_npm_installed()
350 |
351 | if not node_installed:
352 | print("Node.js is not installed!")
353 | install_nodejs_guide()
354 | input("\nPress Enter to return to main menu...")
355 | clear()
356 | call(["python", "../../main.py"])
357 | exit()
358 |
359 | if not npm_installed:
360 | print("npm is not installed!")
361 | print("npm is usually installed with Node.js. Please reinstall Node.js.")
362 | input("\nPress Enter to return to main menu...")
363 | clear()
364 | call(["python", "../../main.py"])
365 | exit()
366 |
367 | print(f"Node.js found: {node_version}")
368 | print(f"npm found: {npm_version}")
369 |
370 | # Check if node exists
371 | print("Checking if Node.js is accessible...")
372 | try:
373 | import node # does node exist
374 | except ModuleNotFoundError:
375 | print("Attempting to install node Python package...")
376 | time.sleep(2)
377 | os.system("pip install node") # installation
378 | clear()
379 |
380 | # Install Node.js packages
381 | if not install_node_packages():
382 | print("Failed to install required Node.js packages!")
383 | print("\nTroubleshooting:")
384 | print("1. Check your internet connection")
385 | print("2. Try running as administrator/sudo")
386 | print("3. Clear npm cache: npm cache clean --force")
387 | input("\nPress Enter to return to main menu...")
388 | clear()
389 | call(["python", "../../main.py"])
390 | exit()
391 |
392 | # Verify installation
393 | if not verify_installation():
394 | print("Warning: Some modules failed verification")
395 | print("The flooder might still work, but some features may be limited")
396 |
397 | proceed = input("Continue anyway? (y/n): ").lower()
398 | if proceed != 'y':
399 | clear()
400 | call(["python", "../../main.py"])
401 | exit()
402 |
403 | clear()
404 | print("Executing!")
405 | time.sleep(2)
406 | clear()
407 |
408 | # Run the flooder
409 | if not run_flood_script():
410 | print("Flooder execution failed!")
411 | input("\nPress Enter to return to main menu...")
412 |
413 | clear()
414 | call(["python", "../../main.py"])
415 |
416 | if choice == "no":
417 | print("\nRe-Running main menu file!")
418 | time.sleep(3)
419 | clear()
420 | call(["python", "../../main.py"])
421 |
--------------------------------------------------------------------------------
/Kitty/Flood/flood.js:
--------------------------------------------------------------------------------
1 | readline = require('readline-sync');
2 | const Kahoot = require("kahoot.js-latest");
3 | var words = require('an-array-of-english-words')
4 | const request = require('request');
5 | var random = require('random-name')
6 | var setTitle = require('console-title');
7 | setTitle('flood.js');
8 | var beep = require('beepbeep')
9 |
10 | function getRandomInt(min, max) {
11 | min = Math.ceil(min);
12 | max = Math.floor(max);
13 | return Math.floor(Math.random() * (max - min + 1)) + min;
14 | }
15 |
16 | function getName() {
17 | ran = getRandomInt(1, 5)
18 | if (ran == 5) {
19 | request('https://apis.kahoot.it/namerator', function(error, response, body) {
20 | if (error) { console.log(error); }
21 | return JSON.parse(body).name
22 | });
23 | }
24 | if (ran == 4) {
25 | return words[getRandomInt(1, words.length)]
26 | }
27 | if (ran == 3) {
28 | return (random.first())
29 | }
30 | if (ran == 2) {
31 | return (random.first() + random.middle() + random.last())
32 | }
33 | if (ran == 1) {
34 | return (random.first() + random.last())
35 | }
36 | }
37 |
38 | function shuffle(array) {
39 | var currentIndex = array.length, temporaryValue, randomIndex;
40 |
41 | while (0 !== currentIndex) {
42 |
43 | randomIndex = Math.floor(Math.random() * currentIndex);
44 | currentIndex -= 1;
45 |
46 | temporaryValue = array[currentIndex];
47 | array[currentIndex] = array[randomIndex];
48 | array[randomIndex] = temporaryValue;
49 | }
50 |
51 | return array;
52 | }
53 |
54 | function getAnswerDelay() {
55 | const baseDelay = 7500; // Delay for promot - question time (DO NOT EDIT)
56 | const userDelay = answerdelay || 1500;
57 | const randomVariation = getRandomInt(-200, 500);
58 |
59 | const totalDelay = baseDelay + userDelay + randomVariation;
60 | return Math.max(1000, totalDelay);
61 | }
62 | function getAnswerCount(question) {
63 | try {
64 | if (question.type !== "quiz" && question.type !== "survey") {
65 | return 0;
66 | }
67 |
68 | if (question.numberOfAnswers && typeof question.numberOfAnswers === 'number') {
69 | return Math.max(1, Math.min(question.numberOfAnswers, 4));
70 | }
71 |
72 | if (question.quizQuestionAnswers && Array.isArray(question.quizQuestionAnswers) &&
73 | typeof question.questionIndex === 'number' &&
74 | question.questionIndex >= 0 &&
75 | question.questionIndex < question.quizQuestionAnswers.length &&
76 | question.quizQuestionAnswers[question.questionIndex] != null) {
77 | return Math.max(1, Math.min(question.quizQuestionAnswers[question.questionIndex], 4));
78 | }
79 |
80 | if (question.choices && Array.isArray(question.choices)) {
81 | return Math.max(1, Math.min(question.choices.length, 4));
82 | }
83 |
84 | if (question.answers && Array.isArray(question.answers)) {
85 | return Math.max(1, Math.min(question.answers.length, 4));
86 | }
87 |
88 | return 4;
89 | } catch (error) {
90 | console.log("Error getting answer count for " + question.type + ", using default of 4:", error.message);
91 | return 4;
92 | }
93 | }
94 |
95 | process.setMaxListeners(Number.POSITIVE_INFINITY)
96 |
97 | function ads() {
98 | console.clear()
99 | console.log("==================================================")
100 | console.log("Made by ZaWarudo1")
101 | console.log("Enhancements by; CPScript")
102 | console.log("==================================================")
103 | console.log("My own version will be made soon with;\n")
104 | console.log("> Better answer handling")
105 | console.log("> Better error handling")
106 | console.log("> Better UI")
107 | console.log("==================================================\n")
108 | }
109 | ads()
110 |
111 | antibotmode = readline.question('Use the new antibot mode? (y/n)> ');
112 | if (antibotmode == "y") {
113 | console.log("NOTE: 2-factor brute forcing does not work with antibot.")
114 | }
115 |
116 | pin = readline.question('Enter Game PIN --> ');
117 | bots = readline.question('Enter number of bots --> ');
118 |
119 | answerdelay = readline.question('Answer delay in milliseconds (1-1000, recommended 500-2000) --> ');
120 | if (answerdelay == "" || isNaN(answerdelay) || answerdelay < 1 || answerdelay > 10000) {
121 | answerdelay = 1500;
122 | console.log("Using default answer delay: 1500ms");
123 | } else {
124 | answerdelay = parseInt(answerdelay);
125 | console.log("Answer delay set to: " + answerdelay + "ms");
126 | }
127 |
128 | if (antibotmode == "y") {
129 | ranname = true
130 | er = "n"
131 | } else {
132 | ranname = readline.question('Use random name? (y/n) --> ');
133 |
134 | if (ranname == "y") {
135 | ranname = true
136 | } else {
137 | ranname = false
138 | botname = readline.question('Enter name --> ');
139 | botprefix = ""
140 | }
141 | er = readline.question('Use name bypass? (y/n) --> ');
142 | }
143 |
144 | usercontrolled = readline.question('Control the bots manually? (y/n) --> ');
145 |
146 | if (usercontrolled == "y") {
147 | usercontrolled = true
148 | beepis = readline.question('Beep when the bots need controlling? (y/n) --> ');
149 | } else {
150 | usercontrolled = false
151 | beepis = "n"
152 | }
153 |
154 | console.clear()
155 |
156 | repeattimes = 0
157 |
158 | function sendjoin(name, id) {
159 | if (ranname) {
160 | join(getName(), id)
161 | } else {
162 | join(name, id)
163 | }
164 | }
165 |
166 | function spam() {
167 |
168 | if (repeattimes == bots) {
169 | console.log("All join requests have finished.")
170 | } else {
171 | repeattimes++
172 |
173 | if (ranname) { rt = getRandomInt(90, 200) } else { rt = 15 }
174 |
175 | setTimeout(() => {
176 | spam()
177 | }, rt);
178 | setTimeout(() => {
179 |
180 | if (ranname) {
181 | sendjoin("This will become a random name!", bots - repeattimes - 1)
182 | } else {
183 | sendjoin(botname + (bots - repeattimes - 1), (bots - repeattimes - 1))
184 | }
185 | }, rt);
186 | }
187 | }
188 |
189 | process.setMaxListeners(Number.POSITIVE_INFINITY)
190 |
191 | Arandomint = 0
192 | todo = false
193 | function join(name, idee) {
194 | while (name == undefined) {
195 | name = getName()
196 | }
197 | const client = new Kahoot();
198 | client.setMaxListeners(Number.POSITIVE_INFINITY)
199 | if (er == "y") {
200 | client.join(pin, name.replace(/a/g, 'ᗩ').replace(/b/g, 'ᗷ').replace(/c/g, 'ᑕ').replace(/d/g, 'ᗪ').replace(/e/g, 'E').replace(/f/g, 'ᖴ').replace(/g/g, 'G').replace(/h/g, 'ᕼ').replace(/i/g, 'I').replace(/j/g, 'ᒍ').replace(/k/g, 'K').replace(/l/g, 'ᒪ').replace(/m/g, 'ᗰ').replace(/n/g, 'ᑎ').replace(/o/g, 'O').replace(/p/g, 'ᑭ').replace(/q/g, 'ᑫ').replace(/r/g, 'ᖇ').replace(/s/g, 'ᔕ').replace(/t/g, 'T').replace(/u/g, 'ᑌ').replace(/v/g, 'ᐯ').replace(/w/g, 'ᗯ').replace(/x/g, '᙭').replace(/y/g, 'Y').replace(/z/g, 'ᘔ').replace(/A/g, 'ᗩ').replace(/B/g, 'ᗷ').replace(/C/g, 'ᑕ').replace(/D/g, 'ᗪ').replace(/E/g, 'E').replace(/F/g, 'ᖴ').replace(/G/g, 'G').replace(/H/g, 'ᕼ').replace(/I/g, 'I').replace(/J/g, 'ᒍ').replace(/K/g, 'K').replace(/L/g, 'ᒪ').replace(/M/g, 'ᗰ').replace(/N/g, 'ᑎ').replace(/O/g, 'O').replace(/P/g, 'ᑭ').replace(/Q/g, 'ᑫ').replace(/R/g, 'ᖇ').replace(/S/g, 'ᔕ').replace(/T/g, 'T').replace(/U/g, 'ᑌ').replace(/V/g, 'ᐯ').replace(/W/g, 'ᗯ').replace(/X/g, '᙭').replace(/Y/g, 'Y').replace(/Z/g, 'ᘔ'), [random.first(), random.last()]).catch(err => {
201 | if (err.description == "Duplicate name" & ranname) {
202 | sendjoin(name, idee)
203 | } else {
204 | console.log("Client " + idee + " failed to join with the error '" + err.description + "'")
205 | client.leave()
206 | }
207 | });
208 | } else {
209 | client.join(pin, name, [random.first(), random.last()]).catch(err => {
210 | if (err.description == "Duplicate name" & ranname) {
211 | sendjoin(name, idee)
212 | } else {
213 | console.log("Client " + idee + " failed to join with the error '" + err.description + "'")
214 | client.leave()
215 | }
216 | });
217 | }
218 | var list = [0, 1, 2, 3]
219 | todo = false
220 | client.on("Joined", info => {
221 | if (info.twoFactorAuth) {
222 | setInterval(() => {
223 | if (!todo == false) {
224 | client.answerTwoFactorAuth(todo)
225 | }
226 | shuffle(list)
227 | client.answerTwoFactorAuth(list)
228 | }, 1000);
229 | }
230 | });
231 | client.on("TwoFactorCorrect", function() {
232 | console.log(name + " Got 2-factor correct!")
233 | todo = list
234 | })
235 |
236 | client.on("QuestionReady", question => {
237 | try {
238 | if (idee == 1 & beepis == "y") {
239 | beep()
240 | }
241 |
242 | if (question.type !== "quiz" && question.type !== "survey" && question.type !== "word_cloud" && question.type !== "open_ended" && question.type !== "jumble") {
243 | console.log(name + " skipped question type: " + question.type + " (not supported)")
244 | return;
245 | }
246 |
247 | everyoneanswerthis = false
248 |
249 | if (question.type == "word_cloud") {
250 | if (usercontrolled) {
251 | if (idee == 1) {
252 | everyoneanswerthis = true
253 | answer = readline.question('type your answer> ');
254 | everyoneanswerthis = answer
255 | Arandomint = answer
256 | setTimeout(() => { client.answer(answer) }, getAnswerDelay());
257 |
258 | } else {
259 | var waittill = setInterval(() => {
260 | if (!everyoneanswerthis == false || !everyoneanswerthis == true) {
261 | clearInterval(waittill);
262 | setTimeout(() => {
263 | client.answer(Arandomint)
264 | }, getAnswerDelay());
265 | }
266 | }, 100);
267 | }
268 | } else {
269 | console.log(name + " answered with 'kahootflood.weebly.com'")
270 | setTimeout(() => { client.answer("kahootflood.weebly.com") }, getAnswerDelay());
271 | }
272 | }
273 |
274 | if (question.type == "jumble") {
275 | console.log("User controlling is not currently available for jumbles. The bot " + name + " responded with a random answer.")
276 | const answerCount = getAnswerCount(question);
277 | if (answerCount > 0) {
278 | setTimeout(() => { client.answer(getRandomInt(0, answerCount - 1)) }, getAnswerDelay());
279 | } else {
280 | console.log(name + " skipped jumble question (no answer count available)")
281 | }
282 | }
283 |
284 | if (question.type == "quiz") {
285 | const answerCount = getAnswerCount(question);
286 |
287 | if (answerCount == 0) {
288 | console.log(name + " skipped quiz question (no answer count available)")
289 | return;
290 | }
291 |
292 | if (usercontrolled) {
293 | if (answerCount == 2) {
294 | if (idee == 1) {
295 | everyoneanswerthis = true
296 | answer = readline.question('t for triangle, d for diamond> ');
297 | answer = answer.replace('t', 1).replace('d', 2)
298 | everyoneanswerthis = answer
299 | Arandomint = answer
300 | setTimeout(() => { client.answer(answer - 1) }, getAnswerDelay());
301 |
302 | } else {
303 | var waittill = setInterval(() => {
304 | if (!everyoneanswerthis == false || !everyoneanswerthis == true) {
305 | clearInterval(waittill);
306 | setTimeout(() => {
307 | client.answer(Arandomint - 1)
308 | }, getAnswerDelay());
309 | }
310 | }, 100);
311 | }
312 | }
313 |
314 | if (answerCount == 3) {
315 | if (idee == 1) {
316 | everyoneanswerthis = true
317 | answer = readline.question('t for triangle, d for diamond, c for circle> ');
318 | answer = answer.replace('t', 1).replace('d', 2).replace('c', 3)
319 | everyoneanswerthis = answer
320 | Arandomint = answer
321 | setTimeout(() => { client.answer(answer - 1) }, getAnswerDelay());
322 |
323 | } else {
324 | var waittill = setInterval(() => {
325 | if (!everyoneanswerthis == false || !everyoneanswerthis == true) {
326 | clearInterval(waittill);
327 | setTimeout(() => {
328 | client.answer(Arandomint - 1)
329 | }, getAnswerDelay());
330 | }
331 | }, 100);
332 | }
333 | }
334 |
335 | if (answerCount == 4) {
336 | if (idee == 1) {
337 | everyoneanswerthis = true
338 | answer = readline.question('t for triangle, d for diamond, c for circle or s for square > ');
339 | answer = answer.replace('t', 1).replace('d', 2).replace('c', 3).replace('s', 4)
340 | everyoneanswerthis = answer
341 | Arandomint = answer
342 | setTimeout(() => { client.answer(answer - 1) }, getAnswerDelay());
343 |
344 | } else {
345 | var waittill = setInterval(() => {
346 | if (!everyoneanswerthis == false || !everyoneanswerthis == true) {
347 | clearInterval(waittill);
348 | setTimeout(() => {
349 | client.answer(Arandomint - 1)
350 | }, getAnswerDelay());
351 | }
352 | }, 100);
353 | }
354 | }
355 | } else {
356 | setTimeout(() => {
357 | try {
358 | toanswer = getRandomInt(0, answerCount - 1)
359 | console.log(name + " answered with option " + (toanswer + 1) + " after " + getAnswerDelay() + "ms delay")
360 | client.answer(toanswer)
361 | } catch (error) {
362 | console.log(name + " had an error answering: " + error.message)
363 | }
364 | }, getAnswerDelay());
365 | }
366 | }
367 |
368 | if (question.type == "survey") {
369 | const answerCount = getAnswerCount(question);
370 |
371 | if (answerCount == 0) {
372 | console.log(name + " skipped survey question (no answer count available)")
373 | return;
374 | }
375 |
376 | if (usercontrolled) {
377 | if (idee == 1) {
378 | everyoneanswerthis = true
379 | answer = readline.question('t for triangle, d for diamond, c for circle or s for square > ');
380 | answer = answer.replace('t', 1).replace('d', 2).replace('c', 3).replace('s', 4)
381 | everyoneanswerthis = answer
382 | Arandomint = answer
383 | setTimeout(() => { client.answer(answer - 1) }, getAnswerDelay());
384 |
385 | } else {
386 | var waittill = setInterval(() => {
387 | if (!everyoneanswerthis == false || !everyoneanswerthis == true) {
388 | clearInterval(waittill);
389 | setTimeout(() => {
390 | client.answer(Arandomint - 1)
391 | }, getAnswerDelay());
392 | }
393 | }, 100);
394 | }
395 | } else {
396 | setTimeout(() => {
397 | try {
398 | toanswer = getRandomInt(0, answerCount - 1)
399 | console.log(name + " answered survey with option " + (toanswer + 1) + " after " + getAnswerDelay() + "ms delay")
400 | client.answer(toanswer)
401 | } catch (error) {
402 | console.log(name + " had an error answering: " + error.message)
403 | }
404 | }, getAnswerDelay());
405 | }
406 | }
407 |
408 | if (question.type == "open_ended") {
409 | if (usercontrolled) {
410 | if (idee == 1) {
411 | everyoneanswerthis = true
412 | answer = readline.question('type your answer> ');
413 | everyoneanswerthis = answer
414 | Arandomint = answer
415 | setTimeout(() => { client.answer(answer) }, getAnswerDelay());
416 |
417 | } else {
418 | var waittill = setInterval(() => {
419 | if (!everyoneanswerthis == false || !everyoneanswerthis == true) {
420 | clearInterval(waittill);
421 | setTimeout(() => {
422 | client.answer(Arandomint)
423 | }, getAnswerDelay());
424 | }
425 | }, 100);
426 | }
427 | } else {
428 | console.log(name + " answered with 'kahootflood.weebly.com'")
429 | setTimeout(() => { client.answer("kahootflood.weebly.com") }, getAnswerDelay());
430 | }
431 | }
432 |
433 | } catch (error) {
434 | console.log(name + " had an error handling question: " + error.message)
435 | }
436 | });
437 |
438 | client.on("Disconnect", reason => {
439 | if (!reason == "Quiz Locked") {
440 | sendjoin(name, idee)
441 | }
442 | })
443 |
444 | client.on("QuestionEnd", data => {
445 | if (data.isCorrect) {
446 | console.log(name + " Got it correct!")
447 | } else {
448 | console.log(name + " Got it wrong.")
449 | }
450 | })
451 | client.on("QuizEnd", data => {
452 | console.log("The quiz has ended and " + name + " got rank " + data.rank)
453 | })
454 | process.on("SIGINT", function() {
455 | process.exit()
456 | });
457 | }
458 |
459 | console.clear()
460 | console.log("Joining bots")
461 | spam()
462 |
--------------------------------------------------------------------------------
/Kitty/client.py:
--------------------------------------------------------------------------------
1 | import os
2 | import time
3 | from os import system
4 | import platform
5 | import json
6 | import re
7 | import ssl
8 | import random
9 | import threading
10 | from urllib.request import urlopen, Request
11 | from urllib.error import HTTPError, URLError
12 | from urllib.parse import urlparse, parse_qs
13 | from http.client import InvalidURL
14 |
15 | print("Attempting to check if imports are installed; colorama, pystyle.")
16 | time.sleep(1)
17 |
18 | def clear():
19 | system = platform.system().lower()
20 |
21 | if system == 'windows':
22 | _ = os.system('cls')
23 | elif system == 'linux' or system == 'darwin':
24 | _ = os.system('clear')
25 | elif system == 'android':
26 | _ = os.system('clear')
27 | print("How are you here, leave!")
28 | print("Please use the 'LITE' version so this kahoot client will run smoothly!")
29 | exit()
30 | clear() # Call clear func
31 |
32 | try:
33 | import colorama
34 | import pystyle
35 | except ModuleNotFoundError:
36 | print("Result: You dont have a certain import(s) installed, installing them now")
37 | time.sleep(1)
38 | os.system("pip install colorama")
39 | os.system("pip install pystyle")
40 | clear()
41 |
42 | from urllib.request import urlopen
43 | from urllib.error import HTTPError
44 | from urllib.parse import urlparse, parse_qs
45 | from json import load
46 | from http.client import InvalidURL
47 | import json, random, string, re, ctypes, threading
48 | from colorama import Fore, Style
49 | from pystyle import Write, System, Colors, Colorate, Anime
50 | from datetime import datetime
51 |
52 | # Colors :D
53 | red = Fore.RED
54 | yellow = Fore.YELLOW
55 | green = Fore.GREEN
56 | blue = Fore.BLUE
57 | orange = Fore.RED + Fore.YELLOW
58 | pretty = Fore.LIGHTMAGENTA_EX + Fore.LIGHTCYAN_EX
59 | magenta = Fore.MAGENTA
60 | lightblue = Fore.LIGHTBLUE_EX
61 | cyan = Fore.CYAN
62 | gray = Fore.LIGHTBLACK_EX + Fore.WHITE
63 | reset = Fore.RESET
64 | pink = Fore.LIGHTGREEN_EX + Fore.LIGHTMAGENTA_EX
65 | dark_green = Fore.GREEN + Style.BRIGHT
66 |
67 | output_lock = threading.Lock()
68 | colorama.init()
69 |
70 | class SSLContextManager:
71 | """Handle SSL certificate issues across platforms"""
72 |
73 | @staticmethod
74 | def create_ssl_context():
75 | """Create SSL context with fallback for certificate issues"""
76 | try:
77 | # Try to create default context first
78 | context = ssl.create_default_context()
79 |
80 | # For macOS, try to use certifi if available
81 | if platform.system() == 'Darwin':
82 | try:
83 | import certifi
84 | context = ssl.create_default_context(cafile=certifi.where())
85 | except ImportError:
86 | # certifi not available, use default
87 | pass
88 |
89 | return context
90 | except Exception:
91 | # If all else fails, create unverified context
92 | print("Warning: Using unverified SSL context due to certificate issues")
93 | return ssl._create_unverified_context()
94 |
95 | class RateLimiter:
96 | """Rate limiter to avoid getting blocked"""
97 |
98 | def __init__(self, min_delay=1.0, max_delay=3.0):
99 | self.min_delay = min_delay
100 | self.max_delay = max_delay
101 | self.last_request = None
102 |
103 | def wait(self):
104 | """Wait appropriate time between requests"""
105 | if self.last_request:
106 | elapsed = time.time() - self.last_request
107 | delay = random.uniform(self.min_delay, self.max_delay)
108 |
109 | if elapsed < delay:
110 | sleep_time = delay - elapsed
111 | time.sleep(sleep_time)
112 |
113 | self.last_request = time.time()
114 |
115 | # Enhanced Kahoot API
116 | api = "https://play.kahoot.it/rest/kahoots/"
117 |
118 | class Kahoot:
119 | def __init__(self, uuid):
120 | self.uuid = uuid
121 | self.rate_limiter = RateLimiter()
122 | self.ssl_context = SSLContextManager.create_ssl_context()
123 |
124 | try:
125 | if not re.fullmatch(r"^[A-Za-z0-9-]*$", uuid):
126 | self.data = False
127 | else:
128 | self.data = self._fetch_quiz_data(uuid)
129 | except HTTPError or InvalidURL:
130 | self.data = False
131 |
132 | def _get_headers(self):
133 | """Generate realistic browser headers"""
134 | user_agents = [
135 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
136 | 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
137 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
138 | ]
139 |
140 | return {
141 | 'User-Agent': random.choice(user_agents),
142 | 'Accept': 'application/json, text/plain, */*',
143 | 'Accept-Language': 'en-US,en;q=0.9',
144 | 'Accept-Encoding': 'gzip, deflate, br',
145 | 'DNT': '1',
146 | 'Connection': 'keep-alive',
147 | 'Cache-Control': 'no-cache',
148 | 'Pragma': 'no-cache'
149 | }
150 |
151 | def _fetch_quiz_data(self, uuid):
152 | """Fetch quiz data with enhanced error handling"""
153 | max_retries = 3
154 |
155 | for attempt in range(max_retries):
156 | try:
157 | # Apply rate limiting
158 | self.rate_limiter.wait()
159 |
160 | url = f"https://play.kahoot.it/rest/kahoots/{uuid}"
161 | headers = self._get_headers()
162 | request = Request(url, headers=headers)
163 |
164 | with urlopen(request, timeout=15, context=self.ssl_context) as response:
165 | return load(response)
166 |
167 | except HTTPError as e:
168 | if e.code == 403:
169 | print(f"Attempt {attempt + 1}: Access forbidden (403)")
170 | if attempt < max_retries - 1:
171 | wait_time = (attempt + 1) * 5
172 | print(f"Waiting {wait_time} seconds before retry...")
173 | time.sleep(wait_time)
174 | continue
175 | else:
176 | print("Error: Access forbidden. This could be due to rate limiting or geographic restrictions.")
177 | return False
178 |
179 | elif e.code == 404:
180 | print("Error: Quiz not found. Please verify the Quiz ID is correct.")
181 | return False
182 |
183 | elif e.code == 429: # Too Many Requests
184 | if attempt < max_retries - 1:
185 | wait_time = (attempt + 1) * 10
186 | print(f"Rate limited. Waiting {wait_time} seconds...")
187 | time.sleep(wait_time)
188 | continue
189 | else:
190 | print("Error: Rate limited by server. Please wait a few minutes and try again.")
191 | return False
192 |
193 | else:
194 | print(f"HTTP Error {e.code}: {e.reason}")
195 | return False
196 |
197 | except URLError as e:
198 | print(f"Connection error: {e.reason}")
199 | return False
200 |
201 | except ssl.SSLError as e:
202 | print(f"SSL Error on attempt {attempt + 1}: {e}")
203 | if attempt < max_retries - 1:
204 | # Try with unverified context on next attempt
205 | self.ssl_context = ssl._create_unverified_context()
206 | print("Retrying with unverified SSL context...")
207 | continue
208 | else:
209 | print(f"SSL connection failed: {e}")
210 | return False
211 |
212 | except json.JSONDecodeError:
213 | print("Error: Failed to parse the response from Kahoot servers.")
214 | return False
215 |
216 | except Exception as e:
217 | if attempt < max_retries - 1:
218 | print(f"Attempt {attempt + 1} failed: {e}")
219 | time.sleep(2)
220 | continue
221 | else:
222 | print(f"Unexpected error: {str(e)}")
223 | return False
224 |
225 | return False
226 |
227 | def get_quiz_details(self):
228 | return {
229 | "uuid": self.data["uuid"],
230 | "creator_username": self.data["creator_username"],
231 | "title": self.data["title"],
232 | "description": self.data["description"],
233 | "cover": self.data["cover"]}
234 |
235 | def get_questions(self):
236 | return self.data["questions"]
237 |
238 | def get_question_names(self):
239 | questions = []
240 | for i in range(self.get_quiz_length()):
241 | if self.get_question_details(i)["type"] == "content":
242 | questions.append(self.get_question_details(i)["title"])
243 | else:
244 | questions.append(self.get_question_details(i)["question"])
245 | return questions
246 |
247 | def get_quiz_length(self):
248 | return len(self.data["questions"])
249 |
250 | def get_question_details(self, question):
251 | if self.data["questions"][question]["type"] == "content":
252 | data = {
253 | "type": "content",
254 | "title": self.data["questions"][question]["title"],
255 | "description": self.data["questions"][question]["description"]
256 | }
257 | else:
258 | data = {
259 | "type": self.data["questions"][question]["type"],
260 | "question": str(self.data["questions"][question]["question"]).replace('"', '\\"').replace("", "").replace("
", "").replace("", "").replace(" ", "").replace(" ", "\n").replace("", "").replace("", "").replace("", "").replace("", "").replace(" ", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace(" ", "").replace(" ", "").replace("", "").replace(" ", ""),
261 | "choices": self.data["questions"][question]["choices"],
262 | "amount_of_answers": len(self.data["questions"][question]["choices"]),
263 | "amount_of_correct_answers": 0}
264 |
265 | for i in range(len(self.data["questions"][question]["choices"])):
266 | self.data["questions"][question]["choices"][i]["answer"] = self.data["questions"][question]["choices"][i]["answer"].replace('"', '\\"').replace("", "").replace("
", "").replace("", "").replace(" ", "").replace(" ", "\n").replace(" ", "").replace("", "").replace("", "").replace("", "").replace(" ", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace(" ", "").replace(" ", "").replace("", "").replace(" ", "")
267 |
268 | for i in range(len(self.data["questions"][question]["choices"])):
269 | if self.data["questions"][question]["choices"][i]["correct"]:
270 | data["amount_of_correct_answers"] += 1
271 |
272 | if "layout" in self.data["questions"][question]:
273 | data["layout"] = self.data["questions"][question]["layout"]
274 | else:
275 | data["layout"] = None
276 |
277 | if "image" in self.data["questions"][question]:
278 | data["image"] = self.data["questions"][question]["image"]
279 | else:
280 | data["image"] = None
281 |
282 | if "pointsMultiplier" in self.data["questions"][question]:
283 | data["pointsMultiplier"] = self.data["questions"][question]["pointsMultiplier"]
284 | else:
285 | data["pointsMultiplier"] = None
286 |
287 | if "time" in self.data["questions"][question]:
288 | data["time"] = self.data["questions"][question]["time"]
289 | else:
290 | data["time"] = None
291 |
292 | return data
293 |
294 | def get_answer(self, question):
295 | answers = []
296 | if self.get_question_details(question)["type"] == "content":
297 | answers = None
298 |
299 | elif self.get_question_details(question)["type"] == "jumble":
300 | for i in self.get_question_details(question)["choices"]:
301 | answers.append(str(i["answer"]).replace('"', '\\"').replace("", "").replace("
", "").replace("", "").replace(" ", "").replace(" ", "\n").replace(" ", "").replace("", "").replace("", "").replace("", "").replace(" ", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace(" ", "").replace(" ", "").replace("", "").replace(" ", ""))
302 |
303 | else:
304 | for i in self.get_question_details(question)["choices"]:
305 | if i["correct"]:
306 | answers.append(str(i["answer"]).replace('"', '\\"').replace("", "").replace("
", "").replace("", "").replace(" ", "").replace(" ", "\n").replace(" ", "").replace("", "").replace("", "").replace("", "").replace(" ", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace("", "").replace(" ", "").replace(" ", "").replace("", "").replace(" ", ""))
307 | if len(answers) == 0:
308 | answers = None
309 | return answers
310 |
311 | def start_kahoot():
312 | print("NOTICE: This version is under development and sometimes it might bug, please create an issue at 'https://github.com/CPScript/Kitty-tools/issues' and we will try to fix it <3")
313 | Write.Print(f"""
314 | _______________________
315 | || Enter your quiz ID ||
316 | || below! <3 ||
317 | |//
318 | (>﹏<)
319 | --------------------------------------
320 | ---- Kitty-Tools | By <> CPScript ----
321 | --------------------------------------
322 | \n""", Colors.orange, interval=0.000)
323 | Write.Print(f"┌─[Enter Kahoot-ID] <> [User-Input]\n", Colors.white, interval=0.000)
324 | Write.Print(f"└─────► ", Colors.white, interval=0.000); quiz_id = input(pretty)
325 | try:
326 | kahoot = Kahoot(quiz_id)
327 |
328 | if not kahoot.data:
329 | print(f"{red}Error: Failed to fetch quiz data. Please check the Quiz ID and try again.{reset}")
330 | input("Press any key to exit...")
331 | return
332 |
333 | print(f"{pretty}{orange}({green}!{orange}) Fetching Answers From: {orange}[{reset}Quiz-ID: {quiz_id}{orange}]\n")
334 | time.sleep(1)
335 | for i in range(kahoot.get_quiz_length()):
336 | if kahoot.get_answer(i) is not None:
337 | if kahoot.get_question_details(i)['type'] == 'open_ended':
338 | with output_lock:
339 | print(f"{pretty}{orange}[{reset}Question{orange}]{green}--{orange}[{reset}{kahoot.get_question_names()[i]}{orange}]{reset}\n{pretty}{orange}[{reset}Answer{orange}]{green}--{orange}[{reset}{', '.join(kahoot.get_answer(i))}{orange}]{reset}\n")
340 | else:
341 | with output_lock:
342 | print(f"{pretty}{orange}[{reset}Question{orange}]{green}--{orange}[{reset}{kahoot.get_question_names()[i]}{orange}]{reset}\n{pretty}{orange}[{reset}Answer{orange}]{green}--{orange}[{reset}{', '.join(kahoot.get_answer(i))}{orange}]{reset}\n")
343 | time.sleep(0.010)
344 | except Exception as err:
345 | os.system('clear')
346 | print("Womp Womp! ")
347 | print("There was an error! Maybe you typed the 'Quiz ID' incorrectly!\n")
348 | print(f"Error details: {err}")
349 | print("\nTroubleshooting tips:")
350 | print("1. Check your internet connection")
351 | print("2. Verify the Quiz ID is correct")
352 | print("3. The quiz might be private or restricted")
353 | print("4. Try again in a few minutes")
354 | Write.Print(f"""
355 | ||=========================================================
356 | ||Thanks for using Kitty-Tools <3
357 | ||Please *STAR* this repo and follow the creator on github!
358 | ||=========================================================\n
359 | """, Colors.red_to_purple, interval=0.000)
360 |
361 | start_kahoot()
362 |
363 | input("Press any key to exit...")
364 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import os
3 | import sys
4 | import time
5 | import platform
6 | import subprocess
7 | from pathlib import Path
8 | from datetime import datetime
9 |
10 | # Terminal Control Constants
11 | class TermCtrl:
12 | RESET = "\033[0m"
13 | BOLD = "\033[1m"
14 | DIM = "\033[2m"
15 | ITALIC = "\033[3m"
16 | UNDERLINE = "\033[4m"
17 |
18 | # Foreground Colors
19 | BLACK = "\033[30m"
20 | RED = "\033[31m"
21 | GREEN = "\033[32m"
22 | YELLOW = "\033[33m"
23 | BLUE = "\033[34m"
24 | MAGENTA = "\033[35m"
25 | CYAN = "\033[36m"
26 | WHITE = "\033[37m"
27 |
28 | # Bright Foreground Colors
29 | BRIGHT_BLACK = "\033[90m"
30 | BRIGHT_RED = "\033[91m"
31 | BRIGHT_GREEN = "\033[92m"
32 | BRIGHT_YELLOW = "\033[93m"
33 | BRIGHT_BLUE = "\033[94m"
34 | BRIGHT_MAGENTA = "\033[95m"
35 | BRIGHT_CYAN = "\033[96m"
36 | BRIGHT_WHITE = "\033[97m"
37 |
38 | # Background Colors
39 | BG_BLACK = "\033[40m"
40 | BG_RED = "\033[41m"
41 | BG_GREEN = "\033[42m"
42 | BG_YELLOW = "\033[43m"
43 | BG_BLUE = "\033[44m"
44 | BG_MAGENTA = "\033[45m"
45 | BG_CYAN = "\033[46m"
46 | BG_WHITE = "\033[47m"
47 |
48 | # Clear Screen
49 | CLEAR = "\033[2J\033[H"
50 |
51 | # Cursor Movement
52 | @staticmethod
53 | def pos(x, y):
54 | return f"\033[{y};{x}H"
55 |
56 | class SystemManager:
57 | @staticmethod
58 | def detect_platform():
59 | system = platform.system().lower()
60 |
61 | if system == 'windows':
62 | return "windows"
63 | elif system == 'linux':
64 | if os.path.exists("/data/data/com.termux"):
65 | return "android"
66 | return "linux"
67 | elif system == 'darwin':
68 | return "macos"
69 | else:
70 | return "unknown"
71 |
72 | @staticmethod
73 | def clear_screen():
74 | system = SystemManager.detect_platform()
75 |
76 | try:
77 | if system == "windows":
78 | os.system('cls')
79 | elif system in ["linux", "macos", "android"]:
80 | os.system('clear')
81 | else:
82 | print("\033[2J\033[H", end="")
83 | except Exception:
84 | print("\n" * 100)
85 |
86 | @staticmethod
87 | def detect_terminal_size():
88 | try:
89 | columns, lines = os.get_terminal_size()
90 | return columns, lines
91 | except:
92 | return 80, 24
93 |
94 | @staticmethod
95 | def is_dependency_installed(command):
96 | try:
97 | devnull = open(os.devnull, 'w')
98 | subprocess.check_call([command, "--version"], stdout=devnull, stderr=devnull)
99 | return True
100 | except:
101 | return False
102 |
103 | class DependencyChecker:
104 | """Check and install Python dependencies"""
105 |
106 | @staticmethod
107 | def check_python_version():
108 | """Check if Python version is compatible"""
109 | if sys.version_info < (3, 6):
110 | print(f"{TermCtrl.BRIGHT_RED}Error: Python 3.6 or higher is required.{TermCtrl.RESET}")
111 | print(f"Current version: {sys.version}")
112 | return False
113 | return True
114 |
115 | @staticmethod
116 | def install_missing_packages():
117 | """Install missing Python packages"""
118 | packages_to_check = ['colorama', 'pystyle']
119 | missing_packages = []
120 |
121 | for package in packages_to_check:
122 | try:
123 | __import__(package)
124 | except ImportError:
125 | missing_packages.append(package)
126 |
127 | if missing_packages:
128 | print(f"{TermCtrl.BRIGHT_YELLOW}Installing missing packages: {', '.join(missing_packages)}{TermCtrl.RESET}")
129 | for package in missing_packages:
130 | try:
131 | subprocess.check_call([sys.executable, '-m', 'pip', 'install', package])
132 | print(f"{TermCtrl.BRIGHT_GREEN}Successfully installed {package}{TermCtrl.RESET}")
133 | except subprocess.CalledProcessError:
134 | print(f"{TermCtrl.BRIGHT_RED}Failed to install {package}{TermCtrl.RESET}")
135 |
136 | return True
137 |
138 | class MenuManager:
139 | def __init__(self):
140 | self.base_dir = os.path.dirname(os.path.abspath(__file__))
141 | self.src_dir = os.path.join(self.base_dir, "src")
142 | self.gui_dir = os.path.join(self.base_dir, "src", "client")
143 | self.kitty_dir = os.path.join(self.base_dir, "Kitty")
144 |
145 | # Check if src directory exists
146 | self.is_src_available = os.path.isdir(self.src_dir)
147 |
148 | # Initialize terminal state
149 | self.term_width, self.term_height = SystemManager.detect_terminal_size()
150 |
151 | # Menu state
152 | self.exit_requested = False
153 | self.current_selection = 0
154 | self.menu_items = [
155 | {"id": "howto", "label": "How to Use", "description": "Interactive guide on using Kitty Tools "},
156 | {"id": "info", "label": "Information", "description": "Credits, license, and additional information "},
157 | {"id": "flood", "label": "Kahoot Flooder", "description": "Advanced Kahoot game flooding utility "},
158 | {"id": "answers", "label": "Answer Hack", "description": "Obtain answers for Kahoot quizzes "},
159 | {"id": "graphical", "label": "GUI", "description": "A graphical user interface for ease of use "},
160 | {"id": "exit", "label": "Exit", "description": "Exit the application "}
161 | ]
162 |
163 | def render_header(self):
164 | version_number = "v36.2 Enhanced"
165 | print(f" {TermCtrl.BRIGHT_YELLOW}┌{'─' * (self.term_width - 4)}┐{TermCtrl.RESET}")
166 | print(f" {TermCtrl.BRIGHT_YELLOW}│{TermCtrl.RESET} {TermCtrl.BOLD}{TermCtrl.BRIGHT_WHITE}KITTY TOOLS{TermCtrl.RESET} {TermCtrl.DIM}by CPScript{TermCtrl.RESET}{' ' * (self.term_width - 28)}{TermCtrl.DIM}{TermCtrl.RESET}{TermCtrl.BRIGHT_YELLOW}│{TermCtrl.RESET}")
167 | print(f" {TermCtrl.BRIGHT_YELLOW}│{TermCtrl.RESET} {TermCtrl.DIM}{version_number}{TermCtrl.RESET}{' ' * (self.term_width - 17 - len(version_number))}{TermCtrl.BRIGHT_YELLOW} │{TermCtrl.RESET}")
168 | print(f" {TermCtrl.BRIGHT_YELLOW}└{'─' * (self.term_width - 4)}┘{TermCtrl.RESET}")
169 | print()
170 |
171 | def render_menu(self):
172 | print(f" {TermCtrl.BRIGHT_CYAN}╭{'─' * (self.term_width - 4)}╮{TermCtrl.RESET}")
173 | print(f" {TermCtrl.BRIGHT_CYAN}│{TermCtrl.RESET} {TermCtrl.BOLD}Main Menu{TermCtrl.RESET}{' ' * (self.term_width - 14)}{TermCtrl.BRIGHT_CYAN}│{TermCtrl.RESET}")
174 | print(f" {TermCtrl.BRIGHT_CYAN}├{'─' * (self.term_width - 4)}┤{TermCtrl.RESET}")
175 |
176 | for idx, item in enumerate(self.menu_items):
177 | if idx == self.current_selection:
178 | selector = f"{TermCtrl.BRIGHT_GREEN}▶{TermCtrl.RESET}"
179 | label = f"{TermCtrl.BRIGHT_WHITE}{TermCtrl.BOLD}{item['label']}{TermCtrl.RESET}"
180 | desc = f"{TermCtrl.BRIGHT_WHITE}{item['description']}{TermCtrl.RESET}"
181 | else:
182 | selector = " "
183 | label = f"{TermCtrl.WHITE}{item['label']}{TermCtrl.RESET}"
184 | desc = f"{TermCtrl.DIM}{item['description']}{TermCtrl.RESET}"
185 |
186 | id_text = f"{idx + 1}. "
187 | spacing = " " * (20 - len(item['label']))
188 | print(f" {TermCtrl.BRIGHT_CYAN}│{TermCtrl.RESET} {selector} {id_text}{label}{spacing}{desc}{' ' * (self.term_width - 50 - len(item['description']))}{TermCtrl.BRIGHT_CYAN}│{TermCtrl.RESET}")
189 |
190 | print(f" {TermCtrl.BRIGHT_CYAN}╰{'─' * (self.term_width - 4)}╯{TermCtrl.RESET}")
191 | print()
192 | print(f" {TermCtrl.DIM}Use number keys to navigate, Enter to select{TermCtrl.RESET}")
193 |
194 | def render_status(self, message=None):
195 | platform_info = SystemManager.detect_platform()
196 | platform_label = f"{platform_info.capitalize()} platform detected"
197 |
198 | if message:
199 | status_text = message
200 | else:
201 | status_text = "Ready"
202 |
203 | print()
204 | print(f" {TermCtrl.BRIGHT_BLACK}Status: {TermCtrl.RESET}{status_text}")
205 | print(f" {TermCtrl.BRIGHT_BLACK}System: {TermCtrl.RESET}{platform_label}")
206 |
207 | # Check if enhanced version is available
208 | if self.is_src_available:
209 | print(f" {TermCtrl.BRIGHT_BLACK}Mode: {TermCtrl.RESET}{TermCtrl.BRIGHT_GREEN}Enhanced Version Available{TermCtrl.RESET}")
210 | else:
211 | print(f" {TermCtrl.BRIGHT_BLACK}Mode: {TermCtrl.RESET}{TermCtrl.YELLOW}Standard Version{TermCtrl.RESET}")
212 |
213 | def get_user_selection(self):
214 | try:
215 | choice = input("\n Make a selection (1-6): ")
216 | if choice.isdigit() and 1 <= int(choice) <= len(self.menu_items):
217 | return int(choice) - 1
218 | return self.current_selection
219 | except KeyboardInterrupt:
220 | return len(self.menu_items) - 1 # Select exit option
221 | except:
222 | return self.current_selection
223 |
224 | def check_dependencies_for_action(self, action_id):
225 | """Check if dependencies are available for the selected action"""
226 | if action_id in ["flood", "answers", "graphical"]:
227 | # Check Python dependencies
228 | try:
229 | import colorama
230 | import pystyle
231 | except ImportError:
232 | print(f"{TermCtrl.BRIGHT_YELLOW}Installing required Python packages...{TermCtrl.RESET}")
233 | DependencyChecker.install_missing_packages()
234 |
235 | # For flooder, check Node.js
236 | if action_id == "flood":
237 | node_available = SystemManager.is_dependency_installed("node")
238 | npm_available = SystemManager.is_dependency_installed("npm")
239 |
240 | if not node_available or not npm_available:
241 | print(f"{TermCtrl.BRIGHT_RED}Node.js is required for the Kahoot Flooder.{TermCtrl.RESET}")
242 | print("The setup script will guide you through installation.")
243 | time.sleep(2)
244 |
245 | return True
246 |
247 | def execute_selected_action(self, selection):
248 | action_id = self.menu_items[selection]["id"]
249 |
250 | # Check dependencies before executing
251 | if not self.check_dependencies_for_action(action_id):
252 | input(f"\n{TermCtrl.BRIGHT_YELLOW}Press Enter to return to the main menu...{TermCtrl.RESET}")
253 | return
254 |
255 | # Prepare to execute selected action
256 | print(f"\n {TermCtrl.BRIGHT_BLUE}Launching {self.menu_items[selection]['label']}...{TermCtrl.RESET}")
257 | time.sleep(1)
258 | SystemManager.clear_screen()
259 |
260 | try:
261 | if action_id == "howto":
262 | self.execute_howto()
263 | elif action_id == "info":
264 | self.execute_info()
265 | elif action_id == "flood":
266 | self.execute_flood()
267 | elif action_id == "answers":
268 | self.execute_answers()
269 | elif action_id == "graphical":
270 | self.execute_graphical()
271 | elif action_id == "exit":
272 | self.exit_requested = True
273 | print(f"{TermCtrl.BRIGHT_GREEN}Thank you for using KITTY TOOLS{TermCtrl.RESET}")
274 | return
275 |
276 | input(f"\n{TermCtrl.BRIGHT_YELLOW}Press Enter to return to the main menu...{TermCtrl.RESET}")
277 |
278 | except KeyboardInterrupt:
279 | print(f"\n{TermCtrl.BRIGHT_YELLOW}Operation cancelled by user{TermCtrl.RESET}")
280 | input(f"\n{TermCtrl.BRIGHT_YELLOW}Press Enter to return to the main menu...{TermCtrl.RESET}")
281 | except Exception as e:
282 | print(f"\n{TermCtrl.BRIGHT_RED}Error executing {action_id}: {str(e)}{TermCtrl.RESET}")
283 | print(f"{TermCtrl.DIM}If this error persists, please report it on GitHub{TermCtrl.RESET}")
284 | input(f"\n{TermCtrl.BRIGHT_YELLOW}Press Enter to return to the main menu...{TermCtrl.RESET}")
285 |
286 | def execute_howto(self):
287 | print(f"{TermCtrl.BOLD}{TermCtrl.BRIGHT_CYAN}How to Use KITTY TOOLS{TermCtrl.RESET}\n")
288 |
289 | if self.is_src_available:
290 | # Enhanced version
291 | print(f"{TermCtrl.BRIGHT_WHITE}KITTY TOOLS is a suite of utilities for Kahoot:{TermCtrl.RESET}\n")
292 | print(f"{TermCtrl.BRIGHT_CYAN}1. Information{TermCtrl.RESET}")
293 | print(f" View credits, license information, and contributors to the project.")
294 | print(f"{TermCtrl.BRIGHT_CYAN}2. Kahoot Flooder{TermCtrl.RESET}")
295 | print(f" Create multiple automated players in Kahoot games with customizable settings.")
296 | print(f" You can control the bots collectively or let them act autonomously.")
297 | print(f" Note: Requires Node.js installation")
298 | print(f"{TermCtrl.BRIGHT_CYAN}3. Answer Hack{TermCtrl.RESET}")
299 | print(f" Retrieve answers for a Kahoot quiz by providing the Quiz ID.")
300 | print(f" Export answers to a file for future reference.")
301 | print(f"{TermCtrl.BRIGHT_CYAN}4. GUI{TermCtrl.RESET}")
302 | print(f" Use the graphical user interface for easier interaction.")
303 | print(f" Requires PyQt5 installation.\n")
304 | print(f"{TermCtrl.BRIGHT_YELLOW}Note: All features require an active internet connection.{TermCtrl.RESET}")
305 | print(f"{TermCtrl.BRIGHT_YELLOW}Troubleshooting: If you encounter SSL errors, the tools will attempt to fix them automatically.{TermCtrl.RESET}")
306 | else:
307 | # Standard version
308 | try:
309 | subprocess.run([sys.executable, os.path.join(self.kitty_dir, "htu.py")])
310 | except Exception as e:
311 | print(f"Error running how-to guide: {e}")
312 | print("Please check the Kitty directory for the htu.py file")
313 |
314 | def execute_info(self):
315 | if self.is_src_available:
316 | # Enhanced version
317 | print(f"{TermCtrl.BOLD}{TermCtrl.BRIGHT_CYAN}KITTY TOOLS Information{TermCtrl.RESET}\n")
318 | print(f"{TermCtrl.BRIGHT_WHITE}KITTY TOOLS v36.2 Enhanced{TermCtrl.RESET}")
319 | print(f"Developed by: {TermCtrl.BRIGHT_YELLOW}CPScript{TermCtrl.RESET}\n")
320 |
321 | print(f"{TermCtrl.UNDERLINE}Contributors:{TermCtrl.RESET}")
322 | print(f"- {TermCtrl.BRIGHT_RED}@Ccode-lang{TermCtrl.RESET} for helping out!")
323 | print(f"- {TermCtrl.BRIGHT_RED}@xTobyPlayZ{TermCtrl.RESET} for Flooder!")
324 | print(f"- {TermCtrl.BRIGHT_RED}@cheepling{TermCtrl.RESET} for finding bugs!")
325 | print(f"- {TermCtrl.BRIGHT_RED}@Zacky2613{TermCtrl.RESET} for helping and fixing issues!")
326 | print(f"- {TermCtrl.BRIGHT_RED}@KiraKenjiro{TermCtrl.RESET} for reviewing and making changes! {TermCtrl.BRIGHT_RED}<3{TermCtrl.RESET}\n")
327 |
328 | print(f"{TermCtrl.UNDERLINE}License:{TermCtrl.RESET}")
329 | print(f"This software is provided for educational purposes only.")
330 | print(f"Use at your own risk. The authors are not responsible for any misuse.")
331 | print(f"Please read the complete license in the repository for more details.\n")
332 |
333 | print(f"{TermCtrl.UNDERLINE}Recent Fixes:{TermCtrl.RESET}")
334 | print(f"- Fixed SSL certificate issues on macOS")
335 | print(f"- Fixed HTTP 403 Forbidden errors with better headers")
336 | print(f"- Fixed Node.js module loading issues")
337 | print(f"- Added automatic dependency installation")
338 | print(f"- Improved error handling and user feedback\n")
339 |
340 | print(f"{TermCtrl.BRIGHT_GREEN}Thank you for using KITTY TOOLS!{TermCtrl.RESET}")
341 | else:
342 | # Standard version
343 | try:
344 | subprocess.run([sys.executable, os.path.join(self.kitty_dir, "Info", "main.py")])
345 | except Exception as e:
346 | print(f"Error running info module: {e}")
347 | print("Please check the Kitty/Info directory")
348 |
349 | def execute_flood(self):
350 | if self.is_src_available:
351 | # Enhanced version
352 | try:
353 | subprocess.run([sys.executable, os.path.join(self.src_dir, "main.py")])
354 | except Exception as e:
355 | print(f"Error running enhanced flooder: {e}")
356 | print("Falling back to standard version...")
357 | try:
358 | subprocess.run([sys.executable, os.path.join(self.kitty_dir, "Flood", "main.py")])
359 | except Exception as e2:
360 | print(f"Error running standard flooder: {e2}")
361 | else:
362 | # Standard version
363 | try:
364 | subprocess.run([sys.executable, os.path.join(self.kitty_dir, "Flood", "main.py")])
365 | except Exception as e:
366 | print(f"Error running flooder: {e}")
367 | print("Please check the Kitty/Flood directory")
368 |
369 | def execute_answers(self):
370 | if self.is_src_available:
371 | # Enhanced version
372 | try:
373 | subprocess.run([sys.executable, os.path.join(self.src_dir, "client.py")])
374 | except Exception as e:
375 | print(f"Error running enhanced client: {e}")
376 | print("Falling back to standard version...")
377 | try:
378 | subprocess.run([sys.executable, os.path.join(self.kitty_dir, "client.py")])
379 | except Exception as e2:
380 | print(f"Error running standard client: {e2}")
381 | else:
382 | # Standard version
383 | try:
384 | subprocess.run([sys.executable, os.path.join(self.kitty_dir, "client.py")])
385 | except Exception as e:
386 | print(f"Error running client: {e}")
387 | print("Please check the Kitty directory")
388 |
389 | def execute_graphical(self):
390 | if self.is_src_available:
391 | # Enhanced version - try GUI first, fallback to client
392 | try:
393 | # Check if PyQt5 is available
394 | import PyQt5
395 | subprocess.run([sys.executable, os.path.join(self.gui_dir, "main.py")])
396 | except ImportError:
397 | print(f"{TermCtrl.BRIGHT_YELLOW}PyQt5 not found. Installing...{TermCtrl.RESET}")
398 | try:
399 | subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'PyQt5'])
400 | print(f"{TermCtrl.BRIGHT_GREEN}PyQt5 installed successfully. Starting GUI...{TermCtrl.RESET}")
401 | subprocess.run([sys.executable, os.path.join(self.gui_dir, "main.py")])
402 | except subprocess.CalledProcessError:
403 | print(f"{TermCtrl.BRIGHT_RED}Failed to install PyQt5. Using console client instead.{TermCtrl.RESET}")
404 | subprocess.run([sys.executable, os.path.join(self.src_dir, "client.py")])
405 | except Exception as e:
406 | print(f"Error running GUI: {e}")
407 | print("Falling back to console client...")
408 | subprocess.run([sys.executable, os.path.join(self.src_dir, "client.py")])
409 | else:
410 | # Standard version - just run the client
411 | try:
412 | subprocess.run([sys.executable, os.path.join(self.kitty_dir, "client.py")])
413 | except Exception as e:
414 | print(f"Error running client: {e}")
415 |
416 | def run(self):
417 | while not self.exit_requested:
418 | SystemManager.clear_screen()
419 | self.term_width, self.term_height = SystemManager.detect_terminal_size()
420 |
421 | self.render_header()
422 | self.render_menu()
423 | self.render_status()
424 |
425 | selection = self.get_user_selection()
426 | if 0 <= selection < len(self.menu_items):
427 | self.current_selection = selection
428 | self.execute_selected_action(selection)
429 |
430 | def check_system_requirements():
431 | """Check system requirements and dependencies"""
432 | print(f"{TermCtrl.BRIGHT_CYAN}Checking system requirements...{TermCtrl.RESET}")
433 |
434 | # Check Python version
435 | if not DependencyChecker.check_python_version():
436 | sys.exit(1)
437 |
438 | # Install missing Python packages
439 | DependencyChecker.install_missing_packages()
440 |
441 | print(f"{TermCtrl.BRIGHT_GREEN}System requirements check completed.{TermCtrl.RESET}")
442 | time.sleep(1)
443 |
444 | def main():
445 | try:
446 | # Check system requirements first
447 | check_system_requirements()
448 |
449 | # Ensure necessary directories exist
450 | menu_manager = MenuManager()
451 | menu_manager.run()
452 |
453 | except KeyboardInterrupt:
454 | SystemManager.clear_screen()
455 | print(f"{TermCtrl.BRIGHT_GREEN}Thank you for using KITTY TOOLS{TermCtrl.RESET}")
456 | except Exception as e:
457 | print(f"{TermCtrl.BRIGHT_RED}A critical error occurred: {str(e)}{TermCtrl.RESET}")
458 | print(f"{TermCtrl.BRIGHT_YELLOW}Please report this issue on GitHub: https://github.com/CPScript/Kitty-Tools/issues{TermCtrl.RESET}")
459 |
460 | if __name__ == "__main__":
461 | main()
462 |
--------------------------------------------------------------------------------
/src/client.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import os
3 | import time
4 | import sys
5 | import platform
6 | import json
7 | import re
8 | import threading
9 | import random
10 | import ssl
11 | from urllib.request import urlopen, Request
12 | from urllib.error import HTTPError, URLError
13 | from urllib.parse import urlparse, parse_qs
14 | from http.client import InvalidURL
15 |
16 | # Dependency management with graceful fallbacks
17 | DEPENDENCIES = {
18 | "colorama": {"package": "colorama", "components": ["Fore", "Style"]},
19 | "pystyle": {"package": "pystyle", "components": ["Write", "System", "Colors", "Colorate", "Anime"]}
20 | }
21 |
22 | def check_and_install_dependencies():
23 | missing_deps = []
24 |
25 | for dep_name, dep_info in DEPENDENCIES.items():
26 | try:
27 | module = __import__(dep_info["package"])
28 | for component in dep_info["components"]:
29 | if not hasattr(module, component):
30 | raise ImportError(f"Component {component} not found in {dep_name}")
31 | except (ImportError, ModuleNotFoundError):
32 | missing_deps.append(dep_info["package"])
33 |
34 | if missing_deps:
35 | print(f"Installing missing dependencies: {', '.join(missing_deps)}")
36 | for dep in missing_deps:
37 | install_status = os.system(f"pip install {dep}")
38 | if install_status != 0:
39 | print(f"Warning: Failed to install {dep}. Some features may not work correctly.")
40 |
41 | check_and_install_dependencies()
42 |
43 | from colorama import Fore, Style
44 | from pystyle import Write, System, Colors, Colorate, Anime
45 |
46 | class ColorScheme:
47 | RED = Fore.RED
48 | YELLOW = Fore.YELLOW
49 | GREEN = Fore.GREEN
50 | BLUE = Fore.BLUE
51 | ORANGE = Fore.RED + Fore.YELLOW
52 | MAGENTA = Fore.MAGENTA
53 | LIGHTBLUE = Fore.LIGHTBLUE_EX
54 | CYAN = Fore.CYAN
55 | GRAY = Fore.LIGHTBLACK_EX + Fore.WHITE
56 | RESET = Fore.RESET
57 | PINK = Fore.LIGHTGREEN_EX + Fore.LIGHTMAGENTA_EX
58 | DARK_GREEN = Fore.GREEN + Style.BRIGHT
59 | PRETTY = Fore.LIGHTMAGENTA_EX + Fore.LIGHTCYAN_EX
60 |
61 | @classmethod
62 | def random(cls):
63 | colors = [cls.RED, cls.YELLOW, cls.GREEN, cls.BLUE, cls.CYAN,
64 | cls.MAGENTA, cls.LIGHTBLUE]
65 | return random.choice(colors)
66 |
67 | class PlatformManager:
68 | @staticmethod
69 | def detect_platform():
70 | system = platform.system().lower()
71 |
72 | if system == 'windows':
73 | return "windows"
74 | elif system == 'linux':
75 | # Check if running in Termux on Android
76 | if os.path.exists("/data/data/com.termux"):
77 | return "android"
78 | return "linux"
79 | elif system == 'darwin':
80 | return "macos"
81 | else:
82 | return "unknown"
83 |
84 | @staticmethod
85 | def clear_screen():
86 | system = PlatformManager.detect_platform()
87 |
88 | try:
89 | if system == "windows":
90 | os.system('cls')
91 | elif system in ["linux", "macos"]:
92 | os.system('clear')
93 | elif system == "android":
94 | os.system('clear')
95 | print("Notice: Running on Android. For optimal performance, consider using Kitty-Tools LITE.")
96 | else:
97 | print("\033[H\033[J", end="")
98 | print(f"Notice: Unsupported platform '{system}'. Some features may not work as expected.")
99 | print("For more info: https://github.com/CPScript/Kitty-Tools/extra.md")
100 | except Exception as e:
101 | # Last resort fallback
102 | print("\n" * 100)
103 | print(f"Screen clearing failed: {e}")
104 |
105 | class SSLContextManager:
106 | """Handle SSL certificate issues across platforms"""
107 |
108 | @staticmethod
109 | def create_ssl_context():
110 | """Create SSL context with fallback for certificate issues"""
111 | try:
112 | # Try to create default context first
113 | context = ssl.create_default_context()
114 |
115 | # For macOS, try to use certifi if available
116 | if platform.system() == 'Darwin':
117 | try:
118 | import certifi
119 | context = ssl.create_default_context(cafile=certifi.where())
120 | except ImportError:
121 | # certifi not available, use default
122 | pass
123 |
124 | return context
125 | except Exception:
126 | # If all else fails, create unverified context
127 | print("Warning: Using unverified SSL context due to certificate issues")
128 | return ssl._create_unverified_context()
129 |
130 | class RateLimiter:
131 | """Rate limiter to avoid getting blocked"""
132 |
133 | def __init__(self, min_delay=1.0, max_delay=3.0):
134 | self.min_delay = min_delay
135 | self.max_delay = max_delay
136 | self.last_request = None
137 |
138 | def wait(self):
139 | """Wait appropriate time between requests"""
140 | if self.last_request:
141 | elapsed = time.time() - self.last_request
142 | delay = random.uniform(self.min_delay, self.max_delay)
143 |
144 | if elapsed < delay:
145 | sleep_time = delay - elapsed
146 | time.sleep(sleep_time)
147 |
148 | self.last_request = time.time()
149 |
150 | # Kahoot API interaction
151 | class KahootAPI:
152 | BASE_API_URL = "https://play.kahoot.it/rest/kahoots/"
153 | CHALLENGE_API_URL = "https://kahoot.it/rest/challenges/pin/"
154 | REQUEST_TIMEOUT = 15 # seconds
155 |
156 | def __init__(self):
157 | self.rate_limiter = RateLimiter()
158 | self.ssl_context = SSLContextManager.create_ssl_context()
159 |
160 | def _get_headers(self):
161 | """Generate realistic browser headers"""
162 | user_agents = [
163 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
164 | 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
165 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
166 | ]
167 |
168 | return {
169 | 'User-Agent': random.choice(user_agents),
170 | 'Accept': 'application/json, text/plain, */*',
171 | 'Accept-Language': 'en-US,en;q=0.9',
172 | 'DNT': '1',
173 | 'Connection': 'keep-alive',
174 | 'Cache-Control': 'no-cache',
175 | 'Pragma': 'no-cache'
176 | }
177 |
178 | def _make_request(self, url, max_retries=3):
179 | """Make request with retry logic and proper error handling"""
180 | for attempt in range(max_retries):
181 | try:
182 | # Apply rate limiting
183 | self.rate_limiter.wait()
184 |
185 | # Create request with headers
186 | headers = self._get_headers()
187 | request = Request(url, headers=headers)
188 |
189 | # Make the request with SSL context
190 | with urlopen(request, timeout=self.REQUEST_TIMEOUT, context=self.ssl_context) as response:
191 | return json.loads(response.read().decode('utf-8'))
192 |
193 | except HTTPError as e:
194 | if e.code == 403:
195 | print(f"Attempt {attempt + 1}: Access forbidden (403)")
196 | if attempt < max_retries - 1:
197 | wait_time = (attempt + 1) * 5
198 | print(f"Waiting {wait_time} seconds before retry...")
199 | time.sleep(wait_time)
200 | continue
201 | else:
202 | return {'error': 'Access forbidden. This could be due to rate limiting, geographic restrictions, or the quiz being private.'}
203 |
204 | elif e.code == 404:
205 | return {'error': 'Quiz not found. The ID may be incorrect.'}
206 |
207 | elif e.code == 429: # Too Many Requests
208 | if attempt < max_retries - 1:
209 | wait_time = (attempt + 1) * 10
210 | print(f"Rate limited. Waiting {wait_time} seconds...")
211 | time.sleep(wait_time)
212 | continue
213 | else:
214 | return {'error': 'Rate limited by server. Please wait a few minutes and try again.'}
215 |
216 | else:
217 | return {'error': f'HTTP Error: {e.code} - {e.reason}'}
218 |
219 | except URLError as e:
220 | return {'error': f'Connection error: {e.reason}. Check your internet connection.'}
221 |
222 | except ssl.SSLError as e:
223 | print(f"SSL Error on attempt {attempt + 1}: {e}")
224 | if attempt < max_retries - 1:
225 | # Try with unverified context on next attempt
226 | self.ssl_context = ssl._create_unverified_context()
227 | print("Retrying with unverified SSL context...")
228 | continue
229 | else:
230 | return {'error': f'SSL connection failed: {e}'}
231 |
232 | except json.JSONDecodeError:
233 | return {'error': 'Failed to parse the response from Kahoot servers.'}
234 |
235 | except Exception as e:
236 | if attempt < max_retries - 1:
237 | print(f"Attempt {attempt + 1} failed: {e}")
238 | time.sleep(2)
239 | continue
240 | else:
241 | return {'error': f'Unexpected error: {str(e)}'}
242 |
243 | return {'error': 'All retry attempts failed'}
244 |
245 | @staticmethod
246 | def get_quiz_by_id(quiz_id):
247 | if not re.fullmatch(r"^[A-Za-z0-9-]*$", quiz_id):
248 | return {'error': 'Invalid quiz ID format'}
249 |
250 | api = KahootAPI()
251 | url = f"{api.BASE_API_URL}{quiz_id}"
252 | return api._make_request(url)
253 |
254 | @staticmethod
255 | def get_quiz_id_from_pin(pin):
256 | if not pin.isdigit():
257 | return {'error': 'PIN must contain only digits'}
258 |
259 | api = KahootAPI()
260 | url = f"{api.CHALLENGE_API_URL}{pin}"
261 | result = api._make_request(url)
262 |
263 | if 'error' not in result and 'id' in result:
264 | return {'quiz_id': result['id']}
265 | elif 'error' not in result:
266 | return {'error': 'No quiz ID found in response'}
267 |
268 | return result
269 |
270 | class KahootQuiz:
271 | def __init__(self, quiz_data):
272 | self.data = quiz_data
273 | self.valid = 'error' not in quiz_data and 'uuid' in quiz_data
274 |
275 | def get_quiz_details(self):
276 | if not self.valid:
277 | return None
278 |
279 | return {
280 | "uuid": self.data["uuid"],
281 | "creator_username": self.data.get("creator_username", "Unknown"),
282 | "title": self.data.get("title", "Untitled Quiz"),
283 | "description": self.data.get("description", ""),
284 | "cover": self.data.get("cover", ""),
285 | "question_count": len(self.data.get("questions", [])),
286 | "visibility": self.data.get("visibility", "Unknown"),
287 | "created": self.data.get("created", "Unknown date")
288 | }
289 |
290 | def get_questions(self):
291 | if not self.valid:
292 | return []
293 | return self.data.get("questions", [])
294 |
295 | def get_quiz_length(self):
296 | if not self.valid:
297 | return 0
298 | return len(self.data.get("questions", []))
299 |
300 | def _clean_text(self, text):
301 | if not text:
302 | return ""
303 |
304 | text = str(text)
305 |
306 | replacements = [
307 | ("", ""), ("
", ""),
308 | ("", ""), (" ", ""),
309 | ("", ""), (" ", ""),
310 | (" ", "\n"), (" ", "\n"),
311 | ("", ""), (" ", ""),
312 | ("", ""), (" ", ""),
313 | ("", ""), (" ", ""),
314 | ("", ""), (" ", ""),
315 | ("", ""), (" ", ""),
316 | ("", ""), (" ", ""),
317 | ("", ""), (" ", ""),
318 | ("", ""), (" ", ""),
319 | ("", ""), (" ", "")
320 | ]
321 |
322 | for old, new in replacements:
323 | text = text.replace(old, new)
324 |
325 | text = re.sub(r'<[^>]+>', '', text)
326 |
327 | text = text.replace('\\"', '"')
328 |
329 | return text.strip()
330 |
331 | def get_question_details(self, question_index):
332 | if not self.valid or question_index >= self.get_quiz_length():
333 | return None
334 |
335 | question = self.data["questions"][question_index]
336 | question_type = question.get("type", "unknown")
337 |
338 | details = {
339 | "type": question_type,
340 | "layout": question.get("layout"),
341 | "image": question.get("image"),
342 | "pointsMultiplier": question.get("pointsMultiplier", 1),
343 | "time": question.get("time"),
344 | "media": question.get("media")
345 | }
346 |
347 | if question_type == "content":
348 | details.update({
349 | "title": self._clean_text(question.get("title", "")),
350 | "description": self._clean_text(question.get("description", ""))
351 | })
352 | else:
353 | # Process question and choices
354 | details.update({
355 | "question": self._clean_text(question.get("question", "")),
356 | "choices": [],
357 | "amount_of_answers": len(question.get("choices", [])),
358 | "amount_of_correct_answers": 0
359 | })
360 |
361 | # Process each answer choice
362 | for choice in question.get("choices", []):
363 | cleaned_choice = {
364 | "answer": self._clean_text(choice.get("answer", "")),
365 | "correct": choice.get("correct", False)
366 | }
367 |
368 | details["choices"].append(cleaned_choice)
369 | if cleaned_choice["correct"]:
370 | details["amount_of_correct_answers"] += 1
371 |
372 | return details
373 |
374 | def get_answer(self, question_index):
375 | details = self.get_question_details(question_index)
376 |
377 | if not details:
378 | return None
379 |
380 | if details["type"] == "content":
381 | return None # Content slides don't have answers
382 |
383 | answers = []
384 |
385 | if details["type"] == "jumble":
386 | for choice in details["choices"]:
387 | answers.append(choice["answer"])
388 | else:
389 | for choice in details["choices"]:
390 | if choice["correct"]:
391 | answers.append(choice["answer"])
392 |
393 | return answers if answers else None
394 |
395 | def export_answers_to_file(self, filename="kahoot_answers.txt"):
396 | if not self.valid:
397 | return False
398 |
399 | try:
400 | with open(filename, 'w', encoding='utf-8') as f:
401 | quiz_details = self.get_quiz_details()
402 | f.write(f"KAHOOT QUIZ ANSWERS\n")
403 | f.write(f"=================\n\n")
404 | f.write(f"Title: {quiz_details['title']}\n")
405 | f.write(f"Creator: {quiz_details['creator_username']}\n")
406 | f.write(f"Quiz ID: {quiz_details['uuid']}\n")
407 | if quiz_details['description']:
408 | f.write(f"Description: {quiz_details['description']}\n")
409 | f.write(f"Question Count: {quiz_details['question_count']}\n\n")
410 | f.write(f"QUESTIONS AND ANSWERS\n")
411 | f.write(f"====================\n\n")
412 |
413 | for i in range(self.get_quiz_length()):
414 | details = self.get_question_details(i)
415 |
416 | if details["type"] == "content":
417 | f.write(f"SLIDE {i+1}: {details['title']}\n")
418 | if details['description']:
419 | f.write(f"Description: {details['description']}\n")
420 | else:
421 | f.write(f"QUESTION {i+1}: {details['question']}\n")
422 | f.write(f"Type: {details['type']}\n")
423 |
424 | answers = self.get_answer(i)
425 | if answers:
426 | f.write(f"Correct Answer(s): {', '.join(answers)}\n")
427 | else:
428 | f.write(f"No correct answers found.\n")
429 |
430 | f.write("\n")
431 |
432 | return True
433 | except Exception as e:
434 | print(f"Failed to export answers: {str(e)}")
435 | return False
436 |
437 | class KahootClientUI:
438 | @staticmethod
439 | def display_intro():
440 | PlatformManager.clear_screen()
441 |
442 | print("NOTICE: This is an enhanced version of the Kitty-Tools Kahoot client.")
443 |
444 | Write.Print(f"""
445 | _______________________
446 | || Enter your quiz ID ||
447 | || below! <3 ||
448 | |//
449 | (>﹏<)
450 | --------------------------------------
451 | ---- Kitty-Tools | By <> CPScript ----
452 | ---- Enhanced Edition v1.0 ----------
453 | --------------------------------------
454 | \n""", Colors.orange, interval=0.000)
455 |
456 | @staticmethod
457 | def get_quiz_id():
458 | while True:
459 | Write.Print(f"┌─[Enter Kahoot-ID] <> [User-Input]\n", Colors.white, interval=0.000)
460 | Write.Print(f"└─────► ", Colors.white, interval=0.000)
461 | user_input = input(ColorScheme.PRETTY)
462 |
463 | if user_input.isdigit():
464 | print(f"{ColorScheme.ORANGE}({ColorScheme.GREEN}!{ColorScheme.ORANGE}) Detected a game PIN. Attempting to fetch the quiz ID...")
465 | result = KahootAPI.get_quiz_id_from_pin(user_input)
466 |
467 | if 'error' in result:
468 | print(f"{ColorScheme.RED}Error: {result['error']}")
469 | retry = input(f"{ColorScheme.YELLOW}Try again? (y/n): {ColorScheme.RESET}").lower()
470 | if retry != 'y':
471 | return None
472 | else:
473 | quiz_id = result['quiz_id']
474 | print(f"{ColorScheme.GREEN}Successfully found quiz ID: {quiz_id}")
475 | time.sleep(1)
476 | return quiz_id
477 | else:
478 | if re.fullmatch(r"^[A-Za-z0-9-]*$", user_input):
479 | return user_input
480 | else:
481 | print(f"{ColorScheme.RED}Invalid quiz ID format. It should contain only letters, numbers, and hyphens.")
482 | retry = input(f"{ColorScheme.YELLOW}Try again? (y/n): {ColorScheme.RESET}").lower()
483 | if retry != 'y':
484 | return None
485 |
486 | @staticmethod
487 | def display_answers(kahoot):
488 | """Display quiz answers with proper formatting and color coding."""
489 | if not kahoot.valid:
490 | print(f"{ColorScheme.RED} Failed to retrieve quiz data. Please check the Quiz ID and try again.")
491 | return
492 |
493 | quiz_details = kahoot.get_quiz_details()
494 |
495 | print(f"\n{ColorScheme.CYAN}========= QUIZ INFORMATION =========")
496 | print(f"{ColorScheme.PRETTY}{ColorScheme.ORANGE}[{ColorScheme.RESET}Title{ColorScheme.ORANGE}]{ColorScheme.GREEN}--{ColorScheme.ORANGE}[{ColorScheme.RESET}{quiz_details['title']}{ColorScheme.ORANGE}]{ColorScheme.RESET}")
497 | print(f"{ColorScheme.PRETTY}{ColorScheme.ORANGE}[{ColorScheme.RESET}Creator{ColorScheme.ORANGE}]{ColorScheme.GREEN}--{ColorScheme.ORANGE}[{ColorScheme.RESET}{quiz_details['creator_username']}{ColorScheme.ORANGE}]{ColorScheme.RESET}")
498 | print(f"{ColorScheme.PRETTY}{ColorScheme.ORANGE}[{ColorScheme.RESET}Questions{ColorScheme.ORANGE}]{ColorScheme.GREEN}--{ColorScheme.ORANGE}[{ColorScheme.RESET}{quiz_details['question_count']}{ColorScheme.ORANGE}]{ColorScheme.RESET}")
499 | print(f"{ColorScheme.CYAN}====================================\n")
500 |
501 | output_lock = threading.Lock()
502 |
503 | for i in range(kahoot.get_quiz_length()):
504 | question_details = kahoot.get_question_details(i)
505 |
506 | with output_lock:
507 | if question_details["type"] == "content":
508 | print(f"{ColorScheme.YELLOW}SLIDE {i+1}: {question_details['title']}")
509 | if question_details.get('description'):
510 | print(f"{ColorScheme.GRAY}Description: {question_details['description']}{ColorScheme.RESET}\n")
511 | else:
512 | print(f"{ColorScheme.PRETTY}{ColorScheme.ORANGE}[{ColorScheme.RESET}Question {i+1}{ColorScheme.ORANGE}]{ColorScheme.GREEN}--{ColorScheme.ORANGE}[{ColorScheme.RESET}{question_details['question']}{ColorScheme.ORANGE}]{ColorScheme.RESET}")
513 |
514 | answers = kahoot.get_answer(i)
515 | if answers:
516 | print(f"{ColorScheme.PRETTY}{ColorScheme.ORANGE}[{ColorScheme.RESET}Answer{ColorScheme.ORANGE}]{ColorScheme.GREEN}--{ColorScheme.ORANGE}[{ColorScheme.RESET}{', '.join(answers)}{ColorScheme.ORANGE}]{ColorScheme.RESET}\n")
517 | else:
518 | print(f"{ColorScheme.PRETTY}{ColorScheme.ORANGE}[{ColorScheme.RESET}Answer{ColorScheme.ORANGE}]{ColorScheme.GREEN}--{ColorScheme.ORANGE}[{ColorScheme.RESET}No correct answers found{ColorScheme.ORANGE}]{ColorScheme.RESET}\n")
519 |
520 | time.sleep(0.010)
521 |
522 | @staticmethod
523 | def display_options(kahoot):
524 | if not kahoot.valid:
525 | return
526 |
527 | print(f"\n{ColorScheme.CYAN}========= ADDITIONAL OPTIONS =========")
528 | print(f"{ColorScheme.YELLOW}1. {ColorScheme.RESET}Export answers to a text file")
529 | print(f"{ColorScheme.YELLOW}2. {ColorScheme.RESET}Return to main menu")
530 | print(f"{ColorScheme.YELLOW}3. {ColorScheme.RESET}Exit")
531 | print(f"{ColorScheme.CYAN}======================================\n")
532 |
533 | choice = input(f"{ColorScheme.PRETTY}Select an option: {ColorScheme.RESET}")
534 |
535 | if choice == "1":
536 | filename = input(f"{ColorScheme.PRETTY}Enter filename (default: kahoot_answers.txt): {ColorScheme.RESET}") or "kahoot_answers.txt"
537 | if kahoot.export_answers_to_file(filename):
538 | print(f"{ColorScheme.GREEN}Answers successfully exported to {filename}")
539 | else:
540 | print(f"{ColorScheme.RED}Failed to export answers.")
541 | elif choice == "2":
542 | return "menu"
543 | else:
544 | return "exit"
545 |
546 | def start_kahoot():
547 | try:
548 | while True:
549 | KahootClientUI.display_intro()
550 |
551 | quiz_id = KahootClientUI.get_quiz_id()
552 | if not quiz_id:
553 | print(f"{ColorScheme.RED}Operation cancelled.")
554 | break
555 |
556 | print(f"{ColorScheme.PRETTY}{ColorScheme.ORANGE}({ColorScheme.GREEN}!{ColorScheme.ORANGE}) Fetching Answers From: {ColorScheme.ORANGE}[{ColorScheme.RESET}Quiz-ID: {quiz_id}{ColorScheme.ORANGE}]\n")
557 |
558 | quiz_data = KahootAPI.get_quiz_by_id(quiz_id)
559 |
560 | if 'error' in quiz_data:
561 | print(f"{ColorScheme.RED}Error: {quiz_data['error']}")
562 | retry = input(f"{ColorScheme.YELLOW}Try again? (y/n): {ColorScheme.RESET}").lower()
563 | if retry != 'y':
564 | break
565 | continue
566 |
567 | kahoot = KahootQuiz(quiz_data)
568 |
569 | KahootClientUI.display_answers(kahoot)
570 |
571 | result = KahootClientUI.display_options(kahoot)
572 |
573 | if result == "exit":
574 | break
575 | elif result != "menu":
576 | input(f"\n{ColorScheme.CYAN}Press any key to exit...{ColorScheme.RESET}")
577 | break
578 |
579 | except KeyboardInterrupt:
580 | print(f"\n{ColorScheme.YELLOW}Operation cancelled by user.{ColorScheme.RESET}")
581 | except Exception as e:
582 | print(f"\n{ColorScheme.RED}An unexpected error occurred: {str(e)}{ColorScheme.RESET}")
583 |
584 | print(f"""
585 | {ColorScheme.RED}||=========================================================
586 | ||Thanks for using Kitty-Tools <3
587 | ||Please *STAR* this repo and follow the creator on github!
588 | ||=========================================================\n
589 | {ColorScheme.RESET}""")
590 |
591 | if __name__ == "__main__":
592 | start_kahoot()
593 |
--------------------------------------------------------------------------------
/docs/index.css:
--------------------------------------------------------------------------------
1 | .header-header {
2 | flex: 0 0 auto;
3 | width: 100%;
4 | height: 75px;
5 | display: flex;
6 | padding: var(--dl-space-space-unitandhalf);
7 | z-index: 1;
8 | max-width: 1320px;
9 | align-items: center;
10 | margin-bottom: 0.75rem;
11 | flex-direction: row;
12 | justify-content: space-between;
13 | }
14 | .header-container {
15 | display: flex;
16 | align-items: center;
17 | flex-direction: row;
18 | justify-content: space-between;
19 | }
20 | .header-navlink {
21 | display: contents;
22 | }
23 | .header-heading {
24 | color: var(--dl-color-gray-white);
25 | font-weight: 700;
26 | margin-right: var(--dl-space-space-unit);
27 | text-decoration: none;
28 | }
29 | .header-link {
30 | display: contents;
31 | }
32 | .header-container1 {
33 | display: flex;
34 | align-items: center;
35 | margin-left: var(--dl-space-space-halfunit);
36 | flex-direction: row;
37 | text-decoration: none;
38 | }
39 | .header-icon {
40 | fill: var(--dl-color-gray-900);
41 | width: 13px;
42 | }
43 | .header-text {
44 | color: var(--dl-color-gray-white);
45 | margin-left: var(--dl-space-space-halfunit);
46 | }
47 | .header-burger-menu {
48 | display: none;
49 | align-items: center;
50 | flex-direction: row;
51 | justify-content: space-between;
52 | }
53 | .header-icon05 {
54 | width: 24px;
55 | height: 24px;
56 | }
57 | .header-container2 {
58 | flex: 0 0 auto;
59 | display: flex;
60 | align-items: center;
61 | flex-direction: row;
62 | }
63 | .header-link1 {
64 | display: contents;
65 | }
66 | .header-icon07 {
67 | fill: var(--dl-color-gray-900);
68 | width: 24px;
69 | height: 24px;
70 | margin-top: var(--dl-space-space-halfunit);
71 | margin-left: 0.75rem;
72 | margin-right: 0.75rem;
73 | margin-bottom: var(--dl-space-space-halfunit);
74 | text-decoration: none;
75 | }
76 | .header-mobile-menu {
77 | top: 0px;
78 | flex: 0 0 auto;
79 | left: 0px;
80 | width: 100%;
81 | height: 100vh;
82 | display: none;
83 | padding: var(--dl-space-space-unitandhalf);
84 | z-index: 100;
85 | position: absolute;
86 | align-items: flex-start;
87 | flex-direction: column;
88 | background-color: #fff;
89 | }
90 | .header-top {
91 | width: 100%;
92 | display: flex;
93 | align-items: center;
94 | margin-bottom: var(--dl-space-space-unit);
95 | flex-direction: row;
96 | justify-content: space-between;
97 | }
98 | .header-navlink1 {
99 | display: contents;
100 | }
101 | .header-heading1 {
102 | color: var(--dl-color-secondary-400);
103 | font-weight: 700;
104 | margin-right: var(--dl-space-space-unit);
105 | text-decoration: none;
106 | }
107 | .header-close-menu {
108 | display: flex;
109 | align-items: center;
110 | flex-direction: row;
111 | justify-content: space-between;
112 | }
113 | .header-icon09 {
114 | width: 24px;
115 | height: 24px;
116 | }
117 | .header-mid {
118 | display: flex;
119 | align-items: flex-start;
120 | flex-direction: column;
121 | justify-content: space-between;
122 | }
123 | .header-navlink2 {
124 | display: contents;
125 | }
126 | .header-container3 {
127 | display: flex;
128 | align-items: center;
129 | margin-bottom: 8px;
130 | flex-direction: row;
131 | text-decoration: none;
132 | }
133 | .header-icon11 {
134 | fill: var(--dl-color-secondary-400);
135 | width: 13px;
136 | }
137 | .header-text1 {
138 | color: var(--dl-color-secondary-400);
139 | margin-left: var(--dl-space-space-halfunit);
140 | }
141 | .header-navlink3 {
142 | display: contents;
143 | }
144 | .header-container4 {
145 | display: flex;
146 | align-items: center;
147 | flex-direction: row;
148 | text-decoration: none;
149 | }
150 | .header-icon13 {
151 | fill: var(--dl-color-secondary-400);
152 | width: 13px;
153 | }
154 | .header-text2 {
155 | color: var(--dl-color-secondary-400);
156 | margin-left: var(--dl-space-space-halfunit);
157 | }
158 | .header-bot {
159 | flex: 0 0 auto;
160 | display: flex;
161 | margin-top: auto;
162 | align-items: center;
163 | flex-direction: row;
164 | }
165 | .header-icon18 {
166 | fill: var(--dl-color-secondary-400);
167 | width: 24px;
168 | height: 24px;
169 | margin-top: var(--dl-space-space-halfunit);
170 | margin-right: 0.75rem;
171 | margin-bottom: var(--dl-space-space-halfunit);
172 | }
173 | .header-icon20 {
174 | fill: var(--dl-color-secondary-400);
175 | width: 24px;
176 | height: 24px;
177 | margin-top: var(--dl-space-space-halfunit);
178 | margin-left: 0.75rem;
179 | margin-right: 0.75rem;
180 | margin-bottom: var(--dl-space-space-halfunit);
181 | }
182 | .header-icon22 {
183 | fill: var(--dl-color-secondary-400);
184 | width: 24px;
185 | height: 24px;
186 | margin-top: var(--dl-space-space-halfunit);
187 | margin-left: 0.75rem;
188 | margin-right: 0.75rem;
189 | margin-bottom: var(--dl-space-space-halfunit);
190 | }
191 | .header-root-class-name {
192 | margin-bottom: 0px;
193 | }
194 | @media(max-width: 991px) {
195 | .header-header {
196 | max-width: 1320px;
197 | margin-bottom: var(--dl-space-space-triplequarter);
198 | }
199 | .header-icon07 {
200 | margin-top: 0px;
201 | margin-bottom: 0px;
202 | }
203 | .header-icon18 {
204 | margin-top: 0px;
205 | margin-bottom: 0px;
206 | }
207 | .header-icon20 {
208 | margin-top: 0px;
209 | margin-bottom: 0px;
210 | }
211 | .header-icon22 {
212 | margin-top: 0px;
213 | margin-bottom: 0px;
214 | }
215 | }
216 | @media(max-width: 767px) {
217 | .header-header {
218 | background-color: var(--dl-color-gray-white);
219 | }
220 | .header-heading {
221 | color: var(--dl-color-secondary-400);
222 | }
223 | .header-container1 {
224 | display: none;
225 | }
226 | .header-burger-menu {
227 | display: flex;
228 | }
229 | .header-icon05 {
230 | fill: var(--dl-color-secondary-400);
231 | }
232 | .header-container2 {
233 | display: none;
234 | }
235 | .header-root-class-name {
236 | margin-bottom: 0px;
237 | }
238 | }
239 |
240 | .label-container {
241 | display: flex;
242 | position: relative;
243 | }
244 | .label-text {
245 | color: var(--dl-color-pimary-500);
246 | font-style: normal;
247 | font-weight: 700;
248 | line-height: 1;
249 | padding-top: 0.25rem;
250 | padding-left: var(--dl-space-space-halfunit);
251 | border-radius: var(--dl-radius-radius-radius25);
252 | padding-right: var(--dl-space-space-halfunit);
253 | padding-bottom: 0.25rem;
254 | text-transform: uppercase;
255 | background-color: var(--dl-color-pimary-700);
256 | }
257 |
258 | .member-details-container {
259 | display: flex;
260 | align-items: center;
261 | flex-direction: column;
262 | justify-content: center;
263 | }
264 | .member-details-image {
265 | width: 100px;
266 | box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05);
267 | object-fit: cover;
268 | border-radius: var(--dl-radius-radius-round);
269 | }
270 | .member-details-text {
271 | color: var(--dl-color-secondary-100);
272 | text-align: center;
273 | padding-top: var(--dl-space-space-unitandhalf);
274 | }
275 | .member-details-text1 {
276 | color: var(--dl-color-secondary-700);
277 | text-align: center;
278 | text-transform: uppercase;
279 | }
280 |
281 | .footer-container {
282 | width: 100%;
283 | display: flex;
284 | padding: var(--dl-space-space-doubleunit);
285 | align-items: center;
286 | flex-direction: column;
287 | justify-content: center;
288 | background-color: var(--dl-color-pimary-900);
289 | }
290 | .footer-container1 {
291 | width: 100%;
292 | display: flex;
293 | max-width: 1320px;
294 | align-items: flex-start;
295 | flex-direction: row;
296 | justify-content: space-between;
297 | }
298 | .footer-container2 {
299 | flex: 0 0 auto;
300 | display: flex;
301 | align-items: flex-start;
302 | flex-direction: column;
303 | }
304 | .footer-text {
305 | color: var(--dl-color-secondary-400);
306 | margin-top: var(--dl-space-space-unit);
307 | font-weight: 600;
308 | }
309 | .footer-text1 {
310 | color: var(--dl-color-secondary-500);
311 | margin-top: var(--dl-space-space-halfunit);
312 | margin-bottom: var(--dl-space-space-halfunit);
313 | }
314 | .footer-container3 {
315 | display: flex;
316 | margin-top: var(--dl-space-space-unitandhalf);
317 | align-items: flex-start;
318 | flex-direction: row;
319 | }
320 | .footer-link {
321 | display: contents;
322 | }
323 | .footer-container4 {
324 | flex: 0 0 auto;
325 | width: 2.5rem;
326 | height: 2.5rem;
327 | display: flex;
328 | box-shadow: 5px 5px 10px 0px #d4d4d4;
329 | align-items: center;
330 | margin-left: var(--dl-space-space-halfunit);
331 | border-radius: var(--dl-radius-radius-round);
332 | flex-direction: row;
333 | justify-content: center;
334 | text-decoration: none;
335 | background-color: var(--dl-color-gray-white);
336 | }
337 | .footer-icon {
338 | width: 16px;
339 | height: 16px;
340 | }
341 | .footer-container5 {
342 | display: flex;
343 | align-items: flex-start;
344 | flex-direction: row;
345 | justify-content: flex-start;
346 | }
347 | .footer-container6 {
348 | flex: 0 0 auto;
349 | display: flex;
350 | align-items: flex-start;
351 | margin-right: 6rem;
352 | flex-direction: column;
353 | }
354 | .footer-text2 {
355 | color: var(--dl-color-secondary-400);
356 | font-weight: 600;
357 | padding-bottom: var(--dl-space-space-halfunit);
358 | }
359 | .footer-link1 {
360 | color: var(--dl-color-secondary-500);
361 | padding-bottom: var(--dl-space-space-halfunit);
362 | text-decoration: none;
363 | }
364 | .footer-container7 {
365 | flex: 0 0 auto;
366 | display: flex;
367 | align-items: flex-start;
368 | flex-direction: column;
369 | }
370 | .footer-text3 {
371 | color: var(--dl-color-secondary-400);
372 | font-weight: 600;
373 | padding-bottom: var(--dl-space-space-halfunit);
374 | }
375 | .footer-link2 {
376 | color: var(--dl-color-secondary-500);
377 | padding-bottom: var(--dl-space-space-halfunit);
378 | text-decoration: none;
379 | }
380 | .footer-link3 {
381 | color: var(--dl-color-secondary-500);
382 | padding-bottom: var(--dl-space-space-halfunit);
383 | text-decoration: none;
384 | }
385 | .footer-link4 {
386 | color: var(--dl-color-secondary-500);
387 | text-decoration: none;
388 | }
389 | .footer-container8 {
390 | flex: 0 0 auto;
391 | width: 100%;
392 | height: 1px;
393 | display: flex;
394 | margin-top: var(--dl-space-space-tripleunit);
395 | align-items: flex-start;
396 | margin-bottom: var(--dl-space-space-tripleunit);
397 | flex-direction: column;
398 | background-color: #e3e8efff;
399 | }
400 | .footer-text4 {
401 | color: var(--dl-color-secondary-500);
402 | align-self: center;
403 | }
404 | @media(max-width: 991px) {
405 | .footer-container1 {
406 | align-items: center;
407 | flex-direction: column;
408 | }
409 | .footer-container2 {
410 | align-items: center;
411 | margin-bottom: var(--dl-space-space-doubleunit);
412 | }
413 | .footer-container6 {
414 | align-items: center;
415 | }
416 | .footer-container7 {
417 | align-items: center;
418 | }
419 | }
420 | @media(max-width: 767px) {
421 | .footer-text1 {
422 | text-align: center;
423 | }
424 | .footer-container5 {
425 | align-items: center;
426 | flex-direction: column;
427 | }
428 | .footer-container6 {
429 | align-items: center;
430 | margin-right: 0px;
431 | margin-bottom: var(--dl-space-space-doubleunit);
432 | }
433 | .footer-container7 {
434 | align-items: center;
435 | }
436 | }
437 |
438 | .home-container {
439 | width: 100%;
440 | height: auto;
441 | display: flex;
442 | min-height: 100vh;
443 | align-items: flex-start;
444 | flex-direction: column;
445 | }
446 | .home-hero {
447 | flex: 0 0 auto;
448 | width: 100%;
449 | height: 80vh;
450 | display: flex;
451 | position: relative;
452 | align-items: center;
453 | flex-direction: column;
454 | background-size: cover;
455 | background-image: url('https://kahoot.com/files/2023/09/desktop-mosaic-HE-bts-v2.jpg');
456 | }
457 | .home-bg {
458 | flex: 0 0 auto;
459 | width: 100%;
460 | height: 100%;
461 | display: flex;
462 | opacity: 0.7;
463 | position: absolute;
464 | align-items: flex-start;
465 | flex-direction: column;
466 | background-color: var(--dl-color-gray-black);
467 | }
468 | .home-container01 {
469 | top: auto;
470 | left: auto;
471 | right: 0px;
472 | width: 100%;
473 | bottom: 0px;
474 | height: 100%;
475 | display: flex;
476 | position: absolute;
477 | align-items: center;
478 | flex-direction: column;
479 | justify-content: center;
480 | }
481 | .home-container02 {
482 | display: flex;
483 | align-items: center;
484 | padding-left: var(--dl-space-space-tripleunit);
485 | padding-right: var(--dl-space-space-tripleunit);
486 | flex-direction: column;
487 | justify-content: center;
488 | }
489 | .home-text {
490 | color: var(--dl-color-gray-white);
491 | text-align: center;
492 | }
493 | .home-text01 {
494 | color: var(--dl-color-gray-900);
495 | max-width: 800px;
496 | text-align: center;
497 | }
498 | .home-image {
499 | top: auto;
500 | left: auto;
501 | right: 0px;
502 | width: 100%;
503 | bottom: -1px;
504 | position: absolute;
505 | object-fit: cover;
506 | }
507 | .home-image1 {
508 | left: auto;
509 | right: 0px;
510 | width: 100%;
511 | bottom: 0px;
512 | display: none;
513 | position: absolute;
514 | object-fit: cover;
515 | }
516 | .home-section1 {
517 | flex: 0 0 auto;
518 | width: 100%;
519 | display: flex;
520 | position: relative;
521 | align-items: center;
522 | padding-top: var(--dl-space-space-tripleunit);
523 | padding-left: var(--dl-space-space-doubleunit);
524 | padding-right: var(--dl-space-space-doubleunit);
525 | flex-direction: column;
526 | padding-bottom: var(--dl-space-space-fiveunits);
527 | background-color: var(--dl-color-pimary-900);
528 | }
529 | .home-container03 {
530 | width: 100%;
531 | height: 572px;
532 | display: flex;
533 | max-width: 1320px;
534 | align-items: center;
535 | flex-direction: column;
536 | }
537 | .home-container04 {
538 | width: 66%;
539 | display: flex;
540 | align-items: center;
541 | padding-left: var(--dl-space-space-unit);
542 | padding-right: var(--dl-space-space-unit);
543 | flex-direction: column;
544 | }
545 | .home-text02 {
546 | color: var(--dl-color-pimary-500);
547 | font-size: 0.875em;
548 | font-style: normal;
549 | font-weight: 700;
550 | text-transform: uppercase;
551 | }
552 | .home-text03 {
553 | color: var(--dl-color-secondary-400);
554 | text-align: center;
555 | }
556 | .home-text04 {
557 | color: var(--dl-color-secondary-700);
558 | text-align: center;
559 | margin-bottom: 0.25rem;
560 | }
561 | .home-cards-container {
562 | display: flex;
563 | margin-top: var(--dl-space-space-tripleunit);
564 | align-items: flex-start;
565 | padding-top: var(--dl-space-space-tripleunit);
566 | margin-bottom: var(--dl-space-space-tripleunit);
567 | flex-direction: row;
568 | padding-bottom: var(--dl-space-space-tripleunit);
569 | justify-content: space-between;
570 | }
571 | .home-card1 {
572 | flex: 0 0 auto;
573 | width: 30%;
574 | display: flex;
575 | padding: var(--dl-space-space-unit);
576 | box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05);
577 | align-items: center;
578 | border-radius: var(--dl-radius-radius-radius50);
579 | flex-direction: column;
580 | background-color: var(--dl-color-gray-white);
581 | }
582 | .home-container05 {
583 | flex: 0 0 auto;
584 | width: 3rem;
585 | height: 3rem;
586 | display: flex;
587 | box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05);
588 | align-items: center;
589 | border-radius: var(--dl-radius-radius-round);
590 | flex-direction: column;
591 | justify-content: center;
592 | background-image: linear-gradient(180deg, #f87171, #dc2626);
593 | }
594 | .home-icon {
595 | fill: var(--dl-color-gray-white);
596 | width: 24px;
597 | height: 24px;
598 | }
599 | .home-text05 {
600 | color: var(--dl-color-secondary-100);
601 | margin-top: var(--dl-space-space-unit);
602 | text-align: center;
603 | font-family: Open Sans;
604 | }
605 | .home-text06 {
606 | color: var(--dl-color-secondary-700);
607 | font-size: 1rem;
608 | margin-top: var(--dl-space-space-halfunit);
609 | text-align: center;
610 | font-family: "Open Sans";
611 | line-height: 1.625rem;
612 | }
613 | .home-card2 {
614 | flex: 0 0 auto;
615 | width: 30%;
616 | display: flex;
617 | padding: var(--dl-space-space-unit);
618 | box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05);
619 | align-items: center;
620 | border-radius: var(--dl-radius-radius-radius50);
621 | flex-direction: column;
622 | background-color: var(--dl-color-gray-white);
623 | }
624 | .home-container06 {
625 | flex: 0 0 auto;
626 | width: 3rem;
627 | height: 3rem;
628 | display: flex;
629 | box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05);
630 | align-items: center;
631 | border-radius: var(--dl-radius-radius-round);
632 | flex-direction: column;
633 | justify-content: center;
634 | background-image: linear-gradient(180deg, #38bdf8, #0284c7);
635 | }
636 | .home-icon02 {
637 | fill: var(--dl-color-gray-white);
638 | width: 24px;
639 | height: 24px;
640 | }
641 | .home-text07 {
642 | color: var(--dl-color-secondary-100);
643 | margin-top: var(--dl-space-space-unit);
644 | text-align: center;
645 | font-family: Open Sans;
646 | }
647 | .home-text08 {
648 | color: var(--dl-color-secondary-700);
649 | font-size: 1rem;
650 | margin-top: var(--dl-space-space-halfunit);
651 | text-align: center;
652 | font-family: "Open Sans";
653 | line-height: 1.625rem;
654 | }
655 | .home-card3 {
656 | flex: 0 0 auto;
657 | width: 30%;
658 | display: flex;
659 | padding: var(--dl-space-space-unit);
660 | box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05);
661 | align-items: center;
662 | border-radius: var(--dl-radius-radius-radius50);
663 | flex-direction: column;
664 | background-color: var(--dl-color-gray-white);
665 | }
666 | .home-container07 {
667 | flex: 0 0 auto;
668 | width: 3rem;
669 | height: 3rem;
670 | display: flex;
671 | box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -2px rgba(0,0,0,0.05);
672 | align-items: center;
673 | border-radius: var(--dl-radius-radius-round);
674 | flex-direction: column;
675 | justify-content: center;
676 | background-image: linear-gradient(180deg, #34d399, #059669);
677 | }
678 | .home-icon04 {
679 | fill: var(--dl-color-gray-white);
680 | width: 24px;
681 | height: 24px;
682 | }
683 | .home-text11 {
684 | color: var(--dl-color-secondary-100);
685 | margin-top: var(--dl-space-space-unit);
686 | text-align: center;
687 | font-family: Open Sans;
688 | }
689 | .home-text12 {
690 | color: var(--dl-color-secondary-700);
691 | font-size: 1rem;
692 | margin-top: var(--dl-space-space-halfunit);
693 | text-align: center;
694 | font-family: "Open Sans";
695 | line-height: 1.625rem;
696 | }
697 | .home-image2 {
698 | top: auto;
699 | left: auto;
700 | right: 0px;
701 | width: 100%;
702 | bottom: -1px;
703 | position: absolute;
704 | object-fit: cover;
705 | }
706 | .home-section2 {
707 | flex: 0 0 auto;
708 | width: 100%;
709 | display: flex;
710 | align-self: center;
711 | align-items: center;
712 | padding-top: var(--dl-space-space-fiveunits);
713 | flex-direction: column;
714 | padding-bottom: var(--dl-space-space-fiveunits);
715 | }
716 | .home-container08 {
717 | width: 897px;
718 | display: flex;
719 | align-items: center;
720 | padding-left: var(--dl-space-space-unit);
721 | padding-right: var(--dl-space-space-unit);
722 | flex-direction: column;
723 | }
724 | .home-text13 {
725 | color: var(--dl-color-secondary-400);
726 | margin-top: var(--dl-space-space-halfunit);
727 | text-align: center;
728 | line-height: 2.5rem;
729 | }
730 | .home-text14 {
731 | line-height: 1.25;
732 | }
733 | .home-text15 {
734 | color: var(--dl-color-secondary-400);
735 | line-height: 2.5rem;
736 | }
737 | .home-text16 {
738 | color: var(--dl-color-secondary-700);
739 | text-align: center;
740 | margin-bottom: var(--dl-space-space-unitandhalf);
741 | }
742 | .home-container09 {
743 | flex: 0 0 auto;
744 | width: 66%;
745 | display: flex;
746 | margin-top: var(--dl-space-space-fiveunits);
747 | align-items: center;
748 | margin-bottom: var(--dl-space-space-fiveunits);
749 | flex-direction: column;
750 | }
751 | .home-text17 {
752 | color: var(--dl-color-secondary-400);
753 | font-size: 33.6px;
754 | text-align: center;
755 | }
756 | .home-text18 {
757 | color: var(--dl-color-secondary-700);
758 | text-align: center;
759 | font-weight: 400;
760 | margin-bottom: 0.25rem;
761 | }
762 | .home-team {
763 | flex: 0 0 auto;
764 | width: 100%;
765 | display: flex;
766 | align-items: flex-start;
767 | flex-direction: row;
768 | justify-content: center;
769 | }
770 | .home-container10 {
771 | width: 100%;
772 | display: flex;
773 | max-width: 1320px;
774 | align-items: flex-start;
775 | padding-left: var(--dl-space-space-doubleunit);
776 | padding-right: var(--dl-space-space-doubleunit);
777 | flex-direction: row;
778 | }
779 | .home-container11 {
780 | width: 50%;
781 | display: flex;
782 | align-items: center;
783 | padding-left: var(--dl-space-space-unit);
784 | padding-right: var(--dl-space-space-unit);
785 | flex-direction: column;
786 | justify-content: center;
787 | }
788 | .home-container12 {
789 | flex: 0 0 auto;
790 | display: flex;
791 | margin-top: var(--dl-space-space-unitandhalf);
792 | align-items: flex-start;
793 | flex-direction: row;
794 | }
795 | .home-link {
796 | display: contents;
797 | }
798 | .home-container13 {
799 | flex: 0 0 auto;
800 | width: 2.5rem;
801 | height: 2.5rem;
802 | display: flex;
803 | box-shadow: 5px 5px 10px 0px #d4d4d4;
804 | align-items: center;
805 | margin-left: 0px;
806 | border-radius: var(--dl-radius-radius-round);
807 | flex-direction: row;
808 | justify-content: center;
809 | text-decoration: none;
810 | background-color: var(--dl-color-gray-white);
811 | }
812 | .home-icon06 {
813 | width: 16px;
814 | height: 16px;
815 | }
816 | .home-container14 {
817 | width: 50%;
818 | display: flex;
819 | align-items: center;
820 | padding-left: var(--dl-space-space-unit);
821 | padding-right: var(--dl-space-space-unit);
822 | flex-direction: column;
823 | justify-content: center;
824 | }
825 | .home-container15 {
826 | flex: 0 0 auto;
827 | display: flex;
828 | margin-top: var(--dl-space-space-unitandhalf);
829 | align-items: flex-start;
830 | flex-direction: row;
831 | }
832 | .home-link1 {
833 | display: contents;
834 | }
835 | .home-container16 {
836 | flex: 0 0 auto;
837 | width: 2.5rem;
838 | height: 2.5rem;
839 | display: flex;
840 | box-shadow: 5px 5px 10px 0px #d4d4d4;
841 | align-items: center;
842 | margin-left: 0px;
843 | border-radius: var(--dl-radius-radius-round);
844 | flex-direction: row;
845 | justify-content: center;
846 | text-decoration: none;
847 | background-color: var(--dl-color-gray-white);
848 | }
849 | .home-icon08 {
850 | width: 16px;
851 | height: 16px;
852 | }
853 | .home-container17 {
854 | width: 50%;
855 | display: flex;
856 | align-items: center;
857 | padding-left: var(--dl-space-space-unit);
858 | padding-right: var(--dl-space-space-unit);
859 | flex-direction: column;
860 | justify-content: center;
861 | }
862 | .home-container18 {
863 | flex: 0 0 auto;
864 | display: flex;
865 | margin-top: var(--dl-space-space-unitandhalf);
866 | align-items: flex-start;
867 | flex-direction: row;
868 | }
869 | .home-link2 {
870 | display: contents;
871 | }
872 | .home-container19 {
873 | flex: 0 0 auto;
874 | width: 2.5rem;
875 | height: 2.5rem;
876 | display: flex;
877 | box-shadow: 5px 5px 10px 0px #d4d4d4;
878 | align-items: center;
879 | margin-left: 0px;
880 | border-radius: var(--dl-radius-radius-round);
881 | flex-direction: row;
882 | justify-content: center;
883 | text-decoration: none;
884 | background-color: var(--dl-color-gray-white);
885 | }
886 | .home-icon10 {
887 | width: 16px;
888 | height: 16px;
889 | }
890 | .home-container20 {
891 | width: 50%;
892 | display: flex;
893 | align-items: center;
894 | padding-left: var(--dl-space-space-unit);
895 | padding-right: var(--dl-space-space-unit);
896 | flex-direction: column;
897 | justify-content: center;
898 | }
899 | .home-container21 {
900 | flex: 0 0 auto;
901 | display: flex;
902 | margin-top: var(--dl-space-space-unitandhalf);
903 | align-items: flex-start;
904 | flex-direction: row;
905 | }
906 | .home-link3 {
907 | display: contents;
908 | }
909 | .home-container22 {
910 | flex: 0 0 auto;
911 | width: 2.5rem;
912 | height: 2.5rem;
913 | display: flex;
914 | box-shadow: 5px 5px 10px 0px #d4d4d4;
915 | align-items: center;
916 | margin-left: 0px;
917 | border-radius: var(--dl-radius-radius-round);
918 | flex-direction: row;
919 | justify-content: center;
920 | text-decoration: none;
921 | background-color: var(--dl-color-gray-white);
922 | }
923 | .home-icon12 {
924 | width: 16px;
925 | height: 16px;
926 | }
927 | .home-container23 {
928 | width: 50%;
929 | display: flex;
930 | align-items: center;
931 | padding-left: var(--dl-space-space-unit);
932 | padding-right: var(--dl-space-space-unit);
933 | flex-direction: column;
934 | justify-content: center;
935 | }
936 | .home-container24 {
937 | flex: 0 0 auto;
938 | display: flex;
939 | margin-top: var(--dl-space-space-unitandhalf);
940 | align-items: flex-start;
941 | flex-direction: row;
942 | }
943 | .home-link4 {
944 | display: contents;
945 | }
946 | .home-container25 {
947 | flex: 0 0 auto;
948 | width: 2.5rem;
949 | height: 2.5rem;
950 | display: flex;
951 | box-shadow: 5px 5px 10px 0px #d4d4d4;
952 | align-items: center;
953 | margin-left: 0px;
954 | border-radius: var(--dl-radius-radius-round);
955 | flex-direction: row;
956 | justify-content: center;
957 | text-decoration: none;
958 | background-color: var(--dl-color-gray-white);
959 | }
960 | .home-icon14 {
961 | width: 16px;
962 | height: 16px;
963 | }
964 | .home-image3 {
965 | width: 100%;
966 | align-self: center;
967 | object-fit: cover;
968 | }
969 | @media(max-width: 991px) {
970 | .home-cards-container {
971 | align-items: center;
972 | flex-direction: column;
973 | }
974 | .home-card1 {
975 | width: 90%;
976 | margin-bottom: var(--dl-space-space-fiveunits);
977 | }
978 | .home-card2 {
979 | width: 90%;
980 | margin-bottom: var(--dl-space-space-fiveunits);
981 | }
982 | .home-card3 {
983 | width: 90%;
984 | }
985 | .home-section2 {
986 | padding-left: var(--dl-space-space-doubleunit);
987 | padding-right: var(--dl-space-space-doubleunit);
988 | }
989 | .home-text13 {
990 | text-align: center;
991 | }
992 | }
993 | @media(max-width: 767px) {
994 | .home-section1 {
995 | padding-left: var(--dl-space-space-doubleunit);
996 | padding-right: var(--dl-space-space-doubleunit);
997 | }
998 | .home-container04 {
999 | width: 100%;
1000 | }
1001 | .home-container08 {
1002 | width: 100%;
1003 | }
1004 | .home-container09 {
1005 | width: auto;
1006 | padding-left: var(--dl-space-space-unit);
1007 | padding-right: var(--dl-space-space-unit);
1008 | }
1009 | .home-container10 {
1010 | flex-wrap: wrap;
1011 | flex-direction: row;
1012 | }
1013 | .home-container11 {
1014 | width: 50%;
1015 | margin-bottom: var(--dl-space-space-tripleunit);
1016 | }
1017 | .home-container14 {
1018 | width: 50%;
1019 | margin-bottom: var(--dl-space-space-tripleunit);
1020 | }
1021 | .home-container17 {
1022 | width: 50%;
1023 | padding-bottom: var(--dl-space-space-tripleunit);
1024 | }
1025 | .home-container20 {
1026 | width: 50%;
1027 | padding-bottom: var(--dl-space-space-tripleunit);
1028 | }
1029 | .home-container23 {
1030 | width: 50%;
1031 | padding-bottom: var(--dl-space-space-tripleunit);
1032 | }
1033 | }
1034 | @media(max-width: 479px) {
1035 | .home-container02 {
1036 | padding-left: var(--dl-space-space-doubleunit);
1037 | padding-right: var(--dl-space-space-doubleunit);
1038 | }
1039 | .home-card1 {
1040 | margin-bottom: var(--dl-space-space-tripleunit);
1041 | }
1042 | .home-text06 {
1043 | text-align: left;
1044 | }
1045 | .home-card2 {
1046 | margin-bottom: var(--dl-space-space-tripleunit);
1047 | }
1048 | .home-text08 {
1049 | text-align: left;
1050 | }
1051 | .home-text12 {
1052 | text-align: left;
1053 | }
1054 | .home-container10 {
1055 | align-items: center;
1056 | flex-direction: column;
1057 | }
1058 | }
1059 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 | Kitty Tools
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
39 |
43 |
48 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
178 |
179 |
180 |
181 | Elevate Entertainment and Enjoyment with Our Tool.
182 |
183 |
184 | Kitty Tools is a straightforward and reliable open-source hack
185 | for Kahoot that operates seamlessly in both web browsers and
186 | desktop environments. The tool includes an answer bot utilizing
187 | the Kahoot API, ensuring its continued functionality unless
188 | there are significant changes to the Kahoot API by its
189 | developers.
190 |
191 |
192 |
193 |
198 |
199 |
200 |
201 |
202 |
203 |
features
204 | 'What tools are there?' You ask!
205 |
206 | Kitty Tools undergoes regular updates, incorporating new tools
207 | and patches. Currently, its primary elements are outlined below.
208 |
209 |
210 |
211 |
212 |
219 |
Securing First Place
220 |
221 | Coming in first place has never been easyer! Using the fully automated answer hack you can be swiftly be propeld
222 | to the top position with impeccable scores.
223 |
224 |
225 |
226 |
233 |
Compatible With All Devices!
234 |
235 |
236 | Compatible with all devices! From Desktop to Mobile with LITE, you can now use this client on mostly any device! (I-phone version in development!)
237 |
238 |
239 |
240 |
241 |
242 |
249 |
Looks Official
250 |
251 | Gives the appearance of an official Kahoot client, eliminating
252 | concerns about your identity being sniffed out.
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
Open Source?
263 |
264 |
265 |
266 | Absolutely! We are entirely open source.
267 |
268 |
269 |
270 |
271 | We highly value contributions from individuals who enhance the
272 | project by introducing new features and addressing bugs. That's
273 | why we've opted to make the project open source.
274 |
275 |
276 |
277 |
Contributers
278 |
279 | These are people who have been helpful and supported the project,
280 | making it grow.
281 |
282 |
283 |
284 |
285 |
286 |
287 |
292 |
293 | CPScript
294 |
295 |
296 | Project Lead
297 |
298 |
299 |
318 |
319 |
320 |
321 |
326 |
327 | Kira Kenjiro
328 |
329 |
330 | Web & Hybrid Dev
331 |
332 |
333 |
352 |
353 |
354 |
355 |
360 |
361 | Cheepling
362 |
363 |
364 | Contributor
365 |
366 |
367 |
386 |
387 |
388 |
389 |
394 |
395 | Zacky2613
396 |
397 |
398 | Contributor
399 |
400 |
401 |
420 |
421 |
422 |
423 |
428 |
429 | Ccode-lang
430 |
431 |
432 | Contributor
433 |
434 |
435 |
454 |
455 |
456 |
457 |
458 |
459 |
531 |
532 |
533 |
534 |
535 |
536 |
--------------------------------------------------------------------------------