pwshEmojiExplorer.psm1
# This is a locally sourced Imports file for local development. # It can be imported by the psm1 in local development to add script level variables. # It will merged in the build process. This is for local development only. # region script variables # $script:resourcePath = "$PSScriptRoot\Resources" $script:unicodeVersion = '16.0' function Get-DataLocation { $folderName = 'pwshEmojiExplorer' if ($PROFILE) { $script:dataPath = (Join-Path (Split-Path -Parent $PROFILE) $folderName) } else { $script:dataPath = "~\${$folderName}" } } $domain = 'cloudfront.net' $target = 'd1nm4o1hreod3r' Get-DataLocation $script:dataFileZip = 'pwshemojis.zip' $script:metadataFile = 'version.json' $script:dataFile = 'pwshEmoji.xml' $script:dlURI = '{0}.{1}' -f $target, $domain $script:glData = $null class PSEmoji { [string]$Group [string]$Subgroup [string]$HexCodePoint [string]$Status [string]$Name [string]$Version [string]$Description [string]$ShortCode [string[]]$HexCodePointArray [string[]]$UnicodeStandard [string[]]$HTMLEntityFormat [string]$pwshEscapedFormat [int[]]$Decimal PSEmoji([string]$Group, [string]$Subgroup, [string]$HexCodePoint, [string]$Status, [string]$Name, [string]$Version, [string]$Description, [string]$ShortCode, [string[]]$HexCodePointArray, [string[]]$UnicodeStandard, [string[]]$HTMLEntityFormat, [string]$pwshEscapedFormat, [int[]]$Decimal) { $this.Group = $Group $this.Subgroup = $Subgroup $this.HexCodePoint = $HexCodePoint $this.Status = $Status $this.Name = $Name $this.Version = $Version $this.Description = $Description $this.ShortCode = $ShortCode $this.HexCodePointArray = $HexCodePointArray $this.UnicodeStandard = $UnicodeStandard $this.HTMLEntityFormat = $HTMLEntityFormat $this.pwshEscapedFormat = $pwshEscapedFormat $this.Decimal = $Decimal } # You can add any additional methods here } $script:stopWords = @( 'a' 'about' 'above' 'actually' 'after' 'again' 'against' 'all' 'almost' 'also' 'although' 'always' 'am' 'an' 'and' 'any' 'are' 'as' 'at' 'be' 'became' 'because' 'become' 'been' 'before' 'being' 'below' 'between' 'both' 'but' 'by' 'can' 'cannot' 'could' 'did' 'do' 'does' 'doing' 'down' 'during' 'each' 'either' 'else' 'few' 'for' 'from' 'further' 'had' 'has' 'have' 'having' 'he' 'hence' 'her' 'here' 'hers' 'herself' 'him' 'himself' 'his' 'how' 'i' 'if' 'i''m' 'in' 'into' 'is' 'it' 'its' 'itself' 'just' 'may' 'maybe' 'me' 'might' 'mine' 'more' 'most' 'must' 'my' 'myself' 'neither' 'no' 'nor' 'not' 'of' 'off' 'oh' 'ok' 'on' 'once' 'only' 'or' 'other' 'ought' 'our' 'ours' 'ourselves' 'out' 'over' 'own' 'same' 'she' 'so' 'some' 'such' 'than' 'that' 'the' 'their' 'theirs' 'them' 'themselves' 'then' 'there' 'these' 'they' 'this' 'those' 'through' 'to' 'too' 'under' 'very' 'wait' 'was' 'we' 'were' 'what' 'whenever' 'whereas' 'wherever' 'whether' 'who' 'whoever' 'whom' 'whose' 'why' 'will' 'with' 'within' 'without' 'would' 'yes' 'yet' 'you' 'your' 'yours' 'yourself' 'yourselves' ) <# .SYNOPSIS Confirm data output location. Creates output dir if not present. .DESCRIPTION Evaluates presence of data output location for xml dataset. If the directory is not found, it will be created. .EXAMPLE Confirm-DataLocation Confirms presence of data output location. Creates if not found. .OUTPUTS System.Boolean .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ .COMPONENT pwshEmojiExplorer #> function Confirm-DataLocation { [CmdletBinding()] param ( ) $result = $true #assume the best Write-Verbose -Message 'Verifying data set output location...' Write-Debug -Message ('Data path: {0}' -f $script:dataPath) try { $pathEval = Test-Path -Path $script:dataPath -ErrorAction Stop } catch { $result = $false Write-Error $_ return $result } Write-Debug -Message ('Path evaluation: {0}' -f $pathEval) if (-not ($pathEval)) { Write-Verbose -Message 'Creating output directory...' try { $newItemSplat = @{ ItemType = 'Directory' Path = $script:dataPath ErrorAction = 'Stop' } $null = New-Item @newItemSplat Write-Verbose -Message 'Created.' } catch { $result = $false Write-Error $_ return $result } } #if_TestPath else { Write-Verbose 'Data path confirmed.' } #else_TestPath return $result } #Confirm-DataLocation <# .SYNOPSIS Compares the local metadata file to the remote metadata file. .DESCRIPTION Evaluates the local metadata file and compares it to the remote metadata file. If the files are the same, returns true. If the files are different, returns false. .EXAMPLE Confirm-MetadataUpdate Compares the local metadata file to the remote metadata file. .OUTPUTS System.Boolean .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ .COMPONENT pwshEmojiExplorer #> function Confirm-MetadataUpdate { [CmdletBinding()] param ( ) $result = $true #assume the best Write-Verbose -Message 'Checking for metadata file...' $localMetaDataFilePath = [System.IO.Path]::Combine($script:dataPath, $script:metadataFile) Write-Debug -Message ('Metadata file path: {0}' -f $localMetaDataFilePath) try { $pathEval = Test-Path -Path $localMetaDataFilePath -ErrorAction Stop } catch { $result = $false Write-Error $_ return $result } if (-not ($pathEval)) { $result = $false Write-Debug -Message 'Metadata file not found.' } #if_pathEval else { Write-Verbose 'Metadata file found. Performing metadata comparison...' try { $localMetadata = Get-Content $localMetaDataFilePath -ErrorAction 'Stop' | ConvertFrom-Json } catch { $result = $false Write-Error $_ return $result } $tempMetadataFile = '{0}_temp' -f $script:metadataFile $tempMetadataFilePath = [System.IO.Path]::Combine($script:dataPath, $tempMetadataFile) Write-Debug -Message ('Temp metadata file path: {0}' -f $tempMetadataFilePath) # if the temp metadata file exists, delete it if (Test-Path -Path $tempMetadataFile) { Write-Debug -Message 'Removing temp metadata file...' Remove-Item -Path $tempMetadataFilePath -Force } # download metadata file for comparison $fileFetchStatus = Get-RemoteFile -File $script:metadataFile -OutFile $tempMetadataFile if ($fileFetchStatus -eq $false) { Write-Error 'Unable to download metadata file.' $result = $false return $result } Write-Debug -Message 'Getting content of temp metadata file...' try { $remoteMetadata = Get-Content $tempMetadataFilePath -ErrorAction 'Stop' | ConvertFrom-Json } catch { $result = $false Write-Error $_ return $result } Write-Debug -Message ('Local metadata version: {0}' -f $localMetadata.version) Write-Debug -Message ('Remote metadata version: {0}' -f $remoteMetadata.version) Write-Verbose -Message ('{0} vs {1}' -f $localMetadata.version, $remoteMetadata.version) if ($localMetadata.version -eq $remoteMetadata.version) { Write-Verbose 'Metadata file is current.' } else { Write-Verbose 'Metadata file requires refresh.' $result = $false } } return $result } #Confirm-MetadataUpdate <# .SYNOPSIS Confirms the XML dataset file is available and not beyond the expiration time. .DESCRIPTION Determines if the XML dataset file is stale or not available. If the file is not available, false will be returned so it can be downloaded. If the file is available, but over 9 days old, the metadata file will be checked to see if an update is available. If an update is available after the metadata file is checked, false will be returned so the data file can be refreshed. .EXAMPLE Confirm-XMLDataSet Checks for XML dataset and determines if it is 9 days older or more. .OUTPUTS System.Boolean .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ .COMPONENT pwshEmojiExplorer #> function Confirm-XMLDataSet { [CmdletBinding()] param ( ) $result = $true #assume the best $dataFile = '{0}/{1}' -f $script:dataPath, $script:dataFile Write-Verbose -Message 'Confirming valid and current data set...' # if the file doesn't exist, we need to download it Write-Verbose -Message 'Checking for data file...' Write-Debug -Message ('Data file path: {0}' -f $dataFile) try { $pathEval = Test-Path -Path $dataFile -ErrorAction Stop } catch { $result = $false Write-Error $_ return $result } if (-not ($pathEval)) { Write-Debug -Message 'Data file not found.' $result = $false } #if_pathEval else { Write-Verbose 'Data file found. Checking date of file...' try { $fileData = Get-Item -Path $dataFile -ErrorAction Stop } catch { $result = $false Write-Error $_ return $result } if ($fileData) { $creationDate = $fileData.LastWriteTime $now = Get-Date if (($now - $creationDate).Days -ge 9) { # Write-Verbose 'Data file requires refresh.' Write-Verbose 'Data file is older than 9 days. Checking if an update is available...' $metadataStatus = Confirm-MetadataUpdate if ($metadataStatus -eq $false) { Write-Verbose 'Refreshing data file...' $result = $false } else { Write-Verbose 'No update available. Data file is current.' } } else { Write-Verbose 'Data file verified' } } #if_fileData else { Write-Warning 'Unable to retrieve file information for pwshEmojiExplorer data set.' $result = $false return $result } #else_fileData } #else_pathEval return $result } #Confirm-XMLDataSet <# .SYNOPSIS Converts an emoji to a string of its hexadecimal code points. .DESCRIPTION The Convert-EmojiToHexCodePoint function translates a given emoji character into its corresponding hexadecimal code points. This function is particularly useful for handling emojis that consist of surrogate pairs in Unicode, ensuring accurate conversion of complex emojis into their full hexadecimal representations. .EXAMPLE $Emoji = "🐸" $searchCriteria = Convert-EmojiToHexCodePoint -Emoji $Emoji $searchCriteria Converts the frog emoji into its hexadecimal representation '1F438'. .PARAMETER Emoji Specifies the emoji character to be converted. .OUTPUTS System.String .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ .COMPONENT pwshEmojiExplorer #> function Convert-EmojiToHexCodePoint { param ( [Parameter(Mandatory = $true)] [string]$Emoji ) Write-Debug -Message 'Converting emoji to hexadecimal code points...' $charArray = $Emoji.ToCharArray() $codePoints = New-Object System.Collections.Generic.List[string] for ($i = 0; $i -lt $charArray.Length; $i++) { $codePoint = [Char]::ConvertToUtf32($Emoji, $i) [void]$codePoints.Add("{0:X}" -f $codePoint) # If this character is a high surrogate, skip the next character if ([Char]::IsHighSurrogate($charArray[$i])) { $i++ } } return $codePoints -join ' ' } # Convert-EmojiToHexCodePoint <# .SYNOPSIS Converts a custom object to a PSEmoji object. .DESCRIPTION The ConvertTo-PSEmoji function takes a custom object as input and converts it to a PSEmoji object. .EXAMPLE $customObject = [PSCustomObject]@{ Group = 'Smileys & Emotion' Subgroup = 'face-smiling' HexCodePoint = '1F600' Status = 'fully-qualified' Name = '😀' Version = 'E1.0' Description = 'grinning face' ShortCode = ':grinning_face:' HexCodePointArray = @('1F600') UnicodeStandard = 'Unicode 6.0' HTMLEntityFormat = '😀' pwshEscapedFormat = '`u{1F600}' Decimal = @('128512') } $emoji = ConvertTo-PSEmoji -CustomObject $customObject This example shows how to convert a custom object to a PSEmoji object. .EXAMPLE [PSEmoji]$emoji = ConvertTo-PSEmoji -CustomObject $customObject s This example shows how to convert a custom object to a PSEmoji object. .PARAMETER CustomObject The custom object to be converted. This object should have properties that match the properties of the PSEmoji class. .INPUTS PSCustomObject .OUTPUTS PSEmoji .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ The properties of the custom object should match the properties of the PSEmoji class. .COMPONENT pwshEmojiExplorer #> function ConvertTo-PSEmoji { param ( [Parameter(Mandatory = $true, HelpMessage = 'Custom object to be converted to a PSEmoji object.')] [PSCustomObject]$CustomObject ) Write-Debug -Message 'Converting custom object to PSEmoji object...' $emoji = [PSEmoji]::new( $CustomObject.Group, $CustomObject.Subgroup, $CustomObject.HexCodePoint, $CustomObject.Status, $CustomObject.Name, $CustomObject.Version, $CustomObject.Description, $CustomObject.ShortCode, $CustomObject.HexCodePointArray, $CustomObject.UnicodeStandard, $CustomObject.HTMLEntityFormat, $CustomObject.pwshEscapedFormat, $CustomObject.Decimal ) return $emoji } #ConvertTo-PSEmoji <# .SYNOPSIS Unzips the XML data set. .DESCRIPTION Evaluates for previous version of XML data set and removes if required. Expands the XML data set for use. .EXAMPLE Expand-XMLDataSet Unzips and expands the XML data set. .OUTPUTS System.Boolean .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ .COMPONENT pwshEmojiExplorer #> function Expand-XMLDataSet { [CmdletBinding()] param ( ) $result = $true #assume the best $dataFile = '{0}/{1}' -f $script:dataPath, $script:dataFile Write-Verbose -Message 'Testing if data set file already exists...' Write-Debug -Message ('Data file path: {0}' -f $dataFile) try { $pathEval = Test-Path -Path $dataFile -ErrorAction Stop Write-Verbose -Message "EVAL: $true" } catch { $result = $false Write-Error $_ return $result } if ($pathEval) { Write-Verbose -Message 'Removing existing data set file...' try { $removeItemSplat = @{ Force = $true Path = $dataFile ErrorAction = 'Stop' } Remove-Item @removeItemSplat } #try catch { $result = $false Write-Error $_ return $result } #catch } #if_pathEval Write-Verbose -Message 'Expanding data set archive...' try { $expandArchiveSplat = @{ DestinationPath = $script:dataPath Force = $true ErrorAction = 'Stop' Path = '{0}/{1}' -f $script:dataPath, $script:dataFileZip } $null = Microsoft.PowerShell.Archive\Expand-Archive @expandArchiveSplat Write-Verbose -Message 'Expand completed.' } #try catch { $result = $false Write-Error $_ } #catch return $result } #Expand-XMLDataSet <# .SYNOPSIS Downloads file to device. .DESCRIPTION Retrieves file from web and downloads to device. .EXAMPLE Get-RemoteFile Downloads file to data path. .PARAMETER File File to download. .PARAMETER OutFileName Specify output file name. .OUTPUTS System.Boolean .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ Overwrites existing zip file. .COMPONENT pwshEmojiExplorer #> function Get-RemoteFile { [CmdletBinding()] param ( [Parameter(Mandatory = $true, HelpMessage = 'File to download')] [string]$File, [Parameter(Mandatory = $false, HelpMessage = 'Specify output file name.')] [string]$OutFileName ) $result = $true #assume the best if ($OutFileName) { $OutFile = $OutFileName } else { $OutFile = $File } Write-Verbose -Message 'Downloading file...' $outFilePath = [System.IO.Path]::Combine($script:dataPath, $OutFile) Write-Debug -Message ('Out file path: {0}' -f $outFilePath) try { $invokeWebRequestSplat = @{ OutFile = $outFilePath Uri = 'https://{0}/{1}' -f $script:dlURI, $File ErrorAction = 'Stop' } $oldProgressPreference = $progressPreference $progressPreference = 'SilentlyContinue' if ($PSEdition -eq 'Desktop') { $null = Invoke-WebRequest @invokeWebRequestSplat -PassThru -UseBasicParsing } else { $null = Invoke-WebRequest @invokeWebRequestSplat -PassThru } } #try catch { $result = $false Write-Error $_ } #catch finally { $progressPreference = $oldProgressPreference } #finally return $result } #Get-RemoteFile <# .SYNOPSIS Evaluates if XML data set is in memory and kicks of child processes to obtain XML data set. .DESCRIPTION XML data set will be evaluated if already in memory. If not, a series of processes will be kicked off to load the XML data set for use. .EXAMPLE Import-XMLDataSet Loads the XML data set into memory. .OUTPUTS System.Boolean .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ Parent process for getting XML data. .COMPONENT pwshEmojiExplorer #> function Import-XMLDataSet { [CmdletBinding()] param ( ) $result = $true #assume the best Write-Verbose -Message 'Verifying current state of XML data set...' if ($null -eq $script:glData) { $dataCheck = Invoke-XMLDataCheck if ($dataCheck) { Write-Debug -Message 'XMLDataCheck returned true.' try { $getContentSplat = @{ Path = "$script:dataPath\$script:dataFile" Raw = $true ErrorAction = 'Stop' } $fileData = Get-Content @getContentSplat $script:glData = $fileData | ConvertFrom-Clixml -ErrorAction Stop } #try catch { $result = $false Write-Error $_ } #catch } #if_dataCheck else { Write-Debug -Message 'XMLDataCheck returned false.' $result = $false } #else_dataCheck } #if_gldata return $result } #Import-XMLDataSet <# .SYNOPSIS Invokes all child functions required to process retrieving the XML data set file. .DESCRIPTION Runs all required child functions to successfully retrieve and process the XML data set file. .EXAMPLE Invoke-XMLDataCheck Downloads, expands, and verified the XML data set file. .OUTPUTS System.Boolean .NOTES Author: Jake Morrison - @jakemorrison - https://www.techthoughts.info/ Confirm-XMLDataSet Get-RemoteFile Expand-XMLDataSet .COMPONENT pwshEmojiExplorer #> function Invoke-XMLDataCheck { [CmdletBinding(ConfirmImpact = 'Low', SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $false, HelpMessage = 'Skip confirmation')] [switch]$Force ) Begin { if (-not $PSBoundParameters.ContainsKey('Verbose')) { $VerbosePreference = $PSCmdlet.SessionState.PSVariable.GetValue('VerbosePreference') } if (-not $PSBoundParameters.ContainsKey('Confirm')) { $ConfirmPreference = $PSCmdlet.SessionState.PSVariable.GetValue('ConfirmPreference') } if (-not $PSBoundParameters.ContainsKey('WhatIf')) { $WhatIfPreference = $PSCmdlet.SessionState.PSVariable.GetValue('WhatIfPreference') } Write-Verbose -Message ('[{0}] Confirm={1} ConfirmPreference={2} WhatIf={3} WhatIfPreference={4}' -f $MyInvocation.MyCommand, $Confirm, $ConfirmPreference, $WhatIf, $WhatIfPreference) $results = $true #assume the best } #begin Process { # -Confirm --> $ConfirmPreference = 'Low' # ShouldProcess intercepts WhatIf* --> no need to pass it on if ($Force -or $PSCmdlet.ShouldProcess("ShouldProcess?")) { Write-Verbose -Message ('[{0}] Reached command' -f $MyInvocation.MyCommand) $ConfirmPreference = 'None' $dataOutputDir = Confirm-DataLocation if ($dataOutputDir -eq $true) { $confirm = Confirm-XMLDataSet if (-not $confirm -eq $true) { $retrieve = Get-RemoteFile -File $script:dataFileZip # remove metadata file if it exists $localMetaDataFilePath = [System.IO.Path]::Combine($script:dataPath, $script:metadataFile) if (Test-Path -Path $localMetaDataFilePath) { Remove-Item -Path $localMetaDataFilePath -Force } $retrieveMetadata = Get-RemoteFile -File $script:metadataFile if ($retrieve -eq $true -and $retrieveMetadata -eq $true) { $expand = Expand-XMLDataSet if (-not $expand -eq $true) { $results = $false } } else { $results = $false } } #if_Confirm } #if_data_output else { $results = $false } #else_data_output } #if_Should } #process End { return $results } #end } #Invoke-XMLDataCheck <# .EXTERNALHELP pwshEmojiExplorer-help.xml #> function Add-EmojiToText { [CmdletBinding()] param ( [Parameter(Mandatory = $true, HelpMessage = 'The text to process')] [ValidateNotNullOrEmpty()] [string]$Text, [Parameter(Mandatory = $false, HelpMessage = 'Replace the word with the emoji instead of adding the emoji after the word')] [switch]$Replace ) Write-Verbose -Message 'Processing text...' $words = $Text -split ' ' # Process each word $processedWords = foreach ($word in $words) { # ____________________________ # resets $emoji = $null $key = $null $emojiObj = $null # ____________________________ if ($word -notin $stopWords) { $key = $word -replace '[^\w\s]', '' # Remove punctuation for matching $emojiObj = Get-Emoji -SearchTerm $key if ($emojiObj) { $emoji = $emojiObj[0].Name } if ($emoji) { Write-Debug -Message ('{0} -> {1}' -f $key, $emoji) if ($Replace -eq $true) { $word = "$emoji" } else { $word = "$word $emoji" # Add the emoji after the word } $word } else { $word } } else { $word } } #foreach_word # Reconstruct the text $processedText = $processedWords -join ' ' return $processedText } #Add-EmojiToText <# .EXTERNALHELP pwshEmojiExplorer-help.xml #> function Get-AllEmoji { [CmdletBinding()] param ( [Parameter(Mandatory = $false, HelpMessage = 'Includes all emojis in the output')] [switch]$IncludeAll ) Write-Verbose -Message 'Verifying XML Data Set Availability...' if (Import-XMLDataSet) { Write-Verbose -Message 'Verified.' $dataSet = $script:glData Write-Verbose -Message 'Retrieving all emojis...' # declare object array empty list $emojiList = New-Object System.Collections.Generic.List[PSEmoji] $dataSet | ForEach-Object { $emoji = $null try { [PSEmoji]$emoji = ConvertTo-PSEmoji -CustomObject $_ } catch { Write-Warning -Message ('Unable to convert the custom object {0} to a PSEmoji object.' -f $_.Description) } [void]$emojiList.Add($emoji) } Write-Debug -Message ('{0} emojis retrieved.' -f $emojiList.Count) if ($IncludeAll -eq $false) { $results = $emojiList | Where-Object { $_.Status -eq 'fully-qualified' } } else { $results = $emojiList } Write-Debug -Message ('{0} emojis returned.' -f $results.Count) } #if_Import-XMLDataSet else { Write-Warning -Message 'pwshEmojiExplorer was unable to source the required data set file.' Write-Warning -Message 'Ensure you have an active internet connection' return } #else_Import-XMLDataSet return $results } #Get-AllEmoji <# .EXTERNALHELP pwshEmojiExplorer-help.xml #> function Get-Emoji { [CmdletBinding()] param ( [Parameter(ParameterSetName = 'Name', HelpMessage = 'Specifies the emoji character to retrieve. Use an exact emoji character for a direct match')] # validate to contain only letters [ValidatePattern('\p{So}|\p{Cs}')] [string]$Emoji, [Parameter(ParameterSetName = 'Group', HelpMessage = 'Specifies the group of emojis to retrieve')] [ArgumentCompleter({ GroupArgumentCompleter @args })] [string]$Group, # * Note: ArgumentCompletions were used previously, but they are not supported in Windows PowerShell 5.1 [Parameter(ParameterSetName = 'SubGroup', HelpMessage = 'Specifies the subgroup of emojis to retrieve')] [ArgumentCompleter({ SubgroupArgumentCompleter @args })] [string]$SubGroup, [Parameter(ParameterSetName = 'ShortCode', HelpMessage = 'Specifies the shortcode of the emoji to retrieve')] [ArgumentCompleter({ ShortCodeCompleter @args })] [string]$ShortCode, [Parameter(ParameterSetName = 'Hex', HelpMessage = 'Specifies the hexadecimal code point of the emoji to retrieve')] [ValidatePattern('^[0-9A-Fa-f]{4,6}( [0-9A-Fa-f]{4,6})*$')] [string]$HexCodePoint, [Parameter(ParameterSetName = 'Decimal', HelpMessage = 'Specifies the decimal code point of the emoji to retrieve')] [ValidatePattern('^\d+$')] [string[]]$Decimal, [Parameter(ParameterSetName = 'Search', HelpMessage = 'Enter a search term to find in the group, subgroup, or description.')] [ValidatePattern('^[a-zA-Z0-9\s\p{P}]+$')] [string]$SearchTerm, [Parameter(Mandatory = $false, HelpMessage = 'Includes all emojis in the output')] [switch]$IncludeAll ) Write-Verbose -Message 'Verifying XML Data Set Availability...' if (Import-XMLDataSet) { Write-Verbose -Message 'Verified.' $dataSet = $script:glData # declare object array empty list $finalEmojiList = New-Object System.Collections.Generic.List[PSEmoji] if ($Emoji) { # * Searching by HexCodePoint instead of Emoji character directly is supported in Windows PowerShell 5.1 # * This could be removed in the future if Windows PowerShell 5.1 is no longer supported $searchCriteria = Convert-EmojiToHexCodePoint -Emoji $Emoji Write-Verbose -Message ('Searching for emoji: {0}' -f $Emoji) Write-Debug -Message ('Searching for emoji with hex code point: {0}' -f $searchCriteria) $find = $dataSet | Where-Object { $_.HexCodePoint -eq $searchCriteria } # * This approach works in higher version of PowerShell, but not for Windows PowerShell 5.1 in all cases # Write-Verbose -Message ('Searching for emoji: {0}' -f $Emoji) # $find = $dataSet | Where-Object { $_.Name -eq $Emoji } } elseif ($Group) { Write-Verbose -Message ('Searching for emojis in group: {0}' -f $Group) if ($Group -like '*`**') { Write-Verbose -Message '....Processing wildcard search...' $find = $dataSet | Where-Object { $_.Group -like $Group } } else { Write-Verbose -Message '....Processing exact search...' $find = $dataSet | Where-Object { $_.Group -eq $Group } if ([string]::IsNullOrEmpty($find)) { Write-Verbose -Message '........Processing relaxed search...' $find = $dataSet | Where-Object { $_.Group -like "*$Group*" } } } } elseif ($SubGroup) { Write-Verbose -Message ('Searching for emojis in subgroup: {0}' -f $SubGroup) if ($SubGroup -like '*`**') { Write-Verbose -Message '....Processing wildcard search...' $find = $dataSet | Where-Object { $_.Subgroup -like $SubGroup } } else { Write-Verbose -Message '....Processing exact search...' $find = $dataSet | Where-Object { $_.Subgroup -eq $SubGroup } if ([string]::IsNullOrEmpty($find)) { Write-Verbose -Message '........Processing relaxed search...' $find = $dataSet | Where-Object { $_.Subgroup -like "*$SubGroup*" } } } } elseif ($ShortCode) { # need to properly escape characters in the shortcode string $ShortCode = $ShortCode.Replace("'", "’") Write-Verbose -Message ('Searching for emojis with shortcode: {0}' -f $ShortCode) if ($ShortCode -like '*`**') { Write-Verbose -Message '....Processing wildcard search...' $find = $dataSet | Where-Object { $_.ShortCode -like $ShortCode } } else { Write-Verbose -Message '....Processing exact search...' $find = $dataSet | Where-Object { $_.ShortCode -eq $ShortCode } if ([string]::IsNullOrEmpty($find)) { Write-Verbose -Message '........Processing relaxed search...' $find = $dataSet | Where-Object { $_.ShortCode -like "*$ShortCode*" } } } } elseif ($HexCodePoint) { Write-Verbose -Message ('Searching for emojis with hex code point: {0}' -f $HexCodePoint) Write-Verbose -Message '....Processing exact search...' $find = $dataSet | Where-Object { $_.HexCodePoint -eq $HexCodePoint } if ([string]::IsNullOrEmpty($find)) { Write-Verbose -Message '........Processing relaxed search...' $find = $dataSet | Where-Object { $_.HexCodePoint -like "*$HexCodePoint*" } } } elseif ($Decimal) { Write-Verbose -Message ('Searching for emojis with decimal: {0}' -f $Decimal) Write-Verbose -Message '....Processing exact search...' $decimalCount = $Decimal | Measure-Object | Select-Object -ExpandProperty Count # Initialize the list with emojis that match the first decimal number $emojiList = $dataSet | Where-Object { $_.Decimal -contains $Decimal[0] } Write-Debug -Message ('Found {0} emojis with the first decimal number.' -f $emojiList.Count) if ($emojiList) { if ($decimalCount -eq 1) { Write-Debug -Message 'Only one decimal number was provided.' # special case where there is only one decimal number $find = $emojiList | Where-Object { $_.Decimal.Count -eq 1 } } else { Write-Debug -Message 'Multiple decimal numbers were provided.' # For each additional decimal number, further refine the list foreach ($dec in $Decimal[1..($Decimal.Length - 1)]) { $emojiList = $emojiList | Where-Object { $_.Decimal -contains $dec } } $find = $emojiList } Write-Debug -Message ('Find now contains {0} emojis with the decimal numbers.' -f $find.Count) } else { Write-Debug -Message 'No emojis found with the first decimal number.' } } elseif ($SearchTerm) { Write-Verbose -Message ('Searching for emojis with search term: {0}' -f $SearchTerm) Write-Verbose -Message '....Processing general wildcard search...' $find = $dataSet | Where-Object { $_.Group -like "*$SearchTerm*" -or $_.Subgroup -like "*$SearchTerm*" -or $_.Description -like "*$SearchTerm*" } } } #if_Import-XMLDataSet else { Write-Warning -Message 'pwshEmojiExplorer was unable to source the required data set file.' Write-Warning -Message 'Ensure you have an active internet connection' return } #else_Import-XMLDataSet Write-Debug -Message ('Found {0} emojis' -f $find.Count) if ($IncludeAll -eq $false) { $results = $find | Where-Object { $_.Status -eq 'fully-qualified' } } else { $results = $find } if ($results) { $results | ForEach-Object { $convertedEmoji = $null try { [PSEmoji]$convertedEmoji = ConvertTo-PSEmoji -CustomObject $_ } catch { Write-Warning -Message ('Unable to convert the custom object {0} to a PSEmoji object.' -f $_.Description) } [void]$finalEmojiList.Add($convertedEmoji) } } Write-Debug -Message ('Results: {0}' -f $results.Count) return $finalEmojiList } #Get-Emoji |