Files
ai6-m3/Joke Bot Upgrade_ SQLite Database Integration _ Beginner's Guide.html
2026-01-16 09:56:17 +03:00

1556 lines
74 KiB
HTML

<!DOCTYPE html>
<!-- saved from url=(0063)file:///Users/home/Downloads/deepseek_html_20260116_9092c1.html -->
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Joke Bot Upgrade: SQLite Database Integration | Beginner's Guide</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
color: #1a1a1a;
line-height: 1.4;
height: 100vh;
overflow: hidden;
background: linear-gradient(rgba(255, 255, 255, 0.98), rgba(255, 255, 255, 0.99));
}
.presentation-container {
max-width: 900px;
height: 100vh;
margin: 0 auto;
display: flex;
flex-direction: column;
padding: 0 20px;
}
.slide {
display: none;
flex: 1;
padding: 40px 30px;
background: transparent;
overflow-y: auto;
max-height: calc(100vh - 100px);
}
.active {
display: block;
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* Title Slide */
.title-slide {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
height: 100%;
}
.thesis-title {
font-weight: 800;
font-size: 2.8rem;
color: #2c3e50;
margin-bottom: 10px;
line-height: 1.1;
}
.thesis-subtitle {
font-size: 1.3rem;
color: #3498db;
margin-bottom: 40px;
font-weight: 500;
}
/* Typography */
h1 {
font-weight: 700;
font-size: 2.2rem;
color: #2c3e50;
margin-bottom: 25px;
line-height: 1.1;
}
h2 {
font-weight: 600;
font-size: 1.8rem;
color: #2c3e50;
margin-bottom: 25px;
}
h3 {
font-weight: 600;
font-size: 1.2rem;
color: #3498db;
margin-bottom: 15px;
margin-top: 25px;
}
p {
font-size: 1rem;
color: #4a5568;
margin-bottom: 15px;
}
.lead {
font-size: 1.1rem;
color: #2c3e50;
font-weight: 500;
margin-bottom: 20px;
}
/* Code Blocks */
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 25px;
border-radius: 8px;
margin: 20px 0;
font-family: 'Courier New', Consolas, Monaco, monospace;
font-size: 0.9rem;
line-height: 1.5;
overflow-x: auto;
white-space: pre;
}
.code-comment { color: #6a9955; }
.code-keyword { color: #569cd6; }
.code-function { color: #dcdcaa; }
.code-string { color: #ce9178; }
.code-builtin { color: #4ec9b0; }
.code-number { color: #b5cea8; }
/* Feature Boxes */
.feature-box {
background: #f8fafc;
padding: 25px;
border-radius: 10px;
margin: 25px 0;
border-left: 4px solid #3498db;
}
.feature-item {
display: flex;
align-items: flex-start;
margin-bottom: 15px;
}
.feature-number {
background: #3498db;
color: white;
width: 28px;
height: 28px;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
flex-shrink: 0;
font-weight: bold;
font-size: 0.9rem;
}
/* Database Concepts */
.database-concept {
background: #fff;
padding: 20px;
border-radius: 8px;
margin: 15px 0;
border: 2px solid #e0e6ed;
}
.concept-title {
color: #2c3e50;
font-weight: 600;
margin-bottom: 10px;
display: block;
}
/* Navigation */
.navigation {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
background: rgba(255, 255, 255, 0.95);
border-top: 1px solid #e2e8f0;
height: 70px;
flex-shrink: 0;
}
.nav-btn {
padding: 10px 25px;
background: #3498db;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 0.95rem;
font-weight: 500;
transition: all 0.3s ease;
}
.nav-btn:hover:not(:disabled) {
background: #2980b9;
}
.nav-btn:disabled {
background: #cbd5e0;
cursor: not-allowed;
}
.slide-counter {
font-weight: 600;
color: #4a5568;
font-size: 0.95rem;
}
/* Responsive */
@media (max-width: 768px) {
.slide {
padding: 30px 20px;
}
.thesis-title {
font-size: 2.2rem;
}
h1 {
font-size: 1.8rem;
}
h2 {
font-size: 1.5rem;
}
.code-block {
padding: 20px;
font-size: 0.85rem;
}
}
</style>
<link href="./Joke Bot Upgrade_ SQLite Database Integration _ Beginner&#39;s Guide_files/css2" rel="stylesheet">
</head>
<body>
<div class="presentation-container">
<!-- Slide 1: Title -->
<div class="slide active" id="slide1">
<div class="title-slide">
<div>
<h1 class="thesis-title">Joke Bot Upgrade</h1>
<p class="thesis-subtitle">From Simple Lists to SQLite Database with User Interaction</p>
</div>
<div class="author-info" style="margin-top: 50px; padding-top: 30px; border-top: 2px solid #e0e6ed; width: 100%; max-width: 600px;">
<p><strong>Goal:</strong> Upgrade joke bot with database, user submissions, and ratings</p>
<p><strong>Prerequisites:</strong> Basic Python knowledge, our existing joke bot</p>
<p><strong>Time:</strong> 60 minutes to complete upgrade</p>
</div>
</div>
</div>
<!-- Slide 2: What is SQLite? -->
<div class="slide" id="slide2">
<h1>What Is SQLite?</h1>
<p class="lead">Serverless, self-contained SQL database perfect for Telegram bots</p>
<div class="feature-box">
<h3>Why SQLite for Our Joke Bot?</h3>
<div class="feature-item">
<div class="feature-number">1</div>
<div><strong>No Server Needed:</strong> Database lives in a single file</div>
</div>
<div class="feature-item">
<div class="feature-number">2</div>
<div><strong>Zero Configuration:</strong> Just import and use</div>
</div>
<div class="feature-item">
<div class="feature-number">3</div>
<div><strong>Lightweight:</strong> Perfect for small to medium applications</div>
</div>
<div class="feature-item">
<div class="feature-number">4</div>
<div><strong>SQL Standard:</strong> Uses standard SQL commands</div>
</div>
<div class="feature-item">
<div class="feature-number">5</div>
<div><strong>Built-in Python:</strong> No extra installation needed</div>
</div>
</div>
<div class="database-concept">
<span class="concept-title">Think of it like this:</span>
<p>SQLite is like an Excel file that speaks SQL. It's a complete database in a single file that you can include with your application.</p>
<p>Perfect for Telegram bots, mobile apps, and desktop applications.</p>
</div>
<div class="code-block" style="margin-top: 20px; padding: 15px; background: #2c3e50;">
<span class="code-comment"># SQLite comes built-in with Python!</span>
<span class="code-keyword">import</span> sqlite3
<span class="code-comment"># Connect to database (creates if doesn't exist)</span>
conn = sqlite3.connect(<span class="code-string">'joke_bot.db'</span>)
cursor = conn.cursor()
<span class="code-comment"># Create a table</span>
cursor.execute(<span class="code-string">'''CREATE TABLE jokes (
id INTEGER PRIMARY KEY,
text TEXT NOT NULL,
added_by TEXT,
likes INTEGER DEFAULT 0,
dislikes INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)'''</span>)
conn.commit()
conn.close()</div>
</div>
<!-- Slide 3: Our Upgrade Goals -->
<div class="slide" id="slide3">
<h2>Our Upgrade Goals</h2>
<p class="lead">From static list to dynamic, user-powered joke database</p>
<div class="database-concept">
<span class="concept-title">Current Limitations:</span>
<p>1. Jokes are hardcoded in Python list</p>
<p>2. No way for users to add new jokes</p>
<p>3. No rating system to find the best jokes</p>
<p>4. Jokes are lost when bot restarts</p>
</div>
<h3>New Features We'll Add:</h3>
<div class="feature-box">
<div class="feature-item">
<div class="feature-number">1</div>
<div>
<strong>SQLite Database:</strong><br>
Store jokes permanently in a database file
</div>
</div>
<div class="feature-item">
<div class="feature-number">2</div>
<div>
<strong>User Submissions:</strong><br>
<code>/addjoke</code> command for users to submit jokes
</div>
</div>
<div class="feature-item">
<div class="feature-number">3</div>
<div>
<strong>Rating System:</strong><br>
<code>/like</code> and <code>/dislike</code> commands to rate jokes
</div>
</div>
<div class="feature-item">
<div class="feature-number">4</div>
<div>
<strong>Top Jokes:</strong><br>
<code>/top</code> command to show most popular jokes
</div>
</div>
<div class="feature-item">
<div class="feature-number">5</div>
<div>
<strong>Random with Weight:</strong><br>
Better jokes appear more often
</div>
</div>
</div>
</div>
<!-- Slide 4: Step 1 - Database Setup -->
<div class="slide" id="slide4">
<h2>Step 1: Database Setup (10 minutes)</h2>
<p class="lead">Create the SQLite database and connection functions</p>
<div class="code-block"><span class="code-comment"># Create a new file: database.py</span>
<span class="code-comment"># This will handle all database operations</span>
<span class="code-keyword">import</span> sqlite3
<span class="code-keyword">from</span> datetime <span class="code-keyword">import</span> datetime
<span class="code-keyword">class</span> <span class="code-builtin">JokeDatabase</span>:
<span class="code-keyword">def</span> <span class="code-function">__init__</span>(<span class="code-keyword">self</span>, db_name=<span class="code-string">'joke_bot.db'</span>):
<span class="code-keyword">self</span>.db_name = db_name
<span class="code-keyword">self</span>.init_database()
<span class="code-keyword">def</span> <span class="code-function">get_connection</span>(<span class="code-keyword">self</span>):
<span class="code-comment">"""Get a database connection"""</span>
conn = sqlite3.connect(<span class="code-keyword">self</span>.db_name)
conn.row_factory = sqlite3.Row <span class="code-comment"># Access columns by name</span>
<span class="code-keyword">return</span> conn
<span class="code-keyword">def</span> <span class="code-function">init_database</span>(<span class="code-keyword">self</span>):
<span class="code-comment">"""Initialize database tables"""</span>
conn = <span class="code-keyword">self</span>.get_connection()
cursor = conn.cursor()
<span class="code-comment"># Create jokes table</span>
cursor.execute(<span class="code-string">'''CREATE TABLE IF NOT EXISTS jokes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
joke_text TEXT NOT NULL,
user_id INTEGER,
username TEXT,
likes INTEGER DEFAULT 0,
dislikes INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)'''</span>)
<span class="code-comment"># Create ratings table to track user votes</span>
cursor.execute(<span class="code-string">'''CREATE TABLE IF NOT EXISTS ratings (
user_id INTEGER,
joke_id INTEGER,
vote INTEGER, <span class="code-comment">-- 1 for like, -1 for dislike</span>
PRIMARY KEY (user_id, joke_id),
FOREIGN KEY (joke_id) REFERENCES jokes(id)
)'''</span>)
conn.commit()
conn.close()
<span class="code-comment"># Add some default jokes if table is empty</span>
<span class="code-keyword">self</span>.add_default_jokes()
<span class="code-keyword">def</span> <span class="code-function">add_default_jokes</span>(<span class="code-keyword">self</span>):
<span class="code-comment">"""Add default jokes if database is empty"""</span>
conn = <span class="code-keyword">self</span>.get_connection()
cursor = conn.cursor()
cursor.execute(<span class="code-string">"SELECT COUNT(*) FROM jokes"</span>)
count = cursor.fetchone()[<span class="code-number">0</span>]
<span class="code-keyword">if</span> count == <span class="code-number">0</span>:
default_jokes = [
(<span class="code-string">"Why did the robot go to school? To recharge his brain! 🔋"</span>, <span class="code-number">0</span>, <span class="code-string">"System"</span>),
(<span class="code-string">"Knock knock!\nWho's there?\nLettuce!\nLettuce who?\nLettuce in!"</span>, <span class="code-number">0</span>, <span class="code-string">"System"</span>),
(<span class="code-string">"Why don't eggs tell jokes? They'd crack each other up! 🥚"</span>, <span class="code-number">0</span>, <span class="code-string">"System"</span>)
]
cursor.executemany(<span class="code-string">'''INSERT INTO jokes (joke_text, user_id, username)
VALUES (?, ?, ?)'''</span>, default_jokes)
conn.commit()
print(<span class="code-string">f"Added </span>{len(default_jokes)}<span class="code-string"> default jokes to database"</span>)
conn.close()</div>
<div class="feature-box">
<h3>Database Schema Explained:</h3>
<div class="feature-item">
<div class="feature-number">📝</div>
<div><strong>jokes table:</strong> Stores all jokes with ratings</div>
</div>
<div class="feature-item">
<div class="feature-number"></div>
<div><strong>ratings table:</strong> Tracks who voted for which joke</div>
</div>
<div class="feature-item">
<div class="feature-number">🆔</div>
<div><strong>Primary keys:</strong> Unique IDs for each record</div>
</div>
<div class="feature-item">
<div class="feature-number">🔗</div>
<div><strong>Foreign key:</strong> Links ratings to jokes</div>
</div>
</div>
</div>
<!-- Slide 5: Step 2 - Database CRUD Operations -->
<div class="slide" id="slide5">
<h2>Step 2: Database Operations (15 minutes)</h2>
<p class="lead">Add CRUD (Create, Read, Update, Delete) functions</p>
<div class="code-block"><span class="code-comment"># Add these methods to the JokeDatabase class</span>
<span class="code-keyword">def</span> <span class="code-function">add_joke</span>(<span class="code-keyword">self</span>, joke_text, user_id, username):
<span class="code-comment">"""Add a new joke to the database"""</span>
conn = <span class="code-keyword">self</span>.get_connection()
cursor = conn.cursor()
cursor.execute(<span class="code-string">'''INSERT INTO jokes (joke_text, user_id, username)
VALUES (?, ?, ?)'''</span>, (joke_text, user_id, username))
joke_id = cursor.lastrowid
conn.commit()
conn.close()
<span class="code-keyword">return</span> joke_id
<span class="code-keyword">def</span> <span class="code-function">get_random_joke</span>(<span class="code-keyword">self</span>):
<span class="code-comment">"""Get a random joke, weighted by rating"""</span>
conn = <span class="code-keyword">self</span>.get_connection()
cursor = conn.cursor()
<span class="code-comment"># Weighted random: jokes with higher likes appear more often</span>
cursor.execute(<span class="code-string">'''SELECT * FROM jokes
ORDER BY (likes - dislikes + 5) * RANDOM() DESC
LIMIT 1'''</span>)
joke = cursor.fetchone()
conn.close()
<span class="code-keyword">return</span> dict(joke) <span class="code-keyword">if</span> joke <span class="code-keyword">else</span> <span class="code-keyword">None</span>
<span class="code-keyword">def</span> <span class="code-function">get_joke_by_id</span>(<span class="code-keyword">self</span>, joke_id):
<span class="code-comment">"""Get a specific joke by ID"""</span>
conn = <span class="code-keyword">self</span>.get_connection()
cursor = conn.cursor()
cursor.execute(<span class="code-string">"SELECT * FROM jokes WHERE id = ?"</span>, (joke_id,))
joke = cursor.fetchone()
conn.close()
<span class="code-keyword">return</span> dict(joke) <span class="code-keyword">if</span> joke <span class="code-keyword">else</span> <span class="code-keyword">None</span>
<span class="code-keyword">def</span> <span class="code-function">get_top_jokes</span>(<span class="code-keyword">self</span>, limit=<span class="code-number">5</span>):
<span class="code-comment">"""Get top-rated jokes"""</span>
conn = <span class="code-keyword">self</span>.get_connection()
cursor = conn.cursor()
cursor.execute(<span class="code-string">'''SELECT * FROM jokes
ORDER BY (likes - dislikes) DESC, likes DESC
LIMIT ?'''</span>, (limit,))
jokes = [dict(row) <span class="code-keyword">for</span> row <span class="code-keyword">in</span> cursor.fetchall()]
conn.close()
<span class="code-keyword">return</span> jokes
<span class="code-keyword">def</span> <span class="code-function">get_total_jokes</span>(<span class="code-keyword">self</span>):
<span class="code-comment">"""Get total number of jokes"""</span>
conn = <span class="code-keyword">self</span>.get_connection()
cursor = conn.cursor()
cursor.execute(<span class="code-string">"SELECT COUNT(*) FROM jokes"</span>)
count = cursor.fetchone()[<span class="code-number">0</span>]
conn.close()
<span class="code-keyword">return</span> count</div>
<div class="feature-box">
<h3>SQL Functions Explained:</h3>
<div class="feature-item">
<div class="feature-number">📊</div>
<div><strong>Weighted Random:</strong> <code>(likes - dislikes + 5) * RANDOM()</code></div>
</div>
<div class="feature-item">
<div class="feature-number">⬆️</div>
<div><strong>Top Jokes:</strong> Sorted by net score (likes - dislikes)</div>
</div>
<div class="feature-item">
<div class="feature-number">💾</div>
<div><strong>Parameterized Queries:</strong> Safe from SQL injection</div>
</div>
<div class="feature-item">
<div class="feature-number">🔄</div>
<div><strong>Connection Management:</strong> Open/close connections properly</div>
</div>
</div>
</div>
<!-- Slide 6: Step 3 - Rating System -->
<div class="slide" id="slide6">
<h2>Step 3: Rating System (15 minutes)</h2>
<p class="lead">Implement like/dislike functionality with vote tracking</p>
<div class="code-block"><span class="code-comment"># Add rating methods to JokeDatabase class</span>
<span class="code-keyword">def</span> <span class="code-function">rate_joke</span>(<span class="code-keyword">self</span>, user_id, joke_id, vote):
<span class="code-comment">"""
Rate a joke (like or dislike)
vote: 1 for like, -1 for dislike, 0 to remove rating
"""</span>
conn = <span class="code-keyword">self</span>.get_connection()
cursor = conn.cursor()
<span class="code-comment"># Check if user already voted</span>
cursor.execute(<span class="code-string">'''SELECT vote FROM ratings
WHERE user_id = ? AND joke_id = ?'''</span>, (user_id, joke_id))
existing_vote = cursor.fetchone()
<span class="code-keyword">if</span> existing_vote:
old_vote = existing_vote[<span class="code-number">0</span>]
<span class="code-keyword">if</span> vote == <span class="code-number">0</span>:
<span class="code-comment"># Remove vote</span>
cursor.execute(<span class="code-string">'''DELETE FROM ratings
WHERE user_id = ? AND joke_id = ?'''</span>, (user_id, joke_id))
<span class="code-comment"># Update joke counts</span>
<span class="code-keyword">if</span> old_vote == <span class="code-number">1</span>:
cursor.execute(<span class="code-string">'''UPDATE jokes SET likes = likes - 1
WHERE id = ?'''</span>, (joke_id,))
<span class="code-keyword">elif</span> old_vote == -<span class="code-number">1</span>:
cursor.execute(<span class="code-string">'''UPDATE jokes SET dislikes = dislikes - 1
WHERE id = ?'''</span>, (joke_id,))
<span class="code-keyword">elif</span> old_vote != vote:
<span class="code-comment"># Change vote</span>
cursor.execute(<span class="code-string">'''UPDATE ratings SET vote = ?
WHERE user_id = ? AND joke_id = ?'''</span>, (vote, user_id, joke_id))
<span class="code-comment"># Update joke counts (remove old, add new)</span>
<span class="code-keyword">if</span> old_vote == <span class="code-number">1</span> <span class="code-keyword">and</span> vote == -<span class="code-number">1</span>:
cursor.execute(<span class="code-string">'''UPDATE jokes
SET likes = likes - 1, dislikes = dislikes + 1
WHERE id = ?'''</span>, (joke_id,))
<span class="code-keyword">elif</span> old_vote == -<span class="code-number">1</span> <span class="code-keyword">and</span> vote == <span class="code-number">1</span>:
cursor.execute(<span class="code-string">'''UPDATE jokes
SET likes = likes + 1, dislikes = dislikes - 1
WHERE id = ?'''</span>, (joke_id,))
<span class="code-keyword">else</span>:
<span class="code-keyword">if</span> vote != <span class="code-number">0</span>:
<span class="code-comment"># New vote</span>
cursor.execute(<span class="code-string">'''INSERT INTO ratings (user_id, joke_id, vote)
VALUES (?, ?, ?)'''</span>, (user_id, joke_id, vote))
<span class="code-comment"># Update joke counts</span>
<span class="code-keyword">if</span> vote == <span class="code-number">1</span>:
cursor.execute(<span class="code-string">'''UPDATE jokes SET likes = likes + 1
WHERE id = ?'''</span>, (joke_id,))
<span class="code-keyword">elif</span> vote == -<span class="code-number">1</span>:
cursor.execute(<span class="code-string">'''UPDATE jokes SET dislikes = dislikes + 1
WHERE id = ?'''</span>, (joke_id,))
conn.commit()
<span class="code-comment"># Get updated joke info</span>
cursor.execute(<span class="code-string">"SELECT likes, dislikes FROM jokes WHERE id = ?"</span>, (joke_id,))
result = cursor.fetchone()
conn.close()
<span class="code-keyword">return</span> {
<span class="code-string">'likes'</span>: result[<span class="code-number">0</span>] <span class="code-keyword">if</span> result <span class="code-keyword">else</span> <span class="code-number">0</span>,
<span class="code-string">'dislikes'</span>: result[<span class="code-number">1</span>] <span class="code-keyword">if</span> result <span class="code-keyword">else</span> <span class="code-number">0</span>
}
<span class="code-keyword">def</span> <span class="code-function">get_user_vote</span>(<span class="code-keyword">self</span>, user_id, joke_id):
<span class="code-comment">"""Get user's vote for a specific joke"""</span>
conn = <span class="code-keyword">self</span>.get_connection()
cursor = conn.cursor()
cursor.execute(<span class="code-string">'''SELECT vote FROM ratings
WHERE user_id = ? AND joke_id = ?'''</span>, (user_id, joke_id))
result = cursor.fetchone()
conn.close()
<span class="code-keyword">return</span> result[<span class="code-number">0</span>] <span class="code-keyword">if</span> result <span class="code-keyword">else</span> <span class="code-number">0</span></div>
<div class="feature-box">
<h3>Rating Logic:</h3>
<div class="feature-item">
<div class="feature-number">1</div>
<div><strong>Prevent Double Voting:</strong> Users can only vote once per joke</div>
</div>
<div class="feature-item">
<div class="feature-number">2</div>
<div><strong>Change Votes:</strong> Users can change their mind</div>
</div>
<div class="feature-item">
<div class="feature-number">3</div>
<div><strong>Remove Votes:</strong> Users can remove their rating</div>
</div>
<div class="feature-item">
<div class="feature-number">4</div>
<div><strong>Real-time Updates:</strong> Counts update immediately</div>
</div>
</div>
</div>
<!-- Slide 7: Step 4 - Telegram Bot Integration -->
<div class="slide" id="slide7">
<h2>Step 4: Telegram Bot Integration (15 minutes)</h2>
<p class="lead">Update app.py to use the database</p>
<div class="code-block"><span class="code-comment"># Updated app.py with database integration</span>
<span class="code-keyword">from</span> telegram <span class="code-keyword">import</span> Update, InlineKeyboardMarkup, InlineKeyboardButton
<span class="code-keyword">from</span> telegram.ext <span class="code-keyword">import</span> Application, CommandHandler, ContextTypes, MessageHandler, filters, CallbackQueryHandler
<span class="code-keyword">from</span> database <span class="code-keyword">import</span> JokeDatabase
<span class="code-keyword">import</span> random
<span class="code-comment"># Initialize database</span>
db = JokeDatabase()
<span class="code-keyword">async</span> <span class="code-keyword">def</span> <span class="code-function">start</span>(update: Update, context: ContextTypes.DEFAULT_TYPE):
<span class="code-comment">"""Start command handler"""</span>
welcome_text = <span class="code-string">"""🤖 Welcome to Joke Bot 2.0! 🤖
Commands:
/joke - Get a random joke
/addjoke - Submit your own joke
/top - See top-rated jokes
/like - Like the last joke
/dislike - Dislike the last joke
/stats - Bot statistics
Now with database, user submissions, and ratings!"""</span>
<span class="code-keyword">await</span> update.message.reply_text(welcome_text)
<span class="code-keyword">async</span> <span class="code-keyword">def</span> <span class="code-function">send_joke</span>(update: Update, context: ContextTypes.DEFAULT_TYPE):
<span class="code-comment">"""Send a random joke from database"""</span>
joke = db.get_random_joke()
<span class="code-keyword">if</span> <span class="code-keyword">not</span> joke:
<span class="code-keyword">await</span> update.message.reply_text(<span class="code-string">"No jokes in database yet! Use /addjoke to add one."</span>)
<span class="code-keyword">return</span>
<span class="code-comment"># Store joke ID in context for rating</span>
context.user_data[<span class="code-string">'last_joke_id'</span>] = joke[<span class="code-string">'id'</span>]
<span class="code-comment"># Format joke with rating info</span>
rating_text = <span class="code-string">f"👍 {joke['likes']} 👎 {joke['dislikes']}"</span>
joke_text = <span class="code-string">f"{joke['joke_text']}\n\n{rating_text}"</span>
<span class="code-comment"># Add inline buttons for quick rating</span>
keyboard = [
[
InlineKeyboardButton(<span class="code-string">"👍 Like"</span>, callback_data=<span class="code-string">f"like_{joke['id']}"</span>),
InlineKeyboardButton(<span class="code-string">"👎 Dislike"</span>, callback_data=<span class="code-string">f"dislike_{joke['id']}"</span>)
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
<span class="code-keyword">await</span> update.message.reply_text(joke_text, reply_markup=reply_markup)
<span class="code-keyword">async</span> <span class="code-keyword">def</span> <span class="code-function">add_joke_command</span>(update: Update, context: ContextTypes.DEFAULT_TYPE):
<span class="code-comment">"""Handle /addjoke command"""</span>
<span class="code-keyword">if</span> <span class="code-keyword">not</span> context.args:
<span class="code-keyword">await</span> update.message.reply_text(
<span class="code-string">"Please provide a joke after the command:\n"</span>
<span class="code-string">"Example: /addjoke Why did the chicken cross the road?"</span>
)
<span class="code-keyword">return</span>
joke_text = <span class="code-string">" "</span>.join(context.args)
user = update.effective_user
<span class="code-comment"># Add to database</span>
joke_id = db.add_joke(joke_text, user.id, user.username)
<span class="code-keyword">await</span> update.message.reply_text(
<span class="code-string">f"✅ Joke added successfully! (ID: {joke_id})\n"</span>
<span class="code-string">"Others can now rate it with /like and /dislike"</span>
)</div>
<div class="feature-box">
<h3>New Bot Features:</h3>
<div class="feature-item">
<div class="feature-number">🤖</div>
<div><strong>Inline Buttons:</strong> Quick like/dislike without commands</div>
</div>
<div class="feature-item">
<div class="feature-number">💾</div>
<div><strong>User Context:</strong> Remember last joke for rating</div>
</div>
<div class="feature-item">
<span class="feature-number">📊</span></div>
<div><strong>Real Stats:</strong> Display actual like/dislike counts</div>
</div>
<div class="feature-item">
<div class="feature-number">👤</div>
<div><strong>User Tracking:</strong> Record who submitted each joke</div>
</div>
</div>
</div>
<!-- Slide 8: Step 5 - Rating Commands -->
<div class="slide" id="slide8">
<h2>Step 5: Rating Commands (10 minutes)</h2>
<p class="lead">Add like/dislike commands and callback handlers</p>
<div class="code-block"><span class="code-keyword">async</span> <span class="code-keyword">def</span> <span class="code-function">like_joke</span>(update: Update, context: ContextTypes.DEFAULT_TYPE):
<span class="code-comment">"""Handle /like command"""</span>
user_id = update.effective_user.id
<span class="code-comment"># Get last joke from context or arguments</span>
<span class="code-keyword">if</span> context.args <span class="code-keyword">and</span> context.args[<span class="code-number">0</span>].isdigit():
joke_id = int(context.args[<span class="code-number">0</span>])
<span class="code-keyword">elif</span> <span class="code-string">'last_joke_id'</span> <span class="code-keyword">in</span> context.user_data:
joke_id = context.user_data[<span class="code-string">'last_joke_id'</span>]
<span class="code-keyword">else</span>:
<span class="code-keyword">await</span> update.message.reply_text(
<span class="code-string">"Please view a joke first with /joke, or specify a joke ID: /like 123"</span>
)
<span class="code-keyword">return</span>
<span class="code-comment"># Rate the joke</span>
result = db.rate_joke(user_id, joke_id, <span class="code-number">1</span>)
joke = db.get_joke_by_id(joke_id)
<span class="code-keyword">if</span> joke:
<span class="code-keyword">await</span> update.message.reply_text(
<span class="code-string">f"👍 Liked joke #{joke_id}!\n"</span>
<span class="code-string">f"Current rating: {result['likes']} 👍 {result['dislikes']} 👎"</span>
)
<span class="code-keyword">else</span>:
<span class="code-keyword">await</span> update.message.reply_text(<span class="code-string">"Joke not found!"</span>)
<span class="code-keyword">async</span> <span class="code-keyword">def</span> <span class="code-function">dislike_joke</span>(update: Update, context: ContextTypes.DEFAULT_TYPE):
<span class="code-comment">"""Handle /dislike command"""</span>
user_id = update.effective_user.id
<span class="code-keyword">if</span> context.args <span class="code-keyword">and</span> context.args[<span class="code-number">0</span>].isdigit():
joke_id = int(context.args[<span class="code-number">0</span>])
<span class="code-keyword">elif</span> <span class="code-string">'last_joke_id'</span> <span class="code-keyword">in</span> context.user_data:
joke_id = context.user_data[<span class="code-string">'last_joke_id'</span>]
<span class="code-keyword">else</span>:
<span class="code-keyword">await</span> update.message.reply_text(
<span class="code-string">"Please view a joke first with /joke, or specify a joke ID: /dislike 123"</span>
)
<span class="code-keyword">return</span>
result = db.rate_joke(user_id, joke_id, -<span class="code-number">1</span>)
joke = db.get_joke_by_id(joke_id)
<span class="code-keyword">if</span> joke:
<span class="code-keyword">await</span> update.message.reply_text(
<span class="code-string">f"👎 Disliked joke #{joke_id}!\n"</span>
<span class="code-string">f"Current rating: {result['likes']} 👍 {result['dislikes']} 👎"</span>
)
<span class="code-keyword">else</span>:
<span class="code-keyword">await</span> update.message.reply_text(<span class="code-string">"Joke not found!"</span>)
<span class="code-keyword">async</span> <span class="code-keyword">def</span> <span class="code-function">button_callback</span>(update: Update, context: ContextTypes.DEFAULT_TYPE):
<span class="code-comment">"""Handle inline button clicks"""</span>
query = update.callback_query
<span class="code-keyword">await</span> query.answer()
data = query.data
user_id = query.from_user.id
<span class="code-keyword">if</span> data.startswith(<span class="code-string">'like_'</span>):
joke_id = int(data.split(<span class="code-string">'_'</span>)[<span class="code-number">1</span>])
result = db.rate_joke(user_id, joke_id, <span class="code-number">1</span>)
<span class="code-keyword">await</span> query.edit_message_text(
text=query.message.text.split(<span class="code-string">'\n\n'</span>)[<span class="code-number">0</span>] +
<span class="code-string">f"\n\n👍 {result['likes']} 👎 {result['dislikes']}"</span>,
reply_markup=query.message.reply_markup
)
<span class="code-keyword">elif</span> data.startswith(<span class="code-string">'dislike_'</span>):
joke_id = int(data.split(<span class="code-string">'_'</span>)[<span class="code-number">1</span>])
result = db.rate_joke(user_id, joke_id, -<span class="code-number">1</span>)
<span class="code-keyword">await</span> query.edit_message_text(
text=query.message.text.split(<span class="code-string">'\n\n'</span>)[<span class="code-number">0</span>] +
<span class="code-string">f"\n\n👍 {result['likes']} 👎 {result['dislikes']}"</span>,
reply_markup=query.message.reply_markup
)</div>
<div class="feature-box">
<h3>Rating Features:</h3>
<div class="feature-item">
<div class="feature-number">1</div>
<div><strong>Multiple Methods:</strong> Command or inline button</div>
</div>
<div class="feature-item">
<div class="feature-number">2</div>
<div><strong>Flexible Targeting:</strong> By ID or last viewed joke</div>
</div>
<div class="feature-item">
<div class="feature-number">3</div>
<div><strong>Real-time Updates:</strong> Buttons update instantly</div>
</div>
<div class="feature-item">
<div class="feature-number">4</div>
<div><strong>Feedback:</strong> Users see their vote was counted</div>
</div>
</div>
</div>
<!-- Slide 9: Step 6 - Additional Features -->
<div class="slide" id="slide9">
<h2>Step 6: Additional Features (10 minutes)</h2>
<p class="lead">Add statistics, top jokes, and admin commands</p>
<div class="code-block"><span class="code-keyword">async</span> <span class="code-keyword">def</span> <span class="code-function">top_jokes</span>(update: Update, context: ContextTypes.DEFAULT_TYPE):
<span class="code-comment">"""Show top-rated jokes"""</span>
limit = <span class="code-number">5</span>
<span class="code-keyword">if</span> context.args <span class="code-keyword">and</span> context.args[<span class="code-number">0</span>].isdigit():
limit = min(int(context.args[<span class="code-number">0</span>]), <span class="code-number">20</span>) <span class="code-comment"># Max 20 jokes</span>
jokes = db.get_top_jokes(limit)
<span class="code-keyword">if</span> <span class="code-keyword">not</span> jokes:
<span class="code-keyword">await</span> update.message.reply_text(<span class="code-string">"No jokes in database yet!"</span>)
<span class="code-keyword">return</span>
response = <span class="code-string">f"🏆 TOP {len(jokes)} JOKES 🏆\n\n"</span>
<span class="code-keyword">for</span> i, joke <span class="code-keyword">in</span> enumerate(jokes, <span class="code-number">1</span>):
score = joke[<span class="code-string">'likes'</span>] - joke[<span class="code-string">'dislikes'</span>]
response += <span class="code-string">f"{i}. ID {joke['id']}: Score {score} (👍{joke['likes']} 👎{joke['dislikes']})\n"</span>
response += <span class="code-string">f" \"{joke['joke_text'][:50]}...\"\n\n"</span>
<span class="code-keyword">await</span> update.message.reply_text(response)
<span class="code-keyword">async</span> <span class="code-keyword">def</span> <span class="code-function">bot_stats</span>(update: Update, context: ContextTypes.DEFAULT_TYPE):
<span class="code-comment">"""Show bot statistics"""</span>
total_jokes = db.get_total_jokes()
<span class="code-comment"># Get total likes/dislikes</span>
conn = db.get_connection()
cursor = conn.cursor()
cursor.execute(<span class="code-string">"SELECT SUM(likes), SUM(dislikes) FROM jokes"</span>)
result = cursor.fetchone()
total_likes = result[<span class="code-number">0</span>] <span class="code-keyword">or</span> <span class="code-number">0</span>
total_dislikes = result[<span class="code-number">1</span>] <span class="code-keyword">or</span> <span class="code-number">0</span>
conn.close()
stats_text = <span class="code-string">f"""📊 JOKE BOT STATISTICS 📊
Total Jokes: {total_jokes}
Total Ratings: {total_likes + total_dislikes}
👍 Total Likes: {total_likes}
👎 Total Dislikes: {total_dislikes}
📈 Approval Rate: {(total_likes/(total_likes+total_dislikes)*100 if (total_likes+total_dislikes) &gt; 0 else 0):.1f}%
Commands Today:
/joke - Get random joke
/addjoke - Submit your joke
/top - See top jokes
/stats - View these stats"""</span>
<span class="code-keyword">await</span> update.message.reply_text(stats_text)
<span class="code-keyword">async</span> <span class="code-keyword">def</span> <span class="code-function">my_jokes</span>(update: Update, context: ContextTypes.DEFAULT_TYPE):
<span class="code-comment">"""Show user's submitted jokes"""</span>
user_id = update.effective_user.id
conn = db.get_connection()
cursor = conn.cursor()
cursor.execute(<span class="code-string">'''SELECT id, joke_text, likes, dislikes
FROM jokes WHERE user_id = ?
ORDER BY created_at DESC LIMIT 10'''</span>, (user_id,))
user_jokes = [dict(row) <span class="code-keyword">for</span> row <span class="code-keyword">in</span> cursor.fetchall()]
conn.close()
<span class="code-keyword">if</span> <span class="code-keyword">not</span> user_jokes:
<span class="code-keyword">await</span> update.message.reply_text(<span class="code-string">"You haven't submitted any jokes yet! Use /addjoke"</span>)
<span class="code-keyword">return</span>
response = <span class="code-string">f"📝 YOUR JOKES ({len(user_jokes)} total)\n\n"</span>
<span class="code-keyword">for</span> joke <span class="code-keyword">in</span> user_jokes:
score = joke[<span class="code-string">'likes'</span>] - joke[<span class="code-string">'dislikes'</span>]
response += <span class="code-string">f"ID {joke['id']}: Score {score} (👍{joke['likes']} 👎{joke['dislikes']})\n"</span>
response += <span class="code-string">f" \"{joke['joke_text'][:60]}...\"\n\n"</span>
<span class="code-keyword">await</span> update.message.reply_text(response)</div>
<div class="feature-box">
<h3>Enhanced Features:</h3>
<div class="feature-item">
<div class="feature-number">📊</div>
<div><strong>Statistics:</strong> Track bot usage and engagement</div>
</div>
<div class="feature-item">
<div class="feature-number">🏆</div>
<div><strong>Leaderboard:</strong> Top jokes ranking system</div>
</div>
<div class="feature-item">
<div class="feature-number">👤</div>
<div><strong>Personal Stats:</strong> Users see their own contributions</div>
</div>
<div class="feature-item">
<div class="feature-number">📈</div>
<div><strong>Analytics:</strong> Approval rate and engagement metrics</div>
</div>
</div>
</div>
<!-- Slide 10: Step 7 - Complete Main Function -->
<div class="slide" id="slide10">
<h2>Step 7: Complete Main Function (5 minutes)</h2>
<p class="lead">Put it all together and run the upgraded bot</p>
<div class="code-block"><span class="code-keyword">def</span> <span class="code-function">main</span>():
<span class="code-comment"># Using the provided bot token</span>
BOT_TOKEN = <span class="code-string">"YOUR_BOT_TOKEN_HERE"</span>
<span class="code-comment"># Create application</span>
app = Application.builder().token(BOT_TOKEN).build()
<span class="code-comment"># Add command handlers</span>
app.add_handler(CommandHandler(<span class="code-string">"start"</span>, start))
app.add_handler(CommandHandler(<span class="code-string">"joke"</span>, send_joke))
app.add_handler(CommandHandler(<span class="code-string">"addjoke"</span>, add_joke_command))
app.add_handler(CommandHandler(<span class="code-string">"like"</span>, like_joke))
app.add_handler(CommandHandler(<span class="code-string">"dislike"</span>, dislike_joke))
app.add_handler(CommandHandler(<span class="code-string">"top"</span>, top_jokes))
app.add_handler(CommandHandler(<span class="code-string">"stats"</span>, bot_stats))
app.add_handler(CommandHandler(<span class="code-string">"myjokes"</span>, my_jokes))
<span class="code-comment"># Add callback handler for inline buttons</span>
app.add_handler(CallbackQueryHandler(button_callback))
print(<span class="code-string">"🤖 Joke Bot 2.0 is running..."</span>)
print(<span class="code-string">"📊 Database initialized: joke_bot.db"</span>)
print(<span class="code-string">"🎯 Features: User submissions, ratings, leaderboard"</span>)
print(<span class="code-string">"Press Ctrl+C to stop.\n"</span>)
app.run_polling()
<span class="code-keyword">if</span> __name__ == <span class="code-string">"__main__"</span>:
main()</div>
<div class="feature-box">
<h3>Project Structure:</h3>
<div class="code-block" style="margin: 10px 0; padding: 15px; background: #2c3e50; font-size: 0.9rem;">
joke_bot_2.0/
├── app.py <span class="code-comment"># Main bot file (updated)</span>
├── database.py <span class="code-comment"># SQLite database class</span>
├── joke_bot.db <span class="code-comment"># SQLite database (auto-created)</span>
├── requirements.txt <span class="code-comment"># python-telegram-bot==20.3</span>
└── README.md <span class="code-comment"># Setup instructions</span></div>
<div class="feature-item">
<div class="feature-number">1</div>
<div><strong>Install:</strong> <code>pip install python-telegram-bot==20.3</code></div>
</div>
<div class="feature-item">
<div class="feature-number">2</div>
<div><strong>Run:</strong> <code>python app.py</code></div>
</div>
<div class="feature-item">
<div class="feature-number">3</div>
<div><strong>Test:</strong> Open Telegram, find your bot, type <code>/start</code></div>
</div>
<div class="feature-item">
<div class="feature-number">4</div>
<div><strong>Deploy:</strong> Can run on Raspberry Pi, VPS, or cloud</div>
</div>
</div>
</div>
<!-- Slide 11: Database Administration -->
<div class="slide" id="slide11">
<h2>Database Administration Tools</h2>
<p class="lead">Tools to view and manage your SQLite database</p>
<div class="code-block"><span class="code-comment"># admin_tools.py - Database management utilities</span>
<span class="code-keyword">import</span> sqlite3
<span class="code-keyword">import</span> pandas <span class="code-keyword">as</span> pd
<span class="code-keyword">from</span> datetime <span class="code-keyword">import</span> datetime
<span class="code-keyword">class</span> <span class="code-builtin">JokeAdmin</span>:
<span class="code-keyword">def</span> <span class="code-function">__init__</span>(<span class="code-keyword">self</span>, db_name=<span class="code-string">'joke_bot.db'</span>):
<span class="code-keyword">self</span>.db_name = db_name
<span class="code-keyword">def</span> <span class="code-function">export_to_csv</span>(<span class="code-keyword">self</span>, filename=<span class="code-string">'jokes_export.csv'</span>):
<span class="code-comment">"""Export all jokes to CSV"""</span>
conn = sqlite3.connect(<span class="code-keyword">self</span>.db_name)
df = pd.read_sql_query(<span class="code-string">"SELECT * FROM jokes"</span>, conn)
df.to_csv(filename, index=False, encoding=<span class="code-string">'utf-8'</span>)
conn.close()
print(<span class="code-string">f"Exported {len(df)} jokes to {filename}"</span>)
<span class="code-keyword">def</span> <span class="code-function">show_stats</span>(<span class="code-keyword">self</span>):
<span class="code-comment">"""Show detailed statistics"""</span>
conn = sqlite3.connect(<span class="code-keyword">self</span>.db_name)
cursor = conn.cursor()
<span class="code-comment"># Basic counts</span>
cursor.execute(<span class="code-string">"SELECT COUNT(*) FROM jokes"</span>)
total_jokes = cursor.fetchone()[<span class="code-number">0</span>]
cursor.execute(<span class="code-string">"SELECT COUNT(DISTINCT user_id) FROM jokes WHERE user_id != 0"</span>)
unique_users = cursor.fetchone()[<span class="code-number">0</span>]
cursor.execute(<span class="code-string">"SELECT SUM(likes), SUM(dislikes) FROM jokes"</span>)
likes, dislikes = cursor.fetchone()
<span class="code-comment"># Top contributors</span>
cursor.execute(<span class="code-string">'''SELECT username, COUNT(*) as joke_count
FROM jokes WHERE user_id != 0
GROUP BY user_id ORDER BY joke_count DESC LIMIT 5'''</span>)
top_contributors = cursor.fetchall()
conn.close()
print(<span class="code-string">f"""📊 DATABASE STATISTICS 📊
Total Jokes: {total_jokes}
Unique Contributors: {unique_users}
Total Likes: {likes or 0}
Total Dislikes: {dislikes or 0}
🏆 Top Contributors:"""</span>)
<span class="code-keyword">for</span> username, count <span class="code-keyword">in</span> top_contributors:
print(<span class="code-string">f" {username}: {count} jokes"</span>)
<span class="code-keyword">def</span> <span class="code-function">backup_database</span>(<span class="code-keyword">self</span>):
<span class="code-comment">"""Create a backup of the database"""</span>
timestamp = datetime.now().strftime(<span class="code-string">'%Y%m%d_%H%M%S'</span>)
backup_name = <span class="code-string">f"backup_joke_bot_{timestamp}.db"</span>
<span class="code-keyword">import</span> shutil
shutil.copy2(<span class="code-keyword">self</span>.db_name, backup_name)
print(<span class="code-string">f"Backup created: {backup_name}"</span>)
<span class="code-comment"># Usage example</span>
<span class="code-keyword">if</span> __name__ == <span class="code-string">"__main__"</span>:
admin = JokeAdmin()
admin.show_stats()
admin.export_to_csv()
admin.backup_database()</div>
<div class="feature-box">
<h3>Admin Features:</h3>
<div class="feature-item">
<div class="feature-number">💾</div>
<div><strong>Backups:</strong> Automatic database backups</div>
</div>
<div class="feature-item">
<div class="feature-number">📊</div>
<div><strong>Analytics:</strong> Detailed usage statistics</div>
</div>
<div class="feature-item">
<div class="feature-number">📤</div>
<div><strong>Export:</strong> CSV export for analysis</div>
</div>
<div class="feature-item">
<div class="feature-number">👑</div>
<div><strong>Leaderboards:</strong> Top contributors recognition</div>
</div>
</div>
<div class="database-concept">
<span class="concept-title">SQLite Browser Tool:</span>
<p>Download <strong>DB Browser for SQLite</strong> (free) to visually explore your database:</p>
<p>• View tables and data</p>
<p>• Run SQL queries</p>
<p>• Export/import data</p>
<p>• Available for Windows, Mac, Linux</p>
</div>
</div>
<!-- Slide 12: Advanced Features -->
<div class="slide" id="slide12">
<h2>Advanced Features (Optional)</h2>
<p class="lead">Take your joke bot to the next level</p>
<div class="code-block"><span class="code-comment"># advanced_features.py - Optional enhancements</span>
<span class="code-comment"># 1. Joke Categories</span>
<span class="code-keyword">def</span> <span class="code-function">add_categories</span>():
<span class="code-comment">"""Add category support to jokes"""</span>
conn = sqlite3.connect(<span class="code-string">'joke_bot.db'</span>)
cursor = conn.cursor()
<span class="code-comment"># Add category column</span>
<span class="code-keyword">try</span>:
cursor.execute(<span class="code-string">"ALTER TABLE jokes ADD COLUMN category TEXT DEFAULT 'general'"</span>)
<span class="code-keyword">except</span> sqlite3.OperationalError:
pass <span class="code-comment"># Column already exists</span>
<span class="code-comment"># Create categories table</span>
cursor.execute(<span class="code-string">'''CREATE TABLE IF NOT EXISTS categories (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE,
description TEXT
)'''</span>)
<span class="code-comment"># Insert default categories</span>
categories = [
(<span class="code-string">'programming'</span>, <span class="code-string">'Programming and tech jokes'</span>),
(<span class="code-string">'dad'</span>, <span class="code-string">'Classic dad jokes'</span>),
(<span class="code-string">'animal'</span>, <span class="code-string">'Animal jokes'</span>),
(<span class="code-string">'school'</span>, <span class="code-string">'School and education jokes'</span>)
]
cursor.executemany(<span class="code-string">'''INSERT OR IGNORE INTO categories (name, description)
VALUES (?, ?)'''</span>, categories)
conn.commit()
conn.close()
<span class="code-comment"># 2. Daily Joke Notification</span>
<span class="code-keyword">async</span> <span class="code-keyword">def</span> <span class="code-function">send_daily_joke</span>(context: ContextTypes.DEFAULT_TYPE):
<span class="code-comment">"""Send daily joke to subscribed users"""</span>
joke = db.get_random_joke()
<span class="code-keyword">if</span> joke:
<span class="code-comment"># Get subscribed users from database</span>
conn = db.get_connection()
cursor = conn.cursor()
cursor.execute(<span class="code-string">"SELECT user_id FROM subscribers WHERE active = 1"</span>)
users = cursor.fetchall()
conn.close()
<span class="code-keyword">for</span> user_id <span class="code-keyword">in</span> users:
<span class="code-keyword">try</span>:
<span class="code-keyword">await</span> context.bot.send_message(
chat_id=user_id[<span class="code-number">0</span>],
text=<span class="code-string">f"📅 Daily Joke!\n\n{joke['joke_text']}"</span>
)
<span class="code-keyword">except</span> Exception <span class="code-keyword">as</span> e:
print(<span class="code-string">f"Failed to send to user {user_id}: {e}"</span>)
<span class="code-comment"># 3. Joke Search Function</span>
<span class="code-keyword">def</span> <span class="code-function">search_jokes</span>(search_term, limit=<span class="code-number">10</span>):
<span class="code-comment">"""Search jokes by keyword"""</span>
conn = sqlite3.connect(<span class="code-string">'joke_bot.db'</span>)
cursor = conn.cursor()
cursor.execute(<span class="code-string">'''SELECT * FROM jokes
WHERE joke_text LIKE ?
ORDER BY (likes - dislikes) DESC
LIMIT ?'''</span>, (<span class="code-string">f'%{search_term}%'</span>, limit))
jokes = [dict(row) <span class="code-keyword">for</span> row <span class="code-keyword">in</span> cursor.fetchall()]
conn.close()
<span class="code-keyword">return</span> jokes</div>
<div class="feature-box">
<h3>Optional Enhancements:</h3>
<div class="feature-item">
<div class="feature-number">🏷️</div>
<div><strong>Categories:</strong> Organize jokes by type</div>
</div>
<div class="feature-item">
<div class="feature-number">🔔</div>
<div><strong>Notifications:</strong> Daily joke subscriptions</div>
</div>
<div class="feature-item">
<div class="feature-number">🔍</div>
<div><strong>Search:</strong> Find jokes by keyword</div>
</div>
<div class="feature-item">
<div class="feature-number">🤖</div>
<div><strong>AI Integration:</strong> Generate jokes with GPT API</div>
</div>
<div class="feature-item">
<div class="feature-number">🌐</div>
<div><strong>Web Dashboard:</strong> View stats in browser</div>
</div>
</div>
</div>
<!-- Slide 13: Testing Your Bot -->
<div class="slide" id="slide13">
<h2>Testing Your Upgraded Bot</h2>
<p class="lead">Comprehensive testing guide</p>
<div class="database-concept">
<span class="concept-title">Test Commands Sequence:</span>
<p>1. <code>/start</code> - See welcome message with all commands</p>
<p>2. <code>/joke</code> - Get random joke with rating buttons</p>
<p>3. Click 👍 and 👎 buttons - Test inline rating</p>
<p>4. <code>/addjoke Why did the Python cross the road? To get to the other side!</code></p>
<p>5. <code>/joke</code> again - See if new joke appears</p>
<p>6. <code>/like</code> and <code>/dislike</code> - Test command rating</p>
<p>7. <code>/top 3</code> - See top jokes leaderboard</p>
<p>8. <code>/stats</code> - Check bot statistics</p>
<p>9. <code>/myjokes</code> - View your submissions</p>
</div>
<div class="feature-box">
<h3>Expected Results:</h3>
<div class="feature-item">
<div class="feature-number"></div>
<div><strong>Database File:</strong> <code>joke_bot.db</code> created automatically</div>
</div>
<div class="feature-item">
<div class="feature-number"></div>
<div><strong>Default Jokes:</strong> 3 jokes pre-loaded</div>
</div>
<div class="feature-item">
<div class="feature-number"></div>
<div><strong>Rating Persistence:</strong> Votes saved between restarts</div>
</div>
<div class="feature-item">
<div class="feature-number"></div>
<div><strong>User Tracking:</strong> Your username saved with submissions</div>
</div>
<div class="feature-item">
<div class="feature-number"></div>
<div><strong>Weighted Random:</strong> Better jokes appear more often</div>
</div>
</div>
<div class="code-block" style="margin-top: 20px; padding: 15px; background: #e8f5e8; color: #2c3e50;">
<span class="code-comment"># Quick database check</span>
<span class="code-keyword">import</span> sqlite3
conn = sqlite3.connect(<span class="code-string">'joke_bot.db'</span>)
cursor = conn.cursor()
cursor.execute(<span class="code-string">"SELECT name FROM sqlite_master WHERE type='table'"</span>)
tables = cursor.fetchall()
print(<span class="code-string">"Tables:"</span>, tables)
cursor.execute(<span class="code-string">"SELECT COUNT(*) FROM jokes"</span>)
count = cursor.fetchone()[<span class="code-number">0</span>]
print(<span class="code-string">f"Total jokes: {count}"</span>)
conn.close()</div>
</div>
<!-- Slide 14: Deployment Options -->
<div class="slide" id="slide14">
<h2>Deployment Options</h2>
<p class="lead">Where to run your upgraded joke bot 24/7</p>
<div class="feature-box">
<h3>Free Options:</h3>
<div class="feature-item">
<div class="feature-number">🖥️</div>
<div>
<strong>Raspberry Pi:</strong><br>
• Always-on at home<br>
• Low power consumption<br>
• Full control
</div>
</div>
<div class="feature-item">
<div class="feature-number">☁️</div>
<div>
<strong>PythonAnywhere:</strong><br>
• Free tier available<br>
• Web-based IDE<br>
• Scheduled tasks
</div>
</div>
<div class="feature-item">
<div class="feature-number">🚀</div>
<div>
<strong>Replit:</strong><br>
• Free cloud IDE<br>
• Always-on option<br>
• Easy sharing
</div>
</div>
</div>
<div class="feature-box">
<h3>Paid Options:</h3>
<div class="feature-item">
<div class="feature-number">💻</div>
<div>
<strong>VPS (DigitalOcean, Linode):</strong><br>
• $5/month<br>
• Full root access<br>
• Scalable
</div>
</div>
<div class="feature-item">
<div class="feature-number"></div>
<div>
<strong>Heroku:</strong><br>
• Easy deployment<br>
• Add-ons available<br>
• Good free tier (with limits)
</div>
</div>
<div class="feature-item">
<div class="feature-number">🐳</div>
<div>
<strong>Docker:</strong><br>
• Containerized deployment<br>
• Runs anywhere<br>
• Easy updates
</div>
</div>
</div>
<div class="database-concept">
<span class="concept-title">Deployment Checklist:</span>
<p>1. Test locally first</p>
<p>2. Backup your database</p>
<p>3. Set up proper logging</p>
<p>4. Configure automatic restarts (using systemd or supervisor)</p>
<p>5. Set up regular database backups</p>
<p>6. Monitor bot activity</p>
</div>
</div>
<!-- Slide 15: Complete Project Review -->
<div class="slide" id="slide15">
<h2>Complete Project Review</h2>
<p class="lead">What you've accomplished in 60 minutes</p>
<div class="database-concept" style="background: #e8f5e8; border-color: #2ecc71;">
<span class="concept-title">🎉 CONGRATULATIONS! 🎉</span>
<p>You've successfully upgraded your simple joke bot to a full-featured application with:</p>
</div>
<div class="feature-box">
<h3>Core Upgrades:</h3>
<div class="feature-item">
<div class="feature-number"></div>
<div><strong>SQLite Database:</strong> Persistent storage for jokes</div>
</div>
<div class="feature-item">
<div class="feature-number"></div>
<div><strong>User Submissions:</strong> Community-driven content</div>
</div>
<div class="feature-item">
<div class="feature-number"></div>
<div><strong>Rating System:</strong> Like/dislike with tracking</div>
</div>
<div class="feature-item">
<div class="feature-number"></div>
<div><strong>Inline Buttons:</strong> Better user experience</div>
</div>
<div class="feature-item">
<div class="feature-number"></div>
<div><strong>Statistics:</strong> Track bot usage and engagement</div>
</div>
<div class="feature-item">
<div class="feature-number"></div>
<div><strong>Leaderboard:</strong> Top jokes ranking</div>
</div>
</div>
<div class="feature-box">
<h3>Technical Skills Learned:</h3>
<div class="feature-item">
<div class="feature-number">🐍</div>
<div><strong>Python:</strong> Advanced Python programming</div>
</div>
<div class="feature-item">
<div class="feature-number">🗄️</div>
<div><strong>SQLite:</strong> Database design and queries</div>
</div>
<div class="feature-item">
<div class="feature-number">🤖</div>
<div><strong>Telegram Bot API:</strong> Advanced bot features</div>
</div>
<div class="feature-item">
<div class="feature-number">📊</div>
<div><strong>Data Modeling:</strong> Table design and relationships</div>
</div>
<div class="feature-item">
<div class="feature-number">🔧</div>
<div><strong>Software Architecture:</strong> Modular, maintainable code</div>
</div>
</div>
<div class="database-concept">
<span class="concept-title">Next Steps:</span>
<p>1. Add joke categories (/programming, /dadjokes)</p>
<p>2. Implement daily joke notifications</p>
<p>3. Create a web dashboard for statistics</p>
<p>4. Add joke search functionality</p>
<p>5. Implement user profiles and achievements</p>
<p>6. Add multi-language support</p>
</div>
<div style="text-align: center; margin-top: 30px; padding: 20px; background: #3498db; color: white; border-radius: 10px;">
<h3 style="color: white;">Your Joke Bot is Now Production-Ready! 🚀</h3>
<p>Share it with friends, deploy it online, and watch your joke collection grow!</p>
</div>
</div>
<!-- Navigation -->
<div class="navigation">
<button class="nav-btn" id="prev-btn" onclick="prevSlide()" disabled="">← Back</button>
<div class="slide-counter">
<span id="current-slide">1</span> of <span id="total-slides">15</span>
</div>
<button class="nav-btn" id="next-btn" onclick="nextSlide()">Next →</button>
</div>
<script>
let currentSlide = 1;
const totalSlides = 15;
function showSlide(slideNumber) {
document.querySelectorAll('.slide').forEach(slide => {
slide.classList.remove('active');
});
document.getElementById(`slide${slideNumber}`).classList.add('active');
document.getElementById(`slide${slideNumber}`).scrollTop = 0;
document.getElementById('current-slide').textContent = slideNumber;
document.getElementById('prev-btn').disabled = slideNumber === 1;
document.getElementById('next-btn').disabled = slideNumber === totalSlides;
if (slideNumber === totalSlides) {
document.getElementById('next-btn').textContent = 'Finish';
} else {
document.getElementById('next-btn').textContent = 'Next →';
}
}
function nextSlide() {
if (currentSlide < totalSlides) {
currentSlide++;
showSlide(currentSlide);
}
}
function prevSlide() {
if (currentSlide > 1) {
currentSlide--;
showSlide(currentSlide);
}
}
// Initialize
showSlide(1);
// Keyboard navigation
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight' || e.key === ' ' || e.key === 'Enter') {
nextSlide();
} else if (e.key === 'ArrowLeft') {
prevSlide();
}
});
// Touch swipe for mobile
let touchStartY = 0;
let touchEndY = 0;
document.addEventListener('touchstart', (e) => {
touchStartY = e.changedTouches[0].screenY;
});
document.addEventListener('touchend', (e) => {
touchEndY = e.changedTouches[0].screenY;
const swipeThreshold = 100;
if (touchEndY < touchStartY - swipeThreshold) {
nextSlide();
}
if (touchEndY > touchStartY + swipeThreshold) {
prevSlide();
}
});
</script>
</body></html>