示例#1
0
def processMessageCoin(game, isHeads, author):
    log.debug("Processing coin toss message: {}".format(str(isHeads)))

    utils.setGamePlayed(game)
    if isHeads == utils.coinToss():
        log.debug("User won coin toss, asking if they want to defer")
        game.status.waitingAction = Action.DEFER
        game.status.waitingOn.set(False)
        utils.setWaitingId(game, 'return')
        game.dirty = True

        if utils.isGameOvertime(game):
            questionString = "do you want to **defend** or **attack**?"
        else:
            questionString = "do you want to **receive** or **defer**?"
        message = "{}, {} won the toss, {}".format(
            utils.getCoachString(game, False), game.away.name, questionString)
        return True, utils.embedTableInMessage(
            message, utils.getActionTable(game, Action.DEFER))
    else:
        log.debug(
            "User lost coin toss, asking other team if they want to defer")
        game.status.waitingAction = Action.DEFER
        game.status.waitingOn.set(True)
        utils.setWaitingId(game, 'return')
        game.dirty = True

        if utils.isGameOvertime(game):
            questionString = "do you want to **defend** or **attack**?"
        else:
            questionString = "do you want to **receive** or **defer**?"
        message = "{}, {} won the toss, {}".format(
            utils.getCoachString(game, True), game.home.name, questionString)
        return True, utils.embedTableInMessage(
            message, utils.getActionTable(game, Action.DEFER))
示例#2
0
def processMessageCoin(game, isHeads, author):
    log.debug("Processing coin toss message: {}".format(str(isHeads)))

    if isHeads == utils.coinToss():
        log.debug("User won coin toss, asking if they want to defer")
        game['waitingAction'] = 'defer'
        game['waitingOn'] = 'home'
        game['waitingId'] = 'return'
        game['dirty'] = True

        message = "{}, {} won the toss, do you want to **receive** or **defer**?".format(
            utils.getCoachString(game, 'home'), game['home']['name'])
        return True, utils.embedTableInMessage(message, {'action': 'defer'})
    else:
        log.debug(
            "User lost coin toss, asking other team if they want to defer")
        game['waitingAction'] = 'defer'
        game['waitingOn'] = 'away'
        game['waitingId'] = 'return'
        game['dirty'] = True

        message = "{}, {} won the toss, do you want to **receive** or **defer**?".format(
            utils.getCoachString(game, 'away'), game['away']['name'])
        return True, utils.embedTableInMessage(message, {'action': 'defer'})
示例#3
0
def processMessageNewGame(body, author):
    log.debug("Processing new game message")

    users = re.findall('(?: /u/)([\w-]*)', body)
    if len(users) == 0:
        log.debug("Could not find an opponent in create game message")
        return "Please resend the message and specify an opponent"
    opponent = users[0]
    log.debug("Found opponent in message /u/{}".format(opponent))

    i, result = utils.verifyCoaches([author, opponent])

    if i == 0 and result == 'team':
        log.debug("Author does not have a team")
        return "It looks like you don't have a team, please contact the /r/FakeCollegeFootball moderators"

    if i == 0 and result == 'game':
        log.debug("Author already has a game")
        return "You're already playing a game, you can't challenge anyone else until that game finishes"

    if result == 'duplicate':
        log.debug("{} challenged themselves to a game".format(author))
        return "You can't challenge yourself to a game"

    if i == 1 and result == 'team':
        log.debug("Opponent does not have a team")
        return "It looks like your opponent doesn't have a team, please contact the /r/FakeCollegeFootball moderators"

    if i == 1 and result == 'game':
        log.debug("Opponent already has a game")
        return "/u/{} is already playing a game".format(opponent)

    authorTeam = wiki.getTeamByCoach(author)
    message = "/u/{}'s {} has challenged you to a game! Reply **accept** or **reject**.".format(
        author, authorTeam['name'])
    data = {'action': 'newgame', 'opponent': author}
    embeddedMessage = utils.embedTableInMessage(message, data)
    log.debug(
        "Sending message to /u/{} that /u/{} has challenged them to a game".
        format(opponent, author))
    if reddit.sendMessage(opponent, "Game challenge", embeddedMessage):
        return "I've let /u/{} know that you have challenged them to a game. I'll message you again when they accept".format(
            opponent)
    else:
        return "Something went wrong, I couldn't find that user"
示例#4
0
def processMessage(message):
    if isinstance(message, praw.models.Message):
        isMessage = True
        log.debug("Processing a message from /u/{} : {}".format(
            str(message.author), message.id))
    else:
        isMessage = False
        log.debug("Processing a comment from /u/{} : {}".format(
            str(message.author), message.id))

    response = None
    success = None
    updateWaiting = True
    dataTable = None

    if message.parent_id is not None and (message.parent_id.startswith("t1") or
                                          message.parent_id.startswith("t4")):
        if isMessage:
            parent = reddit.getMessage(message.parent_id[3:])
        else:
            parent = reddit.getComment(message.parent_id[3:])

        if parent is not None and str(
                parent.author).lower() == globals.ACCOUNT_NAME:
            dataTable = utils.extractTableFromMessage(parent.body)
            if dataTable is not None:
                if 'action' not in dataTable:
                    dataTable = None
                else:
                    dataTable['source'] = parent.fullname
                    log.debug(
                        "Found a valid datatable in parent message: {}".format(
                            str(dataTable)))

    body = message.body.lower()
    author = str(message.author)
    game = None
    if dataTable is not None:
        if dataTable['action'] == 'newgame' and isMessage:
            keywords = ['accept', 'reject']
            keyword = utils.findKeywordInMessage(keywords, body)
            if keyword == "accept":
                success, response = processMessageAcceptGame(
                    dataTable, str(message.author))
            elif keyword == "reject":
                success, response = processMessageRejectGame(
                    dataTable, str(message.author))
            elif keyword == 'mult':
                success = False
                response = "I found both {} in your message. Please reply with just one of them.".format(
                    ' and '.join(keywords))

        else:
            game = utils.getGameByUser(author)
            if game is not None:
                utils.setLogGameID(game['thread'], game['dataID'])

                waitingOn = utils.isGameWaitingOn(game, author,
                                                  dataTable['action'],
                                                  dataTable['source'])
                if waitingOn is not None:
                    response = waitingOn
                    success = False
                    updateWaiting = False

                elif game['errored']:
                    log.debug("Game is errored, skipping")
                    response = "This game is currently in an error state, /u/{} has been contacted to take a look".format(
                        globals.OWNER)
                    success = False
                    updateWaiting = False

                else:
                    if dataTable['action'] == 'coin' and not isMessage:
                        keywords = ['heads', 'tails']
                        keyword = utils.findKeywordInMessage(keywords, body)
                        if keyword == "heads":
                            success, response = processMessageCoin(
                                game, True, str(message.author))
                        elif keyword == "tails":
                            success, response = processMessageCoin(
                                game, False, str(message.author))
                        elif keyword == 'mult':
                            success = False
                            response = "I found both {} in your message. Please reply with just one of them.".format(
                                ' and '.join(keywords))

                    elif dataTable['action'] == 'defer' and not isMessage:
                        keywords = ['defer', 'receive']
                        keyword = utils.findKeywordInMessage(keywords, body)
                        if keyword == "defer":
                            success, response = processMessageDefer(
                                game, True, str(message.author))
                        elif keyword == "receive":
                            success, response = processMessageDefer(
                                game, False, str(message.author))
                        elif keyword == 'mult':
                            success = False
                            response = "I found both {} in your message. Please reply with just one of them.".format(
                                ' and '.join(keywords))

                    elif dataTable['action'] == 'play' and isMessage:
                        success, response = processMessageDefenseNumber(
                            game, body, str(message.author))

                    elif dataTable['action'] == 'play' and not isMessage:
                        success, response = processMessageOffensePlay(
                            game, body, str(message.author))
            else:
                log.debug("Couldn't get a game for /u/{}".format(author))
    else:
        log.debug("Parsing non-datatable message")
        if "newgame" in body and isMessage:
            response = processMessageNewGame(body, str(message.author))
        if "kick" in body and isMessage and str(
                message.author).lower() == globals.OWNER:
            response = processMessageKickGame(body)

    message.mark_read()
    if response is not None:
        if success is not None and not success and dataTable is not None and utils.extractTableFromMessage(
                response) is None:
            log.debug("Embedding datatable in reply on failure")
            response = utils.embedTableInMessage(response, dataTable)
            if updateWaiting and game is not None:
                game['waitingId'] = 'return'
        resultMessage = reddit.replyMessage(message, response)
        if game is not None and game['waitingId'] == 'return':
            game['waitingId'] = resultMessage.fullname
            game['dirty'] = True
            log.debug("Message/comment replied, now waiting on: {}".format(
                game['waitingId']))
    else:
        if isMessage:
            log.debug("Couldn't understand message")
            reddit.replyMessage(
                message,
                "I couldn't understand your message, please try again or message /u/Watchful1 if you need help."
            )

    if game is not None and game['dirty']:
        log.debug("Game is dirty, updating thread")
        utils.updateGameThread(game)
示例#5
0
def processMessageOffensePlay(game, message, author):
    log.debug("Processing offense number message")

    number, numberMessage = utils.extractPlayNumber(message)

    timeoutMessageOffense = None
    timeoutMessageDefense = None
    if message.find("timeout") > 0:
        if game['status']['timeouts'][game['status']['possession']] > 0:
            game['status']['requestedTimeout'][game['status']
                                               ['possession']] = 'requested'
        else:
            timeoutMessageOffense = "The offense requested a timeout, but they don't have any left"

    playOptions = [
        'run', 'pass', 'punt', 'field goal', 'kneel', 'spike', 'two point',
        'pat'
    ]
    playSelected = utils.findKeywordInMessage(playOptions, message)
    play = "default"
    if playSelected == "run":
        play = "run"
    elif playSelected == "pass":
        play = "pass"
    elif playSelected == "punt":
        play = "punt"
    elif playSelected == "field goal":
        play = "fieldGoal"
    elif playSelected == "kneel":
        play = "kneel"
    elif playSelected == "spike":
        play = "spike"
    elif playSelected == "two point":
        play = "twoPoint"
    elif playSelected == "pat":
        play = "pat"
    elif playSelected == "mult":
        log.debug("Found multiple plays")
        return False, "I found multiple plays in your message. Please repost it with just the play and number."
    else:
        log.debug("Didn't find any plays")
        return False, "I couldn't find a play in your message"

    success, resultMessage = state.executePlay(game, play, number,
                                               numberMessage)

    if game['status']['requestedTimeout'][game['status']
                                          ['possession']] == 'used':
        timeoutMessageOffense = "The offense is charged a timeout"
    elif game['status']['requestedTimeout'][game['status']
                                            ['possession']] == 'requested':
        timeoutMessageOffense = "The offense requested a timeout, but it was not used"
    game['status']['requestedTimeout'][game['status']['possession']] = 'none'

    if game['status']['requestedTimeout'][utils.reverseHomeAway(
            game['status']['possession'])] == 'used':
        timeoutMessageDefense = "The defense is charged a timeout"
    elif game['status']['requestedTimeout'][utils.reverseHomeAway(
            game['status']['possession'])] == 'requested':
        timeoutMessageDefense = "The defense requested a timeout, but it was not used"
    game['status']['requestedTimeout'][utils.reverseHomeAway(
        game['status']['possession'])] = 'none'

    result = [resultMessage]
    if timeoutMessageOffense is not None:
        result.append(timeoutMessageOffense)
    if timeoutMessageDefense is not None:
        result.append(timeoutMessageDefense)

    game['waitingOn'] = utils.reverseHomeAway(game['waitingOn'])
    game['dirty'] = True
    if game['waitingAction'] == 'play':
        utils.sendDefensiveNumberMessage(game)

    return success, utils.embedTableInMessage(
        '\n\n'.join(result), {'action': game['waitingAction']})
示例#6
0
def processMessage(message):
	## Determine if comment or dm

	if isinstance(message, praw.models.Message):
		isMessage = True
	else:
		isMessage = False
	log.debug("Processing a comment from /u/{} with id {} and body {}".format(str(message.author), message.id,message.body))

	response = None
	success = None
	updateWaiting = True
	dataTable = None
	resultMessage = None
	tipped = False

	if message.parent_id is not None and (message.parent_id.startswith("t1") or message.parent_id.startswith("t4")):
		if isMessage:
			parent = reddit.getMessage(message.parent_id[3:])
		else:
			parent = reddit.getComment(message.parent_id[3:])

		if parent is not None and str(parent.author).lower() == globals.ACCOUNT_NAME:

			dataTable = utils.extractTableFromMessage(parent.body)
			if dataTable is not None:
				if 'action' not in dataTable:
					dataTable = None
				else:
					dataTable['source'] = parent.fullname
					log.debug("Found a valid datatable in parent message: {}".format(str(dataTable)))

	body = message.body.lower()
	author = str(message.author)
	game = None
	if dataTable is not None:
		game = utils.getGameByUser(author)
		log.debug('game is {}'.format(game))
		if game is not None:
			utils.setLogGameID(game['thread'], game['dataID'])
			print('the action is {}'.format(dataTable['action']))

			waitingOn = utils.isGameWaitingOn(game, author, dataTable['action'], dataTable['source'])
			log.debug("waitingOn is {}".format(waitingOn))
			if waitingOn is not None:
				response = waitingOn
				success = False
				updateWaiting = False

			elif game['errored']:
				log.debug("Game is errored, skipping")
				response = "This game is currently in an error state, /u/{} has been contacted to take a look".format(globals.OWNER)
				success = False
				updateWaiting = False

			else:
				log.debug('Trying to process tip, offense, or defense')
				##this is where we start the game basically
				if dataTable['action'] == 'tip' and isMessage:
					log.debug('About to process tip message from {}'.format(str(message.author)))
					success, response = processMessageTip(game, message)
					if success:
						game['dirty'] = True
					log.debug("The tip message's success was {} and the message's content reads {}".format(success, response))


				elif dataTable['action'] == 'play' and isMessage:
					success, response = processMessageDefenseNumber(game, body, str(message.author))

				elif dataTable['action'] == 'play' and not isMessage:
					pos = game['status']['possession']
					success, response = processMessageOffensePlay(game, body, str(message.author))
					utils.sendPlayResultMessage(utils.reverseHomeAway(pos),game, response)

		else:
			log.debug("Couldn't get a game for /u/{}".format(author))
	else:
		log.debug("Parsing non-datatable message with body {} from author {}".format(body, str(message.author).lower()))
		if "newgame" in body and isMessage:
			response = processMessageNewGame(message.body, str(message.author))
		if "kick" in body and isMessage and str(message.author).lower() == globals.OWNER:
			response = processMessageKickGame(message.body)
		if "pause" in body and isMessage and str(message.author).lower() in wiki.admins:
			response = processMessagePauseGame(message.body)
		if "abandon" in body and isMessage and str(message.author).lower() in wiki.admins:
			log.debug('Going to abandon a game')
			response = processMessageAbandonGame(message.body)
		if "refresh" in body and isMessage and str(message.author).lower() in wiki.admins:
			response = utils.processRefresh()
	message.mark_read()
	if response is not None:
		if success is not None and not success and dataTable is not None and utils.extractTableFromMessage(response) is None:
			log.debug("Embedding datatable in reply on failure")
			response = utils.embedTableInMessage(response, dataTable)
			if updateWaiting and game is not None:
				game['waitingId'] = 'return'
		log.debug("About to send reply Message")


		if game is not None:
			log.debug("game is not none")
			if game['tip']['justTipped']:
				log.debug('sending the winning tip message to the game thread')
				##send the tip update to the game thread instead of to the last
				## person who sent a tip number
				resultMessage = utils.sendGameCommentAfterTip(game, response)
				game['tip']['justTipped'] =  False
				game['tip']['tipped'] = True
			else:
				resultMessage = reddit.replyMessage(message, response)
		else:
			resultMessage = reddit.replyMessage(message, response)
		log.debug("result of sending reply message was {}".format(resultMessage))
		if resultMessage is None:
			log.warning("Could not send message")
		elif game is not None and game['waitingId'] == 'return':
			game['waitingId'] = resultMessage.fullname
			log.debug('About to send. WaitingID is {} when waitingID was return'.format(game['waitingId']))
			game['dirty'] = True
			log.debug("Message/comment replied, now waiting on: {}".format(game['waitingId']))
	else:
		if isMessage:
			log.debug("Couldn't understand message")
			resultMessage = reddit.replyMessage(message,
								"Could not understand you. Please try again or message /u/zenverak if you need help.")
		if resultMessage is None:
			log.warning("Could not send message")
	if game is not None:
		if game['status']['sendDef']:
			utils.sendDefensiveNumberMessage(game)
			game['status']['sendDef'] = False
	if game is not None and game['dirty']:
		log.debug("Game is dirty, updating thread")
		utils.updateGameThread(game)
示例#7
0
def processMessageOffensePlay(game, message, author):
	##set some information for later.
	current = game['status']['possession']
	other = utils.reverseHomeAway(current)
	game['play']['ocoach'] = game[current]['coaches'][0]
	game['play']['dcoach'] = game[other]['coaches'][0]
	game['play']['playMessage'] = message

	log.debug("Processing offense number message")

	if game['status']['ifoul']:
		numberMessage = ''
		number = 0
	else:
		number, numberMessage = utils.extractPlayNumber(message)


	playOptions = ['chew', 'average', 'push', 'regular']
	if not game['status']['ifoul']:
		playSelected = utils.findKeywordInMessage(playOptions, message)
		play = "default"
		if game['status']['free']:
			play = 'free'
		elif playSelected == "chew":
			play = "chew"
		elif playSelected == "average":
			play = "average"
		elif playSelected == "push":
			play = "push"
		elif playSelected == "mult":
			log.debug("Found multiple plays")
			return False, "I found multiple plays in your message. Please repost it with just the play and number."
		else:
			log.debug("Didn't find any plays")
			return False, "I couldn't find a play in your message. Please reply to this one with a play and a number."
	else:
		play = 'foul'
		numberMessage = 'intentional foul'
		playSelected = 'foul'
	game['play']['playType'] = play

	success, resultMessage = state.executePlay(game, play, number, numberMessage)



	result = [resultMessage]
	if playSelected != 'default' and success:
		state.setWaitingOn(game)

		game['dirty'] = True
	if game['waitingAction'] == 'play' and playSelected != 'default' and success:
		log.debug('going to set the play data up, save it, then remove it')
		utils.insertPlayData(game)
		game['status']['sendDef'] = True
	elif game['waitingAction'] == 'overtime':
		log.debug("Starting overtime, posting coin toss comment")
		message = "Overtime has started! {}, you're away, call **heads** or **tails** in the air.".format(
			utils.getCoachString(game, 'away'))
		comment = utils.sendGameComment(game, message, {'action': 'tip'})
		game['waitingId'] = comment.fullname
		game['waitingAction'] = 'tip'

	return success, utils.embedTableInMessage('\n\n'.join(result), {'action': game['waitingAction']})
示例#8
0
def processMessage(message, force=False):
    if isinstance(message, praw.models.Message):
        isMessage = True
        log.debug("Processing a message from /u/{} : {}".format(
            str(message.author), message.id))
    else:
        isMessage = False
        log.debug("Processing a comment from /u/{} : {}".format(
            str(message.author), message.id))

    response = None
    success = None
    updateWaiting = True
    dataTable = None

    if message.parent_id is not None and (message.parent_id.startswith("t1") or
                                          message.parent_id.startswith("t4")):
        if isMessage:
            parent = reddit.getMessage(message.parent_id[3:])
        else:
            parent = reddit.getComment(message.parent_id[3:])

        if parent is not None and str(
                parent.author).lower() == globals.ACCOUNT_NAME:
            dataTable = utils.extractTableFromMessage(parent.body)
            if dataTable is not None:
                if 'action' not in dataTable or 'thread' not in dataTable:
                    dataTable = None
                else:
                    dataTable['source'] = parent.fullname
                    log.debug(
                        "Found a valid datatable in parent message: {}".format(
                            str(dataTable)))

    body = message.body.lower()
    author = str(message.author)
    game = None
    appendMessageId = False
    if dataTable is not None:
        game = index.reloadAndReturn(dataTable['thread'])
        if game is not None:
            utils.cycleStatus(game, message.fullname)
            utils.setLogGameID(game.thread, game)

            waitingOn = utils.isGameWaitingOn(game, author,
                                              dataTable['action'],
                                              dataTable['source'], force)
            if waitingOn is not None:
                response = waitingOn
                success = False
                updateWaiting = False

            elif game.errored:
                log.debug("Game is errored, skipping")
                response = "This game is currently in an error state, /u/{} has been contacted to take a look".format(
                    globals.OWNER)
                success = False
                updateWaiting = False

            else:
                if dataTable['action'] == Action.COIN and not isMessage:
                    keywords = ["heads", "tails"]
                    keyword = utils.findKeywordInMessage(keywords, body)
                    if keyword == "heads":
                        success, response = processMessageCoin(
                            game, True, author)
                    elif keyword == "tails":
                        success, response = processMessageCoin(
                            game, False, author)
                    elif keyword == "mult":
                        success = False
                        response = "I found both {} in your message. Please reply with just one of them.".format(
                            ' and '.join(keywords))

                elif dataTable['action'] == Action.DEFER and not isMessage:
                    if utils.isGameOvertime(game):
                        keywords = ["defend", "attack"]
                    else:
                        keywords = ["defer", "receive"]
                    keyword = utils.findKeywordInMessage(keywords, body)
                    if keyword == "defer" or keyword == "defend":
                        success, response = processMessageDefer(
                            game, True, author)
                    elif keyword == "receive" or keyword == "attack":
                        success, response = processMessageDefer(
                            game, False, author)
                    elif keyword == "mult":
                        success = False
                        response = "I found both {} in your message. Please reply with just one of them.".format(
                            ' and '.join(keywords))

                elif dataTable['action'] in classes.playActions and isMessage:
                    success, response = processMessageDefenseNumber(
                        game, body, author)
                    appendMessageId = not success

                elif dataTable[
                        'action'] in classes.playActions and not isMessage:
                    success, response = processMessageOffensePlay(
                        game, body, author)
        else:
            log.debug("Couldn't get a game for /u/{}".format(author))
    else:
        log.debug("Parsing non-datatable message")
        if isMessage and str(message.author).lower() in wiki.admins:
            if body.startswith("newgame"):
                response = processMessageNewGame(message.body,
                                                 str(message.author))
            elif body.startswith("kick"):
                response = processMessageKickGame(message.body)
            elif body.startswith("pause"):
                response = processMessagePauseGame(message.body)
            elif body.startswith("abandon"):
                response = processMessageAbandonGame(message.body)
            elif body.startswith("status"):
                response = processMessageGameStatus(message.body)
            elif body.startswith("reindex"):
                response = processMessageReindex(message.body)
            elif body.startswith("chew"):
                response = processMessageDefaultChew(message.body)

    message.mark_read()
    if response is not None:
        if success is not None and not success and dataTable is not None and utils.extractTableFromMessage(
                response) is None:
            log.debug("Embedding datatable in reply on failure")
            response = utils.embedTableInMessage(response, dataTable)
            if updateWaiting and game is not None:
                if appendMessageId:
                    utils.addWaitingId(game, 'return')
                else:
                    utils.setWaitingId(game, 'return')
        resultMessage = reddit.replyMessage(message, response)
        if resultMessage is None:
            log.warning("Could not send message")

        elif game is not None and 'return' in game.status.waitingId:
            if appendMessageId:
                utils.clearReturnWaitingId(game)
                utils.addWaitingId(game, resultMessage.fullname)
            else:
                utils.setWaitingId(game, resultMessage.fullname)
            game.dirty = True
            log.debug("Message/comment replied, now waiting on: {}".format(
                game.status.waitingId))
    else:
        if isMessage:
            log.debug("Couldn't understand message")
            resultMessage = reddit.replyMessage(
                message,
                "I couldn't understand your message, please try again or message /u/Watchful1 if you need help."
            )
            if resultMessage is None:
                log.warning("Could not send message")

    if game is not None and game.dirty:
        log.debug("Game is dirty, updating thread")
        utils.updateGameThread(game)
示例#9
0
def processMessageOffensePlay(game, message, author):
    log.debug("Processing offense number message")

    timeoutMessageOffense = None
    timeoutMessageDefense = None
    if "timeout" in message:
        if game.status.state(game.status.possession).timeouts > 0:
            game.status.state(game.status.possession
                              ).requestedTimeout = TimeoutOption.REQUESTED
        else:
            timeoutMessageOffense = "The offense requested a timeout, but they don't have any left"

    if game.forceChew:
        timeOption = TimeOption.CHEW
    else:
        timeOption = TimeOption.NORMAL
    if any(x in message
           for x in ['chew the clock', 'milk the clock', 'chew clock']):
        timeOption = TimeOption.CHEW
    elif any(x in message for x in ['hurry up', 'no huddle', 'no-huddle']):
        timeOption = TimeOption.HURRY
    elif any(x in message for x in ['normal']):
        timeOption = TimeOption.NORMAL

    normalOptions = ["run", "pass", "punt", "field goal", "kneel", "spike"]
    conversionOptions = ["two point", "pat"]
    kickoffOptions = ["normal", "squib", "onside"]
    if game.status.waitingAction == Action.PLAY:
        playSelected = utils.findKeywordInMessage(normalOptions, message)
    elif game.status.waitingAction == Action.CONVERSION:
        playSelected = utils.findKeywordInMessage(conversionOptions, message)
    elif game.status.waitingAction == Action.KICKOFF:
        playSelected = utils.findKeywordInMessage(kickoffOptions, message)
    else:
        return False, "Something went wrong, invalid waiting action: {}".format(
            game.status.waitingAction)

    if playSelected == "run":
        play = Play.RUN
    elif playSelected == "pass":
        play = Play.PASS
    elif playSelected == "punt":
        play = Play.PUNT
    elif playSelected == "field goal":
        play = Play.FIELD_GOAL
    elif playSelected == "kneel":
        play = Play.KNEEL
    elif playSelected == "spike":
        play = Play.SPIKE
    elif playSelected == "two point":
        play = Play.TWO_POINT
    elif playSelected == "pat":
        play = Play.PAT
    elif playSelected == "normal":
        play = Play.KICKOFF_NORMAL
    elif playSelected == "squib":
        play = Play.KICKOFF_SQUIB
    elif playSelected == "onside":
        play = Play.KICKOFF_ONSIDE
    elif playSelected == "mult":
        log.debug("Found multiple plays")
        return False, "I found multiple plays in your message. Please repost it with just the play and number."
    else:
        log.debug("Didn't find any plays")
        return False, "I couldn't find a play in your message"

    number, numberMessage = utils.extractPlayNumber(message)
    if play not in classes.timePlays and number == -1:
        log.debug(
            "Trying to execute a {} play, but didn't have a number".format(
                play))
        return False, numberMessage

    success, resultMessage = state.executePlay(game, play, number, timeOption)

    if game.status.state(
            game.status.possession).requestedTimeout == TimeoutOption.USED:
        timeoutMessageOffense = "The offense is charged a timeout"
    elif game.status.state(game.status.possession
                           ).requestedTimeout == TimeoutOption.REQUESTED:
        timeoutMessageOffense = "The offense requested a timeout, but it was not used"
    game.status.state(
        game.status.possession).requestedTimeout = TimeoutOption.NONE

    if game.status.state(game.status.possession.negate()
                         ).requestedTimeout == TimeoutOption.USED:
        timeoutMessageDefense = "The defense is charged a timeout"
    elif game.status.state(game.status.possession.negate()
                           ).requestedTimeout == TimeoutOption.REQUESTED:
        timeoutMessageDefense = "The defense requested a timeout, but it was not used"
    game.status.state(
        game.status.possession.negate()).requestedTimeout = TimeoutOption.NONE

    result = [resultMessage]
    if timeoutMessageOffense is not None:
        result.append(timeoutMessageOffense)
    if timeoutMessageDefense is not None:
        result.append(timeoutMessageDefense)

    if not game.status.timeRunoff:
        result.append("The clock is stopped.")

    game.status.waitingOn.reverse()
    game.dirty = True
    utils.setGamePlayed(game)
    if game.status.waitingAction in classes.playActions:
        utils.sendDefensiveNumberMessage(game)
    elif game.status.waitingAction == Action.OVERTIME:
        log.debug("Starting overtime, posting coin toss comment")
        message = "Overtime has started! {}, you're away, call **heads** or **tails** in the air.".format(
            utils.getCoachString(game, False))
        comment = utils.sendGameComment(
            game, message, utils.getActionTable(game, Action.COIN))
        utils.setWaitingId(game, comment.fullname)
        game.status.waitingAction = Action.COIN
        game.status.waitingOn = classes.HomeAway(False)

    return success, utils.embedTableInMessage(
        '\n\n'.join(result),
        utils.getActionTable(game, game.status.waitingAction))