Joke Bot Upgrade
From Simple Lists to SQLite Database with User Interaction
Goal: Upgrade joke bot with database, user submissions, and ratings
Prerequisites: Basic Python knowledge, our existing joke bot
Time: 60 minutes to complete upgrade
Tools: Python IDLE, Command Line/PowerShell
How You Earn Points in Class! 🌟
✅
+2 You Came to Class!
Great job! Just by being in class, you get 2 points. Yay!
👂
+1 You Listened Quietly!
You didn't talk when the teacher was talking. Good listening!
✏️
+1 You Tried Your Work!
You didn't finish everything? That's OK! If you tried, you still get a point.
🎉
+1 You Finished ALL Your Work!
Wow! You did every single part — even the last task! You get a big high-five!
📊 Points Legend:
H = You were here! Points depend on what you did.
— = You were absent → 0 points
Today's Goal:
Complete ALL steps of the joke bot upgrade to earn the full 5 points! 🎯
Follow along, ask questions, and help your classmates to maximize your learning!
Learning Outcomes
By the end of this session, you will be able to:
✓
1. Understand SQLite Database Basics
Explain what SQLite is and why it's perfect for small Python projects
✓
2. Design Database Tables
Create tables with proper columns, data types, and relationships
✓
3. Implement CRUD Operations
Write Python code to Create, Read, Update, and Delete database records
✓
4. Integrate Database with Telegram Bot
Connect your existing joke bot to a persistent database
✓
5. Add User Interaction Features
Implement joke submission, rating system, and statistics
✓
6. Use Virtual Environments
Set up and manage Python dependencies using venv
Why These Skills Matter:
💼
Industry Standard: Databases are used in 99% of real-world applications
🚀
Career Boost: Database skills are highly sought after by employers
🧠
Problem Solving: Learn to structure and manage complex data
📱
App Development: Build apps that remember user data between sessions
Why Databases Matter
Moving beyond simple lists to persistent storage
The Problem with Lists:
Our current joke bot stores jokes in a Python list:
JOKE_LIST = [
"Why did the robot go to school? To recharge his brain! 🔋",
"Knock knock!\nWho's there?\nLettuce!\nLettuce who?\nLettuce in!",
"Why don't eggs tell jokes? They'd crack each other up! 🥚"
]
Problems:
• Jokes are lost when bot restarts
• No way for users to add jokes
• Can't track ratings or popularity
• Hard to search or organize jokes
Database Advantages:
💾
Persistence: Data survives bot restarts and crashes
👥
User Contributions: Community can add content
📊
Analytics: Track what jokes are popular
🔍
Searchability: Find jokes by keywords or ratings
⚡
Scalability: Handle thousands of jokes efficiently
Real-World Examples:
Reddit: Database stores posts, votes, comments, user profiles
YouTube: Database stores videos, views, likes, subscriptions
Banking Apps: Database stores accounts, transactions, balances
Games: Database stores player scores, achievements, progress
Step-by-Step Setup Guide
Before We Start - Check Your Python Installation
1
Open Command Prompt or PowerShell:
Press Windows + R, type cmd or powershell, press Enter
2
Check Python Version:
Type the following command and press Enter:
python --version
python3 --version
py --version
Troubleshooting:
If Python is not installed:
1. Go to python.org/downloads
2. Download Python 3.9 or later
3. Run installer (CHECK "Add Python to PATH" option!)
4. Restart your computer, then try again
Verify Installation:
Open Python IDLE to test:
python
Python 3.9.0 (tags/v3.9.0:9cf6752, Oct 5 2020, 15:34:40) ...
Type "help", "copyright", "credits" or "license" for more information.
>>> print("Hello, World!")
Hello, World!
>>> exit()
Create Your Project Folder
Organize your files properly from the start
1
Open PowerShell or Command Prompt:
Make sure you're not in the Python shell (should see C:\> or PS C:\>)
2
Navigate to Desktop or Documents:
Let's create a folder on your Desktop for easy access:
cd Desktop
mkdir joke_bot_upgrade
cd joke_bot_upgrade
pwd
dir
Folder Structure:
joke_bot_upgrade/
├── app.py # Main bot file
├── database.py # SQLite database class
├── joke_bot.db # Database file (auto-created)
├── requirements.txt # Python dependencies
└── venv/ # Virtual environment (we'll create this)
Best Practice: Keep all related files in one folder for easy management!
Set Up Virtual Environment (venv)
Isolate project dependencies for clean development
1
Why Virtual Environment?
• Keeps project dependencies separate
• Avoids version conflicts between projects
• Makes sharing and deployment easier
2
Create Virtual Environment:
Make sure you're in your joke_bot_upgrade folder, then run:
python -m venv venv
dir
What Happened?
The venv command created a complete Python installation in the venv folder:
• Python interpreter copy
• pip (package installer)
• Standard library
• Empty site-packages for our libraries
Activate Virtual Environment:
Windows PowerShell:
venv\Scripts\Activate.ps1
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
venv\Scripts\Activate.ps1
Windows Command Prompt:
venv\Scripts\activate.bat
Mac/Linux Terminal:
source venv/bin/activate
Success Indicator: You should see (venv) at the start of your command line!
Install Required Libraries
Get the Python packages we need for our upgraded bot
1
First, activate your venv:
Make sure you see (venv) before the prompt
2
Install python-telegram-bot:
This is the main library for creating Telegram bots
pip install python-telegram-bot==20.3
pip show python-telegram-bot
What We're Installing:
python-telegram-bot: Official library for Telegram Bot API
Version 20.3: Specific version that matches our code examples
Note: SQLite comes built-in with Python - no need to install!
Create Requirements File:
Save the list of dependencies for future use:
pip freeze > requirements.txt
type requirements.txt
requirements.txt contents:
python-telegram-bot==20.3
Why this matters: Anyone can install exact same versions with pip install -r requirements.txt
3
Test Your Setup:
Open Python IDLE or use Python shell to test imports:
python
>>> import sqlite3
>>> from telegram import Update
>>> from telegram.ext import Application
>>> print("All imports successful!")
All imports successful!
>>> exit()
Copy Existing Bot Code
Start with our working joke bot and upgrade it
1
Create app.py file:
In your joke_bot_upgrade folder, create a new file:
notepad app.py
Our Starting Code:
Copy this code into app.py - this is our current working bot:
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes
import random
JOKE_LIST = [
"Why did the robot go to school? To recharge his brain! 🔋",
"Knock knock!\nWho's there?\nLettuce!\nLettuce who?\nLettuce in!",
"Why don't eggs tell jokes? They'd crack each other up! 🥚"
]
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text("Hi! Type /joke for a funny joke! 😄")
async def send_joke(update: Update, context: ContextTypes.DEFAULT_TYPE):
joke = random.choice(JOKE_LIST)
await update.message.reply_text(joke)
def main():
BOT_TOKEN = "YOUR_BOT_TOKEN_HERE"
app = Application.builder().token(BOT_TOKEN).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler("joke", send_joke))
print("Bot is running... Press Ctrl+C to stop.")
app.run_polling()
if __name__ == "__main__":
main()
Important: Get Your Bot Token
1
Open Telegram: Search for @BotFather
2
Create New Bot: Type /newbot and follow instructions
3
Get Token: Copy the token (looks like: 1234567890:ABCdefGHIjklMNOpqrsTUVwxyz)
4
Replace in Code: Change YOUR_BOT_TOKEN_HERE with your actual token
2
Test Current Bot:
Make sure the basic bot works before we upgrade it:
(venv) C:\Users\YourName\Desktop\joke_bot_upgrade> python app.py
Bot is running... Press Ctrl+C to stop.
What Is SQLite?
Serverless, self-contained SQL database perfect for Telegram bots
Why SQLite for Our Joke Bot?
1
No Server Needed: Database lives in a single file
2
Zero Configuration: Just import and use
3
Lightweight: Perfect for small to medium applications
4
SQL Standard: Uses standard SQL commands
5
Built-in Python: No extra installation needed
Think of it like this:
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.
Perfect for Telegram bots, mobile apps, and desktop applications.
import sqlite3
conn = sqlite3.connect('joke_bot.db')
cursor = conn.cursor()
cursor.execute('''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
)''')
conn.commit()
conn.close()
Our Upgrade Goals
From static list to dynamic, user-powered joke database
Current Limitations:
1. Jokes are hardcoded in Python list
2. No way for users to add new jokes
3. No rating system to find the best jokes
4. Jokes are lost when bot restarts
New Features We'll Add:
1
SQLite Database:
Store jokes permanently in a database file
2
User Submissions:
/addjoke command for users to submit jokes
3
Rating System:
/like and /dislike commands to rate jokes
4
Top Jokes:
/top command to show most popular jokes
5
Random with Weight:
Better jokes appear more often
Step 1: Database Setup (10 minutes)
Create the SQLite database and connection functions
import sqlite3
from datetime import datetime
class JokeDatabase:
def __init__(self, db_name='joke_bot.db'):
self.db_name = db_name
self.init_database()
def get_connection(self):
conn = sqlite3.connect(self.db_name)
conn.row_factory = sqlite3.Row
return conn
def init_database(self):
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute('''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
)''')
cursor.execute('''CREATE TABLE IF NOT EXISTS ratings (
user_id INTEGER,
joke_id INTEGER,
vote INTEGER,
PRIMARY KEY (user_id, joke_id),
FOREIGN KEY (joke_id) REFERENCES jokes(id)
)''')
conn.commit()
conn.close()
self.add_default_jokes()
def add_default_jokes(self):
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM jokes")
count = cursor.fetchone()[0]
if count == 0:
default_jokes = [
("Why did the robot go to school? To recharge his brain! 🔋", 0, "System"),
("Knock knock!\nWho's there?\nLettuce!\nLettuce who?\nLettuce in!", 0, "System"),
("Why don't eggs tell jokes? They'd crack each other up! 🥚", 0, "System")
]
cursor.executemany('''INSERT INTO jokes (joke_text, user_id, username)
VALUES (?, ?, ?)''', default_jokes)
conn.commit()
print(f"Added {len(default_jokes)} default jokes to database")
conn.close()
Database Schema Explained:
📝
jokes table: Stores all jokes with ratings
⭐
ratings table: Tracks who voted for which joke
🆔
Primary keys: Unique IDs for each record
🔗
Foreign key: Links ratings to jokes
Step 2: Database Operations (15 minutes)
Add CRUD (Create, Read, Update, Delete) functions
def add_joke(self, joke_text, user_id, username):
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute('''INSERT INTO jokes (joke_text, user_id, username)
VALUES (?, ?, ?)''', (joke_text, user_id, username))
joke_id = cursor.lastrowid
conn.commit()
conn.close()
return joke_id
def get_random_joke(self):
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute('''SELECT * FROM jokes
ORDER BY (likes - dislikes + 5) * RANDOM() DESC
LIMIT 1''')
joke = cursor.fetchone()
conn.close()
return dict(joke) if joke else None
def get_joke_by_id(self, joke_id):
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute("SELECT * FROM jokes WHERE id = ?", (joke_id,))
joke = cursor.fetchone()
conn.close()
return dict(joke) if joke else None
def get_top_jokes(self, limit=5):
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute('''SELECT * FROM jokes
ORDER BY (likes - dislikes) DESC, likes DESC
LIMIT ?''', (limit,))
jokes = [dict(row) for row in cursor.fetchall()]
conn.close()
return jokes
def get_total_jokes(self):
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM jokes")
count = cursor.fetchone()[0]
conn.close()
return count
SQL Functions Explained:
📊
Weighted Random: (likes - dislikes + 5) * RANDOM()
⬆️
Top Jokes: Sorted by net score (likes - dislikes)
💾
Parameterized Queries: Safe from SQL injection
🔄
Connection Management: Open/close connections properly
Step 3: Rating System (15 minutes)
Implement like/dislike functionality with vote tracking
def rate_joke(self, user_id, joke_id, vote):
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute('''SELECT vote FROM ratings
WHERE user_id = ? AND joke_id = ?''', (user_id, joke_id))
existing_vote = cursor.fetchone()
if existing_vote:
old_vote = existing_vote[0]
if vote == 0:
cursor.execute('''DELETE FROM ratings
WHERE user_id = ? AND joke_id = ?''', (user_id, joke_id))
# Update joke counts
if old_vote == 1:
cursor.execute('''UPDATE jokes SET likes = likes - 1
WHERE id = ?''', (joke_id,))
elif old_vote == -1:
cursor.execute('''UPDATE jokes SET dislikes = dislikes - 1
WHERE id = ?''', (joke_id,))
elif old_vote != vote:
# Change vote
cursor.execute('''UPDATE ratings SET vote = ?
WHERE user_id = ? AND joke_id = ?''', (vote, user_id, joke_id))
# Update joke counts (remove old, add new)
if old_vote == 1 and vote == -1:
cursor.execute('''UPDATE jokes
SET likes = likes - 1, dislikes = dislikes + 1
WHERE id = ?''', (joke_id,))
elif old_vote == -1 and vote == 1:
cursor.execute('''UPDATE jokes
SET likes = likes + 1, dislikes = dislikes - 1
WHERE id = ?''', (joke_id,))
else:
if vote != 0:
# New vote
cursor.execute('''INSERT INTO ratings (user_id, joke_id, vote)
VALUES (?, ?, ?)''', (user_id, joke_id, vote))
# Update joke counts
if vote == 1:
cursor.execute('''UPDATE jokes SET likes = likes + 1
WHERE id = ?''', (joke_id,))
elif vote == -1:
cursor.execute('''UPDATE jokes SET dislikes = dislikes + 1
WHERE id = ?''', (joke_id,))
conn.commit()
# Get updated joke info
cursor.execute("SELECT likes, dislikes FROM jokes WHERE id = ?", (joke_id,))
result = cursor.fetchone()
conn.close()
return {
'likes': result[0] if result else 0,
'dislikes': result[1] else 0
}
def get_user_vote(self, user_id, joke_id):
conn = self.get_connection()
cursor = conn.cursor()
cursor.execute('''SELECT vote FROM ratings
WHERE user_id = ? AND joke_id = ?''', (user_id, joke_id))
result = cursor.fetchone()
conn.close()
return result[0] if result else 0
Rating Logic:
1
Prevent Double Voting: Users can only vote once per joke
2
Change Votes: Users can change their mind
3
Remove Votes: Users can remove their rating
4
Real-time Updates: Counts update immediately
Step 4: Telegram Bot Integration (15 minutes)
Update app.py to use the database
from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters, CallbackQueryHandler
from database import JokeDatabase
import random
db = JokeDatabase()
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
welcome_text = """🤖 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!"""
await update.message.reply_text(welcome_text)
async def send_joke(update: Update, context: ContextTypes.DEFAULT_TYPE):
joke = db.get_random_joke()
if not joke:
await update.message.reply_text("No jokes in database yet! Use /addjoke to add one.")
return
context.user_data['last_joke_id'] = joke['id']
rating_text = f"👍 {joke['likes']} 👎 {joke['dislikes']}"
joke_text = f"{joke['joke_text']}\n\n{rating_text}"
keyboard = [
[
InlineKeyboardButton("👍 Like", callback_data=f"like_{joke['id']}"),
InlineKeyboardButton("👎 Dislike", callback_data=f"dislike_{joke['id']}")
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(joke_text, reply_markup=reply_markup)
async def add_joke_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not context.args:
await update.message.reply_text(
"Please provide a joke after the command:\n"
"Example: /addjoke Why did the chicken cross the road?"
)
return
joke_text = " ".join(context.args)
user = update.effective_user
joke_id = db.add_joke(joke_text, user.id, user.username)
await update.message.reply_text(
f"✅ Joke added successfully! (ID: {joke_id})\n"
"Others can now rate it with /like and /dislike"
)
New Bot Features:
🤖
Inline Buttons: Quick like/dislike without commands
💾
User Context: Remember last joke for rating
📊
Real Stats: Display actual like/dislike counts
👤
User Tracking: Record who submitted each joke