MeetupPS.psm1

function Get-MeetupEvent {
<#
.SYNOPSIS
    Retrieve Meetup event for a specific group
.DESCRIPTION
    Retrieve Meetup event for a specific group
.PARAMETER GroupName
    Specify the name of the group
.PARAMETER Status
    Specify the status of the event(s).
    Values accepted: "cancelled", "draft", "past", "proposed", "suggested", "upcoming"
    Default is 'upcoming'.
.PARAMETER Page
    Number of entries to retrieve.
    Default is 200
.EXAMPLE
    Get-MeetupEvent -GroupName FrenchPSUG -Status past
.NOTES
    https://github.com/lazywinadmin/MeetupPS
#>

    [CmdletBinding()]
    PARAM(
        [Parameter(Mandatory = $true)]
        $GroupName,

        [ValidateSet("cancelled", "draft", "past", "proposed", "suggested", "upcoming")]
        $Status = 'upcoming',

        $page = 200

    )
    TRY {
        $FunctionName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).MyCommand

        $Url = "https://api.meetup.com/$GroupName/events?status=$Status&page=$page"
        Write-Verbose -Message "[$FunctionName] Querying API '$Url'..."
        (invoke-restmethod -uri $Url -UseDefaultCredentials)
    }
    catch {$PSCmdlet.ThrowTerminatingError($_)}
}

function Get-MeetupEventAttendance {
    <#
.SYNOPSIS
    Get the attendance of an event
.DESCRIPTION
    Get the attendance of an event
.PARAMETER GroupName
    Specify GroupName
.PARAMETER ID
    Specify the Event ID
.EXAMPLE
    Get-MeetupEventAttendance -GroupName FrenchPSUG -id 232807877
.EXAMPLE
    Get-MeetupEvent -GroupName FrenchPSUG -Status past |select -first 5 |Get-MeetupEventAttendance
.NOTES
    https://github.com/lazywinadmin/MeetupPS

    #https://www.meetup.com/meetup_api/docs/:urlname/events/:id/attendance/#list
#>

    [Cmdletbinding()]
    PARAM(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $GroupName = "FrenchPSUG",

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('EventID')]
        $Id
    )
    PROCESS {
        try {
            $FunctionName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).MyCommand

            if (-not ($script:MeetupAccessToken)) {
                Write-Warning -Message 'You need to use Set-MeetupConfiguration first to authenticate against the Rest API'
            }

            #$PSCmdlet.MyInvocation.ExpectingInput
            IF ($PSCmdlet.MyInvocation.ExpectingInput) {
                $GroupName = $Groupname.group.urlname
                Write-Verbose -Message "[$FunctionName] GroupName '$GroupName' - Values from Pipeline"
            }
            ELSE {Write-Verbose -Message "[$FunctionName] GroupName '$GroupName' - Values from Parameters"}

            Write-Verbose -Message "[$FunctionName] Prepare Splatting"
            $Splat = @{
                Headers = @{Authorization = 'Bearer ' + $($script:MeetupAccessToken.access_token)}
                Method  = 'GET'
                Uri     = "https://api.meetup.com/$GroupName/events/$id/attendance"
            }

            Write-Verbose -Message "[$FunctionName] Querying API '$($Splat.uri)'..."
            (Invoke-RestMethod @splat)
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
}

function Get-MeetupGroup {
<#
.SYNOPSIS
    Retrieve Meetup group information
.DESCRIPTION
    Retrieve Meetup group information
.PARAMETER GroupName
    Specify the name of the group name
.EXAMPLE
    Get-MeetupGroup -GroupName FrenchPSUG
.NOTES
    https://github.com/lazywinadmin/MeetupPS
#>

    [CmdletBinding()]
    PARAM(
        [Parameter(Mandatory = $true)]
        $GroupName)

    TRY {
        $FunctionName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).MyCommand

        $Url = "https://api.meetup.com/$GroupName"
        Write-Verbose -Message "[$FunctionName] Querying Url = '$Url'"
        $GroupObject = invoke-restmethod -uri $Url -UseDefaultCredentials

        Write-Output -InputObject $GroupObject
    }
    Catch {$PSCmdlet.ThrowTerminatingError($_)}
}

function Get-MeetupGroupMember {
    <#
.SYNOPSIS
    Get the member of a meetup group
.DESCRIPTION
    Get the member of a meetup group
.PARAMETER GroupName
    Specify GroupName
.PARAMETER Page
    Number of requested members to return. Defaults to 200
.PARAMETER Order
    Orders results according to definitions listed below. May be one of "interesting", "name", "joined", or "stepup_recommended"
    -interesting
        Order which may be interesting to the authorized member
    -joined
        Time member joined this group
    -name
        The name of the member
    -stepup_recommended
        Sorts by likelikhood to step up as organizer
.EXAMPLE
    Get-MeetupGroupMember -Groupname FrenchPSUG
.EXAMPLE
    Get-MeetupGroupMember -Groupname FrenchPSUG -Page 500 -order joined
.NOTES
    https://github.com/lazywinadmin/MeetupPS

    https://www.meetup.com/meetup_api/docs/:urlname/members/#list
    # desc Boolean value controling sort order of results. Currently this parameter is only supported for "joined" and "name" sorted results. Defaults to true
    # fields A comma-delimited list of optional fields to append to the response
    # filter May be set to 'stepup_eligible' to return only members eligible to step up as organizer
    # order "interesting", "name", "joined", or "stepup_recommended"
    # page Number of requested members to return. Defaults to 200
    # role May be set to "leads" to filter returned members on the lead team
    # status A comma-delimited list of member statuses. Valid values include "active" or "pending". Defaults to "active". Organizers may request pending
#>

    [Cmdletbinding()]
    PARAM(
        [Parameter(Mandatory = $true)]
        $GroupName = "FrenchPSUG",
        [Alias('Limit')]
        $Page=200,
        [ValidateSet('interesting', 'name', 'joined', 'stepup_recommended')]
        $Order='joined'
    )
    try {
        $FunctionName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).MyCommand

        if (-not ($script:MeetupAccessToken)) {
            Write-Warning -Message 'You need to use Set-MeetupConfiguration first to authenticate against the Rest API'
        }

        Write-Verbose -Message "[$FunctionName] Prepare Splatting"
        $Splat = @{
            Headers = @{Authorization = 'Bearer ' + $($script:MeetupAccessToken.access_token)}
            Method  = 'GET'
            Uri     = "https://api.meetup.com/$GroupName/members?order=$order&page=$page"
        }

        Write-Verbose -Message "[$FunctionName] Querying API '$($Splat.uri)'..."
        (Invoke-RestMethod @splat)

    }
    catch {
        $PSCmdlet.ThrowTerminatingError($_)
    }
}

function Get-MeetupPhoto {
<#
.SYNOPSIS
    Retrieve photos for a specific Meetup group.
.DESCRIPTION
    Retrieve photos for a specific Meetup group.
.PARAMETER GroupName
    Specify the name of the group.
.PARAMETER AlbumId
    Specify the unique identifier of a specific photo album.
.PARAMETER Page
    Number of photos to retrieve.
    Default is 200
.PARAMETER Descending
    Reverses the order in which returned photos are listed.
.EXAMPLE
    Get-MeetupPhoto -GroupName FrenchPSUG

    This command returns all of the French PowerShell User Group's photos.
.EXAMPLE
    Get-MeetupPhoto -GroupName FrenchPSUG -AlbumId 28555599

    This command returns only the photos from the specified album belonging to the French PowerShell User Group.
.EXAMPLE
    Get-MeetupPhotoAlbum -GroupName FrenchPSUG -Page 2 | Get-MeetupPhoto -GroupName FrenchPSUG

    This command returns only the photos from the first 2 albums belonging to the French PowerShell User Group.
.NOTES
    https://github.com/lazywinadmin/MeetupPS
#>

    [CmdletBinding()]
    PARAM(
        [Parameter(Mandatory = $true)]
        [string] $GroupName,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [Alias('Id')]
        [uint64] $AlbumId,

        [int] $Page = 200,

        [switch] $Descending
    )
    BEGIN {
        $FunctionName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).MyCommand
    }
    PROCESS {
        TRY {
            if ($AlbumId) {
                $Url = "https://api.meetup.com/$GroupName/photo_albums/$AlbumId/photos?page=$Page"
            } else {
                $Url = "https://api.meetup.com/$GroupName/photos?page=$Page"
            }
            if ($Descending) {
                $Url = "$Url&desc=true"
            }
            Write-Verbose -Message "[$FunctionName] Querying Url = '$Url'"
            $Photos = invoke-restmethod -uri $Url -UseDefaultCredentials
            Write-Output -InputObject $Photos
        }
        catch {$PSCmdlet.ThrowTerminatingError($_)}
    }
}
function Get-MeetupPhotoAlbum {
<#
.SYNOPSIS
    Retrieve photo albums for a specific Meetup group.
.DESCRIPTION
    Retrieve photo albums for a specific Meetup group.
.PARAMETER GroupName
    Specify the name of the group.
.PARAMETER AlbumId
    Specify the unique identifier of a specific photo album.
.PARAMETER Page
    Number of entries to retrieve.
    Default is 200.
    Cannot be used in conjunction with AlbumId.
.PARAMETER Offset
    Offsets the number of entries to return for pagination in conjunction with Page.
    Cannot be used in conjunction with AlbumId.
.EXAMPLE
    Get-MeetupPhotoAlbum -GroupName FrenchPSUG

    This command returns all of the French PowerShell User Group's photo albums.
.EXAMPLE
    Get-MeetupPhotoAlbum -GroupName FrenchPSUG -AlbumId 28555599

    This command returns only the specified photo album belonging to the French PowerShell User Group.
.NOTES
    https://github.com/lazywinadmin/MeetupPS
#>

    [CmdletBinding(DefaultParameterSetName = 'AllAlbums')]
    PARAM(
        [Parameter(Mandatory = $true)]
        [string] $GroupName,

        [Parameter(Mandatory = $true,
                   ParameterSetName = 'SpecificAlbum')]
        [Alias('Id')]
        [uint64] $AlbumId,

        [Parameter(ParameterSetName = 'AllAlbums')]
        [int] $Page = 200,

        [Parameter(ParameterSetName = 'AllAlbums')]
        [int] $Offset
    )
    TRY {
        $FunctionName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).MyCommand
        if ($AlbumId) {
            $Url = "https://api.meetup.com/$GroupName/photo_albums/$AlbumId"
        } else {
            $Url = "https://api.meetup.com/$GroupName/photo_albums?page=$Page"
            if ($Offset) {
                $Url = "$Url&offset=$Offset"
            }
        }
        Write-Verbose -Message "[$FunctionName] Querying Url = '$Url'"
        $Albums = invoke-restmethod -uri $Url -UseDefaultCredentials
        Write-Output -InputObject $Albums
    }
    catch {$PSCmdlet.ThrowTerminatingError($_)}
}

function New-MeetupEvent {
<#
.SYNOPSIS
    Create a Meetup event
.DESCRIPTION
    Create a Meetup event
.PARAMETER GroupName
    Specify GroupName
.PARAMETER Title
    Specify the Title of the event
.PARAMETER Time
    Specify the date and time to the event
    This will take the local time of the event.
.PARAMETER Description
    Specify the Description of the event
.PARAMETER Announce
    Specify this switch to announce the event to your group members
.PARAMETER PublishStatus
    Specify the Publish status of the event.
    Values accepted: draft or published
.EXAMPLE
    New-MeetupEvent `
        -GroupName FrenchPSUG `
        -Title 'New Event from MeetupPS' `
        -Time '2018/06/01 3:00pm' `
        -Description "PowerShell WorkShop<br><br>In this session we'll talk about ..." `
        -PublishStatus draft
.NOTES
    https://github.com/lazywinadmin/MeetupPS
#>

    [Cmdletbinding()]
    PARAM(
        [Parameter(Mandatory = $true)]
        $GroupName = "FrenchPSUG",

        [Parameter(Mandatory = $true)]
        $Title,

        $Time,

        $Description = "Description:<br><br>New Event from MeetupPS PowerShell module<br><br>Speaker:<br>",

        [switch]$Announce = $false,

        [ValidateSet('draft', 'published')]
        $PublishStatus = 'draft'
    )
    try {
        $FunctionName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).MyCommand

        if (-not ($script:MeetupAccessToken)) {
            Write-Warning -Message 'You need to use Set-MeetupConfiguration first to authenticate against the Rest API'
        }

        Write-Verbose -Message "[$FunctionName] Format date to unix time"
        $Time = Get-Date -date ((Get-Date -Date $Time).ToUniversalTime()) -UFormat %s

        # Append Trailing Zeros (it needs to be 13 digits
        if ($Time.Length -lt 13) {
            $diff = 13 - $Time.Length
            $Time = "{0}{1:$('0'*$diff)}" -f $Time, 0
        }

        Write-Verbose -Message "[$FunctionName] Prepare Splatting"
        $Splat = @{
            Headers = @{Authorization = 'Bearer ' + $($script:MeetupAccessToken.access_token)}
            Method  = 'POST'
            Uri     = "https://api.meetup.com/2/event"
            Body    = "group_urlname=$groupName&name=$Title&time=$Time&publish_status=$PublishStatus&announce=$Announce&description=$description"
        }

        Write-Verbose -Message "[$FunctionName] Creating Event..."
        Invoke-RestMethod @splat

    }
    catch {
        $PSCmdlet.ThrowTerminatingError($_)
    }
}
function Set-MeetupConfiguration {
<#
.SYNOPSIS
    Authenticate against the Rest API
.DESCRIPTION
    Authenticate against the Rest API
.PARAMETER ClientID
    Specify the Key of the Oauth Consumer
.PARAMETER Secret
    Specify the Secret of the Oauth Consumer
.PARAMETER RedirectUri
    Specify the RedirectUri to use
.PARAMETER Scope
    Specify the scope
    Default are the following ("basic", "reporting", "event_management")
.EXAMPLE
    # Connect against Meetup.com API
    $Key = '<Your Oauth Consumer Key>'
    $Secret = '<Your Oauth Consumer Secret>'
    Set-MeetupConfiguration -ClientID $Key -Secret $Secret
.NOTES
    https://github.com/lazywinadmin/MeetupPS
#>

    [CmdletBinding()]
    PARAM(
        [Parameter(Mandatory=$true)]
        $ClientID,
        [Parameter(Mandatory = $true)]
        $Secret,
        $RedirectUri = 'https://github.com/lazywinadmin/MeetupPS',
        $Scope = ("basic", "reporting", "event_management")
        )
    TRY{
        $FunctionName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).MyCommand

        # Retrieve Code
        $Url = "https://secure.meetup.com/oauth2/authorize?client_id=$ClientID&response_type=code&redirect_uri=$RedirectUri"
        Write-Verbose -Message "[$FunctionName] Querying Url = '$Url'"

        # Ask user to authenticate and return Code only
        $OAuthCode = Get-OauthCode -url $Url
        Write-Verbose -Message "[$FunctionName] Retrieving Code from '$OAuthCode'"
        $Code = ($OAuthCode -split "\?code=")[1]
        Write-Verbose -Message "[$FunctionName] Code '$Code'"

        # Retrieve Access Token
        Write-Verbose -Message "[$FunctionName] Querying Access Token with ClientID '$ClientId'"
        $script:MeetupAccessToken = Get-OAuthAccessToken -ClientID $clientID -Secret $Secret -Code $Code -RedirectUri $RedirectUri -Scopes $Scope
    }Catch
    {
        $PSCmdlet.ThrowTerminatingError($_)
    }
}
function Get-OAuthAccessToken {
    <#
    .SYNOPSIS
        Retrieve Oauth Access Token
    .DESCRIPTION
        Retrieve Oauth Access Token
    .NOTES
        https://github.com/lazywinadmin/MeetupPS
    #>

    [CmdletBinding()]
    PARAM(
        $ClientID,
        $Secret,
        $Code,
        $RedirectUri = 'https://github.com/lazywinadmin/MeetupPS',
        $AccessUri = 'https://secure.meetup.com/oauth2/access',
        $Scopes = ("basic", "reporting", "event_management")
        )
    try {
        # Build Body
        $body = "client_id=$ClientID&client_secret=$Secret&grant_type=authorization_code&redirect_uri=$RedirectUri&code=$code"
        # Build Header
        $Headers = @{
            'X-OAuth-Scopes'          = $Scopes
            'X-Accepted-OAuth-Scopes' = $Scopes
        }

        # Build splatting
        $Splatting = @{
            Uri         = $AccessUri
            Method      = 'Post'
            ContentType = 'application/x-www-form-urlencoded'
            Body        = $Body
            headers     = $Headers
        }
        Invoke-RestMethod @Splatting
    }
    catch {
        $PSCmdlet.ThrowTerminatingError($_)
    }
}
Function Get-OauthCode {
    <#
.SYNOPSIS
    Function to show the Microsoft Authentication window
.NOTES
    https://github.com/lazywinadmin/MVP
    
    #Credit to Stephen Owen: https://raw.githubusercontent.com/1RedOne/PSWordPress/master/Private/Show-oAuthWindow.ps1
#>

    [CmdletBinding()]
    Param(
        [Uri]$url, $Width = 440, $Height = 640
    )
    Process {

        $Scriptname = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).MyCommand

        try {
            Write-Verbose -Message "[$ScriptName] Load assembly System.Windows.Forms"
            Add-Type -AssemblyName System.Windows.Forms -ErrorAction Stop

            Write-Verbose -Message "[$ScriptName] Create Form"
            $global:form = New-Object -TypeName System.Windows.Forms.Form -Property @{Width = $Width; Height = $Height}
            
            Write-Verbose -Message "[$ScriptName] Create Web browser"
            $global:web = New-Object -TypeName System.Windows.Forms.WebBrowser -Property @{Width = 420; Height = 600; Url = $url}
            # define $uri in the immediate parent scope: 1
            Write-Verbose -Message "[$ScriptName] Define DocumentCompleted scriptblock"
            $global:DocComp = {
                $global:uri = $web.Url.AbsoluteUri
                if ($global:uri -match 'error=[^&]*|code=[^&]*') {
                    $global:form.Close()
                }
            }
            $global:web.ScriptErrorsSuppressed = $true
            $global:web.Add_DocumentCompleted($DocComp)
            $global:form.Controls.Add($web)
            $global:form.Add_Shown( {$form.Activate()})

            Write-Verbose -Message "[$ScriptName] Show Dialog"
            $null = $form.ShowDialog()

            
            $uri
            
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
}

# things to append at the end of the Psm1