New job: Random joke from remote source

This commit is contained in:
Dejvino 2025-12-23 16:32:16 +01:00
parent 61ec23b3a4
commit b9715a8032
3 changed files with 147 additions and 1 deletions

47
jobs/joke.py Normal file
View File

@ -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()

97
jobs/joke_sources.py Normal file
View File

@ -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

View File

@ -6,6 +6,7 @@ from jobs.chess_puzzle import ChessPuzzleJob
from jobs.maze import MazeJob from jobs.maze import MazeJob
from jobs.division_cipher import DivisionCipherJob from jobs.division_cipher import DivisionCipherJob
from jobs.decimal_division import DecimalDivisionJob from jobs.decimal_division import DecimalDivisionJob
from jobs.joke import JokeJob
# ========================================== # ==========================================
# CONFIGURATION # CONFIGURATION
@ -57,7 +58,8 @@ JOBS = [
ChessPuzzleJob(), ChessPuzzleJob(),
MazeJob(), MazeJob(),
DivisionCipherJob(), DivisionCipherJob(),
DecimalDivisionJob() DecimalDivisionJob(),
JokeJob()
] ]
def run_tui(): def run_tui():