diff --git a/bot.ps1 b/bot.ps1 index 66906b1..db540cf 100644 --- a/bot.ps1 +++ b/bot.ps1 @@ -1,70 +1,224 @@ -param ( - [Parameter(Mandatory=$true)] - [string] - $HomeServer, - - [Parameter(Mandatory=$true)] - [string] - $User, - - [Parameter(Mandatory=$true)] - [securestring] - $AccessToken -) - -function Send-MatrixEvent { - param ( - [Parameter(Mandatory=$true)] - [string] - $RoomId, - - [Parameter(Mandatory=$true)] - [string] - $Event, - - [Parameter(Mandatory=$true)] - [string] - $EventType - ) - - #txn_id should be unique per client, so we use timestamp+random - $txn_id = '{0}{1}' -f (Get-Date -UFormat '%s'),(Get-Random) - - $uri = '{0}/_matrix/client/r0/rooms/{1}/send/{2}/{3}' -f $HomeServer,$RoomId,$EventType,$txn_id - - $header_splat = @{ - Authentication = 'Bearer' - Token = $AccessToken - ContentType = 'application/json' - } - $http_splat = @{ - Uri = $uri - Method = 'Put' - Body = $Event - } - - $response = Invoke-RestMethod @header_splat @http_splat - if($response.event_id) { - Write-Output ('Event {0} sent to room {1}' -f $response.event_id,$RoomId) - } -} -function Send-MatrixNotice { - param ( - [Parameter(Mandatory=$true)] - [string] - $RoomId, - - [Parameter(Mandatory=$true)] - [string] - $Message - ) - - $event = @{ - msgtype = 'm.notice' - body = $Message - } | ConvertTo-Json -Compress - - Send-MatrixEvent -RoomId $RoomId -Event $event -EventType 'm.room.message' -} - -Send-MatrixNotice -RoomId '!amKUIaaKdERatZsJgB:matrix.org' -Message ('asef :) {0}' -f (Get-Date)) \ No newline at end of file +param ( + [Parameter(Mandatory=$true)] + [string] + $HomeServer, + + [Parameter(Mandatory=$true)] + [string] + $User, + + [Parameter(Mandatory=$true)] + [securestring] + $AccessToken +) + +function Send-MatrixEvent { + param ( + [Parameter(Mandatory=$true)] + [string] + $RoomId, + + [Parameter(Mandatory=$true)] + [string] + $Event, + + [Parameter(Mandatory=$true)] + [string] + $EventType + ) + + #txn_id should be unique per client, so we use timestamp+random + $txn_id = '{0}{1}' -f (Get-Date -UFormat '%s'),(Get-Random) + + $uri = '{0}/_matrix/client/r0/rooms/{1}/send/{2}/{3}' -f $HomeServer,$RoomId,$EventType,$txn_id + + $header_splat = @{ + Authentication = 'Bearer' + Token = $AccessToken + ContentType = 'application/json' + } + $http_splat = @{ + Uri = $uri + Method = 'Put' + Body = $Event + } + + $response = Invoke-RestMethod @header_splat @http_splat + if($response.event_id) { + Write-Host ('Event {0} sent to room {1}' -f $response.event_id,$RoomId) + } +} +function Send-MatrixNotice { + param ( + [Parameter(Mandatory=$true)] + [string] + $RoomId, + + [Parameter(Mandatory=$true)] + [string] + $Body, + + [Parameter(Mandatory=$false)] + [string] + $FormattedBody + ) + + $event = @{ + msgtype = 'm.notice' + body = $Body + } + + if($FormattedBody) { + $event += @{ + format = 'org.matrix.custom.html' + formatted_body = $FormattedBody + } + } + + $event_json = $event | ConvertTo-Json -Compress + + Send-MatrixEvent -RoomId $RoomId -Event $event_json -EventType 'm.room.message' +} +function Compare-Timestamps { + param ( + [Parameter(Mandatory=$true)] + [Int64] + $OriginServerTs + ) + + $current_time = (Get-Date).ToUniversalTime() + + #$OriginServerTs is milliseconds since 1970-01-01 (epoch time in milliseconds) + $original_time = Get-Date -Date ((Get-Date -Date '1970-01-01') + [timespan]::FromMilliseconds($OriginServerTs)) + + #return the difference + Write-Output ($current_time - $original_time) +} +function ConvertTo-HumanReadableTimespan { + param ( + [parameter(Mandatory=$true)] + [timespan] + $TimeSpan + ) + + switch($TimeSpan) { + Default { + $output = '{0} days' -f [System.Math]::Round($TimeSpan.TotalDays, 2) + } + {$PSItem.TotalHours -lt 24} { + $output = '{0} h' -f [System.Math]::Round($TimeSpan.TotalHours, 2) + } + {$PSItem.TotalMinutes -lt 120} { + $output = '{0} min' -f [System.Math]::Round($TimeSpan.TotalMinutes, 2) + } + {$PSItem.TotalSeconds -lt 120} { + $output = '{0} s' -f [System.Math]::Round($TimeSpan.TotalSeconds, 2) + } + {$PSItem.TotalMilliseconds -lt 10000} { + $output = '{0} ms' -f [System.Math]::Round($TimeSpan.TotalMilliseconds, 0) + } + } + + Write-Output $output +} +function Join-Pong { + param ( + [Parameter(Mandatory=$true)] + [string] + $RoomId, + + [Parameter(Mandatory=$true)] + [string] + $PingEventId, + + [Parameter(Mandatory=$true)] + [string] + $SenderMxId, + + [Parameter(Mandatory=$true)] + [string] + $ReadableTimespan, + + [Parameter(Mandatory=$false)] + [string] + $Ball + ) + + if($Ball) { + $padded_ball = $Ball+' ' + } + + $body = '{0}: Pong! (ping {1}took {2} to arrive)' -f $SenderMxId,$padded_ball,$ReadableTimespan + $formatted_body = '{0}: Pong! ' -f $SenderMxId + $formatted_body += '(ping {2}took {3} to arrive)' -f $RoomId,$PingEventId,$padded_ball,$ReadableTimespan + + return @{ + Body = $body + FormattedBody = $formatted_body + } +} +function Open-MatrixEvent { + param ( + [Parameter(Mandatory=$true)] + $Event, + + [Parameter(Mandatory=$true)] + $RoomId + ) + + #the "ball" is a string returned by the bot + if($Event.content.msgtype -eq 'm.text' -and $Event.content.body -match '!ping( (?.*))?') { + $difference = Compare-Timestamps -OriginServerTs $Event.origin_server_ts + $readable_timespan = ConvertTo-HumanReadableTimespan -TimeSpan $difference + + #$bodies contains a hashtable with keys Body and FormattedBody + $bodies = Join-Pong -RoomId $RoomId -PingEventId $Event.event_id -SenderMxId $Event.sender -ReadableTimespan $readable_timespan -Ball $Matches.ball + + Send-MatrixNotice -RoomId $RoomId @bodies + } +} +function Start-MatrixSync { + param ( + [string] + $Token, + + [int] + $Timeout = 30000 + ) + + $uri = '{0}/_matrix/client/r0/sync?timeout={1}' -f $HomeServer,$Timeout + if($Token) { + $uri += '&since={0}' -f $Token + } + + $header_splat = @{ + Authentication = 'Bearer' + Token = $AccessToken + ContentType = 'application/json' + } + $http_splat = @{ + Uri = $uri + } + + $response = Invoke-RestMethod @header_splat @http_splat + + #.PSObject.Properties because the rooms under .join are [NoteProperty] + $room_ids = $response.rooms.join.PSObject.Properties.Name + foreach($room_id in $room_ids) { + $events = $response.rooms.join.$room_id.timeline.events + foreach($event in $events) { + Open-MatrixEvent -Event $event -RoomId $room_id + } + } + + Write-Output $response.next_batch +} + +#Send-MatrixNotice -RoomId '!amKUIaaKdERatZsJgB:matrix.org' -Message ('asef :) {0}' -f (Get-Date)) + +#start sync loop +while($true) { + #use the token of the last sync + #initial token is $null + $token = Start-MatrixSync -Token $token +} \ No newline at end of file