StreamXRef.psm1

Set-StrictMode -Version 3

# Add type data
try {

    # Try loading from assembly
    Add-Type -Path "$PSScriptRoot/typedata/StreamXRefTypes.dll"

}
catch {

    # As a fallback, compile from source and load into memory
    if ($PSVersionTable.PSVersion.Major -lt 6) {
        # Because the default compiler available to PowerShell 5.1 doesn't support C# 6+
        Add-Type -Path "$PSScriptRoot/typedata/StreamXRefTypes.Legacy.cs" -ErrorAction Stop
    }
    else {
        Add-Type -Path "$PSScriptRoot/typedata/StreamXRefTypes.cs" -ErrorAction Stop
    }

}

#region Internal shared helper functions ================

filter Get-LastUrlSegment {

    $Url = $_ -split "/" | Select-Object -Last 1
    return $Url -split "\?" | Select-Object -First 1

}

filter ConvertTo-UtcDateTime {

    if ($_ -is [datetime]) {

        if ($_.Kind -eq [System.DateTimeKind]::Utc) {

            # Already formatted correctly
            return $_

        }
        else {

            return $_.ToUniversalTime()

        }

    }
    elseif ($_ -is [string]) {

        return ([datetime]::Parse($_)).ToUniversalTime()

    }
    else {

        throw "Unable to convert to UTC: $_"

    }

}

#endregion Shared helper functions -------------

#region Initialize variables ===================

try {

    $script:TwitchData = [pscustomobject]@{

        # [string] Client ID for API access
        ApiKey         = $null

        # @{ [string] User/channel name; [int] User/channel ID number }
        UserInfoCache  = [System.Collections.Generic.Dictionary[string, int]]::new()

        # @{ [string] Clip slug name; @{ Offset = [int] Time offset in seconds; VideoID = [int] Video ID number; Created = [datetime] UTC date/time clip was created } }
        ClipInfoCache  = [System.Collections.Generic.Dictionary[string, pscustomobject]]::new()

        # @{ [int] Video ID number; [datetime] Starting timestamp in UTC }
        VideoInfoCache = [System.Collections.Generic.Dictionary[int, datetime]]::new()

    }

    $script:TwitchData | Add-Member -MemberType ScriptMethod -Name GetTotalCount -ErrorAction Stop -Value {

        $this.UserInfoCache.Count + $this.ClipInfoCache.Count + $this.VideoInfoCache.Count

    }

}
catch {

    $PSCmdlet.ThrowTerminatingError($_)

}


#endregion Initialize variables ----------------

# If not running at least PowerShell 7.0, get the "PSLegacy" version of the functions
# Otherwise, load the "PSCurrent" version of the functions
if ($PSVersionTable.PSVersion.Major -lt 7) {

    $VersionedFunctions = @( Get-ChildItem $PSScriptRoot/PSLegacy/*.ps1 -ErrorAction SilentlyContinue )

}
else {

    $VersionedFunctions = @( Get-ChildItem $PSScriptRoot/PSCurrent/*.ps1 -ErrorAction SilentlyContinue )

}

$SharedFunctions = @( Get-ChildItem $PSScriptRoot/Shared/*.ps1 -ErrorAction SilentlyContinue )

$AllFunctions = $VersionedFunctions + $SharedFunctions

foreach ($FunctionFile in $AllFunctions) {

    try {

        # Dot source the file to load in function
        . $FunctionFile.FullName

    }
    catch {

        Write-Error "Failed to load $($FunctionFile.Directory.Name)/$($FunctionFile.BaseName): $_"

    }

}

# Workaround for scoping issue with [ArgumentCompleter()] for Find-TwitchXRef
$TXRArgumentCompleter = {
    Param(
        $commandName,
        $parameterName,
        $wordToComplete,
        $commandAst,
        $fakeBoundParameters
    )

    $script:TwitchData.UserInfoCache.Keys | Where-Object { $_ -like "$wordToComplete*" }
}
Register-ArgumentCompleter -CommandName Find-TwitchXRef -ParameterName XRef -ScriptBlock $TXRArgumentCompleter

$FunctionNames = $AllFunctions | ForEach-Object {

    # Use the name of the file to specify function(s) to be exported
    # Filter out potential ".Legacy" from name
    $_.Name.Split('.')[0]

}

New-Alias -Name txr -Value Find-TwitchXRef -ErrorAction Ignore

Export-ModuleMember -Alias "txr"
Export-ModuleMember -Function $FunctionNames