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! 🌟

🏆

EARN UP TO 5 POINTS EACH DAY!

Track your progress and earn rewards for active participation

+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
# Should show: Python 3.x.x

# If that doesn't work, try:
python3 --version
# Or on some systems:
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
# You should see Python interactive shell:
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()
# Type exit() to leave Python shell

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:

# Go to Desktop (Windows)
cd Desktop

# Create a new folder for your joke bot
mkdir joke_bot_upgrade

# Go into your new folder
cd joke_bot_upgrade

# Verify you're in the right place
pwd
# Should show: C:\Users\YourName\Desktop\joke_bot_upgrade
# Or use 'dir' to see files (should be empty)
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:

# Create virtual environment named 'venv'
python -m venv venv

# Check if venv folder was created
dir
# You should see a 'venv' folder in the list
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
# If you get an error about execution policy, run this first:
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

# Install the Telegram bot library (version 20.3)
pip install python-telegram-bot==20.3

# Verify installation
pip show python-telegram-bot
# Should show version 20.3
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

# View the requirements file
type requirements.txt
# (Mac/Linux: use 'cat requirements.txt' instead of 'type')

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:

# Open Python interactive shell
python

# Try importing the libraries
>>> 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:

# Using Python IDLE or any text editor:
notepad app.py
# (Or use VS Code, Sublime Text, or Python's IDLE)
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(): # Using the provided bot token 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:

# Make sure venv is activated and you're in the right folder
(venv) C:\Users\YourName\Desktop\joke_bot_upgrade> python app.py

# Should see:
Bot is running... Press Ctrl+C to stop.

# Open Telegram, find your bot, type /start and /joke
# Press Ctrl+C to stop the bot when done testing

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.

# SQLite comes built-in with Python! import sqlite3 # Connect to database (creates if doesn't exist) conn = sqlite3.connect('joke_bot.db') cursor = conn.cursor() # Create a table 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

# Create a new file: database.py # This will handle all database operations 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): """Get a database connection""" conn = sqlite3.connect(self.db_name) conn.row_factory = sqlite3.Row # Access columns by name return conn def init_database(self): """Initialize database tables""" conn = self.get_connection() cursor = conn.cursor() # Create jokes table 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 )''') # Create ratings table to track user votes cursor.execute('''CREATE TABLE IF NOT EXISTS ratings ( user_id INTEGER, joke_id INTEGER, vote INTEGER, -- 1 for like, -1 for dislike PRIMARY KEY (user_id, joke_id), FOREIGN KEY (joke_id) REFERENCES jokes(id) )''') conn.commit() conn.close() # Add some default jokes if table is empty self.add_default_jokes() def add_default_jokes(self): """Add default jokes if database is empty""" 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

# Add these methods to the JokeDatabase class def add_joke(self, joke_text, user_id, username): """Add a new joke to the database""" 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): """Get a random joke, weighted by rating""" conn = self.get_connection() cursor = conn.cursor() # Weighted random: jokes with higher likes appear more often 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): """Get a specific joke by 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): """Get top-rated jokes""" 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): """Get total number of jokes""" 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

# Add rating methods to JokeDatabase class def rate_joke(self, user_id, joke_id, vote): """ Rate a joke (like or dislike) vote: 1 for like, -1 for dislike, 0 to remove rating """ conn = self.get_connection() cursor = conn.cursor() # Check if user already voted 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: # Remove vote 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): """Get user's vote for a specific joke""" 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

# Updated app.py with database integration from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters, CallbackQueryHandler from database import JokeDatabase import random # Initialize database db = JokeDatabase() async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): """Start command handler""" 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): """Send a random joke from database""" joke = db.get_random_joke() if not joke: await update.message.reply_text("No jokes in database yet! Use /addjoke to add one.") return # Store joke ID in context for rating context.user_data['last_joke_id'] = joke['id'] # Format joke with rating info rating_text = f"👍 {joke['likes']} 👎 {joke['dislikes']}" joke_text = f"{joke['joke_text']}\n\n{rating_text}" # Add inline buttons for quick rating 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): """Handle /addjoke command""" 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 # Add to database 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