Интеграция Amazon Polly с VBA - PullRequest
0 голосов
/ 10 июня 2018

Я хочу использовать Amazon Polly в проекте PowerPoint VBA для преобразования заметок в mp3.Я думаю, что нет SDK для этого, но это возможно через API с подписью?

1 Ответ

0 голосов
/ 16 сентября 2018

См. Модуль VBA и Класс VBA , ниже которого я недавно построил, разрешая прямые вызовы веб-службы Amazon Polly из MS Office, сохраняя полученный MP3 в файл.Пара замечаний:

  • Я только подтвердил успешные звонки в Amazon Polly с версиями MS Word и MS Excel для Office 365 для Windows 10, но, учитывая мой прошлый опыт работы с VBA, у меня нет никаких причинполагать, что код не будет работать с другими вариантами.
  • Точка входа: callAwsPolly .
  • Вам необходимо соответствующим образом настроить параметры AWS (т. е. aws.host , aws.region , aws.service , & aws.uri ).
  • Полномочия ( aws.accessKey & aws.secretKey ) являются стандартными учетными данными примера AWS, и их, очевидно, необходимо будет заменить на свои собственные учетные данные AWS.
  • Обратите внимание, что это очень плохо практика встраивания учетных данных в код.Я не уверен в вашем конкретном приложении, но в моем случае я создал свое собственное хранилище ключей, используя при желании парольную фразу или MAC-адрес в качестве ключа расшифровки для хранилища ключей.Концепция управления учетными данными - это еще одна тема, которую я здесь не рассматриваю ...

Чтобы включить эту функциональность в продукт MS Office, добавьте следующее в качестве модуля VBA ...



Option Explicit

Sub callAwsPolly()

    Dim aws As amazonWebService
    Set aws = New amazonWebService

    aws.host = "polly.us-east-2.amazonaws.com"
    aws.region = Split(aws.host, ".")(1)
    aws.service = "polly"
    aws.uri = "/v1/speech"

    ' !!! NOTE:  The following is very bad practice to embed credentials in code!!!

    aws.accessKey = "AKIAIOSFODNN7EXAMPLE"
    aws.secretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

    Dim requestParameters As String
    requestParameters = "{#OutputFormat#: #mp3#, #Text#: #Polly want a cracker?#, #TextType#: #ssml#, #VoiceId#: #Emma#}"
    requestParameters = Replace(requestParameters, "#", """")

    Dim httpResponse As Object
    Set httpResponse = aws.callWebService("application/json", requestParameters)

    If httpResponse Is Nothing Then
        MsgBox "Call to AWS Polly failed."
    ElseIf httpResponse.Status = 200 Then
        MsgBox "Call to AWS Polly succeeded!  MP3 file being saved."

        Dim iFile As Long:  iFile = FreeFile
        Open ActiveWorkbook.FullName & ".mp3" For Binary Access Write As #iFile
        Put iFile, , httpResponse.Responsebody
        Close #iFile
    Else
        MsgBox "Call to AWS Polly failed:" + CStr(httpResponse.Status) + " " + httpResponse.StatusText + " " + httpResponse.ResponseText
    End If

End Sub


' Adapted from https://stackoverflow.com/questions/36384741/cant-use-getbytes-and-computehash-methods-on-vba

Public Function hmacSha256(key As Variant, stringToHash As Variant) As Byte()

    Dim ssc As Object
    Set ssc = CreateObject("System.Security.Cryptography.HMACSHA256")

    ssc.key = str2byte(key)
    hmacSha256 = ssc.ComputeHash_2(str2byte(stringToHash))

    Set ssc = Nothing

End Function

Public Function sha256(stringToHash As Variant) As Byte()

    Dim ssc As Object
    Set ssc = CreateObject("System.Security.Cryptography.SHA256Managed")

    sha256 = ssc.ComputeHash_2(str2byte(stringToHash))

    Set ssc = Nothing

End Function

Public Function str2byte(s As Variant) As Byte()

    If VarType(s) = vbArray + vbByte Then
        str2byte = s
    ElseIf VarType(s) = vbString Then
        str2byte = StrConv(s, vbFromUnicode)
    Else
        Exit Function
    End If

End Function

Public Function byte2hex(byteArray() As Byte) As String

    Dim i As Long
    For i = 0 To UBound(byteArray)
        byte2hex = byte2hex & Right(Hex(256 Or byteArray(i)), 2)
    Next
    byte2hex = LCase(byte2hex)

End Function


... и добавьте следующий код в качестве модуля класса VBA, назвав его " amazonWebService " ...

'
' Class Module:  amazonWebService
'
Private className As String

Public host As String
Public region As String
Public service As String
Public uri As String

Public accessKey As String
Public secretKey As String

Private Sub Class_Initialize()

    className = "amazonWebService"

    host = ""
    region = ""
    uri = ""
    service = ""

    accessKey = ""
    secretKey = ""

End Sub

Public Function callWebService(contentType As String, requestParameters As String) As Object

    If host = "" Or region = "" Or uri = "" Or service = "" Or accessKey = "" Or secretKey = "" Then
        Err.Raise _
            1000, _
            className, _
            className + ": Please set properties before calling the amazon web service:  host, region, uri, service, accessKey, and secretKey."
        callWebService = Null
        Exit Function
    End If

    Dim amzDate As String, dateStamp As String
    amzDate = date2iso8601(GMT())
    dateStamp = Left(amzDate, 8)

    ' ************* TASK 1: CREATE A CANONICAL REQUEST *************
    ' http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html

    'Step 1 is to define the verb (GET, POST, etc.)
    Dim method As String
    method = "POST"

    ' Step 2: Create canonical URI (set by the calling program)
    Dim canonicalUri As String
    canonicalUri = uri

    ' Step 3: Create the canonical query string. In this example (POST), request
    ' parameters are passed in the body of the request and the query string
    ' is blank.
    Dim canonicalQueryString As String
    canonicalQueryString = ""

    ' Step 4: Create the canonical headers. Header names must be trimmed
    ' and lowercase, and sorted in code point order from low to high.
    ' Note that there is a trailing \n.
    Dim canonicalHeaders As String
    canonicalHeaders = _
        "host:" + host + vbLf + _
        "x-amz-date:" + amzDate + vbLf

    ' Step 5: Create the list of signed headers. This lists the headers
    ' in the canonical_headers list, delimited with ";" and in alpha order.
    ' Note: The request can include any headers; canonical_headers and
    ' signed_headers include those that you want to be included in the
    ' hash of the request. "Host" and "x-amz-date" are always required.
    Dim signedHeaders As String
    signedHeaders = "host;x-amz-date"

    ' Step 6: Create payload hash. In this example, the payload (body of
    ' the request) contains the request parameters.
    Dim payloadHash As String
    payloadHash = byte2hex(sha256(requestParameters))

    ' Step 7: Combine elements to create canonical request
    Dim canonicalRequest As String
    canonicalRequest = method + vbLf + _
        canonicalUri + vbLf + _
        canonicalQueryString + vbLf + _
        canonicalHeaders + vbLf + _
        signedHeaders + vbLf + _
        payloadHash

    ' ************* TASK 2: CREATE THE STRING TO SIGN*************
    ' Match the algorithm to the hashing algorithm you use, either SHA-1 or
    ' SHA-256 (recommended)
    Dim algorithm As String, credentialScope As String, stringToSign As String
    algorithm = "AWS4-HMAC-SHA256"
    credentialScope = dateStamp + "/" + region + "/" + service + "/aws4_request"
    stringToSign = _
        algorithm + vbLf + _
        amzDate + vbLf + _
        credentialScope + vbLf + _
        byte2hex(sha256(canonicalRequest))

    ' ************* TASK 3: CALCULATE THE SIGNATURE *************
    ' Create the signing key using the function defined above.
    Dim signingKey() As Byte, signature As String
    signingKey = getSignatureKey(secretKey, dateStamp, region, service)

    ' Sign the string_to_sign using the signing_key
    signature = byte2hex(hmacSha256(signingKey, stringToSign))

    ' ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
    ' Put the signature information in a header named Authorization.
    Dim authorizationHeader As String
    authorizationHeader = _
        algorithm + " " + _
        "Credential=" + accessKey + "/" + credentialScope + ", " + _
        "SignedHeaders=" + signedHeaders + ", " + _
        "Signature=" + signature

    ' ************* SEND THE REQUEST TO AWS! *************

    Dim http As Object
    Set http = CreateObject("WinHttp.WinHttpRequest.5.1")

    http.Open "POST", "https://" + host + uri, False
    http.setrequestheader "content-type", contentType
    http.setrequestheader "host", host
    http.setrequestheader "x-amz-date", amzDate
    http.setrequestheader "authorization", authorizationHeader

    http.Send requestParameters

    ' If the result is 403 Forbidden, then clear the current selected credentials.

    If http.Status = 403 Then
        accessKey = ""
        secretKey = ""
    End If

    ' Return the HTTP response back to the calling program.

    Set callWebService = http

End Function

Private Function GMT() As Date

    Dim dt As Object
    Set dt = CreateObject("WbemScripting.SWbemDateTime")

    dt.SetVarDate Now
    GMT = dt.GetVarDate(False)

    Set dt = Nothing

End Function

Private Function date2iso8601(dateTime As Date) As String

    date2iso8601 = Format(dateTime, "yyyymmdd\Thhnnss\Z")

End Function

Private Function getSignatureKey(key As String, dateStamp As String, regionName As String, serviceName As String) As Byte()
    Dim kDate() As Byte, kRegion() As Byte, kService() As Byte, kSigning() As Byte

    kDate = hmacSha256("AWS4" + key, dateStamp)
    kRegion = hmacSha256(kDate, regionName)
    kService = hmacSha256(kRegion, serviceName)
    kSigning = hmacSha256(kService, "aws4_request")

    getSignatureKey = kSigning

End Function

...