diff --git a/bot.ps1 b/bot.ps1 index 15b391e..acda622 100644 --- a/bot.ps1 +++ b/bot.ps1 @@ -5,13 +5,19 @@ param ( [Parameter(Mandatory=$true)] [string] - $User, + $UserId, [Parameter(Mandatory=$true)] [securestring] $AccessToken ) +$account_data_type_prefix = 'de.lubiland.pingposh.' +$account_data_types = @{ + next_batch = $account_data_type_prefix+'next_batch' +} + + function Send-MatrixEvent { param ( [Parameter(Mandatory=$true)] @@ -174,7 +180,7 @@ function Join-Pong { FormattedBody = $formatted_body } } -function Open-MatrixEvent { +function Open-Event { param ( [Parameter(Mandatory=$true)] $Event, @@ -195,11 +201,75 @@ function Open-MatrixEvent { Send-Pong -RoomId $RoomId @bodies -OriginHomeServer $origin_homeserver -Duration $difference.TotalMilliseconds -PingEventId $Event.event_id } } -function Start-MatrixSync { +function Get-MatrixAccountData { + param ( + [Parameter(Mandatory=$true)] + [string] + $Type + ) + + $uri = '{0}/_matrix/client/r0/user/{1}/account_data/{2}' -f $HomeServer,$UserId,$Type + + $header_splat = @{ + Authentication = 'Bearer' + Token = $AccessToken + ContentType = 'application/json' + } + $http_splat = @{ + Uri = $uri + Method = 'Get' + } + + $response = Invoke-RestMethod @header_splat @http_splat + return $response +} +function Set-MatrixAccountData { + param ( + [Parameter(Mandatory=$true)] + [string] + $Type, + + [Parameter(Mandatory=$true)] + [System.Object] + $Content + ) + + $uri = '{0}/_matrix/client/r0/user/{1}/account_data/{2}' -f $HomeServer,$UserId,$Type + + $header_splat = @{ + Authentication = 'Bearer' + Token = $AccessToken + ContentType = 'application/json' + } + $http_splat = @{ + Uri = $uri + Method = 'Put' + Body = $Content | ConvertTo-Json -Compress + } + + Invoke-RestMethod @header_splat @http_splat | Out-Null +} +function Get-NextBatchToken { + $account_data = Get-MatrixAccountData -Type $account_data_types.next_batch + return $account_data.next_batch +} +function Set-NextBatchToken { + param ( + [Parameter(Mandatory=$true)] + [string] + $Token + ) + + Set-MatrixAccountData -Type $account_data_types.next_batch -Content @{next_batch = $Token} +} +function Invoke-MatrixSync { param ( [string] $Token, + [string] + $FilterId, + [int] $Timeout = 30000 ) @@ -208,6 +278,9 @@ function Start-MatrixSync { if($Token) { $uri += '&since={0}' -f $Token } + if($FilterId) { + $uri += '&filter={0}' -f $FilterId + } $header_splat = @{ Authentication = 'Bearer' @@ -225,17 +298,105 @@ function Start-MatrixSync { 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 + Open-Event -Event $event -RoomId $room_id } } - Write-Output $response.next_batch + return $response.next_batch +} +function Get-MatrixFilterId { + param ( + [System.Object] + $Filter + ) + + $uri = '{0}/_matrix/client/r0/user/{1}/filter' -f $HomeServer,$UserId + + $header_splat = @{ + Authentication = 'Bearer' + Token = $AccessToken + ContentType = 'application/json' + } + $http_splat = @{ + Uri = $uri + Method = 'Post' + #a filter can be up to 3 layers deep according to spec + #https://matrix.org/docs/spec/client_server/r0.6.0#filtering + Body = $Filter | ConvertTo-Json -Compress -Depth 3 + } + + $response = Invoke-RestMethod @header_splat @http_splat + + return $response.filter_id +} +function Get-FilterId { + $filter = @{ + event_fields = @( + #all fields used in Open-Event + 'event_id' + 'sender' + 'origin_server_ts' + 'content.msgtype' + 'content.body' + ) + presence = @{ + #we don't use presence information + types = @('invalid') + } + account_data = @{ + #we only retrieve account_data via the account_data api endpoint + types = @('invalid') + } + room = @{ + ephemeral = @{ + #we don't use ephemeral events like typing or read receipts + types = @('invalid') + } + state = @{ + #we don't use room state + types = @('invalid') + } + timeline = @{ + #exclude ourself + not_senders = @( + $UserId + ) + types = @( + #to receive !ping commands + 'm.room.message' + ) + } + account_data = @{ + #we don't use room specific account_data + types = @('invalid') + } + } + } + + $filter_id = Get-MatrixFilterId -Filter $filter + return $filter_id +} +function Start-MatrixSync { + #get last used next_batch from account_data + $token = Get-NextBatchToken + + #get /sync filter + $filter_id = Get-FilterId + + Try { + #try first sync with the old token + $token = Invoke-MatrixSync -Token $token -FilterId $filter_id + } Catch { + #if it fails start a fresh sync reset $token + $token = $null + } + + while($true) { + #use $token of the previous sync + Set-NextBatchToken -Token $token + $token = Invoke-MatrixSync -Token $token -FilterId $filter_id + } } -#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 +Start-MatrixSync \ No newline at end of file