NameGender.psm1
|
Set-StrictMode -Version Latest $script:DefaultBaseUrl = 'https://namegender.com/api/v1' # The API's bulk limit; longer pipelines are sent in chunks of this size. $script:Chunk = 100 function Resolve-NameGenderKey { param([string] $ApiKey) if ($ApiKey) { return $ApiKey } if ($env:NAMEGENDER_API_KEY) { return $env:NAMEGENDER_API_KEY } throw 'No API key. Pass -ApiKey or set $env:NAMEGENDER_API_KEY (free key at https://namegender.com).' } function Invoke-NameGenderApi { param( [Parameter(Mandatory)] [string] $Path, [Parameter(Mandatory)] [string] $ApiKey, [string] $Method = 'POST', [hashtable] $Body ) $baseUrl = if ($env:NAMEGENDER_BASE_URL) { $env:NAMEGENDER_BASE_URL.TrimEnd('/') } else { $script:DefaultBaseUrl } $request = @{ Uri = $baseUrl + $Path Method = $Method Headers = @{ Authorization = "Bearer $ApiKey"; Accept = 'application/json' } ErrorAction = 'Stop' } if ($Body) { $request.ContentType = 'application/json; charset=utf-8' # UTF-8 bytes, not a string: Windows PowerShell 5.1 would otherwise send # names such as Ayşe in the system code page. $request.Body = [System.Text.Encoding]::UTF8.GetBytes(($Body | ConvertTo-Json -Depth 5 -Compress)) } try { Invoke-RestMethod @request } catch { # The API's error body is { error, message, request_id, docs }. Surface the # reason code so scripts can branch on it instead of on the message text. $details = $null if ($_.ErrorDetails -and $_.ErrorDetails.Message) { try { $details = $_.ErrorDetails.Message | ConvertFrom-Json } catch { $details = $null } } if ($details -and $details.PSObject.Properties['error']) { $exception = [System.Exception]::new("$($details.message) ($($details.error))") $exception.Data['error'] = $details.error $exception.Data['request_id'] = $details.request_id throw $exception } throw } } function Get-NameGender { <# .SYNOPSIS Looks up the gender behind names, email addresses or usernames. .DESCRIPTION One value calls the single endpoint; several values (from -Name or the pipeline) are sent in bulk requests of up to 100. Every value costs one credit, unknown results included. Results carry gender, probability, sample_size, country, confidence and source. .EXAMPLE Get-NameGender Andrea -Country IT .EXAMPLE Get-Content names.txt | Get-NameGender | Export-Csv genders.csv -NoTypeInformation .EXAMPLE Get-NameGender -Email jane.doe@example.com #> [CmdletBinding(DefaultParameterSetName = 'Name')] param( [Parameter(ParameterSetName = 'Name', Mandatory, Position = 0, ValueFromPipeline)] [AllowEmptyString()] [string[]] $Name, [Parameter(ParameterSetName = 'Email', Mandatory, ValueFromPipeline)] [AllowEmptyString()] [string[]] $Email, [Parameter(ParameterSetName = 'Username', Mandatory, ValueFromPipeline)] [AllowEmptyString()] [string[]] $Username, # Two-letter country code. Andrea is male in Italy and female in Germany. [ValidatePattern('^[A-Za-z]{2}$')] [string] $Country, # Language tag such as it-IT; its region is the country when -Country is absent. [string] $Locale, # End user IP address; its country is used when neither -Country nor a regional -Locale is sent. [string] $Ip, # Return the more likely gender even when the evidence is weak. [switch] $BestGuess, [string] $ApiKey ) begin { $key = Resolve-NameGenderKey $ApiKey $values = [System.Collections.Generic.List[string]]::new() $type = $PSCmdlet.ParameterSetName.ToLowerInvariant() $options = @{} if ($Country) { $options.country = $Country.ToUpperInvariant() } if ($Locale) { $options.locale = $Locale } if ($Ip) { $options.ip = $Ip } if ($BestGuess) { $options.best_guess = $true } } process { # Blank lines are allowed in (Get-Content gives them) and dropped here. $batch = switch ($type) { 'email' { $Email } 'username' { $Username } default { $Name } } foreach ($value in $batch) { $trimmed = "$value".Trim() # Empty lines are not sent: they would be rejected and cost nothing useful. if ($trimmed) { $values.Add($trimmed) } } } end { if ($values.Count -eq 0) { return } if ($values.Count -eq 1) { $path = if ($type -eq 'name') { '/gender' } else { "/gender/$type" } $body = $options.Clone() $body[$type] = $values[0] Invoke-NameGenderApi -Path $path -ApiKey $key -Body $body return } for ($i = 0; $i -lt $values.Count; $i += $script:Chunk) { $body = $options.Clone() $body.names = @($values.GetRange($i, [Math]::Min($script:Chunk, $values.Count - $i))) $body.type = $type $response = Invoke-NameGenderApi -Path '/gender/bulk' -ApiKey $key -Body $body $response.results } } } function Get-NameGenderCountry { <# .SYNOPSIS Which countries a name is recorded in. .DESCRIPTION Not a country-of-origin or ethnicity inference. registrations is counted volume from the countries that publish counted birth statistics; attested_in is presence with no weight. Show basis.note next to any percentage. One credit per call. #> [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [string] $Name, [ValidateRange(1, 100)] [int] $Limit = 25, [string] $ApiKey ) $key = Resolve-NameGenderKey $ApiKey Invoke-NameGenderApi -Path '/gender/countries' -ApiKey $key -Body @{ name = $Name; limit = $Limit } } function Get-NameGenderAccount { <# .SYNOPSIS Credit balance and account status. Free: costs no credit. #> [CmdletBinding()] param([string] $ApiKey) $key = Resolve-NameGenderKey $ApiKey Invoke-NameGenderApi -Path '/me' -ApiKey $key -Method 'GET' } Export-ModuleMember -Function Get-NameGender, Get-NameGenderCountry, Get-NameGenderAccount |