diff --git a/jobs/joke.py b/jobs/joke.py new file mode 100644 index 0000000..c84a240 --- /dev/null +++ b/jobs/joke.py @@ -0,0 +1,47 @@ +import textwrap +from jobs.joke_sources import JednorozecJokeSource, BestPageJokeSource + +class JokeJob: + def __init__(self): + self.sources = [ + JednorozecJokeSource(), + BestPageJokeSource() + ] + self.selected_source = self.sources[0] + + def get_name(self): + return "Random Joke" + + def configure(self): + print("\nSelect Joke Source:") + for i, source in enumerate(self.sources): + print(f" [{i + 1}] {source.get_name()}") + + choice = input(f"Choice [{self.sources.index(self.selected_source) + 1}]: ").strip() + if choice: + try: + idx = int(choice) - 1 + if 0 <= idx < len(self.sources): + self.selected_source = self.sources[idx] + except ValueError: + pass + + def run(self, printer): + try: + joke = self.selected_source.fetch_joke() + + if joke: + # Wrap text to avoid word splitting (assuming ~42 chars for 80mm paper) + wrapped_joke = "\n".join([textwrap.fill(line, width=42) for line in joke.splitlines()]) + + printer.text(f"Joke from {self.selected_source.get_name()}:\n") + printer.text("--------------------------------\n\n") + printer.text(wrapped_joke) + printer.text("\n\n") + else: + printer.text("Sorry, could not extract any jokes from the website.\n") + + except Exception as e: + printer.text(f"Error fetching joke: {e}\n") + + printer.cut() \ No newline at end of file diff --git a/jobs/joke_sources.py b/jobs/joke_sources.py new file mode 100644 index 0000000..0e006a7 --- /dev/null +++ b/jobs/joke_sources.py @@ -0,0 +1,97 @@ +import requests +from bs4 import BeautifulSoup +import random + +class JokeSource: + def get_name(self): + return "Generic Source" + + def fetch_joke(self): + """Returns a single joke string or None.""" + raise NotImplementedError + +class JednorozecJokeSource(JokeSource): + def get_name(self): + return "vtipy.jednorozec.cz" + + def fetch_joke(self): + url = "https://vtipy.jednorozec.cz/" + try: + # Add a User-Agent to be polite and avoid basic blocking + headers = {'User-Agent': 'Mozilla/5.0 (compatible; PrintServer/1.0)'} + response = requests.get(url, headers=headers, timeout=10) + response.raise_for_status() + + soup = BeautifulSoup(response.content, 'html.parser') + + jokes = [] + + # Strategy 1: Look for specific classes often used in blogs/joke sites + # We look for divs that might contain the joke text + potential_classes = ['post', 'entry', 'hentry', 'joke', 'vtip'] + for class_name in potential_classes: + elements = soup.find_all(class_=lambda x: x and class_name in x.split()) + if elements: + for el in elements: + for br in el.find_all("br"): + br.replace_with("\n") + text = el.get_text() + lines = [line.strip() for line in text.splitlines() if line.strip()] + text = "\n".join(lines) + # Filter out very short texts (titles, metadata) and ensure safety limit + if len(text) > 20 and len(lines) <= 20: + jokes.append(text) + if jokes: + break + + # Strategy 2: Fallback to all paragraphs if no specific container found + if not jokes: + for p in soup.find_all('p'): + for br in p.find_all("br"): + br.replace_with("\n") + text = p.get_text() + lines = [line.strip() for line in text.splitlines() if line.strip()] + text = "\n".join(lines) + if len(text) > 50 and len(lines) <= 20: # Assume jokes are somewhat long paragraphs + jokes.append(text) + + if jokes: + return random.choice(jokes) + return None + + except Exception as e: + raise e + +class BestPageJokeSource(JokeSource): + def get_name(self): + return "bestpage.cz" + + def fetch_joke(self): + url = "https://bestpage.cz/vtipy/" + try: + headers = {'User-Agent': 'Mozilla/5.0 (compatible; PrintServer/1.0)'} + response = requests.get(url, headers=headers, timeout=10) + # Older sites often use windows-1250 or iso-8859-2 + response.encoding = response.apparent_encoding + + soup = BeautifulSoup(response.content, 'html.parser') + + jokes = [] + + # Bestpage is an older site, often using tables or simple paragraphs + for el in soup.find_all(['p', 'div', 'td']): + for br in el.find_all("br"): + br.replace_with("\n") + text = el.get_text() + lines = [line.strip() for line in text.splitlines() if line.strip()] + text = "\n".join(lines) + + if 50 < len(text) < 1000 and len(lines) <= 20: + jokes.append(text) + + if jokes: + return random.choice(jokes) + return None + + except Exception as e: + raise e \ No newline at end of file diff --git a/print_server.py b/print_server.py index 73923e6..d668cec 100644 --- a/print_server.py +++ b/print_server.py @@ -6,6 +6,7 @@ from jobs.chess_puzzle import ChessPuzzleJob from jobs.maze import MazeJob from jobs.division_cipher import DivisionCipherJob from jobs.decimal_division import DecimalDivisionJob +from jobs.joke import JokeJob # ========================================== # CONFIGURATION @@ -57,7 +58,8 @@ JOBS = [ ChessPuzzleJob(), MazeJob(), DivisionCipherJob(), - DecimalDivisionJob() + DecimalDivisionJob(), + JokeJob() ] def run_tui():