Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 23 additions & 37 deletions handler.py → api/handler.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import json
import logging
import os
from uspqueuebot.credentials import ADMIN_CHAT_ID
import telegram
from dotenv import load_dotenv

load_dotenv()

from uspqueuebot.main import main

Expand All @@ -24,57 +26,41 @@
'body': json.dumps('Oops, something went wrong!')
}

def configure_telegram():
def webhook(event):
"""
Configures the bot with a Telegram Token.

Returns a bot instance.
Runs the Telegram webhook.
https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html
"""

TELEGRAM_TOKEN = os.environ.get('TELEGRAM_TOKEN')
TELEGRAM_TOKEN = os.getenv('TELEGRAM_TOKEN')
if not TELEGRAM_TOKEN:
logger.error('The TELEGRAM_TOKEN must be set')
raise NotImplementedError

bot = telegram.Bot(TELEGRAM_TOKEN)

return telegram.Bot(TELEGRAM_TOKEN)

def set_webhook(event, context):
"""
Sets the Telegram bot webhook.
"""

logger.info('Event: {}'.format(event))
bot = configure_telegram()
url = 'https://{}/{}/'.format(
event.get('headers').get('Host'),
event.get('requestContext').get('stage'),
)
webhook = bot.set_webhook(url)

if webhook:
return OK_RESPONSE

return ERROR_RESPONSE

def webhook(event, context):
"""
Runs the Telegram webhook.
https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html
"""
# Check if the webhook is set correctly
webhook_info = bot.get_webhook_info()
desired_webhook_url = os.getenv("VERCEL_URL") + "/api/handler"

bot = configure_telegram()
# If the current webhook URL is not the desired one, update it
if webhook_info.url != desired_webhook_url:
bot.set_webhook(url=desired_webhook_url)
logger.info(f"Webhook updated to: {desired_webhook_url}")
else:
logger.info("Webhook is already set correctly.")

if event.get('httpMethod') == 'POST' and event.get('body'):
logger.info('Message received')
body = telegram.Update.de_json(json.loads(event.get('body')), bot).to_dict()
update = telegram.Update.de_json(json.loads(event.get('body')), bot)

try:
main(bot, body)
main(bot, update)

except Exception as error:
error_message = "There is an unhandled exception, please debug immediately.\n" + error.__str__()
bot.send_message(chat_id=ADMIN_CHAT_ID, text=error_message)
bot.send_message(chat_id=os.getenv("ADMIN_CHAT_ID"), text=error_message)
logger.error(error_message)
logger.error('Event: {}'.format(body))
logger.error('Event: {}'.format(update.to_dict()))

return OK_RESPONSE

Expand Down
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
python-telegram-bot==8.1.1
pymongo
bson
datetime
python-dotenv
38 changes: 0 additions & 38 deletions uspqueuebot/constants.py

This file was deleted.

241 changes: 170 additions & 71 deletions uspqueuebot/database.py
Original file line number Diff line number Diff line change
@@ -1,83 +1,182 @@
import logging
import boto3
from pymongo import MongoClient
from bson.objectid import ObjectId
import os
import datetime
from dotenv import load_dotenv

# Logging is cool!
load_dotenv()

'''
The stucture of the Event object is as follows:
Queue number is implicit from array index + 1
{
"_id": "17826cy78ey7",
"event_name": "Concert",
"event_date": "2024-01-01", # automatically initialised using datetime.now()
"queue": [
{"hashid": "user123", "chat_id": "chat123", "username": "user1"},
{"hashid": "user456", "chat_id": "chat456", "username": "user2"},
// ... More users
]
}
'''
'''
The structure of the User object is as follows:
Queue number is implicit from array index + 1
{
"_id": "284873nuyu43",
"chat_id": "chat123",
"last_command": "/join",
}
'''
# Configure logging
logger = logging.getLogger()
if logger.handlers:
for handler in logger.handlers:
logger.removeHandler(handler)
logging.basicConfig(level=logging.INFO)

# Setting up client with AWS
client = boto3.resource("dynamodb")
TABLE_NAME = "USPMultiQueueBotTable"
table = client.Table(TABLE_NAME)
# Setup MongoDB client
client = MongoClient(os.getenv("MONGO_URL"))
db = client.USPQUEUEBOT # Change to your database name
events_collection = db.Events # Using the Events collection
users_collection = db.Users # Using the Users collection

def create_table():
"""
Creates a DynamoDB table
"""
# Function to create a new event
def create_event(event_name):
event_document = {
"event_name": event_name,
"event_date": datetime.now(),
"queue": []
}
result = events_collection.insert_one(event_document)
logger.info(f"New event created with ID: {result.inserted_id}")
return result.inserted_id

def delete_event(event_id):
events_collection.delete_one({"_id": ObjectId(event_id)})
logger.info("Event deleted.")
return

def delete_all_events():
'''
Deletes all events in the database
'''
#TODO @xinnnyeee for `purge_database_command`
pass

def view_history(date):
'''
Filter the events collection by date and return the events that are before and inclusive of the date
'''
#TODO @nhptrangg for `view_history_command`
pass

# Function to get all events, result can be displayed using an inline keyboard, with the callback data
# being the _id fields
def get_all_events():
try:
client.create_table(
TableName = TABLE_NAME,
KeySchema = [
{
"AttributeName": 'hashid',
"KeyType": "HASH"
}
],
AttributeDefinitions = [
{
"AttributeName": "hashid",
"AttributeType": "S"
}
],
ProvisionedThroughput = {
'ReadCapacityUnits': 5,
'WriteCapacityUnits': 5
}
)
logger.info("Table named " + TABLE_NAME + " was created in DynamoDB.")
except:
logger.info("Table named " + TABLE_NAME + " already exists in DynamoDB.")
return

def get_table():
"""
Retrieve all contents of the table

Returns
-------
dic
Response from scan requeston DynamoDB
"""
try:
response = table.scan()
logger.info("All entries have been retrieved and returned.")
return response
except:
create_table()
response = get_table()
return response

def insert_user(hashid, chat_id, username, queue_number):
"""
Insert a new entry into the table
"""
table.update_item(
Key = {"hashid": hashid},
UpdateExpression = "SET {} = :val1, {} =:val2, {} = :val3".format("chat_id", "username", "queue_number"),
ExpressionAttributeValues = {":val1": chat_id, ":val2": username, ":val3": queue_number}
)
logger.info("New entry successfully added into DynamoDB.")

def remove_user(hashid):
"""
Removes an entry from the table using hashid
"""

table.delete_item(
Key = {"hashid": hashid}
events = list(events_collection.find({}))
logger.info("All events have been retrieved and returned.")
return events # Returns a list of event documents
except Exception as e:
logger.error(f"An error occurred: {e}")
return []

# Function to add a user to an event queue
def add_user_to_event(event_id, hashid, chat_id, username):
user_details = {"hashid": hashid, "chat_id": chat_id, "username": username}
events_collection.update_one(
{"_id": ObjectId(event_id)},
{"$push": {"queue": user_details}}
)
logger.info("New user added to event queue.")
return

def remove_user_from_event(event_id, hashid):
'''
Returns true if the user was removed from the event queue else false
'''
result = events_collection.update_one(
{"_id": ObjectId(event_id)},
{"$pull": {"queue": {"hashid": hashid}}}
)
if result.modified_count == 1:
logger.info("User removed from event queue.")
return True
elif result.modified_count == 0:
logger.info("No user removed from event queue. User does not exist in queue.")
return False
else:
logger.exception("Multiple instances of the same user existed and have now been removed from event queue. Please check database.")
return True

# Function to get the queue of a specific event
def get_event_queue(event_id):
'''
Returns the queue of a specific event in array format
[
{"hashid": "user123", "chat_id": "chat123"},
{"hashid": "user456", "chat_id": "chat456"}
// ... More users
]
'''
event = events_collection.find_one({"_id": ObjectId(event_id)})
if event:
return event.get("queue", [])
else:
logger.error("Event not found.")
return []

def record_last_command(chat_id, command):
'''
Records the last command of a user given their chat_id and command
'''
users_collection.update_one(
{"chat_id": chat_id},
{"$set": {"last_command": command}},
upsert=True
)
logger.info("User has been successfully removed from the database.")
logger.info("Last command, {} of user {} recorded.".format(command, chat_id))
return

def get_last_command(chat_id):
'''
Gets the last command of a user given their chat_id \n
Returns:
Empty string if user not found or no last command \n
String of last command if user found and there is a last command
'''
user = users_collection.find_one({"chat_id": chat_id})
if user:
return user.get("last_command", "")
else:
logger.error("User not found.")
return ""

# # Example usage
# if __name__ == "__main__":
# # Example of creating a new event
# event_id = create_event("Concert")

# # Example of adding a user to the event queue
# add_user_to_event(event_id, "user123", "chat123", "user1", 1)

# # Example of removing a user from the event queue
# remove_user_from_event(event_id, "user123")

# # Example of retrieving the event queue
# queue = get_event_queue(event_id)
# print("Event Queue:", queue)

# # Example of recording the last command of a user
# record_last_command("chat123", "/join")

# # Example of retrieving the last command of a user
# last_command = get_last_command("chat123")
# print("Last Command:", last_command)

# # Example of deleting an event
# delete_event(event_id)
# print("Event Deleted")
Loading