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.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():
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user