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
}