Skip to content

Commit

Permalink
(GH-1) Initial implementation
Browse files Browse the repository at this point in the history
- Make use of the GItter Streaming API
https://developer.gitter.im/docs/streaming-api
- Listen for messages from someone other than the bot identity and respond accordingly
- Wrap everything in a ``` tag, so that it formats nicely
  • Loading branch information
gep13 committed Feb 24, 2019
1 parent 898ece9 commit 7bbbe67
Show file tree
Hide file tree
Showing 4 changed files with 365 additions and 46 deletions.
241 changes: 206 additions & 35 deletions PoshBot.Gitter.Backend/Classes/GitterBackend.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@

class GitterBackend : Backend {

# Constructor
GitterBackend ([string]$Token, [string]$RoomId) {
$config = [ConnectionConfig]::new()
Expand All @@ -14,11 +12,10 @@ class GitterBackend : Backend {

# Connect to the chat network
[void]Connect() {
$this.LogInfo('Connecting to backend')
$this.LogInfo('Connecting to backend...')
$this.Connection.Connect()
$this.BotId = $this.GetBotIdentity()
$this.LoadUsers()
$this.LoadRooms()
}

# Disconnect from the chat network
Expand All @@ -39,65 +36,239 @@ class GitterBackend : Backend {
}

# Receive a message from the chat network
[Message]ReceiveMessage() {
# Implement logic to receive a message from the
# chat network using network-specific APIs.
[Message[]]ReceiveMessage() {
$messages = New-Object -TypeName System.Collections.ArrayList

# This method assumes that a connection to the chat network
# has already been made using $this.Connect()
try {
# Read the output stream from the receive job and get any messages since our last read
[string[]]$jsonResults = $this.Connection.ReadReceiveJob()

# This method should return quickly (no blocking calls)
# so PoshBot can continue in its message processing loop
return $null
}
foreach ($jsonResult in $jsonResults) {
if ($null -ne $jsonResult -and $jsonResult -ne [string]::Empty) {
#Write-Debug -Message "[SlackBackend:ReceiveMessage] Received `n$jsonResult"
$this.LogDebug('Received message', $jsonResult)

# Send a message back to the chat network
[void]SendMessage([Response]$Response) {
# Implement logic to send a message
# back to the chat network
$gitterMessage = @($jsonResult | ConvertFrom-Json)

$msg = [Message]::new()
$msg.From = $gitterMessage.fromUser.id
$msg.Text = $gitterMessage.text
$msg.Time = $gitterMessage.sent

# ** Important safety tip, don't cross the streams **
# Only return messages that didn't come from the bot
# else we'd cause a feedback loop with the bot processing
# it's own responses
if (-not $this.MsgFromBot($msg.From)) {
$messages.Add($msg) > $null
}
}
}
} catch {
Write-Error $_
}

return $messages
}

# Return a user object given an Id
[Person]GetUser([string]$UserId) {
# Return a [Person] instance (or a class derived from [Person])
return $null
$user = $this.Users[$UserId]
if (-not $user) {
$this.LogDebug([LogSeverity]::Warning, "User [$UserId] not found. Refreshing users")
$this.LoadUsers()
$user = $this.Users[$UserId]
}

if ($user) {
$this.LogDebug("Resolved user [$UserId]", $user)
} else {
$this.LogDebug([LogSeverity]::Warning, "Could not resolve user [$UserId]")
}
return $user
}

# Get all user info by their ID
[hashtable]GetUserInfo([string]$UserId) {
$user = $null
if ($this.Users.ContainsKey($UserId)) {
$user = $this.Users[$UserId]
} else {
$this.LogDebug([LogSeverity]::Warning, "User [$UserId] not found. Refreshing users")
$this.LoadUsers()
$user = $this.Users[$UserId]
}

if ($user) {
$this.LogDebug("Resolved [$UserId] to [$($user.UserName)]")
return $user.ToHash()
} else {
$this.LogDebug([LogSeverity]::Warning, "Could not resolve channel [$UserId]")
return $null
}
}

# Add a reaction to an existing chat message
[void]AddReaction([Message]$Message, [ReactionType]$Type, [string]$Reaction) {
$this.LogDebug("Reactions are not yet supported in Gitter - Ignoring")
# TODO: Must build out once Gitter supports it.
}

# Remove a reaction from an existing chat message
[void]RemoveReaction([Message]$Message, [ReactionType]$Type, [string]$Reaction) {
$this.LogDebug("Reactions are not yet supported in Gitter - Ignoring")
# TODO: Must build out once Gitter supports it.
}

# Send a message back to Gitter
[void]SendMessage([Response]$Response) {
# Process any custom responses
$this.LogVerbose("[$($Response.Data.Count)] custom responses")
$NL = [System.Environment]::NewLine
foreach ($customResponse in $Response.Data) {
[string]$sendTo = $Response.To
if ($customResponse.DM) {
$sendTo = "@$($this.UserIdToUsername($Response.MessageFrom))"
}

switch -Regex ($customResponse.PSObject.TypeNames[0]) {
'(.*?)PoshBot\.Card\.Response' {
$this.LogDebug('Custom response is [PoshBot.Card.Response]')
$t = '```' + $NL + $customResponse.Text + '```'
$this.SendGitterMessage($t)
break
}
'(.*?)PoshBot\.Text\.Response' {
$this.LogDebug('Custom response is [PoshBot.Text.Response]')
$t = '```' + $NL + $customResponse.Text + '```'
$this.SendGitterMessage($t)
break
}
'(.*?)PoshBot\.File\.Upload' {
$this.LogDebug('Custom response is [PoshBot.File.Upload]')
$this.LogVerbose('Not currently implemented')
break
}
}
}

if ($Response.Text.Count -gt 0) {
foreach ($t in $Response.Text) {
$this.LogDebug("Sending response back to Gitter channel [$($Response.To)]", $t)
$t = '```' + $NL + $t + '```'
$this.SendGitterMessage($t)
}
}
}

[void]SendGitterMessage([string]$message) {
$token = $this.Connection.Config.Credential.GetNetworkCredential().Password
$roomId = $this.Connection.Config.Endpoint
$restParams = @{
Method = 'Post'
ContentType = 'application/json'
Verbose = $false
Headers = @{
Authorization = "Bearer $($token)"
}
Uri = "https://api.gitter.im/v1/rooms/$roomId/chatMessages"
Body = @{
text = "$message"
} | ConvertTo-Json
}

$gitterResponse = Invoke-RestMethod @restParams
}

# Resolve a user name to user id
[string]UsernameToUserId([string]$Username) {
# Do something using the chat network APIs to
# resolve a username to an Id and return it
return '12345'
$Username = $Username.TrimStart('@')
$user = $this.Users.Values | Where-Object {$_.UserName -eq $Username}
$id = $null

if ($user) {
$id = $user.Id
} else {
# User each doesn't exist or is not in the local cache
# Refresh it and try again
$this.LogDebug([LogSeverity]::Warning, "User [$Username] not found. Refreshing users")
$this.LoadUsers()
$user = $this.Users.Values | Where-Object {$_.Nickname -eq $Username}

if (-not $user) {
$id = $null
} else {
$id = $user.Id
}
}
if ($id) {
$this.LogDebug("Resolved [$Username] to [$id]")
} else {
$this.LogDebug([LogSeverity]::Warning, "Could not resolve user [$Username]")
}

return $id
}

# Resolve a user ID to a username/nickname
[string]UserIdToUsername([string]$UserId) {
# Do something using the network APIs to
# resolve a username from an Id and return it
return 'JoeUser'
$name = $null
if ($this.Users.ContainsKey($UserId)) {
$name = $this.Users[$UserId].UserName
} else {
$this.LogDebug([LogSeverity]::Warning, "User [$UserId] not found. Refreshing users")
$this.LoadUsers()
$name = $this.Users[$UserId].UserName
}

if ($name) {
$this.LogDebug("Resolved [$UserId] to [$name]")
} else {
$this.LogDebug([LogSeverity]::Warning, "Could not resolve user [$UserId]")
}

return $name
}

[void]LoadUsers() {
$this.LogDebug('Getting Gitter Room Users')
# Get the bot identity Id
[string]GetBotIdentity() {
$id = $this.Connection.LoginData.id
$this.LogVerbose("Bot identity is [$id]")
return $id
}

#$allUsers = Get-Slackuser -Token $this.Connection.Config.Credential.GetNetworkCredential().Password -Verbose:$false
# Determine if incoming message was from the bot
[bool]MsgFromBot([string]$From) {
$frombot = ($this.BotId -eq $From)
if ($fromBot) {
$this.LogDebug("Message is from bot [From: $From == Bot: $($this.BotId)]. Ignoring")
} else {
$this.LogDebug("Message is not from bot [From: $From <> Bot: $($this.BotId)]")
}
return $fromBot
}

$token = $this.Config.Credential.GetNetworkCredential().Password
$roomId = $this.Config.Endpoint
[void]LoadUsers() {
$this.LogVerbose('Getting Gitter Room Users...')

$token = $this.Connection.Config.Credential.GetNetworkCredential().Password
$roomId = $this.Connection.Config.Endpoint
$restParams = @{
ContentType = 'application/json'
Verbose = $false
Headers = @{
Verbose = $false
Headers = @{
Authorization = "Bearer $($token)"
}
Uri = "https://api.gitter.im/v1/rooms/$roomId/users"
Uri = "https://api.gitter.im/v1/rooms/$roomId/users"
}

$allUsers = Invoke-RestMethod @restParams

$this.LogDebug("[$($allUsers.Count)] users returned")
$this.LogVerbose("[$($allUsers.Count)] users returned")
$allUsers | ForEach-Object {
$user = [GitterPerson]::new()
$user.Id = $_.id
$user.UserName = $_.username
$user.DisplayName = $_.displayname
$user.Url = $_.url
$user.AvatarUrl = $_.avatarUrl
Expand All @@ -108,7 +279,7 @@ class GitterBackend : Backend {
$user.GV = $_.gv
if (-not $this.Users.ContainsKey($_.ID)) {
$this.LogDebug("Adding user [$($_.ID):$($_.Name)]")
$this.Users[$_.ID] = $user
$this.Users[$_.ID] = $user
}
}

Expand Down
Loading

0 comments on commit 7bbbe67

Please sign in to comment.