Last active
August 29, 2015 14:20
-
-
Save agermanidis/98da0cbf8ecee7e0cda2 to your computer and use it in GitHub Desktop.
CrowdInstructor 0.1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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