645 lines
25 KiB
Python
645 lines
25 KiB
Python
# database.py
|
|
import sqlite3
|
|
import logging
|
|
import time # Import time for timestamps
|
|
from config import DATABASE_PATH
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# --- Database Initialization ---
|
|
def init_db():
|
|
conn = sqlite3.connect(DATABASE_PATH)
|
|
cur = conn.cursor()
|
|
# ... (raffles table definition remains the same) ...
|
|
cur.execute("""
|
|
CREATE TABLE IF NOT EXISTS raffles (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
description TEXT,
|
|
image_file_id TEXT,
|
|
price INTEGER NOT NULL,
|
|
channel_id TEXT NOT NULL,
|
|
main_message_id INTEGER,
|
|
update_message_id INTEGER,
|
|
international_shipping INTEGER DEFAULT 0,
|
|
active INTEGER DEFAULT 1
|
|
)
|
|
""")
|
|
|
|
cur.execute("""
|
|
CREATE TABLE IF NOT EXISTS participants (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL,
|
|
user_name TEXT,
|
|
raffle_id INTEGER NOT NULL,
|
|
numbers TEXT,
|
|
step TEXT NOT NULL,
|
|
invoice_id TEXT,
|
|
reservation_timestamp INTEGER,
|
|
completion_timestamp INTEGER,
|
|
UNIQUE(user_id, raffle_id, invoice_id),
|
|
FOREIGN KEY (raffle_id) REFERENCES raffles(id) ON DELETE CASCADE
|
|
)
|
|
""")
|
|
|
|
cur.execute("""
|
|
CREATE TABLE IF NOT EXISTS paypal_token (
|
|
id INTEGER PRIMARY KEY,
|
|
access_token TEXT,
|
|
expires_at INTEGER
|
|
)
|
|
""")
|
|
|
|
# Indexes
|
|
cur.execute("CREATE INDEX IF NOT EXISTS idx_participants_step_timestamp ON participants (step, reservation_timestamp)")
|
|
cur.execute("CREATE INDEX IF NOT EXISTS idx_participants_user_raffle_step ON participants (user_id, raffle_id, step)")
|
|
cur.execute("CREATE INDEX IF NOT EXISTS idx_raffles_active ON raffles (active)")
|
|
cur.execute("CREATE INDEX IF NOT EXISTS idx_participants_completion_ts ON participants (raffle_id, completion_timestamp DESC, step)")
|
|
|
|
conn.commit()
|
|
conn.close()
|
|
logger.info("Database initialized (ensuring participants.reservation_timestamp exists).")
|
|
|
|
# --- Database Connection --- (remains the same)
|
|
def connect_db():
|
|
conn = sqlite3.connect(DATABASE_PATH)
|
|
conn.row_factory = sqlite3.Row
|
|
return conn
|
|
|
|
# --- Raffle Management --- (remains the same)
|
|
# ... create_raffle, end_raffle, get_raffle, etc. ...
|
|
def create_raffle(name, description, price, image_file_id, channel_id, international_shipping=0):
|
|
"""Creates a new raffle in the database."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute(
|
|
"INSERT INTO raffles (name, description, price, image_file_id, channel_id, international_shipping) VALUES (?, ?, ?, ?, ?, ?)",
|
|
(name, description, price, image_file_id, channel_id, international_shipping)
|
|
)
|
|
raffle_id = cur.lastrowid
|
|
conn.commit()
|
|
logging.info(f"Created raffle '{name}' (ID: {raffle_id}) for channel: {channel_id}")
|
|
return raffle_id
|
|
except sqlite3.IntegrityError as e:
|
|
logging.error(f"Error creating raffle: Name '{name}' likely already exists. {e}")
|
|
return None
|
|
except Exception as e:
|
|
logging.error(f"Error creating raffle '{name}': {e}")
|
|
conn.rollback() # Rollback on error
|
|
return None
|
|
finally:
|
|
conn.close()
|
|
|
|
def end_raffle(raffle_id):
|
|
"""Marks a raffle as inactive."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute("UPDATE raffles SET active=0 WHERE id=? AND active=1", (raffle_id,))
|
|
updated = conn.total_changes > 0
|
|
conn.commit()
|
|
if updated:
|
|
logging.info(f"Ended raffle ID: {raffle_id}")
|
|
else:
|
|
logging.warning(f"Attempted to end raffle ID {raffle_id}, but it was not found or already inactive.")
|
|
return updated
|
|
except Exception as e:
|
|
logging.error(f"Error ending raffle ID {raffle_id}: {e}")
|
|
conn.rollback()
|
|
return False
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_raffle(raffle_id):
|
|
"""Gets basic raffle details by ID."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
# Does not include price directly, price is per channel
|
|
cur.execute("SELECT * FROM raffles WHERE id=?", (raffle_id,))
|
|
raffle = cur.fetchone()
|
|
return raffle
|
|
except Exception as e:
|
|
logger.error(f"Error getting raffle ID {raffle_id}: {e}")
|
|
return None
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_active_raffles():
|
|
"""Gets all active raffles (basic info)."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
# No price here, as it's per-channel
|
|
cur.execute("SELECT id, name, description, image_file_id FROM raffles WHERE active=1")
|
|
raffles = cur.fetchall()
|
|
return raffles
|
|
except Exception as e:
|
|
logger.error(f"Error getting active raffles: {e}")
|
|
return []
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_raffle_name(raffle_id):
|
|
"""Gets raffle name by ID."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute("SELECT name FROM raffles WHERE id=?", (raffle_id,))
|
|
name = cur.fetchone()
|
|
return name[0] if name else None
|
|
except Exception as e:
|
|
logging.error(f"Error getting raffle name for ID {raffle_id}: {e}")
|
|
return None
|
|
finally:
|
|
conn.close()
|
|
|
|
# --- Participant Management ---
|
|
|
|
def reserve_number(user_id, user_name, raffle_id, number):
|
|
"""Adds or updates a participant's reserved numbers."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute("SELECT id, numbers FROM participants WHERE raffle_id=? AND user_id=? AND step='waiting_for_payment'", (raffle_id, user_id))
|
|
existing_reservation = cur.fetchone()
|
|
|
|
if existing_reservation:
|
|
participant_id = existing_reservation['id']
|
|
numbers = existing_reservation['numbers']
|
|
numbers_list = numbers.split(',') if numbers else []
|
|
if str(number) not in numbers_list:
|
|
numbers_list.append(str(number))
|
|
numbers_str = ','.join(sorted(numbers_list))
|
|
cur.execute("UPDATE participants SET numbers=?, reservation_timestamp=? WHERE id=?",
|
|
(numbers_str, int(time.time()), participant_id))
|
|
else:
|
|
cur.execute(
|
|
"INSERT INTO participants (user_id, user_name, raffle_id, numbers, step, reservation_timestamp) VALUES (?, ?, ?, ?, ?, ?)",
|
|
(user_id, user_name, raffle_id, str(number), "waiting_for_payment", int(time.time()))
|
|
)
|
|
conn.commit()
|
|
except Exception as e:
|
|
logger.error(f"Error reserving number {number} for user {user_id} in raffle {raffle_id}: {e}")
|
|
conn.rollback()
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_total_participations(raffle_id):
|
|
"""Gets the total number of participations (both reserved and confirmed) for a raffle."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute("SELECT COUNT(*) FROM participants WHERE raffle_id=?", (raffle_id,))
|
|
count = cur.fetchone()
|
|
return count[0] if count else 0
|
|
except Exception as e:
|
|
logger.error(f"Error getting total participations for raffle {raffle_id}: {e}")
|
|
return 0
|
|
finally:
|
|
conn.close()
|
|
|
|
def remove_reserved_number(participant_id, number):
|
|
"""Removes a specific number from a 'waiting_for_payment' reservation."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute("SELECT numbers FROM participants WHERE id=? AND step='waiting_for_payment'", (participant_id,))
|
|
result = cur.fetchone()
|
|
if result and result['numbers']:
|
|
numbers = result['numbers'].split(',')
|
|
if str(number) in numbers:
|
|
numbers.remove(str(number))
|
|
if numbers: # If list is not empty after removal
|
|
numbers_str = ','.join(sorted(numbers))
|
|
cur.execute("UPDATE participants SET numbers=? WHERE id=?", (numbers_str, participant_id))
|
|
else:
|
|
# If no numbers left, delete the entire reservation row
|
|
logger.info(f"No numbers left for participant {participant_id}, deleting reservation.")
|
|
cur.execute("DELETE FROM participants WHERE id=?", (participant_id,))
|
|
conn.commit()
|
|
return True
|
|
else:
|
|
logger.warning(f"Attempted to remove number {number} from non-existent or empty reservation {participant_id}")
|
|
return False # Indicate nothing changed
|
|
except Exception as e:
|
|
logger.error(f"Error removing reserved number {number} for participant {participant_id}: {e}")
|
|
conn.rollback()
|
|
return False
|
|
finally:
|
|
conn.close()
|
|
|
|
def mark_reservation_pending(participant_id, invoice_id):
|
|
"""Sets the invoice ID and reservation timestamp for a participant moving to pending payment."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute(
|
|
"UPDATE participants SET invoice_id=?, reservation_timestamp=? WHERE id=? AND step='waiting_for_payment'",
|
|
(invoice_id, int(time.time()), participant_id)
|
|
)
|
|
conn.commit()
|
|
logger.info(f"Marked reservation pending for participant {participant_id} with invoice {invoice_id} at {time.time()}")
|
|
except Exception as e:
|
|
logger.error(f"Error marking reservation pending for participant {participant_id}: {e}")
|
|
conn.rollback()
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_expired_reservations(expiry_threshold_timestamp):
|
|
"""Finds participants whose reservations have expired."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute(
|
|
"""SELECT p.id, p.user_id, p.user_name, p.raffle_id, p.numbers, r.name as raffle_name
|
|
FROM participants p
|
|
JOIN raffles r ON p.raffle_id = r.id
|
|
WHERE p.step = 'waiting_for_payment'
|
|
AND p.reservation_timestamp IS NOT NULL
|
|
AND p.reservation_timestamp < ?""",
|
|
(int(expiry_threshold_timestamp),)
|
|
)
|
|
expired = cur.fetchall()
|
|
return expired # Returns list of Row objects
|
|
except Exception as e:
|
|
logger.error(f"Error fetching expired reservations: {e}")
|
|
return []
|
|
finally:
|
|
conn.close()
|
|
|
|
def cancel_reserved_numbers(user_id, raffle_id):
|
|
"""Deletes a 'waiting_for_payment' reservation for a user/raffle."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
deleted_count = 0
|
|
try:
|
|
cur.execute("DELETE FROM participants WHERE user_id=? AND raffle_id=? AND step='waiting_for_payment'", (user_id, raffle_id))
|
|
deleted_count = conn.total_changes
|
|
conn.commit()
|
|
if deleted_count > 0:
|
|
logger.info(f"Cancelled reservation for user {user_id}, raffle {raffle_id}.")
|
|
else:
|
|
logger.debug(f"No reservation found to cancel for user {user_id}, raffle {raffle_id}.")
|
|
except Exception as e:
|
|
logger.error(f"Error cancelling reservation for user {user_id}, raffle {raffle_id}: {e}")
|
|
conn.rollback()
|
|
finally:
|
|
conn.close()
|
|
return deleted_count > 0 # Return True if something was deleted
|
|
|
|
def cancel_reservation_by_id(participant_id):
|
|
"""Deletes a reservation using its specific participant ID."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
deleted_count = 0
|
|
try:
|
|
# Ensure we only delete if it's still in the correct state
|
|
cur.execute("DELETE FROM participants WHERE id=? AND step='waiting_for_payment'", (participant_id,))
|
|
deleted_count = conn.total_changes
|
|
conn.commit()
|
|
if deleted_count > 0:
|
|
logger.info(f"Cancelled reservation by participant ID: {participant_id}.")
|
|
else:
|
|
logger.warning(f"Attempted to cancel reservation by ID {participant_id}, but it was not found or not in 'waiting_for_payment' state.")
|
|
except Exception as e:
|
|
logger.error(f"Error cancelling reservation by ID {participant_id}: {e}")
|
|
conn.rollback()
|
|
finally:
|
|
conn.close()
|
|
return deleted_count > 0
|
|
|
|
def confirm_reserved_numbers(user_id, raffle_id, invoice_id):
|
|
"""Confirms payment, sets step to completed, removes reservation timestamp, and sets completion_timestamp."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
current_completion_timestamp = int(time.time()) # Timestamp for completion
|
|
cur.execute(
|
|
"""UPDATE participants SET
|
|
step='completed',
|
|
invoice_id=?,
|
|
reservation_timestamp=NULL,
|
|
completion_timestamp=?
|
|
WHERE user_id=? AND raffle_id=? AND step='waiting_for_payment'""",
|
|
(invoice_id, current_completion_timestamp, user_id, raffle_id)
|
|
)
|
|
updated_count = conn.total_changes
|
|
conn.commit()
|
|
return updated_count > 0
|
|
except Exception as e:
|
|
logger.error(f"Error confirming reserved numbers for user {user_id}, raffle {raffle_id}: {e}")
|
|
conn.rollback()
|
|
return False
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_last_n_other_participants(raffle_id, n=4):
|
|
"""
|
|
Gets the last N "completed" participants for a raffle, excluding the current one.
|
|
Returns a list of dictionaries [{'user_name', 'numbers'}].
|
|
"""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
participants_info = []
|
|
try:
|
|
# Fetch N+1 recent completed participants, then filter out the current one in code
|
|
# if they are among them, ensuring we get N *other* participants.
|
|
# Order by completion_timestamp DESC.
|
|
cur.execute(
|
|
"""SELECT user_name, numbers
|
|
FROM participants
|
|
WHERE raffle_id = ? AND step = 'completed'
|
|
ORDER BY completion_timestamp DESC
|
|
LIMIT ?""",
|
|
(raffle_id, n)
|
|
)
|
|
rows = cur.fetchall()
|
|
skip_first = True
|
|
for row in rows:
|
|
if skip_first:
|
|
skip_first = False
|
|
continue
|
|
participants_info.append({'user_name': row['user_name'], 'numbers': row['numbers']})
|
|
# The list is already ordered newest first by the SQL query.
|
|
return participants_info # Newest other participants first
|
|
except Exception as e:
|
|
logger.error(f"Error getting last N other participants for raffle {raffle_id}: {e}")
|
|
return []
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
# --- Other participant functions (get_participant, get_participants, etc.) remain largely the same ---
|
|
# Ensure they exist as in your previous version. Add them back here if needed.
|
|
|
|
def get_participant_by_user_id_and_step(user_id, step):
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute("SELECT id, raffle_id, numbers, user_name FROM participants WHERE user_id=? AND step=?", (user_id, step))
|
|
participant = cur.fetchone()
|
|
return participant
|
|
except Exception as e:
|
|
logger.error(f"Error getting participant by user ID {user_id} and step {step}: {e}")
|
|
return None
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_reserved_numbers(user_id, raffle_id):
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute("SELECT numbers FROM participants WHERE user_id=? AND raffle_id=? AND step='waiting_for_payment'", (user_id, raffle_id))
|
|
numbers = cur.fetchone()
|
|
return numbers['numbers'].split(',') if numbers and numbers['numbers'] else []
|
|
except Exception as e:
|
|
logger.error(f"Error getting reserved numbers for user {user_id}, raffle {raffle_id}: {e}")
|
|
return []
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_participants(raffle_id):
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute("SELECT id, user_id, user_name, numbers, step FROM participants WHERE raffle_id=?", (raffle_id,))
|
|
participants = cur.fetchall()
|
|
return participants
|
|
except Exception as e:
|
|
logging.error(f"Error getting participants for raffle {raffle_id}: {e}")
|
|
return []
|
|
finally:
|
|
conn.close()
|
|
|
|
# Add any other necessary participant functions from your original code here
|
|
def get_participant_by_number(raffle_id, number):
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
# Check both waiting and completed steps
|
|
cur.execute(
|
|
"SELECT id, user_id, user_name, numbers, step FROM participants WHERE raffle_id=? AND numbers LIKE ? AND (step='waiting_for_payment' OR step='completed')",
|
|
(raffle_id, f"%{number}%")
|
|
)
|
|
# This LIKE might match partial numbers if not careful (e.g., 1 matches 10).
|
|
# A better approach is to fetch all and check in Python, or use JSON functions if SQLite version supports it.
|
|
# For simplicity, let's refine the query slightly assuming numbers are comma-separated:
|
|
cur.execute(
|
|
"""SELECT id, user_id, user_name, numbers, step FROM participants
|
|
WHERE raffle_id=? AND (step='waiting_for_payment' OR step='completed')
|
|
AND (numbers = ? OR numbers LIKE ? OR numbers LIKE ? OR numbers LIKE ?)""",
|
|
(raffle_id, number, f"{number},%", f"%,{number},%", f"%,{number}")
|
|
)
|
|
participant = cur.fetchone() # Get the first match
|
|
# Verify the number actually exists in the list
|
|
if participant and participant['numbers']:
|
|
if str(number) in participant['numbers'].split(','):
|
|
return participant
|
|
else: # False positive from LIKE, try fetching next
|
|
# This gets complex quickly. Fetching all and filtering might be easier.
|
|
# Let's assume for now the refined LIKE works reasonably well for 00-99.
|
|
return participant # Return the first match found by SQL for now
|
|
return None # No match found
|
|
except Exception as e:
|
|
logger.error(f"Error getting participant by number {number} for raffle {raffle_id}: {e}")
|
|
return None
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_raffle_by_user_id_waiting_payment(user_id):
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute("SELECT raffle_id FROM participants WHERE user_id=? AND step='waiting_for_payment'", (user_id,))
|
|
raffle_id = cur.fetchone()
|
|
return raffle_id[0] if raffle_id else None
|
|
except Exception as e:
|
|
logger.error(f"Error checking waiting payment raffle for user {user_id}: {e}")
|
|
return None
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_user_by_invoice_id(invoice_id):
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
# No need to join with raffles for price here, get price from raffle_channel_prices
|
|
cur.execute("SELECT * FROM participants WHERE invoice_id=? AND step='waiting_for_payment'", (invoice_id,))
|
|
data = cur.fetchone()
|
|
return data
|
|
except Exception as e:
|
|
logger.error(f"Error getting user/raffle by invoice ID {invoice_id}: {e}")
|
|
return None
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_remaining_numbers_amount(raffle_id):
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
# Count numbers from both 'waiting_for_payment' and 'completed' steps
|
|
cur.execute("SELECT numbers FROM participants WHERE raffle_id=? AND (step='waiting_for_payment' OR step='completed')", (raffle_id,))
|
|
all_numbers_rows = cur.fetchall()
|
|
taken_count = 0
|
|
for row in all_numbers_rows:
|
|
if row['numbers']:
|
|
try:
|
|
taken_count += len(row['numbers'].split(','))
|
|
except:
|
|
logger.warning(f"Invalid numbers format '{row['numbers']}' while counting remaining for raffle {raffle_id}")
|
|
return 100 - taken_count
|
|
except Exception as e:
|
|
logger.error(f"Error calculating remaining numbers for raffle {raffle_id}: {e}")
|
|
return -1 # Indicate error
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_confirmed_numbers(user_id, raffle_id):
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute("SELECT numbers FROM participants WHERE user_id=? AND raffle_id=? AND step='completed'", (user_id, raffle_id))
|
|
numbers = cur.fetchone()
|
|
return numbers['numbers'].split(',') if numbers and numbers['numbers'] else []
|
|
except Exception as e:
|
|
logger.error(f"Error getting confirmed numbers for user {user_id}, raffle {raffle_id}: {e}")
|
|
return []
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_remaining_numbers(raffle_id):
|
|
"""Gets a list of all remaining numbers for a raffle, formatted as two digits."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute(
|
|
"SELECT numbers FROM participants WHERE raffle_id=? AND (step='waiting_for_payment' OR step='completed')",
|
|
(raffle_id,)
|
|
)
|
|
all_numbers_rows = cur.fetchall()
|
|
taken_numbers_str_set = set() # Store taken numbers as strings (potentially "01", "10", etc.)
|
|
|
|
for row in all_numbers_rows:
|
|
if row['numbers']: # Ensure 'numbers' column is not None
|
|
# Split the comma-separated string and add individual numbers to the set
|
|
# Assumes numbers in DB are stored like "01,05,10"
|
|
taken_numbers_str_set.update(num.strip() for num in row['numbers'].split(',') if num.strip())
|
|
|
|
logger.debug(f"Raffle {raffle_id} - Taken numbers (string set): {taken_numbers_str_set}")
|
|
|
|
remaining_numbers_formatted = []
|
|
for num_int in range(100): # Iterate 0 through 99 as integers
|
|
# Format the integer as a two-digit string (e.g., 0 -> "00", 5 -> "05", 12 -> "12")
|
|
num_str_formatted = f"{num_int:02}"
|
|
|
|
# Check if this formatted string representation is in the set of taken numbers
|
|
if num_str_formatted not in taken_numbers_str_set:
|
|
remaining_numbers_formatted.append(num_str_formatted)
|
|
|
|
logger.debug(f"Raffle {raffle_id} - Remaining numbers formatted: {remaining_numbers_formatted}")
|
|
return remaining_numbers_formatted
|
|
except Exception as e:
|
|
logger.error(f"Error getting remaining numbers for raffle {raffle_id}: {e}")
|
|
return []
|
|
finally:
|
|
conn.close()
|
|
|
|
def store_main_message_id(raffle_id, message_id):
|
|
"""Updates the main_message_id for a given raffle."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
cur.execute("UPDATE raffles SET main_message_id=? WHERE id=?", (message_id, raffle_id))
|
|
conn.commit()
|
|
conn.close()
|
|
logger.info(f"Updated main_message_id for raffle {raffle_id} to {message_id}")
|
|
|
|
def get_main_message_id(raffle_id):
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute(
|
|
"SELECT main_message_id FROM raffles WHERE id=?",
|
|
(raffle_id,)
|
|
)
|
|
result = cur.fetchone()
|
|
return result['main_message_id'] if result else None
|
|
except Exception as e:
|
|
logger.error(f"Error getting main_message_id for raffle {raffle_id}: {e}")
|
|
return None
|
|
finally:
|
|
conn.close()
|
|
|
|
def store_update_message_id(raffle_id, message_id):
|
|
"""Updates the update_message_id for a given raffle."""
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
cur.execute("UPDATE raffles SET update_message_id=? WHERE id=?", (message_id, raffle_id))
|
|
conn.commit()
|
|
conn.close()
|
|
logger.info(f"Updated update_message_id for raffle {raffle_id} to {message_id}")
|
|
|
|
def get_update_message_id(raffle_id):
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute(
|
|
"SELECT update_message_id FROM raffles WHERE id=?",
|
|
(raffle_id,)
|
|
)
|
|
result = cur.fetchone()
|
|
return result['update_message_id'] if result else None
|
|
except Exception as e:
|
|
logger.error(f"Error getting update_message_id for raffle {raffle_id}: {e}")
|
|
return None
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_paypal_access_token_db():
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute(
|
|
"SELECT * FROM paypal_token ORDER BY id DESC LIMIT 1"
|
|
)
|
|
result = cur.fetchone()
|
|
if result:
|
|
# Check if the token is still valid
|
|
if int(result['expires_at']) > int(time.time()):
|
|
return result['access_token']
|
|
except Exception as e:
|
|
logger.error(f"Error getting PayPal access token from DB: {e}")
|
|
return None
|
|
finally:
|
|
conn.close()
|
|
|
|
def store_paypal_access_token(access_token, expires_in):
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute(
|
|
"INSERT INTO paypal_token (access_token, expires_at) VALUES (?, ?)",
|
|
(access_token, int(time.time()) + expires_in)
|
|
)
|
|
conn.commit()
|
|
except Exception as e:
|
|
logger.error(f"Error storing PayPal access token in DB: {e}")
|
|
finally:
|
|
conn.close()
|
|
|
|
def get_all_invoice_ids(raffle_id):
|
|
conn = connect_db()
|
|
cur = conn.cursor()
|
|
try:
|
|
cur.execute(
|
|
"SELECT invoice_id FROM participants WHERE raffle_id=? AND step='completed' AND invoice_id IS NOT NULL",
|
|
(raffle_id,)
|
|
)
|
|
rows = cur.fetchall()
|
|
return [row['invoice_id'] for row in rows if row['invoice_id']]
|
|
except Exception as e:
|
|
logger.error(f"Error getting invoice IDs for raffle {raffle_id}: {e}")
|
|
return []
|
|
finally:
|
|
conn.close() |