Skip to content

Instantly share code, notes, and snippets.

@agermanidis
Last active August 29, 2015 14:20
Show Gist options
  • Save agermanidis/98da0cbf8ecee7e0cda2 to your computer and use it in GitHub Desktop.
Save agermanidis/98da0cbf8ecee7e0cda2 to your computer and use it in GitHub Desktop.
CrowdInstructor 0.1
import pymongo, twilio.twiml
from flask import Flask, jsonify, render_template, request, abort
from operator import itemgetter
from twilio.rest import TwilioRestClient
account_sid = "your twilio account sid"
auth_token = "your twilio auth token"
client = TwilioRestClient(account_sid, auth_token)
CROWDINSTRUCTOR_NUMBER = "your twilio number"
app = Flask(__name__)
client = pymongo.MongoClient()
db = client.hac
instructions = db.instructions
users = db.users
logs = db.logs
reserved_keywords = [
'register',
'instruct',
'everyone',
'as',
'to',
'who',
'is',
'here'
]
commands = [
['Register as [your first name]', "Register in CrowdInstructor as [your first name]."],
['Instruct [list of people] to [action]', 'Instruct one or more people (seperated by comma) to perform an action, for example: "Instruct John, Mary to jump".'],
['Instruct everyone to [action]', 'Instruct everyone to peform an action.'],
['Who is here', "List all people participating."]
]
NOT_REGISTERED = 'You are not registered. To register, reply with "Register as [your first name]."'
ALREADY_REGISTERED = 'You are already registered!'
JUST_REGISTERED = 'Welcome to CrowdInstructor v0.1, {name}! To make things easier, save this number as "CrowdInstructor" in your contacts. To see the available commands, just say "Help".'
DID_UNDERSTAND_COMMAND = 'Sorry, I do not understand this command. To see available commands, just say "Help"'
USER_DOES_NOT_EXIST = "Sorry, your instruction failed because I could not find {name} in the list of registered users."
NEW_INSTRUCTION = "New instruction to you{detail} from {name}: {instruction}"
INSTRUCTION_SUCCESS = "Your instruction was sent to {names}. Fingers crossed."
def join_names(names):
if len(names) == 1: return names[0]
else: return ', '.join(names[:-1]) + " and " + names[-1]
def list_users():
return map(itemgetter('name'), list(users.find()))
def get_user_with_phone(phone):
return users.find_one({'phone': phone})
def get_phone_for_name(name):
return users.find_one({'name': name})['phone']
def submit_instruction(instruction):
return instructions.insert(instruction)
def check_users_exist(names):
registered_names = list_users()
for name in names:
if not (name in registered_names):
return False, USER_DOES_NOT_EXIST.format(name=name)
return True, None
def validate_name(name):
if name in reserved_keywords: return False, "Your name must not be one of the reserved words, which are: " + ', '.join(reserved_keywords)
if name in list_users(): return False, "Your name has already been taken, sorry! Try a different one."
return True, None
def help_text():
ret = []
ret.append("Available commands:")
for command, desc in commands:
ret.append("")
ret.append(command)
return '\n'.join(ret)
def register_user(name, phone):
return users.insert(dict(name=name, phone=phone))
def log_received(phone, message):
logs.insert(dict(kind='received', phone=phone, message=message))
def log_sent(phone, message):
logs.insert(dict(kind='sent', phone=phone, message=message))
def detail_text(count):
if count == 1:
return ""
elif count == 2:
return " (and 1 other)"
else:
return " (and %d others)" % count-1
def send_message(phone, msg):
log_sent(phone, message)
client.messages.create(from_=CROWDINSTRUCTOR_NUMBER, to=phone, body=msg)
def respond(text):
resp = twilio.twiml.Response()
resp.message(text)
return str(resp)
def execute_command(phone, message):
log_received(phone, message)
cmd = message.split()[0]
rest = message.split()[1:]
cmd = cmd.lower()
user = get_user_with_phone(phone)
if cmd == 'register':
if user: return ALREADY_REGISTERED
if rest[0] != 'as': return DID_UNDERSTAND_COMMAND
name = rest[1].capitalize()
success, error_msg = validate_name(name)
if not success: return msg
register_user(name, phone)
return JUST_REGISTERED.format(name=name)
if cmd == 'help':
return help_text()
if cmd == 'who':
if not user: return NOT_REGISTERED
return '\n'.join(list_users())
if cmd == 'instruct' and user:
if not user: return NOT_REGISTERED
try:
to_index = rest.index("to")
except ValueError:
return DID_UNDERSTAND_COMMAND
names = map(lambda name: name.strip(",").capitalize(), rest[:to_index])
if len(names) == 0 and names[0] == 'everyone':
names = list_users()
if user['name'] in names:
names.remove(user['name'])
success, error_msg = check_users_exist(name)
if not success: return error_msg
phones = map(get_phone_for_name, names)
instruction = ' '.join(rest[to_index+1:])
message = NEW_INSTRUCTION.format(detail = detail_text(len(names)),
name = user['name'],
instruction = instruction)
for phone in phones:
send_message(phone, message)
success_msg = INSTRUCTION_SUCCESS.format(names=join_names(names))
send_message(user['phone'], success_msg)
return DID_UNDERSTAND_COMMAND
@app.route("/", methods=['GET', 'POST'])
def command():
message = request.values.get('Body', None)
phone = request.values.get('From', None)
if not message or not phone: abort(404)
response = execute_command(phone, message)
return respond(response)
if __name__ == '__main__':
app.debug = True
app.run(host='0.0.0.0', port = 8080)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment