Automation: Building a Reservation/Waiting List Text-Bot For My Weekly Poker Games

Pranav Ahluwalia
7 min readOct 31, 2019

--

Problem:

Every week, I host a poker game at my apartment. I usually have anywhere between 5 to 8 players at my table. The issue? People often decide to come, or cancel last minute and I am constantly texting people back and forth the day of the game to make sure I have a full table.

I decided to build a bot that could handle inviting my friends to my apartment, notify me of who is coming, and place extra players on waiting lists in case people cancel last minute. In the event that there is a cancellation, the bot will take the first person off the waiting list and re-invite them.

Furthermore: I made use of the Iphone’s automation features to send commands to the bot such as adding and removing people from the guest list, and requesting a list of attendees from the bot.

Technologies Used:

  • Python
  • Twilio: Messaging service that allows you to send texts via python
  • Flask: web server framework for receiving text messages and handling responses.
  • Iphone: I used the new shortcuts feature in my iphone to trigger commands on the Twilio server

Setting Up Twilio:

The first task is to go to twilio.com and create an account. You’ll have to request a number from the website. Once you have completed those steps navigate to the console and copy your Account SID and ‘Auth Token’.

Your next step will be to install the Twilio helper package in python. This can be done by simply navigating to your terminal or command prompt and typing ‘python’ and then

pip install twilio

or

easy_install twilio

Sending A Message Via Python:

Before we get our hands too dirty, let’s make sure we’re able to send a message using Twilio.

#Imports
from twilio.rest import Client
import csv
from flask import Flask, request, redirect
from twilio import twiml
from twilio.twiml.messaging_response import Message, MessagingResponse
# Your Account SID from twilio.com/console
account_sid = "YourSID"
# Your Auth Token from twilio.com/console
auth_token = "Your Auth Token"

client = Client(account_sid, auth_token)
def sendMsg(msg,number):
message = client.messages.create(
to=number,
from_="Twilio Number You Registered",
body=msg)

print(message.sid)
sendMsg("Test Message","Your Number")

Note: you’ll have to confirm your number in Twilio before it will allow you to send a message.

Intercepting A Message and Handling Responses:

Now that we have the capability to send a message, let’s try intercepting one.

app = Flask('PokerMessenger')

@app.route("/", methods=['GET', 'POST'])
def sms_reply():
# Get the message the user sent our Twilio number
body = request.values.get('Body', None)
number = request.values.get('From',None)
print("Message Recieved From")
print(number)
"""Respond to incoming calls with a simple text message."""
# Start our TwiML response
resp = MessagingResponse()


# Add a message
resp.message(handleMsg(body,number))

return str(resp)


if __name__ == "__main__":
app.run(debug=True)

Adding this code will allow you to receive messages. Note: you will have to write your own “handleMsg(body,number)” function which will handle the control flow of how your bot will respond. This is what mine looks like:

##Handles Responses and adjusts the invite List accordingly
def handleMsg(msg,number):
if "/addGuest" in msg:
addContact(msg)
elif msg == "/Send:92115":
inviteAllContacts()
elif msg == "/requestWList":
sendWaitingList()
elif msg == "/requestCList":
if number == Admin:
sendGuestList()
elif msg == "Confirm" or msg == "confirm":
if isTableFull(maxTable):
waitingList.append(number)
outputGuests()
return "You have been added to the waiting list. There are " + str(len(waitingList) - 1) + " people ahead of you"
else:
handleConfirmation(number)
outputGuests()
return "Your seat has been reserved. Text 'No' to undo."
elif msg == "No" or msg == "no":
handleCancellation(number)
outputGuests()
return "Thank you for responding. Hope to see you next time!"
else:
return "Error: Invalid Response"

The first few conditions are commands that can only be processed if a text is sent from my number. This allows me to initiate the bot to send invites to my guest-list, or notify me of relevant information. The ‘Admin’ variable is a global variable with my number stored.

Below is the code to all my helper functions included in the handleMsg function:

###Prints the confirmed and waiting List
def outputGuests():
print("Confirmed List:")
print(confirmedList)
print("Waiting List:")
print(waitingList)

##Sends an invite message to all contacts
def inviteAllContacts():
for k in NumberToName.keys():
msg = "You have been invited to Pran's Poker Game this Thursday 7:00PM at 43 Westland Avenue." \
"Reply: 'Confirm' to reserve your seat, or Reply: 'No' if you won't make it."
sendMsg(msg,k)

##

##Sends a message to a number provided it is included on the contact list
def sendMsg(msg,number):
if NumberToName.has_key(number):
message = client.messages.create(
to=number,
from_="+19543629753",
body=msg)

print(message.sid)
else:
print("Number not included in contact List")
##

##Sends the admin the attendee list
def sendGuestList():
sendMsg(confirmedList,Admin)
##

##Sends admin the waiting List
def sendWaitingList():
sendMsg(confirmedList,Admin)
##

##checks if the table is full
def isTableFull(max):
return len(confirmedList) >= max
####

##Removes atendee from list and adds first from waiting list to confirmed
def handleCancellation(number):
print("Cancelling Number ")
print(number)
outputGuests()
if containsNumber(number,confirmedList):
removegrepl(number,confirmedList)
if len(waitingList) > 0:
sendMsg("You have been taken off the Waiting List! Reply: 'Confirm' to reserve your seat!",waitingList.pop(0))

##If the attendee is not yet in the confirmed list, add him
def handleConfirmation(number):
if not containsNumber(number,confirmedList):
confirmedList.append(number)

##Checks in a thorough manner if the list contains the suppliedNumber
def containsNumber(number,list):
t = False
for i in list:
if number in i:
return not t;

###Removes a string that contains the supplied string froma list
def removegrepl(number, list):
for i in list:
if number in i:
list.remove(i)

Maintaining The Contact List:

For my guest list, I decided to port my contacts to a csv. I then wrote a function that reads the csv and adds every contact to a dictionary containing their number and name.

maxTable = 1
waitingList = []
confirmedList = ["MyPhoneNumber"]
NumberToName = {'MyPhoneNumber' : "Pranav Ahluwalia"}
Admin = "MyPhoneNumber"

##Loads all names and numbers from guests.csv into dictionary
def loadContacts():
sheet = csv.reader('guests.csv')
count = 0
for row in sheet:
if count > 1:
NumberToName[row[2]] = row[0] + " " + row[1]
##

I then used the following function to send out invites:

##Sends an invite message to all contacts
def inviteAllContacts():
for k in NumberToName.keys():
msg = "You have been invited to Pran's Poker Game this Thursday 7:00PM at ______" \
"Reply: 'Confirm' to reserve your seat, or Reply: 'No' if you won't make it."
sendMsg(msg,k)

##

Handling Waiting List and Confirmations:

Below are the functions used for the following operations

  • Handling a cancellation
  • Handling a confirmation
##Removes atendee from list and adds first from waiting list to ##confirmed
def handleCancellation(number):
print("Cancelling Number ")
print(number)
outputGuests()
if containsNumber(number,confirmedList):
removegrepl(number,confirmedList)
if len(waitingList) > 0:
sendMsg("You have been taken off the Waiting List! Reply: 'Confirm' to reserve your seat!",waitingList.pop(0))

##If the attendee is not yet in the confirmed list, add him
def handleConfirmation(number):
if not containsNumber(number,confirmedList):
confirmedList.append(number)

##Checks in a thorough manner if the list contains the ##suppliedNumber
def containsNumber(number,list):
t = False
for i in list:
if number in i:
return not t;

###Removes a string that contains the supplied string from a list
def removegrepl(number, list):
for i in list:
if number in i:
list.remove(i)

Notice that in the handleCancellation(number) function, another invite is sent to the first person to be taken of the waiting list. Also: it is worth mentioning that I had to define the function “removegrepl(number, list)” which removes any number containing the supplied number as a substring.

This is a key feature as there will often be discrepancies with regards to how the flask server is reading an atendees number and how I recorded it into a spreadsheet.

Triggering Scripts With Iphone:

Now for the fun part. I made use of the Shortcuts app included in the new IOS update to send an automated trigger to my flask server. This way, I can press a button on my iphone and it will automatically send the desired commands to my bot.

Shortcut that sends command to add a contact to the Guest List

The possibilities are virtually endless as you can add any number of control commands to your server.

Wrapping It Up:

Using Python, Twilio, and Flask, we created an end to end application that allows for both user access as well as server administration through text-based keyword commands. The specific use-case has automated the entire process of having to invite people to my poker game, deal with re-inviting others when certain players cancel, and notifying extra players of how many people are ahead of them on the waiting list. Additionally, my server is able to notify me of my guest list and gives me the ability to modify the guest list itself all from my phone.

--

--