New job: Random joke from remote source
This commit is contained in:
parent
61ec23b3a4
commit
b9715a8032
47
jobs/joke.py
Normal file
47
jobs/joke.py
Normal 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
97
jobs/joke_sources.py
Normal 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
|
||||
@ -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():
|
||||
|
||||
Loading…
Reference in New Issue
Block a user