Skip to content

Commit

Permalink
Merge pull request #10 from farhaan444/Major-Refactor
Browse files Browse the repository at this point in the history
#Added Feature
  • Loading branch information
farhaan444 authored May 6, 2024
2 parents 4b77b7d + 7aa2b12 commit 8908b2d
Show file tree
Hide file tree
Showing 13 changed files with 462 additions and 150 deletions.
7 changes: 4 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# RENAME THIS FILE NAME FROM .env.example to .env
# Enter your credentials bellow
# Enter your credentials below

# TELEGRAM BOT CREDENTIALS FROM BOT FATHER
BOT_USERNAME = 'Your telegram bot username'
BOT_TOKEN = 'Your secret bot token key'

# KIWI CREDENTIALS FLIGHT API
KIWI_API_KEY = 'Your secret kiwi api key'
KIWI_API_KEY_STD = 'Your secret kiwi api key used for searching Oneway and Return flights only.'
KIWI_API_KEY_MULTICITY = 'Your secret kiwi api key used for searching Multi-city flights only'


# DATABASE PATH
DATABASE_PATH = 'database\database.db'
Expand Down
2 changes: 1 addition & 1 deletion bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@


if __name__ == '__main__':
bot = TelegramBot(username=config.BOT_USERNAME, token=config.BOT_TOKEN)
bot = TelegramBot(token=config.BOT_TOKEN)
34 changes: 24 additions & 10 deletions bot_config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import logging
import tracemalloc
import warnings
import config
from telegram.warnings import PTBDeprecationWarning
from telegram.ext import ApplicationBuilder, CommandHandler, CallbackQueryHandler, MessageHandler, filters
from utils.jobs import flight_search_job

Expand All @@ -18,18 +15,35 @@


class TelegramBot:
def __init__(self, username: str, token: str) -> None:
def __init__(self, token: str) -> None:
# TELEGRAM BOT FATHER CREDENTIALS
self.username = username
self.token = token

# LOGGING
self.log = logging.basicConfig(
self.console_logger = logging.basicConfig( # Global logger --> Print to console
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
self.dep_warning = warnings.filterwarnings(
'error', category=PTBDeprecationWarning)
# set this to only recieve errors/execptions --> comment/uncomment.
self.log = logging.getLogger('httpx').setLevel(logging.WARNING)
self.logger = logging.getLogger()
self.logger.setLevel(level=logging.INFO)
self.httpx_logger = logging.getLogger("httpx")
self.httpx_logger.setLevel(level=logging.WARNING)
# LOGGING GLOBAL FORMAT
self.log_gformat = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s")
# LOG HANDLERS
self.global_log_handler = logging.FileHandler(
filename=r"Logs/global.log")
self.global_log_handler.setFormatter(self.log_gformat)
self.global_log_handler.setLevel(logging.INFO)

self.exception_handler = logging.FileHandler(
filename=r"Logs/exceptions.log")
self.exception_handler.setFormatter(self.log_gformat)
self.exception_handler.setLevel(logging.ERROR)

# ADD HANDLERS TO LOGGERS
self.logger.addHandler(self.global_log_handler)
self.logger.addHandler(self.exception_handler)
self.httpx_logger.addHandler(self.global_log_handler)

# APP BUILD
self.app = ApplicationBuilder().token(
Expand Down
5 changes: 2 additions & 3 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@
load_dotenv()

# BOT CREDENTIALS PROVIDED BY BOT FATHER
BOT_USERNAME: str = os.getenv('BOT_USERNAME')
BOT_TOKEN: str = os.getenv('BOT_TOKEN')

# KIWI CREDENTIALS FLIGHT API
KIWI_API_KEY: str = os.getenv('KIWI_API_KEY')
KIWI_API_KEY_STD: str = os.getenv('KIWI_API_KEY_STD')
KIWI_API_KEY_MULTICITY: str = os.getenv('KIWI_API_KEY_MULTICITY')

# DATABASE FILES
DATABASE_PATH: str = os.getenv('DATABASE_PATH')

# USER TYPES
ADMINISTRATOR: list = os.getenv('ADMINISTRATOR')


# JOB SCHEDULING INTERVALS - SECONDS
FIRST_RUN_FS = int(os.getenv('FIRST_RUN_FS'))
JOB_INTERVAL_FS = int(os.getenv('JOB_INTERVAL_FS'))
Expand Down
131 changes: 96 additions & 35 deletions handlers/button.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# THIS TEMPLATE DEFINES AND HANDLES THE CALLBACK QUERIES FROM ANY INLINE KEYBOARD
# CALLBACKQUERY HANDLER
# THIS TEMPLATE DEFINES AND HANDLES THE CALLBACK QUERIES FROM ANY INLINE KEYBOARD

from telegram import Update
from telegram.ext import ContextTypes
import config
from utils.flight_search import next_step
from utils.decorators import check_save_alert_limit
from utils.database import DB
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
import json

# HANDLER IMPORTS
from handlers.flight_alerts import flight_alerts
Expand All @@ -20,13 +22,13 @@ async def button(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""This function is a CALLBACKQUERY HANDLER. This function will handler any callback queries from any inline keyboard"""
callback = update.callback_query
await callback.answer()
callback_data = callback.data
chat_id = update.effective_chat.id
first_name = update.effective_chat.first_name
last_name = update.effective_chat.last_name
username = update.effective_chat.username
db = DB()

match callback_data:
match callback.data:
case 'start_flight_search':
# Start flight search
context.user_data.clear()
Expand All @@ -40,57 +42,102 @@ async def button(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['currency'] = 'ZAR'
context.user_data['flight_type'] = None
await context.bot.send_message(chat_id=chat_id, reply_markup=flight_type_menu, text='🤖 Please select option 👇')
case 'oneway' | 'return':
case 'oneway' | 'return' | 'multi-city':
# sends option on flight search type
# This will delete the inline keyboard after user has clicked on option
current_menu = callback
await current_menu.delete_message()

if callback_data == 'oneway':
if callback.data == 'oneway':
context.user_data['flight_type'] = 'ONEWAY'
context.user_data['Minimum Lenth Of Stay'] = 'VOID'
context.user_data['Maximum Lenth Of Stay'] = 'VOID'
await next_step(update=update, context=context)
elif callback_data == 'return':
elif callback.data == 'return':
context.user_data['flight_type'] = 'RETURN'
await next_step(update=update, context=context)
elif callback.data == 'multi-city':
context.user_data.clear()
context.user_data['Departure Airport'] = None
context.user_data['Destination Airport'] = None
context.user_data['date_from'] = None
context.user_data['How Many Adults'] = None
context.user_data['currency'] = 'ZAR'
context.user_data['flight_type'] = 'MULTI-CITY'
context.user_data['requests'] = []
await next_step(update=update, context=context)
case 'main_menu':
# This will send the user the main menu with all available options.
await context.bot.send_message(chat_id=chat_id, text='🤖 What can i do for you? 👇', reply_markup=main_menu)
case 'track_flight':
# This will add flight data to db to be tracked.
if 'link' in context.user_data:
db = DB(file=config.DATABASE_PATH)
if context.user_data["flight_type"] == "ONEWAY" or context.user_data["flight_type"] == "RETURN":

menu = flight_result_menu(
link=context.user_data['link'], tracked=True)

Departure_Airport = context.user_data['Departure Airport']
Destination_Airport = context.user_data['Destination Airport']
Departure_Date_Earliest = context.user_data['Departure Date (Earliest)']
Departure_Date_Latest = context.user_data['Departure Date (Latest)']
Minimum_Lenth_Of_Stay = context.user_data['Minimum Lenth Of Stay']
Maximum_Lenth_Of_Stay = context.user_data['Maximum Lenth Of Stay']
How_Many_Adults = context.user_data['How Many Adults']
currency = context.user_data['currency']
flight_type = context.user_data['flight_type']
price = context.user_data['price']

menu = flight_result_menu(
link=context.user_data['link'], tracked=True)
# add user if not exist
user = db.cursor.execute(
"SELECT * FROM users WHERE chat_id = ?", (chat_id,)).fetchone()
if user != None:
db.add_flight_data(chat_id=chat_id, fly_from=Departure_Airport, fly_to=Destination_Airport, date_from=Departure_Date_Earliest, date_to=Departure_Date_Latest,
nights_from=Minimum_Lenth_Of_Stay, nights_to=Maximum_Lenth_Of_Stay, adults=How_Many_Adults, curr=currency, flight_type=flight_type, current_price=price)
db.close()
await callback.edit_message_reply_markup(reply_markup=menu)
else:
db.add_user(chat_id=chat_id, username=username,
first_name=first_name, last_name=last_name)
db.add_flight_data(chat_id=chat_id, fly_from=Departure_Airport, fly_to=Destination_Airport, date_from=Departure_Date_Earliest, date_to=Departure_Date_Latest,
nights_from=Minimum_Lenth_Of_Stay, nights_to=Maximum_Lenth_Of_Stay, adults=How_Many_Adults, curr=currency, flight_type=flight_type, current_price=price)
db.close()
await callback.edit_message_reply_markup(reply_markup=menu)
elif context.user_data["flight_type"] == "MULTI-CITY":

Departure_Airport = context.user_data['Departure Airport']
Destination_Airport = context.user_data['Destination Airport']
Departure_Date_Earliest = context.user_data['Departure Date (Earliest)']
Departure_Date_Latest = context.user_data['Departure Date (Latest)']
Minimum_Lenth_Of_Stay = context.user_data['Minimum Lenth Of Stay']
Maximum_Lenth_Of_Stay = context.user_data['Maximum Lenth Of Stay']
How_Many_Adults = context.user_data['How Many Adults']
currency = context.user_data['currency']
flight_type = context.user_data['flight_type']
price = context.user_data['price']
menu = flight_result_menu(
link=context.user_data['link'], tracked=True)

Departure_Airport = "VOID"
Destination_Airport = "VOID"
Departure_Date_Earliest = "VOID"
Departure_Date_Latest = "VOID"
Minimum_Lenth_Of_Stay = "VOID"
Maximum_Lenth_Of_Stay = "VOID"
How_Many_Adults = context.user_data['How Many Adults']
currency = context.user_data['currency']
flight_type = context.user_data['flight_type']
price = context.user_data['price']
# Convert List to string to store to DB
multi_city_req = json.dumps(context.user_data['requests'])

# add user if not exist
user = db.cursor.execute(
"SELECT * FROM users WHERE chat_id = ?", (chat_id,)).fetchone()

if user != None:
db.add_flight_data(chat_id=chat_id, fly_from=Departure_Airport, fly_to=Destination_Airport, date_from=Departure_Date_Earliest, date_to=Departure_Date_Latest,
nights_from=Minimum_Lenth_Of_Stay, nights_to=Maximum_Lenth_Of_Stay, adults=How_Many_Adults, curr=currency, flight_type=flight_type, current_price=price, multi_city_req=multi_city_req)
db.close()
await callback.edit_message_reply_markup(reply_markup=menu)
else:
db.add_user(chat_id=chat_id, username=username,
first_name=first_name, last_name=last_name)
db.add_flight_data(chat_id=chat_id, fly_from=Departure_Airport, fly_to=Destination_Airport, date_from=Departure_Date_Earliest, date_to=Departure_Date_Latest,
nights_from=Minimum_Lenth_Of_Stay, nights_to=Maximum_Lenth_Of_Stay, adults=How_Many_Adults, curr=currency, flight_type=flight_type, current_price=price, multi_city_req=multi_city_req)
db.close()
await callback.edit_message_reply_markup(reply_markup=menu)

# add user if not exist
user = db.cursor.execute(
"SELECT * FROM users WHERE chat_id = ?", (chat_id,)).fetchone()
if user != None:
db.add_flight_data(chat_id=chat_id, fly_from=Departure_Airport, fly_to=Destination_Airport, date_from=Departure_Date_Earliest, date_to=Departure_Date_Latest,
nights_from=Minimum_Lenth_Of_Stay, nights_to=Maximum_Lenth_Of_Stay, adults=How_Many_Adults, curr=currency, flight_type=flight_type, current_price=price)
db.close()
await callback.edit_message_reply_markup(reply_markup=menu)
else:
db.add_user(chat_id=chat_id, username=username,
first_name=first_name, last_name=last_name)
db.add_flight_data(chat_id=chat_id, fly_from=Departure_Airport, fly_to=Destination_Airport, date_from=Departure_Date_Earliest, date_to=Departure_Date_Latest,
nights_from=Minimum_Lenth_Of_Stay, nights_to=Maximum_Lenth_Of_Stay, adults=How_Many_Adults, curr=currency, flight_type=flight_type, current_price=price)
db.close()
await callback.edit_message_reply_markup(reply_markup=menu)
else:
menu = flight_result_menu(link=None, err=True)
await callback.edit_message_reply_markup(reply_markup=menu)
Expand All @@ -99,8 +146,22 @@ async def button(update: Update, context: ContextTypes.DEFAULT_TYPE):
await flight_alerts(update, context)
case 'del_all_FA':
# This deletes all tracked flight data from db
db = DB(file=config.DATABASE_PATH)
db.del_all_flight_data(chat_id=chat_id)
db.close()
menu = delete_all_menu(success=True)
await callback.edit_message_text(text='You have no flight alerts yet. Start a flight search and create a new flight alert.', reply_markup=menu)
case 'add_flight':
# This will add flights to the flight list in the multi-city search
if len(context.user_data['requests']) < 8:
context.user_data['Departure Airport'] = None
context.user_data['Destination Airport'] = None
context.user_data['date_from'] = None
await next_step(update=update, context=context)
else:
menu = [[InlineKeyboardButton(
'🔎 Get result', callback_data='multicity_result')]]
menu = InlineKeyboardMarkup(menu)
await context.bot.send_message(chat_id=chat_id, text='❗You cannot add anymore flights.', reply_markup=menu)
case 'multicity_result':
# This will call function to get and send out multi-city result
await next_step(update=update, context=context, MT_result=True)
Loading

0 comments on commit 8908b2d

Please sign in to comment.