Private/TwiML.psm1

#!/usr/bin/env pwsh
using namespace System
using namespace System.Xml

class TwiML {
  [string] $RootName
  [XmlDocument] $Document
  [XmlElement] $Root

  TwiML([string]$rootName) {
    $this.RootName = $rootName
    $this.Document = [XmlDocument]::new()
    $this.Root = $this.Document.CreateElement($rootName)
    [void]$this.Document.AppendChild($this.Root)
  }

  [XmlElement] Append([string]$name, [string]$text, [hashtable]$attributes) {
    return $this.AppendTo($this.Root, $name, $text, $attributes)
  }

  [XmlElement] AppendTo([XmlElement]$parent, [string]$name, [string]$text, [hashtable]$attributes) {
    $node = $this.Document.CreateElement($name)
    if ($text) { $node.InnerText = $text }
    if ($attributes) {
      foreach ($k in $attributes.Keys) {
        if ($null -ne $attributes[$k]) { $node.SetAttribute([string]$k, [string]$attributes[$k]) }
      }
    }
    [void]$parent.AppendChild($node)
    return $node
  }

  [string] ToString() {
    return $this.Document.OuterXml
  }
}

class VoiceResponse : TwiML {
  VoiceResponse() : base('Response') {}
  [void] Say([string]$text, [hashtable]$attributes) { [void]$this.Append('Say', $text, $attributes) }
  [void] Play([string]$url, [hashtable]$attributes) { [void]$this.Append('Play', $url, $attributes) }
  [void] Dial([string]$number, [hashtable]$attributes) { [void]$this.Append('Dial', $number, $attributes) }
  [void] Pause([hashtable]$attributes) { [void]$this.Append('Pause', $null, $attributes) }
  [void] Redirect([string]$url, [hashtable]$attributes) { [void]$this.Append('Redirect', $url, $attributes) }
  [void] Hangup() { [void]$this.Append('Hangup', $null, $null) }

  [XmlElement] Gather([hashtable]$attributes) {
    return $this.Append('Gather', $null, $attributes)
  }

  [void] SayIn([XmlElement]$parent, [string]$text, [hashtable]$attributes) {
    [void]$this.AppendTo($parent, 'Say', $text, $attributes)
  }

  [void] PlayIn([XmlElement]$parent, [string]$url, [hashtable]$attributes) {
    [void]$this.AppendTo($parent, 'Play', $url, $attributes)
  }
}

class MessagingResponse : TwiML {
  MessagingResponse() : base('Response') {}
  [XmlElement] Message([string]$text, [hashtable]$attributes) { return $this.Append('Message', $text, $attributes) }
  [void] Body([string]$text) { [void]$this.Append('Body', $text, $null) }
  [void] Media([string]$url) { [void]$this.Append('Media', $url, $null) }
  [void] Redirect([string]$url, [hashtable]$attributes) { [void]$this.Append('Redirect', $url, $attributes) }

  [void] BodyIn([XmlElement]$parent, [string]$text) { [void]$this.AppendTo($parent, 'Body', $text, $null) }
  [void] MediaIn([XmlElement]$parent, [string]$url) { [void]$this.AppendTo($parent, 'Media', $url, $null) }
}

class FaxResponse : TwiML {
  FaxResponse() : base('Response') {}
  [void] Receive([hashtable]$attributes) { [void]$this.Append('Receive', $null, $attributes) }
}