Posting as pages..

Dec 16, 2011 at 1:21 AM

A lot of what people will want to do with an API is use it to manage their pages.  This is the only legitimate way to post in a companies name and other things.  The New-FBPost as provided doesn't seem to provide a way to do this.

The process to post to a page feed (eg: /222100764521507/feed/ rather than /me/feed/) is:

1. Connect as normal. - but make sure when you approve the app you include the permission: manage_pages
2. Get the pages access_token (these can be enumerated from /me/accounts), each page you admin will have the pages access_token, name and id.
3. Do a post, but instead of /me/feed use the page identified (eg: example /222100764521507/feed), include the access_token as an additional parameter.

As a proof of concept (there may be bugs, but works for me!):

In New-Feed:

Add these parameters:

[string][Parameter(ValueFromPipelineByPropertyName=$true,Mandatory=$false)]$FeedPath = "me/feed",
[string][Parameter(ValueFromPipelineByPropertyName=$true,Mandatory=$false)]$AccessID = $(Get-FBConnection).AccessToken,

Then see bolded changes below:

        $parameters = new-object 'system.collections.generic.dictionary[string,object]'
        $parameters["message"] = $Message
        $parameters["access_token"] = $AccessID
        switch ($PsCmdlet.ParameterSetName)
        {
            "PicturePath" {$parameters["picture"] = $PicturePath; break}
            "PictureId"   {$parameters["object_attachment"] = $PictureId; break}
        }
        if (-not $Connection)
        {
            $Connection = Get-FBConnection
        }
        $raw = $Connection.Post($FeedPath, $parameters)

A helper function to find the access token for the page, and the path might be helpful, but this can all be looked up through Get-FBObject /me/accounts

I hope this makes sense.. pages are what most applications should be operating on.

Jason.

Dec 16, 2011 at 1:23 AM

There is a good walkthrough of this here: http://www.yorkstreetlabs.com/blog/Publish-to-Your-Facebook-Pages-Wall-with-PHP

Coordinator
Dec 16, 2011 at 4:55 AM

Thanks Jason, this is great feedback! My big question is: Is this something which you personally will use, or just something which seems like a good idea? What are your specific scenarios?

Anyhow, the current infrastructure isn't ideal for working with pages, but you should be able to perform most functions using something like:

import-module Facebook
$FB_DefaultExtendedPermissions += "manage_pages"
$null = Show-FBConnectionDialog
$accounts = @(Get-FBAssociation -Type "accounts")
$pageid = $accounts[0].id
$pagetoken = $accounts[0].access_token
$pageconnection = new-object Facebook.FacebookClient -ArgumentList $pagetoken
Get-FBFeed -Id $pageid -Connection $pageconnection

I think these are some workitems to add proper support:

  • Add "manage_pages" to default permissions list
  • New-FBFeed etc. need an Id parameter
  • Probably there should be a way to set "default target page" so you don't need to keep specifying the -Id parameter

I'm also thinking about rejiggering New-FBConnection and Show-FBConnectionDialog such that

  • Show-FBConnectionDialog just returns the AccessToken string and doesn't mess with FacebookClient or the cached connection
  • New-FBConnection takes an AccessToken parameter, if specified it just creates and caches the FacebookClient, if not it gets the connection from Show-FBConnectionDialog

Thanks,

Jon

Coordinator
Dec 19, 2011 at 8:00 PM

I am reposting an email discussion to the discussion forum with Jason's approval:

 

From: Jason.Taylor@ceredigion.gov.uk
To: jonn_msft@hotmail.com
Subject: RE: Facebook module
Date: Mon, 19 Dec 2011 08:37:10 +0000

<!-- .ExternalClass .ecxshape {;} --><!-- .ExternalClass p.ecxMsoNormal, .ExternalClass li.ecxMsoNormal, .ExternalClass div.ecxMsoNormal {margin-bottom:.0001pt;font-size:12.0pt;font-family:"Times New Roman","serif";} .ExternalClass a:link, .ExternalClass span.ecxMsoHyperlink {color:blue;text-decoration:underline;} .ExternalClass a:visited, .ExternalClass span.ecxMsoHyperlinkFollowed {color:purple;text-decoration:underline;} .ExternalClass p {margin-right:0cm;margin-left:0cm;font-size:12.0pt;font-family:"Times New Roman","serif";} .ExternalClass pre {margin-bottom:.0001pt;font-size:10.0pt;font-family:"Courier New";} .ExternalClass p.ecxMsoAcetate, .ExternalClass li.ecxMsoAcetate, .ExternalClass div.ecxMsoAcetate {margin-bottom:.0001pt;font-size:8.0pt;font-family:"Tahoma","sans-serif";} .ExternalClass span.ecxHTMLPreformattedChar {font-family:Consolas;} .ExternalClass p.ecxecxmsonormal, .ExternalClass li.ecxecxmsonormal, .ExternalClass div.ecxecxmsonormal {margin-right:0cm;margin-left:0cm;font-size:12.0pt;font-family:"Times New Roman","serif";} .ExternalClass p.ecxecxmsoacetate, .ExternalClass li.ecxecxmsoacetate, .ExternalClass div.ecxecxmsoacetate {margin-right:0cm;margin-left:0cm;font-size:12.0pt;font-family:"Times New Roman","serif";} .ExternalClass p.ecxecxmsochpdefault, .ExternalClass li.ecxecxmsochpdefault, .ExternalClass div.ecxecxmsochpdefault {margin-right:0cm;margin-left:0cm;font-size:12.0pt;font-family:"Times New Roman","serif";} .ExternalClass span.ecxecxmsohyperlink {;} .ExternalClass span.ecxecxmsohyperlinkfollowed {;} .ExternalClass span.ecxecxhtmlpreformattedchar {;} .ExternalClass span.ecxecxballoontextchar {;} .ExternalClass span.ecxecxemailstyle22 {;} .ExternalClass p.ecxecxmsonormal1, .ExternalClass li.ecxecxmsonormal1, .ExternalClass div.ecxecxmsonormal1 {margin-right:0cm;margin-bottom:0cm;margin-left:0cm;margin-bottom:.0001pt;font-size:12.0pt;font-family:"Times New Roman","serif";} .ExternalClass span.ecxecxmsohyperlink1 {color:blue;text-decoration:underline;} .ExternalClass span.ecxecxmsohyperlinkfollowed1 {color:purple;text-decoration:underline;} .ExternalClass p.ecxecxmsoacetate1, .ExternalClass li.ecxecxmsoacetate1, .ExternalClass div.ecxecxmsoacetate1 {margin-right:0cm;margin-bottom:0cm;margin-left:0cm;margin-bottom:.0001pt;font-size:8.0pt;font-family:"Tahoma","sans-serif";} .ExternalClass span.ecxecxhtmlpreformattedchar1 {font-family:Consolas;} .ExternalClass span.ecxecxballoontextchar1 {font-family:"Tahoma","sans-serif";} .ExternalClass span.ecxecxemailstyle221 {font-family:"Calibri","sans-serif";color:#1F497D;} .ExternalClass p.ecxecxmsochpdefault1, .ExternalClass li.ecxecxmsochpdefault1, .ExternalClass div.ecxecxmsochpdefault1 {margin-right:0cm;margin-left:0cm;font-size:10.0pt;font-family:"Times New Roman","serif";} .ExternalClass span.ecxBalloonTextChar {font-family:"Tahoma","sans-serif";} .ExternalClass span.ecxEmailStyle38 {font-family:"Calibri","sans-serif";color:#1F497D;} .ExternalClass .ecxMsoChpDefault {font-size:10.0pt;} @page WordSection1 {size:612.0pt 792.0pt;} .ExternalClass div.ecxWordSection1 {page:WordSection1;} .ExternalClass ol {margin-bottom:0cm;} .ExternalClass ul {margin-bottom:0cm;} -->

>Thanks for the detailed reply! Are you comfortable if I copy this conversation to the Codeplex discussion area?

No problem.


If you are comfortable changing Facebook.ps1 by hand, the New-FBFeed change would be pretty straightforward: add parameter $Id, and change

            $raw = $Connection.Post("me/feed", $parameters)

to
 
            $raw = $Connection.Post("$Id/feed", $parameters)

I’ve already incorporated a similar amendment, so I’m OK for now.  I’ll look forward to that in your next release, but it’s working in my environment.

 

One thing which is important to me in your scenario is that you are working with a very small number of pages / access tokens. If your scenario worked against a larger number of target pages, this would suggest that you would benefit from FacebookPSModule having a built-in mechanism to manage multiple access tokens. As is, I can stick with manual management of connections and cache files.

One approach that I liked from a twitter powershell script, was simply using export/import-cliXml to dump / restore the access token.  What’s good about this, is that it makes it much simpler to manage multiple accounts.  This is actually less of a problem though on Facebook as you can access all your pages from a single account, on Twitter it’s different credentials for each account (still using the oauth / app approval authentical model though).

 

I agree that facebook applications in general should request the minimum set of permissions. However, IMO FacebookPSModule is not so much a Facebook application, as it is a local management tool which uses an API which was designed for Facebook applications. Since the access token never leaves the user's control, I am pretty liberal about requesting any permission which any command in the module could ever want. I want to save the user from seeing permission errors -- when a command lacks sufficient permission, the user gets either a pretty non-actionable error message, or else the information they requested is missing for no obvious reason. 

That’s a reasonable justification.

 

Don't think I've ever seen Welsh in official communications before -- very cool.
 
We are similar to portions of Canada with a legal requirement to produce and stick to a bilingual language scheme.  I guess we’ll discuss facebook language targeting another time
J

Jason. 


From: Jason.Taylor@ceredigion.gov.uk
To: jonn_msft@hotmail.com
Subject: RE: Facebook module
Date: Sun, 18 Dec 2011 21:18:57 +0000

That code looks fine to read a feed, I can’t see how I can write to it though.

 

>Thanks Jason, this is great feedback! My big question is: Is this something which you personally will use, or just something which seems like a good idea? What are your specific scenarios?

 My particular current scenario is that I have an IVR App built using the UCMA SDK (Windows Workflows Based), this is used to allow schools to report in closure information (ie. Snow / Boiler Failures / Strikes etc.).  This information is then broadcast out to our public website (on a status page), on a facebook page (www.facebook.com/ceredigionsa and www.facebook.com/rhyceredigion ) and also via a Cell Phone paging service.  Originally I was just adding the facebook / twitter integration into the UCMA workflow, but felt that I’d rather use a simpler to maintain scheduled script (hence my use of your module), rather than re-compiling a .NET project after every tweak. 

Although I had no trouble using the UCMA SDK and C# within my team we generally work with Powershell and scripts rather than compiled code.  So it makes more sense for us to use Powershell for automation than cracking open Visual Studio.  Also the UCMA SDK uses .NET Framework 3.5, for which the corresponding Facebook SDK is pretty messy, the .NET 4.0 framework uses dynamic variables which makes everything much tidier, but is out of reach for this app.

 

Facebook pages are the way a business or organisation should be communicatingthrough Facebook, if you take a look at the example pages above you should get the idea.  Many people can admin a facebook page, yet they can all hide behind the identity of the page.  You wouldn’t know that either of those pages have anything to do with me J

 

  • Add "manage_pages" to default permissions list
  • New-FBFeed etc. need an Id parameter
  • Probably there should be a way to set "default target page" so you don't need to keep specifying the -Id parameter

All the above seems sensible, although I don’t see a serious issue with keeping manage_pages optional.  It’s generally better that application developers use the minimum permissions possible.   I like the idea of some sort of default parameter to switch to a page.  The main thing at this stage is to get New-FBFeed accepting an ID.

 

I'm also thinking about rejiggering New-FBConnection and Show-FBConnectionDialog such that

  • Show-FBConnectionDialog just returns the AccessToken string and doesn't mess with FacebookClient or the cached connection
  • New-FBConnection takes an AccessToken parameter, if specified it just creates and caches the FacebookClient, if not it gets the connection from Show-FBConnectionDialog

I think that might be best, although as you’ve designed it makes it pretty straightforward to use.

Our new service will be fully functional from tomorrow and is incorporating your code in a quite important, but not mission critical application J 

Regards

Jason Taylor
--
ICT Infrastructure and Development Manager
Cyngor Sir CEREDIGION County Council.

Dec 19, 2011 at 11:56 PM

OK - one more thing.  In retrospect, encrypting the connection information in the users profile actually makes it pretty difficult to move the script around or run as a different user in a scheduled task.  I've found it somewhat easier to break my script up, using your New-FBConnection to build the connection and save out the access keys, then using the straight Facebook SDK, ie.:

function Post-Facebook () {
  param([string]$message,[string]$token, [string]$feed)
  Add-Type -path "..\..\Facebook.dll"
  $fb = New-Object Facebook.FaceBookClient("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
  $parameters = new-object 'system.collections.generic.dictionary[string,object]'
  $parameters["message"] = "$message";
  $parameters["caption"] = "$message";
  $parameters["description"] = "$message";
  $parameters["name"] = "$message";
  $parameters["access_token"] = "$token";
  $fb.Post("/$feed/feed",$parameters)
}

IT is somewhat easy to use the straight API once you've got the association and keys.

Jason.

Coordinator
Dec 20, 2011 at 12:57 AM
Edited Jan 1, 2012 at 12:59 AM

I agree, the encryption is a pain. I do this because these access tokens are very powerful, especially given all the permissions FacebookPSModule requests. If one of these access tokens were to get into the wrong hands, someone could make a lot of mischief. I believe it is possible to revoke access tokens by changing your Facebook password, or by revoking the permission of my test AppId. (Presumably OAUTH 2.0 blocks reverse-engineering the actual Facebook password from the Access Token, but I can't vouch for that.)

I suppose I could add an option to either encrypt or not encrypt, although I'd almost rather not take responsibility for someone not encrypting these.

BTW, be warned that New-FBConnection in the current drop is not something you want to do in automation (e.g. your "school is closed today" scenario). I have upcoming changes which give New-FBConnection the "-AccessToken" parameter, in which case automation will be OK.

Coordinator
Jan 1, 2012 at 12:59 AM

I just posted version 0.6.0 which should take care of your scenario. Note that, instead of adding New-FBFeed -Id, I am recommending that users do New-FBConnection -PageId <id> to get a connection (including access token etc) which is specific to the page they are managing. New-FBFeed with a page token should write to the page by default.

New-FBConnection now has the -AccessToken parameter.