Jump to content

C# wrapper for Alexa Responses


chef

Recommended Posts

chef

It is still in an infant form, but there isn't a whole lot of wrappers out there for a c# application and I got bored.

 

I share this with the emby community because it's my favorite.

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using AlexaCognitiveSenseApi.log;
using Newtonsoft.Json.Linq;

namespace AlexaCognitiveSenseApi.Api.Amazon
{
    
    /// <summary>
    ///     C# wrapper for AlexaApi Custom Skills
    /// </summary>
   
    public class AlexaApi
    {
        public enum Effect
        {
            // ReSharper disable once InconsistentNaming
            whispered = 0
        }

        public enum Emphasis
        {
            // ReSharper disable once InconsistentNaming
            /// <summary>
            ///     Decrease the volume and speed up the speaking rate. The speech is softer and faster.
            /// </summary>
            reduced = 0,
            // ReSharper disable once InconsistentNaming
            /// <summary>
            ///     Increase the volume and slow down the speaking rate so the speech is louder and slower.
            /// </summary>
            strong = 1,

            // ReSharper disable once InconsistentNaming
            /// <summary>
            ///     Increase the volume and slow down the speaking rate, but not as much as when set to strong. This is used as a
            ///     default if level is not provided.
            /// </summary>
            moderate = 2
        }
        
        public enum Rate
        {
            // ReSharper disable once InconsistentNaming
            slow = 0,
            // ReSharper disable once InconsistentNaming
            medium = 1,
            // ReSharper disable once InconsistentNaming
            fast = 2
        }

        public enum StrengthBreaks
        {
            // ReSharper disable once InconsistentNaming
            weak = 0,
            // ReSharper disable once InconsistentNaming
            medium = 1,
            // ReSharper disable once InconsistentNaming
            strong = 2
        }
        // ReSharper disable once InconsistentNaming

        //Random used to choose a list item.
        private static readonly Random random = new Random();

        public class HumanizeResponses
        {
            public static readonly List<string> Apologetic = new List<string>
            {
                "Sorry" + AlexaInsertStrengthBreak(StrengthBreaks.weak),
                "I'm sorry" + AlexaInsertStrengthBreak(StrengthBreaks.weak),
                "Ever so sorry" + AlexaInsertStrengthBreak(StrengthBreaks.weak),
                "I apologize" + AlexaInsertStrengthBreak(StrengthBreaks.weak)
            };

            public static readonly List<string> Compliances = new List<string>
            {
                "OK" + AlexaInsertStrengthBreak(StrengthBreaks.weak),
                "Alright" + AlexaInsertStrengthBreak(StrengthBreaks.weak),
                "On it!" + AlexaInsertStrengthBreak(StrengthBreaks.weak),
                "I'll get on that right away" + AlexaInsertStrengthBreak(StrengthBreaks.weak),
                "Sure thing!" + AlexaInsertStrengthBreak(StrengthBreaks.weak),
                "I'll do it" + AlexaInsertStrengthBreak(StrengthBreaks.weak) + " sure thing" +
                AlexaInsertStrengthBreak(StrengthBreaks.weak),
                "Looking into it now" + AlexaInsertStrengthBreak(StrengthBreaks.weak)
            };
        }

        /// <summary>
        /// This request is sent to Amazon to allow for the Alexa device to speak before the custom skill returns its information
        /// </summary>
        /// <param name="token"></param>
        /// <param name="requestId"></param>
        public static void DirectiveResponseBuilder(string token, string requestId)
        {
            //Our Directive POST info
            var res = new DirectiveResponse
            {
                header = new DirectiveResponse.Header {requestId = requestId},
                directive = new DirectiveResponse.Directive
                {
                    type = "VoicePlayer.Speak",
                    speech = "<speak>" + GetHumanizeResponse(HumanizedResponseType.compliance) + "Searching Security Feed" +
                             AlexaInsertStrengthBreak(StrengthBreaks.weak) + " One moment please." +
                             "<audio src='https://s3.amazonaws.com/ask-soundlibrary/scifi/amzn_sfx_scifi_radar_high_ping_01.mp3'/></speak>"
                }
            };

            //Serialize the C# class itno a Json string
            string json = new NewtonsoftJsonSerializer().SerializeToString(res);

            //Encode the JSON string into a byte array
            byte[] data = Encoding.ASCII.GetBytes(json);
            //Where we will send the info and how: POST
            var request = (HttpWebRequest) WebRequest.Create("https://api.amazonalexa.com/v1/directives");
            request.Method = "POST";
            request.ContentType = "application/json";
            request.Headers.Add("Authorization", "Bearer " + token);
            request.ContentLength = data.Length;
            //Write the data to amazon
            using (Stream stream = request.GetRequestStream())
            {
                stream.Write(data, 0, data.Length);
            }
            //Doesn't matter about the response, either AlexaApi spoke or it didn't
            //This is the response for debugging
            var response = (HttpWebResponse) request.GetResponse();
            // ReSharper disable once AssignNullToNotNullAttribute
            string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
            Logger.LogInfo(Logger.LogType.Info, responseString);
        }

        /// <summary>
        /// The response to the Custom Skill Alexa will speak back to the user.
        /// </summary>
        /// <param name="text"></param>
        /// <param name="sessionEnd"></param>
        /// <param name="displayCard"></param>
        /// <returns></returns>
        public object ResponseBuilder(string text, bool sessionEnd = true, Card displayCard = null)
        {
            var res = new AlexaResponse
            {
                version = "1.0",
                response = new Response
                {
                    outputSpeech = new OutputSpeech
                    {
                        type = "SSML",
                        ssml = "<speak>" +
                               text + "</speak>"
                    },
                    shouldEndSession = sessionEnd,
                    card = displayCard
                }
            };

            //Serialize the AlexaApi into a JSON string
            string json = new NewtonsoftJsonSerializer().SerializeToString(res);

            //Convert the JSON string to an actual JSON Object
            return JObject.Parse(json);
        }

        public static Card CardBuilder(Image imageUrl, string content, string title, string text, string type = "Standard")
        {
            return new Card {content = content, image = imageUrl, text = text, title = title, type = type};
        }


        // ReSharper disable InconsistentNaming
        // ReSharper disable ClassNeverInstantiated.Local


        public static string AlexaSayWithEffect(Effect effect, string text)
        {
            return string.Format("<amazon:effect name=\"{0}\">{1}</amazon:effect>", effect, text);
        }

        public static string AlexaSayAsCardinal(string text)
        {
            return string.Format("<say-as interpret-as=\"cardinal\">{0}</say-as>", text);
        }

        public static string AlexaSpellOut(string text)
        {
            return string.Format("<say-as interpret-as=\"spell-out\">" + text + "</say-as>.");
        }

        public static string AlexaInsertTimedBreak(string intDurationSeconds)
        {
            return string.Format("<break time=\"{0}s\"/>", intDurationSeconds);
        }

        public static string AlexaInsertStrengthBreak(StrengthBreaks strength)
        {
            return string.Format("<break strength=\"{0}\"/>", strength);
        }

        public static string AlexaEmphasis(string text, Emphasis emphasis)
        {
            return string.Format("<emphasis level=\"{0}\">{1}</emphasis>", emphasis, text);
        }

        public static string AlexaSpeechRate(Rate rate, string text)
        {
            return string.Format("<prosody rate=\"{0}\">{1}</prosody>", rate, text);
        }

        public static string AlexaExpressiveInterjection(string text)
        {
            return string.Format("<say-as interpret-as=\"interjection\">{0}</say-as>", text);
        }

        public enum HumanizedResponseType
        {
            apologetic = 0,
            compliance = 1,
        }
        public static string GetHumanizeResponse(HumanizedResponseType type)
        {
            switch (type.ToString())
            {
                case "compliance":
                    return HumanizeResponses.Compliances[random.Next(0, HumanizeResponses.Compliances.Count)];
                case "apologetic":
                    return HumanizeResponses.Apologetic[random.Next(0, HumanizeResponses.Apologetic.Count)];
            }
            return string.Empty;
        }

        [Serializable]
        private class AlexaResponse
        {
            /// <summary>
            ///     Manditory
            /// </summary>
            // ReSharper disable once UnusedAutoPropertyAccessor.Local
            public string version { get; set; }

            /// <summary>
            ///     Not Manditory
            /// </summary>
            // ReSharper disable once UnusedMember.Local
            public SessionAttributes sessionAttributes { get; set; }

            /// <summary>
            ///     Manditory
            /// </summary>
            // ReSharper disable once UnusedAutoPropertyAccessor.Local
            public Response response { get; set; }
        }

        [Serializable]
        public class Card
        {
            public string type { get; set; }
            public string title { get; set; }
            public string content { get; set; }
            public string text { get; set; }
            public Image image { get; set; }
        }

        [Serializable]
        public class Directive
        {
            public string type { get; set; }
            public string speech { get; set; }
        }

        [Serializable]
        public class Image
        {
            public string smallImageUrl { get; set; }
            public string largeImageUrl { get; set; }
        }

        [Serializable]
        internal class OutputSpeech
        {
            public string type { get; set; }
            public string text { get; set; }
            public string ssml { get; set; }
        }

        [Serializable]
        internal class OutputSpeech2
        {
            public string type { get; set; }
            public string text { get; set; }
            public string ssml { get; set; }
        }

        [Serializable]
        internal class Reprompt
        {
            public OutputSpeech2 outputSpeech { get; set; }
        }

        [Serializable]
        internal class Response
        {
            public OutputSpeech outputSpeech { get; set; }
            public Card card { get; set; }
            public Reprompt reprompt { get; set; }
            public List<Directive> directives { get; set; }
            public bool shouldEndSession { get; set; }
        }

        [Serializable]
        internal abstract class SessionAttributes
        {
            public string key { get; set; }
        }
    }

    public class DirectiveResponse
    {
        public Header header { get; set; }
        public Directive directive { get; set; }

        public class Directive
        {
            public string type { get; set; }
            public string speech { get; set; }
        }

        public class Header
        {
            public string requestId { get; set; }
        }
    }
}
 
There are some cool extras in this wrapper as well:

 

All the speech emphasis types are usable through their own enumerations, and there is an editable list of possible response builder options using types like:

 

1. Compliances

2. Apologetics

 

This makes Alexa create its own random sentences.

 

It also uses Directives, so Alexa will respond to the user right away, while the custom skill is gathering information in the background prior to the Skills final response.

 

It might be useful. Dunno...

 

I'm using it for my security camera web service, so some of the code needs to be slightly edited for your own use.

 

- Ben

Edited by chef
  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...