Skip to content

Instantly share code, notes, and snippets.

@JPRuskin
Last active January 4, 2024 10:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JPRuskin/8e68d6d5d9d826cc3795c6cf90c4cbf6 to your computer and use it in GitHub Desktop.
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
# 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