Stimulator

機械学習とか好きな技術話とかエンジニア的な話とかを書く

Workplace by FacebookのGraph APIによる投稿、情報取得、DMの操作メモ

- はじめに -

弊社では、WorkplaceなるFacebookを模した社内SNSを利用している。
1年弱使ったが、非常に出来が良くなりつつある社内ツールである。

見やすく扱いやすくするTipsも書いたくらい使っている。
vaaaaaanquish.hatenablog.com

 
WorkplaceにはGraph APIが用意されており、それによる様々な自動化が可能である。

本記事ではPython 3.xを用いながら、WorkplaceAPI周りのメモをまとめておく。

Workplaceリファレンスは以下だが、現状あまり親切なリファレンスではない。
Reference - Workplace - ドキュメンテーション - 開発者向けFacebook

WorkplaceのGraph APIは多分実装をFacebookからそのまま持ってきており、FacebookAPIと同じ使い方が動く場合が多々あるため以下のリファレンスが時に参考になる。
Graph API Reference - Documentation - Facebook for Developers

 
Pythonではrequestsモジュールを基本的に使う。入って無ければpipで導入。

sudo pip install requests

 

- アクセストークンの取得と利用 -

アクセストークンは管理者権限のあるアカウントによって取得する。
例えば社内であれば偉い人が持っている。

私自身は管理者になった経験がないが、多分Facebookと同様にDeveloperページの右上からログインし、社内ダッシュボードから外部アプリを発行してもらい、そのアクセストークンとコミュニティIDを使う。

f:id:vaaaaaanquish:20170929062208p:plain:w250:h150

 
Graph APIには、App Access Token(access_token)とMember Access Token(impersonate_token)が存在する。

Appのトークンは、「Permission情報の取得、変更」や「管理者の取得」「グループ情報の取得」「メンバー情報の取得」「Memberのトークンの取得」などが行えるものである。一般的なSNSAPIとして、投稿や削除を行うには「Memberのトークンを取得」しMemberのトークンによってMemberのアカウントを操作する。

Appのトークンは無制限に使えるが、Memberのトークンは24時間でAppトークンによって再発行する必要がある。

f:id:vaaaaaanquish:20170929070101p:plain:w500:h350
トークンの扱い

上記の図では、社内の特定人物のimpersonate_tokenを自由に取得して色々できるように(例えば社長のアカウントから投稿できるように)見えるが、その点は管理者によるaccess_tokenの権限設定によってコントロールするのがWorkplaceAPIの思想だと思われる。
多分、中央管理する管理者がコントロールしやすいように。

 
前述した通り、Pythonではrequestsモジュールを基本的に使う。入って無ければpipで導入。

sudo pip install requests

参加している全ユーザの名前とidの取得

impersonate_tokenを取得せず、その企業に参加しているユーザのidと名前、Adminかどうか取得する。
これらはAppのaccess_tokenだけでもできる。

import requests
import json

TOKEN = "管理者より得たAppのAccessトークン"
GRAPH_URL_PREFIX = "https://graph.facebook.com/"
COMMUNITY_ID = "管理者より得たコミュニティID"

headers = {'Authorization': 'Bearer ' + TOKEN}
graph_url = GRAPH_URL_PREFIX + COMMUNITY_ID + "/members"
result = requests.get(graph_url, headers=headers)
result_json = json.loads(result.text)

この状態で25件までユーザ情報の取得が可能。

それ以上の情報を得るには返却された結果の['paging']['next']に入っているURLを叩く。
以下のようなコードを追記すればよい

while(1):
    result = requests.get(result_json['paging']['next'], headers=headers)
    result_json = json.loads(result.text)
    if "next" not in result_json['paging'].keys():
        break
    print(result_json['paging']['next'])

また、一回で取得できる量や、内容はパラメータによって設定できる。
例えばユーザが1000人程でidのみ欲しい場合は、whileで回さず以下のようなURLを叩くと一発で返ってくる。

graph_url = GRAPH_URL_PREFIX + COMMUNITY_ID + "/members?fields=id&limit=1500"

 

メンバーのimpersonate_tokenの取得

ユーザのidは上記方法で取得するか、ユーザのプロフィールページのURLにidというパラメータで記載されている。
例:https://hogehoge.facebook.com/profile.php?id=100000000000081

そのidに対してimpersonate_tokenを取得するには以下のURLをフィードの取得と同じようにgetで叩く。

import requests
import json

TOKEN = "管理者より得たAppのAccessトークン"
GRAPH_URL_PREFIX = "https://graph.facebook.com/"

headers = {'Authorization': 'Bearer ' + TOKEN}
graph_url = GRAPH_URL_PREFIX + "100000000000081" + "?fields=impersonate_token"
result = requests.get(graph_url, headers=headers)
result_json = json.loads(result.text)

print(result_json["impersonate_token"])

このimpersonate_tokenを利用してユーザに成りすます形で投稿や情報取得を行う事ができる。

 

メンバーの情報の取得

上記のidには、impersonate_token以外にEmailや電話番号、姓名、部署やプロフィール、ヘッダ画像といった情報も紐付いており、それらもAppのaccess_tokenによって取得が可能である。
詳細なフィールドは以下のReferenceのFieldsを参考に。
Member - Workplace - ドキュメンテーション - 開発者向けFacebook

 
例えば参加しているユーザの「Email」「名字」「impersonate_token」「画像URL」を取得したい場合は、上記のimpersonate_tokenを取得する時のコードを参考にURLのフィールドを以下のように変更する。

graph_url = GRAPH_URL_PREFIX + "100000000000081" + "?fields=impersonate_token,email,first_name,picture"

これによりユーザの情報取得が可能である。

上記参考にしているReferenceのURLのEdges 以降はimpersonate_tokenが必要になってくる。


 

- impersonate_tokenによる情報取得 -

impersonate_tokenを取得する事で、そのユーザに成りすます形でWorkplace上の情報の取得編集が可能である。

以降は上記したimpersonate_tokenを取得する方法24時間以内に取得したimpersonate_tokenがある状態で進める。

 

ユーザプロフィール画面のフィードの投稿取得

WorkplaceにはFacebook同様、自分のフィードが用意されている。

f:id:vaaaaaanquish:20170929104457p:plain:w500:h300

最もシンプルに、id=100000000000081のフィードを取得するには以下

import requests
import json

TOKEN = "取得したimpersonate_token"
GRAPH_URL_PREFIX = "https://graph.facebook.com/"

headers = {'Authorization': 'Bearer ' + TOKEN}
graph_url = GRAPH_URL_PREFIX + "100000000000081/feed"
result = requests.get(graph_url, headers=headers)
print(json.loads(result.text))

上記コードにより、以下のような結果が返ってくる。

{'data':
  [
    {'message': 'これはテストです',
     'created_time': '2017-09-29T01:36:19+0000',
     'id': 'この投稿のid'}]}

 
このPostには様々なFieldが付随している。詳細は以下。
Post - Workplace - ドキュメンテーション - 開発者向けFacebook

例えば投稿に付随する「permalink_url」や「画像」の情報を取得するには以下のようにfieldsパラメータをつけたURLを叩く。

graph_url = GRAPH_URL_PREFIX + "100000000000081/feed?fields=permalink_url,image"

 
上記方法では25件まで投稿の取得が可能である。
limitは上記access_tokenによる情報取得の時同様limitパラメータを付ける。
また、Feed全般における時系列情報についてはsince等のパラメータの利用も可能である。

以下は10日以内の情報を最大100件取得できるURLを生成している例である。

import datetime

DAYS = 10
SINCE = datetime.datetime.now() - datetime.timedelta(days=DAYS)

graph_url = GRAPH_URL_PREFIX + "100000000000081/feed?fields=permalink_url,image&limit=100&since="
graph_url += SINCE.strftime("%S")

 
パラメータについてはWorkplaceのページには一切解説がなく、FacebookのGraph APIを参考に試行錯誤していくしかない状態である。
Graph API Reference Post /post - ドキュメンテーション - 開発者向けFacebook

中には機能的に動作しないパラメータもあるので注意が必要。

 

ユーザのプロフィール画面の投稿に対するコメントの取得

WorkplaceではFacebook同様、投稿に対するコメントと、そのコメントに対するコメントの二種類がある。

f:id:vaaaaaanquish:20170929111514p:plain:w500:h200

面倒だが、現状「投稿の取得」「投稿のコメントの取得」「投稿のコメントへのコメントの取得」はそれぞれAPIを叩いて行う必要がある。


投稿にはそれぞれ独自のidが振られている。
上記のフィード取得の方法ではレスポンスに['data']['id']が含まれているのでそれ。

また投稿の時間部分のリンクをクリックすると投稿のページに飛べるが、このリンクには以下のようにstory_fbidとidパラメータがあり、こちらをアンダースコアで繋ぎ合わせると投稿のidが作成できる。

f:id:vaaaaaanquish:20170929112642p:plain
例:https://hogehoge.facebook.com/permalink.php?story_fbid=300000000000003&id=100000000000001&pnref=story
上記例における投稿のid:100000000000001_300000000000003

この投稿のコメントを取得するには以下

import requests
import json

TOKEN = "取得したimpersonate_token"
GRAPH_URL_PREFIX = "https://graph.facebook.com/"

headers = {'Authorization': 'Bearer ' + TOKEN}
graph_url = GRAPH_URL_PREFIX + "100000000000001_300000000000003" + "/comments"
result = requests.get(graph_url, headers=headers)
print(json.loads(result.text))

結果としてはこんな感じ

{'data': 
  [
    {'created_time': '2017-09-29T02:11:13+0000',
     'from': {'name': 'ユーザ名', 'id': '投稿したユーザのID'},
     'message': 'テストに対するコメントに対するコメントです', 'id': 'コメントのid'}],
     'paging': {'cursors': {'before': 'WT~', 'after': 'WT~'}, 'next': URL}}

返却されたデータの中に「コメントの投稿id」があるため、それを利用して「コメントへのコメント」を取得する形になる。
やり方は投稿のコメントの取得方法と同じ。

上限は25件のため、アクセストークンによる情報取得と同じようにlimit上限をあげたり、pagingのnextに書いてあるURLを利用して上限の次の情報を取得する。

 

グループ情報の取得

Workplaceにはプロジェクトやチーム、部署ごとにGroupを作って、その中にスレッドを立てて投稿できる機能がある。

f:id:vaaaaaanquish:20170929114735p:plain:w300:h320


グループの名前やidはimpersonate_tokenなしでもaccess_tokenのみで取得できる。
しかし、Workplaceには「参加した人しか見れない非公開グループ(CLOSED)」「グループ一覧にも表示されない秘密のグループ(PRIVATE)」を設定する事が可能で、それらはaccess_tokenでは取得できず、そのグループに参加しているユーザのimpersonate_tokenのみで取得が可能となる。


全てのグループ情報を取得するには、フィードの取得同様getで以下のURLを叩く。

GRAPH_URL_PREFIX = "https://graph.facebook.com/"
COMMUNITY_ID = "管理者より得たコミュニティID"
graph_url = GRAPH_URL_PREFIX + COMMUNITY_ID + "/groups"
||< 

以下のような、グループの名前とグループid、そのグループの状態を含めた結果が帰ってくる。
>|json|
{'data':
  [
    {'name': 'test',
     'privacy': 'OPEN',
     'id': '1000000000000006'},
    {'name': 'hogehoge',
     'privacy': 'CLOSED',
     'id': '1000000000000001'},
    {'name': 'TeamA',
     'privacy': 'SECRET',
     'id': '1000000000000003'}],
 'paging': {'cursors': {'before': 'QV', 'after': 'QV'}, 'next': URL}}

アクセストークンによる情報取得と同様、デフォルトでは一度に取得できる件数が25なので、limitを上げるかnextを見てさらに追加で情報を取得する。

 
グループ情報に付随するFieldにはiconやDescription、グループ管理者等があり、以下を参考にフィールドを追加する形で取得が可能である。
Group - Workplace - ドキュメンテーション - 開発者向けFacebook
 
特定の1グループに関する周辺情報を取得するには、グループidに対してリクエストを叩く。
グループIDは上記のグループリストのような形で取得する他、グループページのURLからも判断できる。
例:https://hogehoge.facebook.com/groups/1000000000000006/
例のURLのグループID:1000000000000006

例えば上のグループには以下のようにfieldsを指定したURLを叩く。

GROUP_ID = "1000000000000006"
graph_url = GRAPH_URL_PREFIX + GROUP_ID + "?fields=cover,description,name"

上記例ではカバー画像のURLやグループの名前、説明を取得している。
レスポンスが大きくなりそうな情報(メンバーのリストやファイル情報)については、Edge情報として下記のやり方で取得する。

 

グループの関連情報の取得

上記のグループの情報において、Edgeになる情報は別の形で取得する。
以下のEdgeを参考。
Group - Workplace - ドキュメンテーション - 開発者向けFacebook

取得するにはURLの末尾を以下欲しい情報に合わせて叩く

  • /admins グループ管理者
  • /albums グループに投稿された画像群
  • /docs グループに投稿された文書
  • /events グループで開催されているイベント
  • /files グループに投稿された上記以外のファイル
  • /member_requests 現在のメンバーリクエスト(鍵グループのみ)
  • /members メンバーのリスト
  • /moderators グループ作成者 
  • /feed (後述)

例えばGROUP_ID = "1000000000000006"に対してグループの管理者が知りたい場合は、以下のようなURLを叩く。

GROUP_ID = "1000000000000006"
graph_url = GRAPH_URL_PREFIX + GROUP_ID + "/admins"

一回のリクエストにおける情報取得件数のデフォルト値は25件であり、アクセストークンによる情報取得と同様に、大きなlimitなどを利用し情報を取得する。

 

グループのフィードへの投稿の取得

グループフィードへ投稿する。
以下のReferenceのEdge情報を参考にする。
Group - Workplace - ドキュメンテーション - 開発者向けFacebook


グループへの投稿のidは、上記グループフィードの情報取得からも得られるが、URLからも判別できる。
例:https://hogehoge.facebook.com/groups/1972434329706466/permalink/1000000000000006
例のURLのグループID:1000000000000006


基本的には個人フィードへの投稿の取得と同じである。
グループのフィードの投稿はidに対して/feedを付ければ取得できる。

例としては以下

GROUP_ID = "1000000000000006"
graph_url = GRAPH_URL_PREFIX + GROUP_ID + "/feed"

個人フィードへの投稿の取得と同様に、デフォルトでは25件まで投稿の取得が可能である。
limitパラメータ、sinceパラメータの利用も可能である。

  

投稿に対するコメントの取得

ユーザのプロフィール画面の投稿に対するコメントの取得と同様にフィードの投稿等のコメントも取得可能であるが、グループへの投稿と個人フィードの投稿に付くIDの形式が違うので注意する必要がある。


取得の方法は同じで、投稿ID + "/comments"のURLを叩く。

graph_url = GRAPH_URL_PREFIX + "100000000000001" + "/comments"

コメントのコメントの取得の方法もユーザのプロフィール画面の投稿に対するコメントの取得に書いてある方法と同様の形で取得できる。

 

投稿に対するLike数やメディア情報の取得

WorkplaceにはFacebookと同じくコメントに「ライク」「リアクション」を付ける事ができる。
また、投稿に対して動画や画像、ワードファイル等を直接貼り付ける事ができる。


コメントに付随するそれらの情報を取得するには、上記の投稿へのコメントの取得と同じように投稿idに対するEdgeにそれぞれ欲しい情報の名前を付けたURLを叩く。

以下のReferenceのEdge情報を参考にする。
Group - Workplace - ドキュメンテーション - 開発者向けFacebook


例えば、投稿idが100000000000001の投稿に対してライクした人を知りたければ以下のURLを叩く。
(グループへの投稿と個人フィードの投稿に付く投稿idの形式がかなり違うので戸惑うがやり方は同じ)

graph_url = GRAPH_URL_PREFIX + "100000000000001" + "/likes"

これでLikeしたユーザの名前とidのリストを取得できる。

リアクションした人のリストは/reactions、動画や文書添付情報は/attachmentsを叩く。

 

ユーザのDMの取得

ユーザのDMの取得には、ユーザIDとimpersonate_tokenを利用する。
一応Referenceには、impersonate_tokenが示す当人のDMしか取得できないことになっている。
Graph API Reference: User conversations - Documentation - Facebook for Developers


ユーザIDはユーザのimpersonate_tokenの取得時にも出てきた個人のID。

GRAPH_URL_PREFIX + USER_ID  +"/conversations?fields=messages{message,attachments}

上記にはfieldsパラメータを付与しているが、現時点ではそれらが無いと取得できなかった。

リファレンスでは、Creating、Updating、Deletingも「You can't perform this operation on this endpoint.」となっているため、現状DMの操作は取得だけでDMを送る、更新する、削除するといった操作は記事執筆時点ではできない


 

- impersonate_tokenによる投稿 -

impersonate_tokenを取得する事で、そのユーザに成りすます形でWorkplace上への投稿が可能である。

以降は上記したimpersonate_tokenの取得の方法で24時間以内に取得したimpersonate_tokenがある状態で進める。

 

プロフィール画面のフィードへの投稿

requestのPOSTメソッドを使ってユーザとして投稿する。


ユーザIDが100000000000081のユーザのフィードに投稿するには以下のようにIDのfeedエッジに対してPOSTを叩く

import requests
TOKEN = "取得したimpersonate_token"
GRAPH_URL_PREFIX = "https://graph.facebook.com/"
headers = {'Authorization': 'Bearer ' + TOKEN}

USER_ID = "100000000000081"
graph_url = GRAPH_URL_PREFIX + USER_ID + "/feed"
data={
    "message":"hello",
    "link":"https://developers.facebook.com/docs/workplace/custom-integrations/apps"}
requests.post(graph_url, headers=headers, data=data)

f:id:vaaaaaanquish:20170929145931p:plain:w400:h280

投稿者名の隣にtool_appなどApp名が表示されるので、APIから投稿しているかどうかがわかる。

また、POSTには以下のような情報を付与できる。
Post - Workplace - ドキュメンテーション - 開発者向けFacebook

例えば以下

  • message 本文
  • formatting 本文をマークダウンにするか普通のテキストにするか
  • link リンク情報
  • permalink_url 投稿の固定リンク
  • picture 添付する画像のURL
  • place 位置情報
  • poll 投票
  • properties 動画やドキュメントの詳細
  • type 添付ファイルのタイプ
  • to 投稿に紐づけるユーザ
  • with_tags ポストにつくタグ

よしなにdataの中身に付ければつく。

 

グループへの投稿

上記のフィードへの投稿のUSER_IDがGROUP_IDに変わるだけ

graph_url = GRAPH_URL_PREFIX + GROUP_ID + "/feed"

 

投稿へコメントする

全ての投稿には投稿IDが付いており、そのIDのcommentsエッジに対してポストする。
投稿IDは、上記プロフィールフィードのコメント取得グループフィードのコメント取得を参照。


上記のフィードへの投稿を参考にポストする。

graph_url = GRAPH_URL_PREFIX + 投稿のID + "/comments"
requests.post(graph_url, headers=headers, data={"message":"hello"})

f:id:vaaaaaanquish:20170929151258p:plain:h220:w450

投稿の投稿にコメントする場合も同様に、投稿idのcommentsエッジに対してpostする。

 

投稿のフォーマット

最近やっとAPI経由での投稿でMarkdown形式やユーザへのリプライが可能になった。
Workplace公開当初、いち早く弊社は導入しAPIも真っ先に触ったがこの辺りが真っ当に動かず本当にアレだった。


フィードへの投稿を参考に、message内で@[ユーザID]とする事でリプライ、formattingにMARKDOWNを指定する事でマークダウン形式での投稿ができる。

使える構文は以下のReferenceページが参考になる。
Post - Workplace - ドキュメンテーション - 開発者向けFacebook

例を示すとこんな感じ

data = {
    "message":"[@100013051545981]\n``` code block ```",
    "formatting":"MARKDOWN"}
requests.post(graph_url, headers=headers, data=data) 

また、最近ではpoll情報をdataに追加することで投票投稿なども作れるようになった。

f:id:vaaaaaanquish:20170929152545p:plain:h200:w400


 

- おわりに -

WorkplaceのGraphAPIは公開当初こそ使い物にならないレベルだったが、最近はよくなっている。

通知の取得とかDM操作ができない、Likeやリアクションができない、ビデオ配信機能もあるのに取得できない…みたいな不満は多々あって、一応Workplaceの公式ユーザページで文句は出ているが・・・


以前技術ブログじゃないほうで社内SNSのススメという記事を書いたが、この時よりは幾分かマシになっていると思う。

vaaaaaanquish.hatenadiary.jp


更新にもスピード感があるし、それだけエンジニアが投入されているという証だろうか。