22import time
33from os import system
44import 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
514
615print ("Attempting to check if imports are installed; colorama, pystyle." )
716time .sleep (1 )
@@ -14,17 +23,17 @@ def clear():
1423 elif system == 'linux' or system == 'darwin' :
1524 _ = os .system ('clear' )
1625 elif system == 'android' :
17- System . Clear ( )
26+ _ = os . system ( 'clear' )
1827 print ("How are you here, leave!" )
19- sprint ( f "Please use the 'LITE' version so this kahoot client will run smoothly! { r } <3 " )
28+ print ( "Please use the 'LITE' version so this kahoot client will run smoothly!" )
2029 exit ()
2130clear () # Call clear func
31+
2232try :
2333 import colorama
2434 import pystyle
2535except ModuleNotFoundError :
26- #from scripts.check import try_install | Idk how to fix the error for line 16, its 2am so idc
27- print ("Result: You dont have a certan import(s) installed, installing them now" )
36+ print ("Result: You dont have a certain import(s) installed, installing them now" )
2837 time .sleep (1 )
2938 os .system ("pip install colorama" )
3039 os .system ("pip install pystyle" )
@@ -58,18 +67,163 @@ def clear():
5867output_lock = threading .Lock ()
5968colorama .init ()
6069
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
61116api = "https://play.kahoot.it/rest/kahoots/"
117+
62118class Kahoot :
63119 def __init__ (self , uuid ):
64120 self .uuid = uuid
121+ self .rate_limiter = RateLimiter ()
122+ self .ssl_context = SSLContextManager .create_ssl_context ()
123+
65124 try :
66125 if not re .fullmatch (r"^[A-Za-z0-9-]*$" , uuid ):
67126 self .data = False
68127 else :
69- self .data = load ( urlopen ( f"https://play.kahoot.it/rest/kahoots/ { uuid } " ) )
128+ self .data = self . _fetch_quiz_data ( uuid )
70129 except HTTPError or InvalidURL :
71130 self .data = False
72131
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+
73227 def get_quiz_details (self ):
74228 return {
75229 "uuid" : self .data ["uuid" ],
@@ -153,6 +307,7 @@ def get_answer(self, question):
153307 if len (answers ) == 0 :
154308 answers = None
155309 return answers
310+
156311def start_kahoot ():
157312 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" )
158313 Write .Print (f"""
@@ -169,6 +324,12 @@ def start_kahoot():
169324 Write .Print (f"└─────► " , Colors .white , interval = 0.000 ); quiz_id = input (pretty )
170325 try :
171326 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+
172333 print (f"{ pretty } { orange } ({ green } !{ orange } ) Fetching Answers From: { orange } [{ reset } Quiz-ID: { quiz_id } { orange } ]\n " )
173334 time .sleep (1 )
174335 for i in range (kahoot .get_quiz_length ()):
@@ -183,8 +344,13 @@ def start_kahoot():
183344 except Exception as err :
184345 os .system ('clear' )
185346 print ("Womp Womp! " )
186- print ("There was an error! Mabey you typed the 'Quiz ID' incorrectly!\n " )
187- print (err )
347+ print ("There was an error! Maybe you typed the 'Quiz ID' incorrectly!\n " )
348+ print (f"Error details: { err } " )
349+ print ("\n Troubleshooting 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" )
188354 Write .Print (f"""
189355||=========================================================
190356||Thanks for using Kitty-Tools <3
@@ -194,5 +360,4 @@ def start_kahoot():
194360
195361start_kahoot ()
196362
197-
198363input ("Press any key to exit..." )
0 commit comments