def user_to_actor(self, user):
        """Converts a user to an actor.

    Args:
      user: python_instagram.models.Comment

    Returns:
      an ActivityStreams actor dict, ready to be JSON-encoded
    """
        if not user:
            return {}

        id = getattr(user, "id", None)
        username = getattr(user, "username", None)
        if not id or not username:
            return {"id": self.tag_uri(id or username), "username": username}

        url = getattr(user, "website", None)
        if not url:
            url = "http://instagram.com/" + username

        actor = {
            "displayName": user.full_name,
            "image": {"url": user.profile_picture},
            "id": self.tag_uri(username),
            "url": url,
            "username": username,
            "description": getattr(user, "bio", None),
        }

        return util.trim_nulls(actor)
Beispiel #2
0
def build_user_json(me, resp=None):
  """user_json contains an h-card, rel-me links, and "me"

  Args:
    me: string, URL of the user, returned by
    resp: requests.Response (optional), re-use response if it's already been fetched

  Return:
    dict, with 'me', the URL for this person; 'h-card', the representative h-card
      for this page; 'rel-me', a list of rel-me URLs found at this page
  """
  user_json = {'me': me}

  resp = resp or util.requests_get(me)
  if resp.status_code // 100 != 2:
    logging.warning(
      'could not fetch user url "%s". got response code: %d',
      me, resp.status_code)
    return user_json
  # Requests doesn't look at the HTML body to find <meta charset>
  # tags, so if the character encoding isn't given in a header, then
  # we pass on the raw bytes and let BS4 deal with it.
  p = mf2py.parse(doc=resp.text
                  if 'charset' in resp.headers.get('content-type', '')
                  else resp.content, url=me)
  user_json['rel-me'] = p.get('rels', {}).get('me')
  user_json['h-card'] = mf2util.representative_hcard(p, me)
  logging.debug('built user-json %r', user_json)
  return util.trim_nulls(user_json)
Beispiel #3
0
    def get(self):
        # lookup the request token
        token_key = self.request.get('oauth_token')
        token = TumblrOAuthRequestToken.get_by_key_name(token_key)
        if token is None:
            raise exc.HTTPBadRequest('Invalid oauth_token: %s' % token_key)

        # generate and store the final token
        tp = tumblpy.Tumblpy(app_key=TUMBLR_APP_KEY,
                             app_secret=TUMBLR_APP_SECRET,
                             oauth_token=token_key,
                             oauth_token_secret=token.secret)
        auth_token = tp.get_authorized_tokens(
            self.request.params['oauth_verifier'])
        final_token = auth_token['oauth_token']
        final_secret = auth_token['oauth_token_secret']
        TumblrOAuthFinalToken.new(final_token, final_secret)

        # get the user's blogs
        # http://www.tumblr.com/docs/en/api/v2#user-methods
        tp = tumblpy.Tumblpy(app_key=TUMBLR_APP_KEY,
                             app_secret=TUMBLR_APP_SECRET,
                             oauth_token=final_token,
                             oauth_token_secret=final_secret)
        resp = tp.post('user/info')
        logging.debug(resp)
        user = resp['user']
        hostnames = [util.domain_from_link(b['url']) for b in user['blogs']]
        hostnames = util.trim_nulls(hostnames)
        # titles = [b[title] for b in user['blogs']]

        # redirect so that refreshing the page doesn't try to regenerate the oauth
        # token, which won't work.
        self.redirect('/?' + urllib.urlencode(
            {
                'tumblr_username': user['name'],
                'tumblr_hostnames': hostnames,
                # 'tumblr_titles': titles,
                'oauth_token': auth_token['oauth_token'],
            },
            True))
    def user_to_actor(self, user):
        """Converts a user to an actor.

    Args:
      user: dict, a decoded JSON Facebook user

    Returns:
      an ActivityStreams actor dict, ready to be JSON-encoded
    """
        if not user:
            return {}

        id = user.get("id")
        username = user.get("username")
        handle = username or id
        if not handle:
            return {}

        url = user.get("link")
        if not url:
            url = "http://facebook.com/" + handle

        # facebook implements this as a 302 redirect
        image_url = "http://graph.facebook.com/%s/picture?type=large" % handle
        actor = {
            "displayName": user.get("name"),
            "image": {"url": image_url},
            "id": self.tag_uri(handle),
            "updated": util.maybe_iso8601_to_rfc3339(user.get("updated_time")),
            "url": url,
            "username": username,
            "description": user.get("bio"),
        }

        location = user.get("location")
        if location:
            actor["location"] = {"id": location.get("id"), "displayName": location.get("name")}

        return util.trim_nulls(actor)
Beispiel #5
0
  def get(self):
    # lookup the request token
    token_key = self.request.get('oauth_token')
    token = TumblrOAuthRequestToken.get_by_key_name(token_key)
    if token is None:
      raise exc.HTTPBadRequest('Invalid oauth_token: %s' % token_key)

    # generate and store the final token
    tp = tumblpy.Tumblpy(app_key=TUMBLR_APP_KEY,
                         app_secret=TUMBLR_APP_SECRET,
                         oauth_token=token_key,
                         oauth_token_secret=token.secret)
    auth_token = tp.get_authorized_tokens(self.request.params['oauth_verifier'])
    final_token = auth_token['oauth_token']
    final_secret = auth_token['oauth_token_secret']
    TumblrOAuthFinalToken.new(final_token, final_secret)

    # get the user's blogs
    # http://www.tumblr.com/docs/en/api/v2#user-methods
    tp = tumblpy.Tumblpy(app_key=TUMBLR_APP_KEY,
                         app_secret=TUMBLR_APP_SECRET,
                         oauth_token=final_token,
                         oauth_token_secret=final_secret)
    resp = tp.post('user/info')
    logging.debug(resp)
    user = resp['user']
    hostnames = [util.domain_from_link(b['url']) for b in user['blogs']]
    hostnames = util.trim_nulls(hostnames)
    # titles = [b[title] for b in user['blogs']]

    # redirect so that refreshing the page doesn't try to regenerate the oauth
    # token, which won't work.
    self.redirect('/?' + urllib.urlencode({
          'tumblr_username': user['name'],
          'tumblr_hostnames': hostnames,
           # 'tumblr_titles': titles,
          'oauth_token': auth_token['oauth_token'],
          }, True))
  def user_to_actor(self, user):
    """Converts a tweet to an activity.

    Args:
      user: dict, a decoded JSON Twitter user

    Returns:
      an ActivityStreams actor dict, ready to be JSON-encoded
    """
    username = user.get('screen_name')
    if not username:
      return {}

    return util.trim_nulls({
      'displayName': user.get('name'),
      'image': {'url': user.get('profile_image_url')},
      'id': self.tag_uri(username) if username else None,
      'published': self.rfc2822_to_iso8601(user.get('created_at')),
      'url': self.user_url(username),
      'location': {'displayName': user.get('location')},
      'username': username,
      'description': user.get('description'),
      })
  def postprocess_activity(self, activity):
    """Does source-independent post-processing of an activity, in place.

    Right now just populates the title field.

    Args:
      activity: activity dict
    """
    # maps object type to human-readable name to use in title
    TYPE_DISPLAY_NAMES = {'image': 'photo', 'product': 'gift'}

    # maps verb to human-readable verb
    DISPLAY_VERBS = {'like': 'likes', 'listen': 'listened to',
                     'play': 'watched', 'read': 'read', 'give': 'gave'}

    activity = util.trim_nulls(activity)
    content = activity.get('object', {}).get('content')
    actor_name = self.actor_name(activity.get('actor'))
    object = activity.get('object')

    if 'title' not in activity:
      if content:
        activity['title'] = '%s%s%s' % (
          actor_name + ': ' if actor_name else '',
          content[:TITLE_LENGTH],
          '...' if len(content) > TITLE_LENGTH else '')
      elif object:
        app = activity.get('generator', {}).get('displayName')
        obj_name = object.get('displayName')
        obj_type = TYPE_DISPLAY_NAMES.get(object.get('objectType'), 'unknown')
        activity['title'] = '%s %s %s%s.' % (
          actor_name,
          DISPLAY_VERBS.get(activity['verb'], 'posted'),
          obj_name if obj_name else 'a %s' % obj_type,
          ' on %s' % app if app else '')

    return activity
    def post_to_object(self, post):
        """Converts a post to an object.

    Args:
      post: dict, a decoded JSON post

    Returns:
      an ActivityStreams object dict, ready to be JSON-encoded
    """
        object = {}

        id = post.get("id")
        if not id:
            return {}

        post_type = post.get("type")
        status_type = post.get("status_type")
        url = "http://facebook.com/" + id.replace("_", "/posts/")
        picture = post.get("picture")
        message = post.get("message")
        if not message:
            message = post.get("story")
        display_name = None

        data = post.get("data", {})
        for field in ("object", "song"):
            obj = data.get(field)
            if obj:
                id = obj.get("id")
                post_type = obj.get("type")
                url = obj.get("url")
                display_name = obj.get("title")

        object_type = OBJECT_TYPES.get(post_type)
        author = self.user_to_actor(post.get("from"))
        link = post.get("link", "")

        if link.startswith("/gifts/"):
            object_type = "product"
        if not object_type:
            if picture and not message:
                object_type = "image"
            else:
                object_type = "note"

        object = {
            "id": self.tag_uri(str(id)),
            "objectType": object_type,
            "published": util.maybe_iso8601_to_rfc3339(post.get("created_time")),
            "updated": util.maybe_iso8601_to_rfc3339(post.get("updated_time")),
            "author": author,
            "content": message,
            # FB post ids are of the form USERID_POSTID
            "url": url,
            "image": {"url": picture},
            "displayName": display_name,
        }

        # tags
        tags = itertools.chain(
            post.get("to", {}).get("data", []),
            post.get("with_tags", {}).get("data", []),
            *post.get("message_tags", {}).values()
        )
        object["tags"] = [
            {
                "objectType": OBJECT_TYPES.get(t.get("type"), "person"),
                "id": self.tag_uri(t.get("id")),
                "url": "http://facebook.com/%s" % t.get("id"),
                "displayName": t.get("name"),
                "startIndex": t.get("offset"),
                "length": t.get("length"),
            }
            for t in tags
        ]

        # is there an attachment? prefer to represent it as a picture (ie image
        # object), but if not, fall back to a link.
        att = {
            "url": link if link else url,
            "image": {"url": picture},
            "displayName": post.get("name"),
            "summary": post.get("caption"),
            "content": post.get("description"),
        }

        if picture and picture.endswith("_s.jpg") and (post_type == "photo" or status_type == "added_photos"):
            # a picture the user posted. get a larger size.
            att.update({"objectType": "image", "image": {"url": picture[:-6] + "_o.jpg"}})
            object["attachments"] = [att]
        elif link and not link.startswith("/gifts/"):
            att["objectType"] = "article"
            object["attachments"] = [att]

        # location
        place = post.get("place")
        if place:
            id = place.get("id")
            object["location"] = {"displayName": place.get("name"), "id": id, "url": "http://facebook.com/" + id}
            location = place.get("location", None)
            if isinstance(location, dict):
                lat = location.get("latitude")
                lon = location.get("longitude")
                if lat and lon:
                    object["location"].update(
                        {
                            "latitude": lat,
                            "longitude": lon,
                            # ISO 6709 location string. details: http://en.wikipedia.org/wiki/ISO_6709
                            "position": "%+f%+f/" % (lat, lon),
                        }
                    )

        # comments go in the replies field, according to the "Responses for
        # Activity Streams" extension spec:
        # http://activitystrea.ms/specs/json/replies/1.0/
        comments = post.get("comments", {}).get("data")
        if comments:
            items = [self.comment_to_object(c) for c in comments]
            object["replies"] = {"items": items, "totalItems": len(items)}

        return util.trim_nulls(object)
    def media_to_object(self, media):
        """Converts a media to an object.

    Args:
      media: python_instagram.models.Media

    Returns:
      an ActivityStreams object dict, ready to be JSON-encoded
    """
        # TODO: location
        # http://instagram.com/developer/endpoints/locations/
        id = media.id

        object = {
            "id": self.tag_uri(id),
            # TODO: detect videos. (the type field is in the JSON respose but not
            # propagated into the Media object.)
            "objectType": OBJECT_TYPES.get("image", "photo"),
            "published": media.created_time.isoformat("T"),
            "author": self.user_to_actor(media.user),
            "content": media.caption.text if media.caption else None,
            "url": media.link,
            "attachments": [
                {"objectType": "image", "image": {"url": image.url, "width": image.width, "height": image.height}}
                for image in media.images.values()
            ],
            # comments go in the replies field, according to the "Responses for
            # Activity Streams" extension spec:
            # http://activitystrea.ms/specs/json/replies/1.0/
            "replies": {
                "items": [self.comment_to_object(c, id, media.link) for c in media.comments],
                "totalItems": media.comment_count,
            },
            "tags": [
                {
                    "objectType": "hashtag",
                    "id": self.tag_uri(tag.name),
                    "displayName": tag.name,
                    # TODO: url
                }
                for tag in getattr(media, "tags", [])
            ]
            + [
                {
                    "objectType": "person",
                    "id": self.tag_uri(user.user.username),
                    "displayName": user.user.full_name,
                    "url": "http://instagram.com/" + user.user.username,
                    "image": {"url": user.user.profile_picture},
                }
                for user in getattr(media, "users_in_photo", [])
            ],
        }

        for version in ("standard_resolution", "low_resolution", "thumbnail"):
            image = media.images.get(version)
            if image:
                object["image"] = {"url": image.url}
                break

        return util.trim_nulls(object)
  def tweet_to_object(self, tweet):
    """Converts a tweet to an object.

    Args:
      tweet: dict, a decoded JSON tweet

    Returns:
      an ActivityStreams object dict, ready to be JSON-encoded
    """
    object = {}

    id = tweet.get('id')
    if not id:
      return {}

    object = {
      'objectType': 'note',
      'published': self.rfc2822_to_iso8601(tweet.get('created_at')),
      # don't linkify embedded URLs. (they'll all be t.co URLs.) instead, use
      # url entities below to replace them with the real URLs, and then linkify.
      'content': tweet.get('text'),
      'attachments': [],
      }

    user = tweet.get('user')
    if user:
      object['author'] = self.user_to_actor(user)
      username = object['author'].get('username')
      if username:
        object['id'] = self.tag_uri(id)
        object['url'] = self.status_url(username, id)

    entities = tweet.get('entities', {})

    # currently the media list will only have photos. if that changes, though,
    # we'll need to make this conditional on media.type.
    # https://dev.twitter.com/docs/tweet-entities
    media_url = entities.get('media', [{}])[0].get('media_url')
    if media_url:
      object['image'] = {'url': media_url}
      object['attachments'].append({
          'objectType': 'image',
          'image': {'url': media_url},
          })

    # tags
    object['tags'] = [
      {'objectType': 'person',
       'id': self.tag_uri(t.get('screen_name')),
       'url': self.user_url(t.get('screen_name')),
       'displayName': t.get('name'),
       'indices': t.get('indices')
       } for t in entities.get('user_mentions', [])
      ] + [
      {'objectType': 'hashtag',
       'url': 'https://twitter.com/search?q=%23' + t.get('text'),
       'indices': t.get('indices'),
       } for t in entities.get('hashtags', [])
      ] + [
      # TODO: links are both tags and attachments right now. should they be one
      # or the other?
      # file:///home/ryanb/docs/activitystreams_schema_spec_1.0.html#tags-property
      # file:///home/ryanb/docs/activitystreams_json_spec_1.0.html#object
      {'objectType': 'article',
       'url': t.get('expanded_url'),
       # TODO: elide full URL?
       'indices': t.get('indices'),
       } for t in entities.get('urls', [])
      ]
    for t in object['tags']:
      indices = t.get('indices')
      if indices:
        t.update({
            'startIndex': indices[0],
            'length': indices[1] - indices[0],
            })
        del t['indices']

    # location
    place = tweet.get('place')
    if place:
      object['location'] = {
        'displayName': place.get('full_name'),
        'id': place.get('id'),
        }

      # place['url'] is a JSON API url, not useful for end users. get the
      # lat/lon from geo instead.
      geo = tweet.get('geo')
      if geo:
        coords = geo.get('coordinates')
        if coords:
          object['location']['url'] = ('https://maps.google.com/maps?q=%s,%s' %
                                       tuple(coords))

    return util.trim_nulls(object)