view src/facebook.coffee @ 13:58a069d12b0c

edit INSTALL.md and use correct params in pumpbridge.json
author Mathias Gebbe <mgebbe@intevation.de>
date Thu, 12 Jun 2014 10:34:02 +0200
parents f190abf340ef
children c1b736a859c9
line wrap: on
line source
# Copyright (C) 2014 by Intevation GmbH
# Author: Mathias Gebbe <mgebbe@intevation.de>
#
# This file is Free Software under the Apache License, Version 2.0;
# and comes with NO WARRANTY!
# See the documentation coming with pumpbridge for details.

https = require("https")
async = require("async")
_ = require("underscore")
Routes = require("./routes")
FB = require('fb')
EdgeControl = require("./edgecontrol")
Edge = require("./edge")
CommentToESN = require("./commenttoesn")
ToESN = require("./toESN")
FromESN = require("./fromESN")
Pump = require("./pumpio")
User = require("./user")
Sync = require("./sync")
Usermap = require("./usermap")
Config = require("./config")

config = Config.config

bridgeid = config.bridgeid
secret = config.fbSECRET
appid = config.fbAPPID

#######################################
###### facebook sync              #####
#######################################

sync = (user) ->
  me = user.user_pumpio
  id = user.user_ESN.substr(0,user.user_ESN.indexOf('@'))
  pubuser = ""
  post = ""

  # GET NEW POSTS
  ####
  async.waterfall [
    (callback) ->
      getFriends(user)
      getPages(user)
      getUser(user)
      getStream user,callback
  ], (err, posts) ->
    return if not (posts?) or posts.length is 0
    #console.log JSON.stringify(posts) # all posts from network (100)
    async.eachSeries posts, ((post, callback) ->
      async.waterfall [
        (callback) ->
          FromESN.search {uid: post.id + "@facebook_to_" + me, recipientUser: me}, callback
        (result, callback) ->
          return if result.length isnt 0 or post.from.id is id
          #console.log "postid: " + post.from.id + "id:" + id
          # if this is your own post return!!!
          getPublicUserInfo(post.from.id , callback)
        (pubuser, callback) ->
          pubuser = pubuser
          User.search {id: pubuser.id + "@facebook"}, callback
        (dbuser, callback) ->
          if (dbuser[0]?)
            Sync.postParser post, dbuser[0], 'facebook', callback
          else
            return
        (parsed, callback) ->
          Pump.postUser bridgeid, me, parsed, callback
        (pumppost, callback) ->
          pumppost = JSON.parse(pumppost)
          if (post.actions[1].link?)
            postlink = post.actions[1].link
          else
            postlink = post.actions[0].link
          FromESN.create
            postid: post.id + "@facebook"
            sourceUser: post.from.id
            sourcePost: postlink
            pumpPost: pumppost.object.id
            recipientUser: me
            created: Date.now()
          , callback
     ], (err, result) ->
          #console.log result
          #console.log 'done.'
      callback null, 'done'
    ), (err) ->
      if err
        console.log 'one post fail to process'
      else
        console.log 'all posts processed'
      return
  ####
  # TO DO: GET NEW COMMENTS
  # for each fromESN check the comments
  # if comment author = me
  # post comment
  # if comment author != me
  # post comment with 'user schreibt'
  async.waterfall [
    (callback) ->
      FromESN.search {recipientUser: me}, callback
    (allESN, callback) ->
      async.each allESN, ((fromesn, callback) ->
        async.waterfall [
          (cb) ->
            if fromesn.sourcePost.indexOf('www.facebook.com') is -1
              return
            Usermap.search {id: me + '_to_' + me}, cb
          (pumpuser, cb) ->
            Pump.getNote(pumpuser[0], fromesn.pumpPost, cb)
          (note, cb) ->
            return if not (note?)
            if note.liked is true
              obj = fromesn.postid.substr(0,fromesn.postid.indexOf('@'))
              postLike(user,obj)
            if (note.replies?)
              reply = JSON.stringify(note.replies)
              rep = JSON.parse(reply)
              if rep.totalItems >= 1
                async.each rep.items, ((r, callback) ->
                  if r.author.id is "acct:" + me
                    obj = fromesn.postid.substr(0,fromesn.postid.indexOf('@'))
                    postComment(user, obj, r.id , r.content)
                  callback null, 'done'
                ), (err) ->
            cb null, 'done'
        ], (err, result) ->
         #done
      ), (err) ->
        callback null, 'done'
  ], (err, result) ->
    #done


  # GET PUBLIC PUMP POSTS AND POST THEM
  async.waterfall [
    (callback) ->
      Usermap.search {id: me + '_to_' + me}, callback
    (user, callback) ->
      Pump.getUserFeed(user[0],callback)
    (feed, callback) ->
      interval = config.interval
      if not (interval?)
        interval =  15 * 60 * 1000 # 900 000 ms (15min)
      ti = new Date().getTime() - interval
      async.eachSeries feed.items, ((post, callback) ->
        # do for each post
        ts = Date.parse(post.updated)
        if (ts >= ti and post.verb is "post" or post.verb is "share") and (post.object.objectType is "note" or post.object.objectType is "image") and (Pump.isPublicActivity(post)) and not (post.object.deleted?)
          postStream(user,post)
        callback null, 'done'
      ), (err) ->
        callback null, 'done'
  ],(err, result) ->
        #console.log 'done.'

  return


#######################################
###### get user facebook          #####
#######################################
getUser = (user) ->
  data = ""
  id = user.user_ESN.substr(0,user.user_ESN.indexOf('@'))
  token = user.oauth_token
  fields = 'fields=id,name,picture,link'
  options =
   host: 'graph.facebook.com'
   port: 443
   path: '/' + id + '?access_token=' + token + '&' + fields

  https.get(options, (res) ->
    #console.log "Got response: " + res.statusCode

    res.on "data", (chunk) ->
      data += chunk
      return

    res.on "end", () ->
      if (data?)
        try
          user = JSON.parse(data)
          ### with app id ###
          Routes.updateUserDB(user.id+'@facebook',user.name,user.name,user.link,user.picture.data.url) if user?
          ### with link ###
          Routes.updateUserDB(user.link,user.name,user.name,user.link,user.picture.data.url) if user?
        catch err
          console.log "User Error"
      return

  ).on "error", (e) ->
    console.log "Got error: " + e.message

  return

##############################################
###### get facebook likes               ######
##############################################
getFriends = (user) ->
  me = user.user_pumpio
  token = user.oauth_token

  FB.setAccessToken token

  FB.api "me/friends?limit=5000",
    fields: [
      "id"
    ]
  , (res) ->
    console.log res
    if not res or res.error
      console.log (if not res then "error occurred" else res.error)

    _.each res.data, (person) ->
      getUserById(me,person.id,token)
      return

  return

#######################################
###### get user facebook3         #####
#######################################
getFriends3 = (user) ->
  data = ""
  id = user.user_ESN.substr(0,user.user_ESN.indexOf('@'))
  token = user.oauth_token
  options =
   host: 'graph.facebook.com'
   port: 443
   path: 'me/friends?limit=5000&access_token=' + token + '&client_id='+ appid + '&client_secret=' + secret

  https.get(options, (res) ->
    console.log "Got response: " + res.statusCode

    res.on "data", (chunk) ->
      data += chunk
      return

    res.on "end", () ->
      console.log data
      return

  ).on "error", (e) ->
    console.log "Got error: " + e.message

  return

##############################################
###### get facebook friends2            ######
##############################################
getFriends2 = (user) ->
  me = user.user_pumpio
  id = user.user_ESN.substr(0,user.user_ESN.indexOf('@'))
  token = user.oauth_token

  EdgeControl.removeEdges(me,'@facebook')
  EdgeControl.removeEdges(me,'www.facebook.com')

  FB.setAccessToken token
  FB.api "fql",
    q: "SELECT uid2 FROM friend WHERE uid1 = me()"
  , (res) ->
    console.log res
    if not res or res.error
      console.log (if not res then "error occurred" else res.error)
      return

    _.each res.data, (user) ->
      getUserById(me,user.uid2,token)
      return

    return

  return


##############################################
###### get facebook likes               ######
##############################################
getPages = (user) ->
  me = user.user_pumpio
  token = user.oauth_token

  FB.setAccessToken token

  FB.api "me/likes?limit=5000",
    fields: [
      "id"
    ]
  , (res) ->
    if not res or res.error
      console.log (if not res then "error occurred" else res.error)

    _.each res.data, (page) ->
      getUserById(me,page.id,token)
      return

  return


#######################################
###### get facebook stream       ######
#######################################
getStream = (user,callback) ->
  token = user.oauth_token
  interval = config.interval
  if not (interval?)
    interval =  15 * 60 * 1000 # 900 000 ms (15min)
  ts = Math.round(((new Date()).getTime() - interval)/1000)

  FB.setAccessToken token

  FB.api "me/home?limit=35&?since="+ts,
    fields: [
      "id"
      "type"
      "from"
      "privacy"
      "message"
      "picture"
      "link"
      "status_type"
      "caption"
      "created_time"
      "updated_time"
      "picture"
      "actions"
    ]
  , (res) ->
    if not res or res.error
      console.log (if not res then "error occurred" else res.error)
      callback null, null

    newposts = new Array()
    _.each res.data, (post) ->
      newposts.push(post) if (post.type is 'status' or post.type is 'photo' or post.type is 'link' or post.type is 'video') and (post.status_type is 'mobile_status_update' or post.status_type is 'added_photos' or post.status_type is 'shared_story')

    callback null, newposts.reverse()

#######################################
###### post facebook stream      ######
#######################################
postStream = (user, post) ->
  token = user.oauth_token
  text = ""

  FB.setAccessToken token

  return if not post.object.content? or post.object.content is ""
  ToESN.search {uid: post.object.id + "@facebook"}, (err, result) ->
    if result.length is 0
      body = post.object.content.replace(/<(?:.|\n)*?>/gm, '') + " " + post.object.url
      if post.verb is "share"
        text = post.object.author.url + " wrotes:"
      FB.api "me/feed", "post",
        message: text + body
      , (res) ->
        if not res or res.error
          console.log (if not res then "error occurred" else res.error)
          return

        async.waterfall [
          (callback) ->
            savePost = new ToESN()
            savePost.uid = post.object.id + "@facebook"
            savePost.sourceUser = post.actor.id
            savePost.sourcePost = post.object.id
            savePost.targetUser = user.user_ESN
            savePost.targetPost = res.id
            savePost.recipientUser = 'public'
            savePost.updated = Date.now()
            savePost.save callback
        ], (err, result) ->

        return

  return

##################################################
###### post comment to facebook stream      ######
##################################################
postComment = (user, object_id, pumpid, text) ->

# check if the comment is allready posted (search bla return)

  CommentToESN.search { uid: pumpid + "_to_" + "https://facebook.com/" + object_id}, (err, result) ->
    if result.length is 0
      token = user.oauth_token
      text = text.replace /<(?:.|\n)*?>/g, ""

      FB.setAccessToken token

      FB.api "" + object_id + "/comments", "POST",
        message: text
      , (res) ->
        if not res or res.error
          console.log (if not res then "error occurred" else res.error)
          return

      CommentToESN.create
        ESNPost: "https://facebook.com/" + object_id
        pumpComment: pumpid
        created: Date.now()
      , (err, result) ->
        console.log 'comment saved'

  return

##################################################
###### like post on facebook stream         ######
##################################################
postLike = (user, object_id) ->
  token = user.oauth_token
  FB.setAccessToken token

  FB.api "" + object_id + "/likes", "POST"
  , (res) ->
    if not res or res.error
      console.log (if not res then "error occurred" else res.error)
      return

    console.log 'liked'
    return

  return


######################################################################
###### get user facebook by id and add him to UserDB and EDGES   #####
######################################################################
getUserById = (me,id,token) ->
  data = ""
  token = token
  fields = 'fields=id,name,picture,link'
  options =
   host: 'graph.facebook.com'
   port: 443
   path: '/' + id + '?access_token=' + token + '&client_secret=' + secret + '&' + fields

  https.get(options, (res) ->
    #console.log "Got response: " + res.statusCode

    res.on "data", (chunk) ->
      data += chunk
      return

    res.on "end", () ->
      user = JSON.parse(data) if data?

      #### with app user id ###
      if user?
        Routes.updateUserDB(user.id+'@facebook',user.name,user.name,user.link,user.picture.data.url)
        EdgeControl.addEdge(me,user.id+'@facebook')
        #### with profile url ###
        Routes.updateUserDB(user.link,user.name,user.name,user.link,user.picture.data.url)
        EdgeControl.addEdge(me,user.link)

      return

  ).on "error", (e) ->
    console.log "Got error: " + e.message

  return


######################################################################
###### get user facebook by id and add him to UserDB and EDGES   #####
######################################################################
getPublicUserInfo = (id, callback) ->
  FB.api ""+id,
   (res) ->
     if not res or res.error
       console.log (if not res then "error occurred" else res.error)
     callback null,res

  return

######################################################################
###### get facebook long lived token                             #####
######################################################################
getLongLivedToken = (token, callback) ->
  data = ""
  options =
   host: 'graph.facebook.com'
   port: 443
   path: '/oauth/access_token?' + 'grant_type=fb_exchange_token&client_id='+ appid + '&client_secret=' + secret + '&fb_exchange_token=' + token + ''

  https.get(options, (res) ->
    res.on "data", (chunk) ->
      data += chunk
      return

    res.on "end", () ->
      # returns --> "access_token=CAAK9efmXh2IBAAm9abitB98TvF6HHF5ducYDkV5PBrooG6MVNoP9eOy06yvyL0hMKQVzh1xvJPM8XMAYe8L0ZARzZCdolahSymrZCDXN2ZAfr0aIFbWocr8K5DMLu64ZD&expires=5183792"
      ltoken = JSON.stringify(data)
      callback ltoken.substring(ltoken.indexOf('=')+1,ltoken.indexOf('&'))
      return

  ).on "error", (e) ->
    console.log "Got error: " + e.message

  return

exports.getUser = getUser
exports.getPages = getPages
exports.getStream = getStream
exports.getFriends = getFriends
exports.postStream = postStream
exports.getLongLivedToken = getLongLivedToken
exports.getPublicUserInfo = getPublicUserInfo
exports.getUserById = getUserById
exports.sync = sync
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)