public override async Task <SkillResponse> ProcessMessageAsync(SkillRequest skill, ILambdaContext context) { try { // load adventure from S3 string source; try { using (var s3Response = await _s3Client.GetObjectAsync(_adventureFileBucket, _adventureFileKey)) { var memory = new MemoryStream(); await s3Response.ResponseStream.CopyToAsync(memory); source = Encoding.UTF8.GetString(memory.ToArray()); } } catch (AmazonS3Exception e) when(e.StatusCode == HttpStatusCode.NotFound) { throw new Exception($"unable to load file from 's3://{_adventureFileBucket}/{_adventureFileKey}'"); } // process adventure file var adventure = Adventure.Parse(source, Path.GetExtension(_adventureFileKey)); // restore player object from session var state = await RestoreAdventureState(adventure, skill.Session); var engine = new AdventureEngine(adventure, state); LogInfo($"player status: {state.Status}"); // decode skill request IOutputSpeech response = null; IOutputSpeech reprompt = null; switch (skill.Request) { // skill was activated without an intent case LaunchRequest launch: LogInfo("launch"); // kick off the adventure! if (state.Status == AdventureStatus.New) { state.Status = AdventureStatus.InProgress; response = Do(engine, AdventureCommandType.Restart, new XElement("speak", new XElement("p", new XText(PROMPT_WELCOME)))); } else { response = Do(engine, AdventureCommandType.Describe, new XElement("speak", new XElement("p", new XText(PROMPT_WELCOME_BACK)))); } reprompt = Do(engine, AdventureCommandType.Help); break; // skill was activated with an intent case IntentRequest intent: var isAdventureCommand = Enum.TryParse(intent.Intent.Name, true, out AdventureCommandType command); // check if the intent is an adventure intent if (isAdventureCommand) { LogInfo($"adventure intent ({intent.Intent.Name})"); response = Do(engine, command); reprompt = Do(engine, AdventureCommandType.Help); } else { // built-in intents switch (intent.Intent.Name) { case BuiltInIntent.Help: LogInfo($"built-in help intent ({intent.Intent.Name})"); response = Do(engine, AdventureCommandType.Help); reprompt = Do(engine, AdventureCommandType.Help); break; case BuiltInIntent.Stop: case BuiltInIntent.Cancel: LogInfo($"built-in stop/cancel intent ({intent.Intent.Name})"); response = Do(engine, AdventureCommandType.Quit); break; default: // unknown & unsupported intents LogWarn("intent not recognized"); response = new PlainTextOutputSpeech { Text = PROMPT_MISUNDERSTOOD }; reprompt = Do(engine, AdventureCommandType.Help); break; } } break; // skill session ended (no response expected) case SessionEndedRequest ended: LogInfo("session ended"); return(ResponseBuilder.Empty()); // exception reported on previous response (no response expected) case SystemExceptionRequest error: LogWarn($"skill request exception: {JsonConvert.SerializeObject(skill)}"); return(ResponseBuilder.Empty()); // unknown skill received (no response expected) default: LogWarn($"unrecognized skill request: {JsonConvert.SerializeObject(skill)}"); return(ResponseBuilder.Empty()); } // check if the player reached the end var roomCompleteCounter = 0; Dictionary <string, string> result = new Dictionary <string, string>(); if (adventure.Places[state.CurrentPlaceId].Finished) { // TODO: send completion notification with player statistics state.RoomCount++; LogInfo("Current place at room end: " + state.CurrentPlaceId); LogInfo("STATUS at room end: " + state.Status); LogInfo("TIME at room complete: " + state.StartTime); LogInfo("ROOM COUNT at end: " + state.RoomCount); //if(state.CurrentPlaceId == "end-room-good"){ result.Add("rooms", roomCompleteCounter.ToString()); await _SNSClient.PublishAsync(_adventurePlayerFinishedTopic, "Rooms completed: " + result["rooms"]); //} } // create/update player record so we can continue in a future session await StoreAdventureState(state); // respond with serialized player state if (reprompt != null) { return(ResponseBuilder.Ask( response, new Reprompt { OutputSpeech = reprompt }, new Session { Attributes = new Dictionary <string, object> { [SESSION_STATE_KEY] = state } } )); } return(ResponseBuilder.Tell(response)); } catch (Exception e) { LogError(e, "exception during skill processing"); return(ResponseBuilder.Tell(new PlainTextOutputSpeech { Text = PROMPT_OOPS })); } }