Last active
January 4, 2024 10:50
-
-
Save JPRuskin/8e68d6d5d9d826cc3795c6cf90c4cbf6 to your computer and use it in GitHub Desktop.
A quick and dirty set of functions to work with SettleUp.app, along with
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# See: https://docs.google.com/document/d/18mxnyYSm39cbceA2FxFLiOfyyanaBY6ogG7oscgghxU/ for some documentation | |
# and, for some context: https://github.com/cad0p/settleup-stripe/blob/master/functions/index.js | |
param( | |
[ValidateSet("settle-up-live", "settle-up-sandbox")] | |
$script:FirebaseEnvironment = "settle-up-sandbox" | |
) | |
enum currency { | |
GBP | |
USD | |
EUR | |
} | |
enum SettleUpGroupPermission { | |
None = 0 | |
Read = 10 | |
Write = 20 | |
Admin = 30 | |
} | |
enum SettleUpTransactionType { | |
Expense | |
Transfer | |
} | |
# Add convert-datetime-to-epoch here | |
class SettleUpGroupMember { | |
[string]$Id | |
[string]$Name | |
[bool]$Active | |
[float]$DefaultWeight | |
[string]$BankAccount | |
[string]$PhotoUrl | |
[string] ToString() { | |
return $this.Name | |
} | |
SettleUpGroupMember ([string]$GroupId, [string]$Id) { | |
$Member = (Get-SettleUpGroupMembers -Id $GroupId).Where({$_.id -eq $Id -or $_.name -eq $Id}, 1) | |
if ($Member) { | |
$this.Id = $Member.id | |
$this.Name = $Member.name | |
$this.Active = $Member.active | |
$this.DefaultWeight = $Member.defaultWeight | |
$this.BankAccount = $Member.bankAccount | |
$this.PhotoUrl = $Member.photoUrl | |
} else { | |
Write-Error "Member '$Id' not found in group '$GroupId'" -ErrorAction Stop | |
} | |
} | |
} | |
class SettleUpDebt { | |
hidden [string]$GroupId | |
[float]$Amount | |
[SettleUpGroupMember]$From | |
[SettleUpGroupMember]$To | |
SettleUpDebt ([string]$GroupId, [float]$Amount, [SettleUpGroupMember]$From, [SettleUpGroupMember]$To) { | |
$this.GroupId = $GroupId | |
$this.Amount = $Amount | |
$this.From = [SettleUpGroupMember]::new($GroupId, $From) | |
$this.To = [SettleUpGroupMember]::new($GroupId, $To) | |
} | |
} | |
class SettleUpTransaction { | |
[string]$Id | |
[string]$Purpose | |
[SettleUpGroup]$Group | |
[float]$Amount | |
[string]$CurrencyCode | |
[datetime]$DateTime | |
[SettleUpTransactionType]$Type | |
[string]$WhoPaid | |
} | |
class SettleUpInviteLink { | |
[string]$Link | |
[bool]$Active | |
[string]$Hash | |
} | |
class SettleUpGroup { | |
[string]$Id | |
[string]$Name | |
[datetime]$LastChanged | |
[bool]$MinimizeDebts | |
[string]$ConvertedToCurrency | |
[string]$Color | |
[SettleUpGroupMember[]]$Members | |
[SettleUpInviteLink]$InviteLink | |
} | |
function Convert-NotePropertiesToObjects { | |
param( | |
[Parameter(Mandatory, ValueFromPipeline)] | |
[PSCustomObject]$InputObject | |
) | |
process { | |
$InputObject.PSObject.Properties.Where{$_.MemberType -eq 'NoteProperty'} | ForEach-Object { | |
$ReturnObject = @{ | |
id = $_.Name | |
} | |
foreach ($Property in $_.Value.PSObject.Properties.Where{$_.MemberType -eq 'NoteProperty'}) { | |
$ReturnObject[$Property.Name] = $Property.Value | |
} | |
[PSCustomObject]$ReturnObject | |
} | |
} | |
} | |
function Get-FirebaseAuthToken { | |
[CmdletBinding()] | |
param( | |
[Parameter()] | |
[PSCredential]$Credential, | |
[Parameter()] | |
[string]$ApiKey = $( | |
switch ($script:FirebaseEnvironment) { | |
"settle-up-live" { Write-Error "No Key Provided" -ErrorAction Stop } | |
"settle-up-sandbox" { "AIzaSyCfMEZut1bOgu9d1NHrJiZ7ruRdzfKEHbk" } | |
default { Write-Error "Unknown Environment '$($script:FirebaseEnvironment)'" -ErrorAction Stop } | |
} | |
), | |
[Parameter()] | |
[switch]$Force | |
) | |
if (-not $script:FirebaseAuthToken -or ((Get-Date) -gt $script:FirebaseAuthToken.expiry) -or $Force) { | |
if (-not $Credential) { | |
$Credential = Get-Credential -Message "SettleUp User" | |
} | |
$Response = Invoke-RestMethod -Method Post -Uri "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=$ApiKey" -Body @{ | |
email = $Credential.UserName | |
password = $Credential.GetNetworkCredential().Password | |
returnSecureToken = $true | |
} | |
Add-Member -InputObject $Response -MemberType ScriptProperty -Name expiry -Value {(Get-Date).AddSeconds($Response.expiresIn)} -Force | |
$script:FirebaseAuthToken = $Response | |
} # Add token refresh method here? | |
return $script:FirebaseAuthToken.idToken | |
} | |
function Invoke-SettleUpApi { | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory)] | |
[string]$Path, | |
[Parameter()] | |
[string]$Method = "GET", | |
[Parameter()] | |
[string]$ApiKey = $(Get-FirebaseAuthToken), | |
[Parameter()] | |
[object]$Body, | |
[Parameter()] | |
[string]$ContentType = "application/json" | |
) | |
process { | |
$RestArgs = @{ | |
Uri = "https://$($script:FirebaseEnvironment).firebaseio.com/$($Path).json?auth=$($ApiKey)" | |
Method = $Method | |
} | |
Write-Verbose "Invoking $($RestArgs.Method) $($RestArgs.Uri)" | |
if ($Body) { | |
$RestArgs.Body = $Body | |
$RestArgs.ContentType = $ContentType | |
} | |
Invoke-RestMethod @RestArgs | |
} | |
} | |
function Get-SettleUpGroups { | |
[CmdletBinding()] | |
param( | |
# Defaults to current authenticated user, as that is the most likely available scope | |
[string]$UserId = $script:FirebaseAuthToken.localId | |
) | |
process { | |
Invoke-SettleUpApi "userGroups/$UserId" | Convert-NotePropertiesToObjects | |
} | |
} | |
function Get-SettleUpGroup { | |
[CmdletBinding()] | |
param( | |
# [Parameter(ParameterSetName = "Name", Mandatory)] | |
# [string]$Name, | |
[Parameter(ParameterSetName = "Id", Mandatory)] | |
[string]$Id #= (Get-SettleUpGroups).Where{$_.} | |
) | |
process { | |
Invoke-SettleUpApi "groups/$Id" | Add-Member -MemberType NoteProperty -Name id -Value $Id -PassThru | |
} | |
} | |
function Get-SettleUpGroupPermissions { | |
[CmdletBinding()] | |
param( | |
[Parameter(ParameterSetName = "Id", Mandatory)] | |
[string]$Id | |
) | |
process { | |
Invoke-SettleUpApi "permissions/$Id" | Convert-NotePropertiesToObjects | |
} | |
} | |
function Get-SettleUpGroupMembers { | |
param( | |
[Parameter(Mandatory, ValueFromPipelineByPropertyName)] | |
[string]$Id | |
) | |
process { | |
[SettleUpGroupMember[]](Invoke-SettleUpApi "members/$Id" | Convert-NotePropertiesToObjects) | |
} | |
} | |
function Add-SettleUpGroupMember { | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory)] | |
[string]$Id, | |
[ValidateLength(1, 20)] | |
[Parameter(Mandatory)] | |
[string]$Name, | |
[ValidateRange(1, 10)] | |
[uint]$DefaultWeight = 1, | |
# [string]$BankAccount, | |
# [string]$PhotoUrl, | |
[switch]$Inactive | |
) | |
process { | |
$NewMember = @{ | |
name = $Name | |
defaultWeight = "$DefaultWeight" | |
# bankAccount = $BankAccount | |
# photoUrl = $PhotoUrl | |
active = -not $Inactive.ToBool() | |
} | |
if ($NewMember.name -in @(Get-SettleUpGroupMembers -Id $Id).name) { | |
# Set? | |
Write-Warning "Member '$Name' already exists in group '$Id'" | |
} else { | |
$null = Invoke-SettleUpApi "members/$Id" -Method POST -Body ($NewMember | ConvertTo-Json) | |
} | |
} | |
} | |
function Get-SettleUpGroupDebts { | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory, ValueFromPipelineByPropertyName)] | |
[string]$Id | |
) | |
process { | |
Invoke-SettleUpApi "debts/$Id" | ForEach-Object { | |
[SettleUpDebt]::new($Id, $_.amount, $_.from, $_.to) | |
} | |
} | |
} | |
function Get-SettleUpTransactions { | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory, ValueFromPipelineByPropertyName)] | |
[string]$Id | |
) | |
process { | |
Invoke-SettleUpApi "transactions/$Id" | Convert-NotePropertiesToObjects | |
} | |
} | |
function New-SettleUpTransaction { | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory)] | |
[string]$GroupId, | |
[ValidateLength(1, 128)] | |
[string]$Purpose, | |
[datetime]$Date = (Get-Date), | |
[Parameter(Mandatory)] | |
[decimal]$Amount, | |
[ValidateSet("expense", "transfer")] | |
[string]$Type = "expense", | |
[string]$Payer, | |
[string]$Currency = "GBP" | |
) | |
# Not currently functional | |
begin { | |
$GroupMembers = Get-SettleUpGroupMembers -Id $GroupId | |
} | |
process { | |
$Transaction = @{ | |
# category = '' | |
currencyCode = $Currency.ToUpper() | |
dateTime = ($Date - (Get-Date 01.01.1970)).TotalMilliseconds | |
fixedExchangeRate = $true | |
items = @( | |
@{ | |
amount = $Amount | |
forWhom = @( | |
) | |
} | |
) | |
} | |
if ($Purpose) { | |
$Transaction.purpose = $Purpose | |
} | |
Invoke-SettleUpApi "transactions/$Id" -Method POST -Body ($Transaction | ConvertTo-Json) | |
} | |
} | |
function Import-SplitwiseExport { | |
param( | |
[Parameter(Mandatory, ParameterSetName="File")] | |
[string]$Path, | |
[Parameter(Mandatory, ParameterSetName="Object", ValueFromPipeline)] | |
$InputObject, | |
[Parameter(Mandatory)] | |
[ValidateScript({[bool](Get-SettleUpGroup -Id $_)})] | |
[string]$GroupId | |
) | |
begin { | |
if ($PSCmdlet.ParameterSetName -eq "File") { | |
$Headers = (Get-Content $Path -TotalCount 1).Split(',') | |
$InputObject = Import-Csv $Path -Header $Headers | Select-Object -Skip 2 | |
} | |
} | |
process { | |
foreach ($Transaction in $InputObject) { | |
$PaidBy = @{} | |
if (-not $GroupMembers) { | |
$GroupMembers = Get-SettleUpGroupMembers -Id $GroupId | |
} | |
if ($GroupMembers.name -notcontains $PaidBy) { | |
New-SettleUpGroupMember -Id $GroupId -Name $PaidBy | |
$GroupMembers = Get-SettleUpGroupMembers -Id $GroupId | |
} | |
$TransactionRequest = @{ | |
Purpose = $Transaction.Description | |
Amount = $Transaction.Cost | |
Date = Get-Date $Transaction.Date | |
Payer = $GroupMembers.Where{$_.name -eq $PaidBy}.id | |
Currency = $Transaction.Currency | |
} | |
New-SettleUpTransaction @TransactionRequest | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment