Gab.psm1

$GabRoot = "https://gab.ai"
$HttpArgs = @{
    UseBasicParsing = $true
    TimeoutSec = 30
    MaximumRedirection = 3
    WebSession = $Global:GabSession
}
$Context = @{
    AuthUser = $Null
}

#.SYNOPSIS
# Authenticates with the Gab.ai service and stores the session information
# in memory in a global variable so that future calls will be authenticated.
function Connect-Gab {

    [CmdletBinding()]
    param (

        # The username (without the @) to authenticate as
        [Parameter(Mandatory=$true)]
        [String]$Username,

        # The password for authentication
        [Parameter(Mandatory=$true)]
        [String]$Password

    )
    
    # Set up a new, empty session container
    $Global:GabSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession
    $HttpArgs.WebSession = $Global:GabSession

    # Initial request creates the session variable, cookie container, etc.
    # Also will include a _token variable in the response needed to auth.
    $Response = Invoke-WebRequest @HttpArgs "$GabRoot/auth/login"
    if ($Response.Content -match '<input type="hidden" name="_token" value="(.+?)"') {
        $AuthToken = $Matches[1]
    }

    # Subsequent request (a POST) sends the credentials, with the token from the initial request,
    # and the response will set the cookies needed to maintain authentication.
    $Response = Invoke-WebRequest @HttpArgs "$GabRoot/auth/login" -Method POST -Body @{
        _token = $AuthToken
        username = $Username
        password = $Password
        remember = 'on'
    }

    # There's also a JSON object in the HTML that represents the logged-in user
    # Not currently using this for anything in PowerShell, but we'll spit it out
    # anyway as proof that we are logged in.
    if ($Response.Content -match '(?m)^\s*window\.authUser = ({.*?});\s*$') {
        
        $AuthUserJson = $Matches[1]
        $Context.AuthUser = ConvertFrom-Json $AuthUserJson

        Write-Output $Context.AuthUser

    }
}

#.SYNOPSIS
# Shows information about the currently logged-in account.
function Get-GabAccount {

    [CmdletBinding()]
    param(
    )

    Write-Output $Context.AuthUser

}

#.SYNOPSIS
# Gets the most recent notifications for the logged-in user, which includes recent
# mentions, follows, likes, etc.
function Get-GabNotification {

    [CmdletBinding()]
    param(

        [Parameter()]
        [ValidateSet('All', 'Mentions', 'Likes', 'Reposts', 'Follows', 'Spam')]
        [String]$Type = 'All'

    )

    # The API accepts these filter values as integers, except for spam for some reason
    # For now, rather than doing a mapping table, I'll just use if/else.
    $TypeArg = 'null'
    if ($Type -eq 'Mentions') { $TypeArg = 1 }
    elseif ($Type -eq 'Likes') { $TypeArg = 2 }
    elseif ($Type -eq 'Reposts' ) { $TypeArg = 3 }
    elseif ($Type -eq 'Follows' ) { $TypeArg = 4 }
    elseif ($Type -eq 'Spam' ) { $TypeArg = 'spam' }

    if ($Global:GabSession) {

        $Response = Invoke-WebRequest @HttpArgs "$GabRoot/api/notifications?type=$TypeArg"
        $ResponseObj = $Response.Content | ConvertFrom-Json

        Write-Output $ResponseObj.Data

    }
    else {
        Write-Error "No active session. Use Connect-Gab first."
    }

}

#.SYNOPSIS
# Gets the most recent events in the home feed, which includes posts by users that
# the logged-in user follows.
function Get-GabFeed {

    [CmdletBinding()]
    param(

        # If specified, the named user's feed is returned instead of the home feed.
        [Parameter()]
        [String]$User

    )

    if ($Global:GabSession) {

        $FeedUri = "$GabRoot/feed"
        if ($User) {
            $FeedUri += "/$User"
        }

        $Response = Invoke-WebRequest @HttpArgs $FeedUri
        $ResponseObj = $Response.Content | ConvertFrom-Json

        Write-Output $ResponseObj.Data

    }
    else {
        Write-Error "No active session. Use Connect-Gab first."
    }

}

#.SYNOPSIS
# Gets information about the specified user profile.
function Get-GabUser {

    [CmdletBinding()]
    param(
        
        # The name of the user (without the @) to retrieve details for.
        [Parameter(Position=1, Mandatory=$true)]
        [String]$Name
    )

    if ($Global:GabSession) {

        $Response = Invoke-WebRequest @HttpArgs "$GabRoot/users/$Name"
        $ResponseObj = $Response.Content | ConvertFrom-Json

        Write-Output $ResponseObj

    }
    else {
        Write-Error "No active session. Use Connect-Gab first."
    }

}

#.SYNOPSIS
# Follows the specified user so that their posts will show up in the feed.
function Add-GabFollow {

    [CmdletBinding(DefaultParameterSetName='ByID', SupportsShouldProcess=$true)]
    param(
        
        # The ID of the user to follow
        [Parameter(ParameterSetName='ByID', Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
        [Int64]$ID,

        # The name of the user (without the @) to follow
        [Parameter(ParameterSetName='ByName', Position=1, Mandatory=$true)]
        [String]$Name
    )

    process {

        if ($Global:GabSession) {

            # Follow/unfollow APIs currently require user id, not name
            if ($Name) {
                $User = Get-GabUser -Name:$Name
                $ID = $User.id
            }

            if ($ID -eq $Context.AuthUser.id) {
                Write-Warning "You can't follow yourself."
            }
            elseif ($PSCmdlet.ShouldProcess($ID, 'Follow')) {

                $Response = Invoke-WebRequest @HttpArgs "$GabRoot/users/$ID/follow" -Method Post -Body '{}' -ContentType 'application/json'
                $ResponseObj = $Response.Content | ConvertFrom-Json

                Write-Output $ResponseObj

            }

        }
        else {
            Write-Error "No active session. Use Connect-Gab first."
        }

    }

}

#.SYNOPSIS
# Unfollows the specified user so that their posts will no longer show up in the feed.
function Remove-GabFollow {

    [CmdletBinding(DefaultParameterSetName='ByID', SupportsShouldProcess=$true)]
    param(
        
        # The ID of the user to unfollow
        [Parameter(ParameterSetName='ByID', Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
        [Int64]$ID,

        # The name of the user (without the @) to unfollow
        [Parameter(ParameterSetName='ByName', Position=1, Mandatory=$true)]
        [String]$Name

    )

    begin {

        if (!$Global:GabSession) {
            Write-Error "No active session. Use Connect-Gab first."
        }

    }

    process {

        if ($Global:GabSession) {

            # Follow/unfollow APIs currently require user id, not name
            if ($Name) {
                $User = Get-GabUser -Name:$Name
                $ID = $User.id
            }

            if ($ID -eq $Context.AuthUser.id) {
                Write-Warning "You can't unfollow yourself."
            }
            elseif ($PSCmdlet.ShouldProcess($ID, 'Unfollow')) {

                $Response = Invoke-WebRequest @HttpArgs "$GabRoot/users/$ID/follow" -Method Post -Body '{_method: "delete"}' -ContentType 'application/json'
                $ResponseObj = $Response.Content | ConvertFrom-Json

                Write-Output $ResponseObj

            }

        }

    }

}

#.SYNOPSIS
# Gets a list of followers following the specified user - or - if no user is
# specified, lists the followers following the logged-in user.
function Get-GabFollowers {

    [CmdletBinding()]
    param(
        
        # The name of the user whose followers to get
        [Parameter(Position=1, Mandatory=$false)]
        [String]$Name = $Null
    )

    if ($Global:GabSession) {

        if (!$Name) {
            $Name = $Context.AuthUser.username
        }

        $Response = Invoke-WebRequest @HttpArgs "$GabRoot/users/$Name/followers"
        $ResponseObj = $Response.Content | ConvertFrom-Json

        Write-Output $ResponseObj.Data

    }
    else {
        Write-Error "No active session. Use Connect-Gab first."
    }

}

#.SYNOPSIS
# Gets a list of users the specified user follows - or - if no user is
# specified, lists the users the logged-in user follows.
function Get-GabFollowing {

    [CmdletBinding()]
    param(
        
        # The name of the user whose followed users list to get
        [Parameter(Position=1, Mandatory=$false)]
        [String]$Name = $Null
    )

    if ($Global:GabSession) {

        if (!$Name) {
            $Name = $Context.AuthUser.username
        }

        $Response = Invoke-WebRequest @HttpArgs "$GabRoot/users/$Name/following"
        $ResponseObj = $Response.Content | ConvertFrom-Json

        Write-Output $ResponseObj.Data

    }
    else {
        Write-Error "No active session. Use Connect-Gab first."
    }

}

#.SYNOPSIS
# Publishes a post to the logged-in user's timeline.
function Publish-Gab {

    [CmdletBinding()]
    param(
        
        # The text body of the post
        [Parameter(Position=1, Mandatory=$true)]
        [String]$Text,

        # The category of the post
        [Parameter()]
        [String]$Category = $Null

    )

    if ($Global:GabSession) {

        $PostObj = @{
            _method = 'post'
            body = $Text
            category = $Category
            gif = ''
            reply_to = ''
            file = $null
        }

        $PostData = ConvertTo-Json $PostObj

        $Response = Invoke-WebRequest @HttpArgs "$GabRoot/posts" -Method Post -Body $PostData -ContentType 'application/json'
        $ResponseObj = $Response.Content | ConvertFrom-Json

        Write-Output $ResponseObj

    }
    else {
        Write-Error "No active session. Use Connect-Gab first."
    }

}

#.SYNOPSIS
# Deletes a post that was previously published by the logged-in user.
function Unpublish-Gab {

    [CmdletBinding()]
    param(
        
        # The ID of the post to remove.
        [Parameter(Position=1, Mandatory=$true)]
        [ValidateRange(1, 9223372036854775807)]
        [Int64]$ID

    )

    if ($Global:GabSession) {

        $Response = Invoke-WebRequest @HttpArgs "$GabRoot/posts/$ID" -Method Post -Body '{_method: "delete"}' -ContentType 'application/json'
        $ResponseObj = $Response.Content | ConvertFrom-Json

        Write-Output $ResponseObj

    }
    else {
        Write-Error "No active session. Use Connect-Gab first."
    }

}